##// END OF EJS Templates
osutil: treat open modes 'w' and 'a' as 'w+' and 'a+' in posixfile...
Adrian Buehlmann -
r13273:764441ec default
parent child Browse files
Show More
@@ -1,559 +1,554
1 1 /*
2 2 osutil.c - native operating system services
3 3
4 4 Copyright 2007 Matt Mackall and others
5 5
6 6 This software may be used and distributed according to the terms of
7 7 the GNU General Public License, incorporated herein by reference.
8 8 */
9 9
10 10 #define _ATFILE_SOURCE
11 11 #include <Python.h>
12 12 #include <fcntl.h>
13 13 #include <stdio.h>
14 14 #include <string.h>
15 15
16 16 #ifdef _WIN32
17 17 #include <windows.h>
18 18 #include <io.h>
19 19 #else
20 20 #include <dirent.h>
21 21 #include <sys/stat.h>
22 22 #include <sys/types.h>
23 23 #include <unistd.h>
24 24 #endif
25 25
26 26 #include "util.h"
27 27
28 28 /* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */
29 29 #ifndef PATH_MAX
30 30 #define PATH_MAX 4096
31 31 #endif
32 32
33 33 #ifdef _WIN32
34 34 /*
35 35 stat struct compatible with hg expectations
36 36 Mercurial only uses st_mode, st_size and st_mtime
37 37 the rest is kept to minimize changes between implementations
38 38 */
39 39 struct hg_stat {
40 40 int st_dev;
41 41 int st_mode;
42 42 int st_nlink;
43 43 __int64 st_size;
44 44 int st_mtime;
45 45 int st_ctime;
46 46 };
47 47 struct listdir_stat {
48 48 PyObject_HEAD
49 49 struct hg_stat st;
50 50 };
51 51 #else
52 52 struct listdir_stat {
53 53 PyObject_HEAD
54 54 struct stat st;
55 55 };
56 56 #endif
57 57
58 58 #define listdir_slot(name) \
59 59 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
60 60 { \
61 61 return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
62 62 }
63 63
64 64 listdir_slot(st_dev)
65 65 listdir_slot(st_mode)
66 66 listdir_slot(st_nlink)
67 67 #ifdef _WIN32
68 68 static PyObject *listdir_stat_st_size(PyObject *self, void *x)
69 69 {
70 70 return PyLong_FromLongLong(
71 71 (PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
72 72 }
73 73 #else
74 74 listdir_slot(st_size)
75 75 #endif
76 76 listdir_slot(st_mtime)
77 77 listdir_slot(st_ctime)
78 78
79 79 static struct PyGetSetDef listdir_stat_getsets[] = {
80 80 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
81 81 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
82 82 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
83 83 {"st_size", listdir_stat_st_size, 0, 0, 0},
84 84 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
85 85 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
86 86 {0, 0, 0, 0, 0}
87 87 };
88 88
89 89 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
90 90 {
91 91 return t->tp_alloc(t, 0);
92 92 }
93 93
94 94 static void listdir_stat_dealloc(PyObject *o)
95 95 {
96 96 o->ob_type->tp_free(o);
97 97 }
98 98
99 99 static PyTypeObject listdir_stat_type = {
100 100 PyVarObject_HEAD_INIT(NULL, 0)
101 101 "osutil.stat", /*tp_name*/
102 102 sizeof(struct listdir_stat), /*tp_basicsize*/
103 103 0, /*tp_itemsize*/
104 104 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
105 105 0, /*tp_print*/
106 106 0, /*tp_getattr*/
107 107 0, /*tp_setattr*/
108 108 0, /*tp_compare*/
109 109 0, /*tp_repr*/
110 110 0, /*tp_as_number*/
111 111 0, /*tp_as_sequence*/
112 112 0, /*tp_as_mapping*/
113 113 0, /*tp_hash */
114 114 0, /*tp_call*/
115 115 0, /*tp_str*/
116 116 0, /*tp_getattro*/
117 117 0, /*tp_setattro*/
118 118 0, /*tp_as_buffer*/
119 119 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
120 120 "stat objects", /* tp_doc */
121 121 0, /* tp_traverse */
122 122 0, /* tp_clear */
123 123 0, /* tp_richcompare */
124 124 0, /* tp_weaklistoffset */
125 125 0, /* tp_iter */
126 126 0, /* tp_iternext */
127 127 0, /* tp_methods */
128 128 0, /* tp_members */
129 129 listdir_stat_getsets, /* tp_getset */
130 130 0, /* tp_base */
131 131 0, /* tp_dict */
132 132 0, /* tp_descr_get */
133 133 0, /* tp_descr_set */
134 134 0, /* tp_dictoffset */
135 135 0, /* tp_init */
136 136 0, /* tp_alloc */
137 137 listdir_stat_new, /* tp_new */
138 138 };
139 139
140 140 #ifdef _WIN32
141 141
142 142 static int to_python_time(const FILETIME *tm)
143 143 {
144 144 /* number of seconds between epoch and January 1 1601 */
145 145 const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
146 146 /* conversion factor from 100ns to 1s */
147 147 const __int64 a1 = 10000000;
148 148 /* explicit (int) cast to suspend compiler warnings */
149 149 return (int)((((__int64)tm->dwHighDateTime << 32)
150 150 + tm->dwLowDateTime) / a1 - a0);
151 151 }
152 152
153 153 static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
154 154 {
155 155 PyObject *py_st;
156 156 struct hg_stat *stp;
157 157
158 158 int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
159 159 ? _S_IFDIR : _S_IFREG;
160 160
161 161 if (!wantstat)
162 162 return Py_BuildValue("si", fd->cFileName, kind);
163 163
164 164 py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
165 165 if (!py_st)
166 166 return NULL;
167 167
168 168 stp = &((struct listdir_stat *)py_st)->st;
169 169 /*
170 170 use kind as st_mode
171 171 rwx bits on Win32 are meaningless
172 172 and Hg does not use them anyway
173 173 */
174 174 stp->st_mode = kind;
175 175 stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
176 176 stp->st_ctime = to_python_time(&fd->ftCreationTime);
177 177 if (kind == _S_IFREG)
178 178 stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
179 179 + fd->nFileSizeLow;
180 180 return Py_BuildValue("siN", fd->cFileName,
181 181 kind, py_st);
182 182 }
183 183
184 184 static PyObject *_listdir(char *path, int plen, int wantstat, char *skip)
185 185 {
186 186 PyObject *rval = NULL; /* initialize - return value */
187 187 PyObject *list;
188 188 HANDLE fh;
189 189 WIN32_FIND_DATAA fd;
190 190 char *pattern;
191 191
192 192 /* build the path + \* pattern string */
193 193 pattern = malloc(plen + 3); /* path + \* + \0 */
194 194 if (!pattern) {
195 195 PyErr_NoMemory();
196 196 goto error_nomem;
197 197 }
198 198 strcpy(pattern, path);
199 199
200 200 if (plen > 0) {
201 201 char c = path[plen-1];
202 202 if (c != ':' && c != '/' && c != '\\')
203 203 pattern[plen++] = '\\';
204 204 }
205 205 strcpy(pattern + plen, "*");
206 206
207 207 fh = FindFirstFileA(pattern, &fd);
208 208 if (fh == INVALID_HANDLE_VALUE) {
209 209 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
210 210 goto error_file;
211 211 }
212 212
213 213 list = PyList_New(0);
214 214 if (!list)
215 215 goto error_list;
216 216
217 217 do {
218 218 PyObject *item;
219 219
220 220 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
221 221 if (!strcmp(fd.cFileName, ".")
222 222 || !strcmp(fd.cFileName, ".."))
223 223 continue;
224 224
225 225 if (skip && !strcmp(fd.cFileName, skip)) {
226 226 rval = PyList_New(0);
227 227 goto error;
228 228 }
229 229 }
230 230
231 231 item = make_item(&fd, wantstat);
232 232 if (!item)
233 233 goto error;
234 234
235 235 if (PyList_Append(list, item)) {
236 236 Py_XDECREF(item);
237 237 goto error;
238 238 }
239 239
240 240 Py_XDECREF(item);
241 241 } while (FindNextFileA(fh, &fd));
242 242
243 243 if (GetLastError() != ERROR_NO_MORE_FILES) {
244 244 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
245 245 goto error;
246 246 }
247 247
248 248 rval = list;
249 249 Py_XINCREF(rval);
250 250 error:
251 251 Py_XDECREF(list);
252 252 error_list:
253 253 FindClose(fh);
254 254 error_file:
255 255 free(pattern);
256 256 error_nomem:
257 257 return rval;
258 258 }
259 259
260 260 #else
261 261
262 262 int entkind(struct dirent *ent)
263 263 {
264 264 #ifdef DT_REG
265 265 switch (ent->d_type) {
266 266 case DT_REG: return S_IFREG;
267 267 case DT_DIR: return S_IFDIR;
268 268 case DT_LNK: return S_IFLNK;
269 269 case DT_BLK: return S_IFBLK;
270 270 case DT_CHR: return S_IFCHR;
271 271 case DT_FIFO: return S_IFIFO;
272 272 case DT_SOCK: return S_IFSOCK;
273 273 }
274 274 #endif
275 275 return -1;
276 276 }
277 277
278 278 static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
279 279 {
280 280 PyObject *list, *elem, *stat, *ret = NULL;
281 281 char fullpath[PATH_MAX + 10];
282 282 int kind, err;
283 283 struct stat st;
284 284 struct dirent *ent;
285 285 DIR *dir;
286 286 #ifdef AT_SYMLINK_NOFOLLOW
287 287 int dfd = -1;
288 288 #endif
289 289
290 290 if (pathlen >= PATH_MAX) {
291 291 PyErr_SetString(PyExc_ValueError, "path too long");
292 292 goto error_value;
293 293 }
294 294 strncpy(fullpath, path, PATH_MAX);
295 295 fullpath[pathlen] = '/';
296 296
297 297 #ifdef AT_SYMLINK_NOFOLLOW
298 298 dfd = open(path, O_RDONLY);
299 299 if (dfd == -1) {
300 300 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
301 301 goto error_value;
302 302 }
303 303 dir = fdopendir(dfd);
304 304 #else
305 305 dir = opendir(path);
306 306 #endif
307 307 if (!dir) {
308 308 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
309 309 goto error_dir;
310 310 }
311 311
312 312 list = PyList_New(0);
313 313 if (!list)
314 314 goto error_list;
315 315
316 316 while ((ent = readdir(dir))) {
317 317 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
318 318 continue;
319 319
320 320 kind = entkind(ent);
321 321 if (kind == -1 || keepstat) {
322 322 #ifdef AT_SYMLINK_NOFOLLOW
323 323 err = fstatat(dfd, ent->d_name, &st,
324 324 AT_SYMLINK_NOFOLLOW);
325 325 #else
326 326 strncpy(fullpath + pathlen + 1, ent->d_name,
327 327 PATH_MAX - pathlen);
328 328 fullpath[PATH_MAX] = 0;
329 329 err = lstat(fullpath, &st);
330 330 #endif
331 331 if (err == -1) {
332 332 strncpy(fullpath + pathlen + 1, ent->d_name,
333 333 PATH_MAX - pathlen);
334 334 fullpath[PATH_MAX] = 0;
335 335 PyErr_SetFromErrnoWithFilename(PyExc_OSError,
336 336 fullpath);
337 337 goto error;
338 338 }
339 339 kind = st.st_mode & S_IFMT;
340 340 }
341 341
342 342 /* quit early? */
343 343 if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
344 344 ret = PyList_New(0);
345 345 goto error;
346 346 }
347 347
348 348 if (keepstat) {
349 349 stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
350 350 if (!stat)
351 351 goto error;
352 352 memcpy(&((struct listdir_stat *)stat)->st, &st, sizeof(st));
353 353 elem = Py_BuildValue("siN", ent->d_name, kind, stat);
354 354 } else
355 355 elem = Py_BuildValue("si", ent->d_name, kind);
356 356 if (!elem)
357 357 goto error;
358 358
359 359 PyList_Append(list, elem);
360 360 Py_DECREF(elem);
361 361 }
362 362
363 363 ret = list;
364 364 Py_INCREF(ret);
365 365
366 366 error:
367 367 Py_DECREF(list);
368 368 error_list:
369 369 closedir(dir);
370 370 error_dir:
371 371 #ifdef AT_SYMLINK_NOFOLLOW
372 372 close(dfd);
373 373 #endif
374 374 error_value:
375 375 return ret;
376 376 }
377 377
378 378 #endif /* ndef _WIN32 */
379 379
380 380 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
381 381 {
382 382 PyObject *statobj = NULL; /* initialize - optional arg */
383 383 PyObject *skipobj = NULL; /* initialize - optional arg */
384 384 char *path, *skip = NULL;
385 385 int wantstat, plen;
386 386
387 387 static char *kwlist[] = {"path", "stat", "skip", NULL};
388 388
389 389 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|OO:listdir",
390 390 kwlist, &path, &plen, &statobj, &skipobj))
391 391 return NULL;
392 392
393 393 wantstat = statobj && PyObject_IsTrue(statobj);
394 394
395 395 if (skipobj && skipobj != Py_None) {
396 396 skip = PyBytes_AsString(skipobj);
397 397 if (!skip)
398 398 return NULL;
399 399 }
400 400
401 401 return _listdir(path, plen, wantstat, skip);
402 402 }
403 403
404 404 #ifdef _WIN32
405 405 static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
406 406 {
407 407 static char *kwlist[] = {"name", "mode", "buffering", NULL};
408 408 PyObject *file_obj = NULL;
409 409 char *name = NULL;
410 410 char *mode = "rb";
411 411 DWORD access = 0;
412 412 DWORD creation;
413 413 HANDLE handle;
414 414 int fd, flags = 0;
415 415 int bufsize = -1;
416 416 char m0, m1, m2;
417 417 char fpmode[4];
418 418 int fppos = 0;
419 419 int plus;
420 420 FILE *fp;
421 421
422 422 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist,
423 423 Py_FileSystemDefaultEncoding,
424 424 &name, &mode, &bufsize))
425 425 return NULL;
426 426
427 427 m0 = mode[0];
428 428 m1 = m0 ? mode[1] : '\0';
429 429 m2 = m1 ? mode[2] : '\0';
430 430 plus = m1 == '+' || m2 == '+';
431 431
432 432 fpmode[fppos++] = m0;
433 433 if (m1 == 'b' || m2 == 'b') {
434 434 flags = _O_BINARY;
435 435 fpmode[fppos++] = 'b';
436 436 }
437 437 else
438 438 flags = _O_TEXT;
439 if (plus) {
439 if (m0 == 'r' && !plus) {
440 flags |= _O_RDONLY;
441 access = GENERIC_READ;
442 } else {
443 /*
444 work around http://support.microsoft.com/kb/899149 and
445 set _O_RDWR for 'w' and 'a', even if mode has no '+'
446 */
440 447 flags |= _O_RDWR;
441 448 access = GENERIC_READ | GENERIC_WRITE;
442 449 fpmode[fppos++] = '+';
443 450 }
444 451 fpmode[fppos++] = '\0';
445 452
446 453 switch (m0) {
447 454 case 'r':
448 455 creation = OPEN_EXISTING;
449 if (!plus) {
450 flags |= _O_RDONLY;
451 access = GENERIC_READ;
452 }
453 456 break;
454 457 case 'w':
455 458 creation = CREATE_ALWAYS;
456 if (!plus) {
457 access = GENERIC_WRITE;
458 flags |= _O_WRONLY;
459 }
460 459 break;
461 460 case 'a':
462 461 creation = OPEN_ALWAYS;
463 462 flags |= _O_APPEND;
464 if (!plus) {
465 flags |= _O_WRONLY;
466 access = GENERIC_WRITE;
467 }
468 463 break;
469 464 default:
470 465 PyErr_Format(PyExc_ValueError,
471 466 "mode string must begin with one of 'r', 'w', "
472 467 "or 'a', not '%c'", m0);
473 468 goto bail;
474 469 }
475 470
476 471 handle = CreateFile(name, access,
477 472 FILE_SHARE_READ | FILE_SHARE_WRITE |
478 473 FILE_SHARE_DELETE,
479 474 NULL,
480 475 creation,
481 476 FILE_ATTRIBUTE_NORMAL,
482 477 0);
483 478
484 479 if (handle == INVALID_HANDLE_VALUE) {
485 480 PyErr_SetFromWindowsErrWithFilename(GetLastError(), name);
486 481 goto bail;
487 482 }
488 483
489 484 fd = _open_osfhandle((intptr_t)handle, flags);
490 485
491 486 if (fd == -1) {
492 487 CloseHandle(handle);
493 488 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
494 489 goto bail;
495 490 }
496 491 #ifndef IS_PY3K
497 492 fp = _fdopen(fd, fpmode);
498 493 if (fp == NULL) {
499 494 _close(fd);
500 495 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
501 496 goto bail;
502 497 }
503 498
504 499 file_obj = PyFile_FromFile(fp, name, mode, fclose);
505 500 if (file_obj == NULL) {
506 501 fclose(fp);
507 502 goto bail;
508 503 }
509 504
510 505 PyFile_SetBufSize(file_obj, bufsize);
511 506 #else
512 507 file_obj = PyFile_FromFd(fd, name, mode, bufsize, NULL, NULL, NULL, 1);
513 508 if (file_obj == NULL)
514 509 goto bail;
515 510 #endif
516 511 bail:
517 512 PyMem_Free(name);
518 513 return file_obj;
519 514 }
520 515 #endif
521 516
522 517 static char osutil_doc[] = "Native operating system services.";
523 518
524 519 static PyMethodDef methods[] = {
525 520 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
526 521 "list a directory\n"},
527 522 #ifdef _WIN32
528 523 {"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
529 524 "Open a file with POSIX-like semantics.\n"
530 525 "On error, this function may raise either a WindowsError or an IOError."},
531 526 #endif
532 527 {NULL, NULL}
533 528 };
534 529
535 530 #ifdef IS_PY3K
536 531 static struct PyModuleDef osutil_module = {
537 532 PyModuleDef_HEAD_INIT,
538 533 "osutil",
539 534 osutil_doc,
540 535 -1,
541 536 methods
542 537 };
543 538
544 539 PyMODINIT_FUNC PyInit_osutil(void)
545 540 {
546 541 if (PyType_Ready(&listdir_stat_type) < 0)
547 542 return NULL;
548 543
549 544 return PyModule_Create(&osutil_module);
550 545 }
551 546 #else
552 547 PyMODINIT_FUNC initosutil(void)
553 548 {
554 549 if (PyType_Ready(&listdir_stat_type) == -1)
555 550 return;
556 551
557 552 Py_InitModule3("osutil", methods, osutil_doc);
558 553 }
559 554 #endif
General Comments 0
You need to be logged in to leave comments. Login now