##// END OF EJS Templates
osutil: fix potential wrong fd close...
Jun Wu -
r31471:95be8b71 default
parent child Browse files
Show More
@@ -1,997 +1,999 b''
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 <stdlib.h>
15 15 #include <string.h>
16 16 #include <errno.h>
17 17
18 18 #ifdef _WIN32
19 19 #include <windows.h>
20 20 #include <io.h>
21 21 #else
22 22 #include <dirent.h>
23 23 #include <sys/socket.h>
24 24 #include <sys/stat.h>
25 25 #include <sys/types.h>
26 26 #include <unistd.h>
27 27 #endif
28 28
29 29 #ifdef __APPLE__
30 30 #include <sys/attr.h>
31 31 #include <sys/vnode.h>
32 32 #endif
33 33
34 34 #include "util.h"
35 35
36 36 /* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */
37 37 #ifndef PATH_MAX
38 38 #define PATH_MAX 4096
39 39 #endif
40 40
41 41 #ifdef _WIN32
42 42 /*
43 43 stat struct compatible with hg expectations
44 44 Mercurial only uses st_mode, st_size and st_mtime
45 45 the rest is kept to minimize changes between implementations
46 46 */
47 47 struct hg_stat {
48 48 int st_dev;
49 49 int st_mode;
50 50 int st_nlink;
51 51 __int64 st_size;
52 52 int st_mtime;
53 53 int st_ctime;
54 54 };
55 55 struct listdir_stat {
56 56 PyObject_HEAD
57 57 struct hg_stat st;
58 58 };
59 59 #else
60 60 struct listdir_stat {
61 61 PyObject_HEAD
62 62 struct stat st;
63 63 };
64 64 #endif
65 65
66 66 #ifdef IS_PY3K
67 67 #define listdir_slot(name) \
68 68 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
69 69 { \
70 70 return PyLong_FromLong(((struct listdir_stat *)self)->st.name); \
71 71 }
72 72 #else
73 73 #define listdir_slot(name) \
74 74 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
75 75 { \
76 76 return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
77 77 }
78 78 #endif
79 79
80 80 listdir_slot(st_dev)
81 81 listdir_slot(st_mode)
82 82 listdir_slot(st_nlink)
83 83 #ifdef _WIN32
84 84 static PyObject *listdir_stat_st_size(PyObject *self, void *x)
85 85 {
86 86 return PyLong_FromLongLong(
87 87 (PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
88 88 }
89 89 #else
90 90 listdir_slot(st_size)
91 91 #endif
92 92 listdir_slot(st_mtime)
93 93 listdir_slot(st_ctime)
94 94
95 95 static struct PyGetSetDef listdir_stat_getsets[] = {
96 96 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
97 97 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
98 98 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
99 99 {"st_size", listdir_stat_st_size, 0, 0, 0},
100 100 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
101 101 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
102 102 {0, 0, 0, 0, 0}
103 103 };
104 104
105 105 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
106 106 {
107 107 return t->tp_alloc(t, 0);
108 108 }
109 109
110 110 static void listdir_stat_dealloc(PyObject *o)
111 111 {
112 112 o->ob_type->tp_free(o);
113 113 }
114 114
115 115 static PyTypeObject listdir_stat_type = {
116 116 PyVarObject_HEAD_INIT(NULL, 0)
117 117 "osutil.stat", /*tp_name*/
118 118 sizeof(struct listdir_stat), /*tp_basicsize*/
119 119 0, /*tp_itemsize*/
120 120 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
121 121 0, /*tp_print*/
122 122 0, /*tp_getattr*/
123 123 0, /*tp_setattr*/
124 124 0, /*tp_compare*/
125 125 0, /*tp_repr*/
126 126 0, /*tp_as_number*/
127 127 0, /*tp_as_sequence*/
128 128 0, /*tp_as_mapping*/
129 129 0, /*tp_hash */
130 130 0, /*tp_call*/
131 131 0, /*tp_str*/
132 132 0, /*tp_getattro*/
133 133 0, /*tp_setattro*/
134 134 0, /*tp_as_buffer*/
135 135 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
136 136 "stat objects", /* tp_doc */
137 137 0, /* tp_traverse */
138 138 0, /* tp_clear */
139 139 0, /* tp_richcompare */
140 140 0, /* tp_weaklistoffset */
141 141 0, /* tp_iter */
142 142 0, /* tp_iternext */
143 143 0, /* tp_methods */
144 144 0, /* tp_members */
145 145 listdir_stat_getsets, /* tp_getset */
146 146 0, /* tp_base */
147 147 0, /* tp_dict */
148 148 0, /* tp_descr_get */
149 149 0, /* tp_descr_set */
150 150 0, /* tp_dictoffset */
151 151 0, /* tp_init */
152 152 0, /* tp_alloc */
153 153 listdir_stat_new, /* tp_new */
154 154 };
155 155
156 156 #ifdef _WIN32
157 157
158 158 static int to_python_time(const FILETIME *tm)
159 159 {
160 160 /* number of seconds between epoch and January 1 1601 */
161 161 const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
162 162 /* conversion factor from 100ns to 1s */
163 163 const __int64 a1 = 10000000;
164 164 /* explicit (int) cast to suspend compiler warnings */
165 165 return (int)((((__int64)tm->dwHighDateTime << 32)
166 166 + tm->dwLowDateTime) / a1 - a0);
167 167 }
168 168
169 169 static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
170 170 {
171 171 PyObject *py_st;
172 172 struct hg_stat *stp;
173 173
174 174 int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
175 175 ? _S_IFDIR : _S_IFREG;
176 176
177 177 if (!wantstat)
178 178 return Py_BuildValue("si", fd->cFileName, kind);
179 179
180 180 py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
181 181 if (!py_st)
182 182 return NULL;
183 183
184 184 stp = &((struct listdir_stat *)py_st)->st;
185 185 /*
186 186 use kind as st_mode
187 187 rwx bits on Win32 are meaningless
188 188 and Hg does not use them anyway
189 189 */
190 190 stp->st_mode = kind;
191 191 stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
192 192 stp->st_ctime = to_python_time(&fd->ftCreationTime);
193 193 if (kind == _S_IFREG)
194 194 stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
195 195 + fd->nFileSizeLow;
196 196 return Py_BuildValue("siN", fd->cFileName,
197 197 kind, py_st);
198 198 }
199 199
200 200 static PyObject *_listdir(char *path, int plen, int wantstat, char *skip)
201 201 {
202 202 PyObject *rval = NULL; /* initialize - return value */
203 203 PyObject *list;
204 204 HANDLE fh;
205 205 WIN32_FIND_DATAA fd;
206 206 char *pattern;
207 207
208 208 /* build the path + \* pattern string */
209 209 pattern = PyMem_Malloc(plen + 3); /* path + \* + \0 */
210 210 if (!pattern) {
211 211 PyErr_NoMemory();
212 212 goto error_nomem;
213 213 }
214 214 memcpy(pattern, path, plen);
215 215
216 216 if (plen > 0) {
217 217 char c = path[plen-1];
218 218 if (c != ':' && c != '/' && c != '\\')
219 219 pattern[plen++] = '\\';
220 220 }
221 221 pattern[plen++] = '*';
222 222 pattern[plen] = '\0';
223 223
224 224 fh = FindFirstFileA(pattern, &fd);
225 225 if (fh == INVALID_HANDLE_VALUE) {
226 226 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
227 227 goto error_file;
228 228 }
229 229
230 230 list = PyList_New(0);
231 231 if (!list)
232 232 goto error_list;
233 233
234 234 do {
235 235 PyObject *item;
236 236
237 237 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
238 238 if (!strcmp(fd.cFileName, ".")
239 239 || !strcmp(fd.cFileName, ".."))
240 240 continue;
241 241
242 242 if (skip && !strcmp(fd.cFileName, skip)) {
243 243 rval = PyList_New(0);
244 244 goto error;
245 245 }
246 246 }
247 247
248 248 item = make_item(&fd, wantstat);
249 249 if (!item)
250 250 goto error;
251 251
252 252 if (PyList_Append(list, item)) {
253 253 Py_XDECREF(item);
254 254 goto error;
255 255 }
256 256
257 257 Py_XDECREF(item);
258 258 } while (FindNextFileA(fh, &fd));
259 259
260 260 if (GetLastError() != ERROR_NO_MORE_FILES) {
261 261 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
262 262 goto error;
263 263 }
264 264
265 265 rval = list;
266 266 Py_XINCREF(rval);
267 267 error:
268 268 Py_XDECREF(list);
269 269 error_list:
270 270 FindClose(fh);
271 271 error_file:
272 272 PyMem_Free(pattern);
273 273 error_nomem:
274 274 return rval;
275 275 }
276 276
277 277 #else
278 278
279 279 int entkind(struct dirent *ent)
280 280 {
281 281 #ifdef DT_REG
282 282 switch (ent->d_type) {
283 283 case DT_REG: return S_IFREG;
284 284 case DT_DIR: return S_IFDIR;
285 285 case DT_LNK: return S_IFLNK;
286 286 case DT_BLK: return S_IFBLK;
287 287 case DT_CHR: return S_IFCHR;
288 288 case DT_FIFO: return S_IFIFO;
289 289 case DT_SOCK: return S_IFSOCK;
290 290 }
291 291 #endif
292 292 return -1;
293 293 }
294 294
295 295 static PyObject *makestat(const struct stat *st)
296 296 {
297 297 PyObject *stat;
298 298
299 299 stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
300 300 if (stat)
301 301 memcpy(&((struct listdir_stat *)stat)->st, st, sizeof(*st));
302 302 return stat;
303 303 }
304 304
305 305 static PyObject *_listdir_stat(char *path, int pathlen, int keepstat,
306 306 char *skip)
307 307 {
308 308 PyObject *list, *elem, *stat = NULL, *ret = NULL;
309 309 char fullpath[PATH_MAX + 10];
310 310 int kind, err;
311 311 struct stat st;
312 312 struct dirent *ent;
313 313 DIR *dir;
314 314 #ifdef AT_SYMLINK_NOFOLLOW
315 315 int dfd = -1;
316 316 #endif
317 317
318 318 if (pathlen >= PATH_MAX) {
319 319 errno = ENAMETOOLONG;
320 320 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
321 321 goto error_value;
322 322 }
323 323 strncpy(fullpath, path, PATH_MAX);
324 324 fullpath[pathlen] = '/';
325 325
326 326 #ifdef AT_SYMLINK_NOFOLLOW
327 327 dfd = open(path, O_RDONLY);
328 328 if (dfd == -1) {
329 329 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
330 330 goto error_value;
331 331 }
332 332 dir = fdopendir(dfd);
333 333 #else
334 334 dir = opendir(path);
335 335 #endif
336 336 if (!dir) {
337 337 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
338 338 goto error_dir;
339 339 }
340 340
341 341 list = PyList_New(0);
342 342 if (!list)
343 343 goto error_list;
344 344
345 345 while ((ent = readdir(dir))) {
346 346 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
347 347 continue;
348 348
349 349 kind = entkind(ent);
350 350 if (kind == -1 || keepstat) {
351 351 #ifdef AT_SYMLINK_NOFOLLOW
352 352 err = fstatat(dfd, ent->d_name, &st,
353 353 AT_SYMLINK_NOFOLLOW);
354 354 #else
355 355 strncpy(fullpath + pathlen + 1, ent->d_name,
356 356 PATH_MAX - pathlen);
357 357 fullpath[PATH_MAX] = '\0';
358 358 err = lstat(fullpath, &st);
359 359 #endif
360 360 if (err == -1) {
361 361 /* race with file deletion? */
362 362 if (errno == ENOENT)
363 363 continue;
364 364 strncpy(fullpath + pathlen + 1, ent->d_name,
365 365 PATH_MAX - pathlen);
366 366 fullpath[PATH_MAX] = 0;
367 367 PyErr_SetFromErrnoWithFilename(PyExc_OSError,
368 368 fullpath);
369 369 goto error;
370 370 }
371 371 kind = st.st_mode & S_IFMT;
372 372 }
373 373
374 374 /* quit early? */
375 375 if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
376 376 ret = PyList_New(0);
377 377 goto error;
378 378 }
379 379
380 380 if (keepstat) {
381 381 stat = makestat(&st);
382 382 if (!stat)
383 383 goto error;
384 384 elem = Py_BuildValue("siN", ent->d_name, kind, stat);
385 385 } else
386 386 elem = Py_BuildValue("si", ent->d_name, kind);
387 387 if (!elem)
388 388 goto error;
389 389 stat = NULL;
390 390
391 391 PyList_Append(list, elem);
392 392 Py_DECREF(elem);
393 393 }
394 394
395 395 ret = list;
396 396 Py_INCREF(ret);
397 397
398 398 error:
399 399 Py_DECREF(list);
400 400 Py_XDECREF(stat);
401 401 error_list:
402 402 closedir(dir);
403 /* closedir also closes its dirfd */
404 goto error_value;
403 405 error_dir:
404 406 #ifdef AT_SYMLINK_NOFOLLOW
405 407 close(dfd);
406 408 #endif
407 409 error_value:
408 410 return ret;
409 411 }
410 412
411 413 #ifdef __APPLE__
412 414
413 415 typedef struct {
414 416 u_int32_t length;
415 417 attrreference_t name;
416 418 fsobj_type_t obj_type;
417 419 struct timespec mtime;
418 420 #if __LITTLE_ENDIAN__
419 421 mode_t access_mask;
420 422 uint16_t padding;
421 423 #else
422 424 uint16_t padding;
423 425 mode_t access_mask;
424 426 #endif
425 427 off_t size;
426 428 } __attribute__((packed)) attrbuf_entry;
427 429
428 430 int attrkind(attrbuf_entry *entry)
429 431 {
430 432 switch (entry->obj_type) {
431 433 case VREG: return S_IFREG;
432 434 case VDIR: return S_IFDIR;
433 435 case VLNK: return S_IFLNK;
434 436 case VBLK: return S_IFBLK;
435 437 case VCHR: return S_IFCHR;
436 438 case VFIFO: return S_IFIFO;
437 439 case VSOCK: return S_IFSOCK;
438 440 }
439 441 return -1;
440 442 }
441 443
442 444 /* get these many entries at a time */
443 445 #define LISTDIR_BATCH_SIZE 50
444 446
445 447 static PyObject *_listdir_batch(char *path, int pathlen, int keepstat,
446 448 char *skip, bool *fallback)
447 449 {
448 450 PyObject *list, *elem, *stat = NULL, *ret = NULL;
449 451 int kind, err;
450 452 unsigned long index;
451 453 unsigned int count, old_state, new_state;
452 454 bool state_seen = false;
453 455 attrbuf_entry *entry;
454 456 /* from the getattrlist(2) man page: a path can be no longer than
455 457 (NAME_MAX * 3 + 1) bytes. Also, "The getattrlist() function will
456 458 silently truncate attribute data if attrBufSize is too small." So
457 459 pass in a buffer big enough for the worst case. */
458 460 char attrbuf[LISTDIR_BATCH_SIZE * (sizeof(attrbuf_entry) + NAME_MAX * 3 + 1)];
459 461 unsigned int basep_unused;
460 462
461 463 struct stat st;
462 464 int dfd = -1;
463 465
464 466 /* these must match the attrbuf_entry struct, otherwise you'll end up
465 467 with garbage */
466 468 struct attrlist requested_attr = {0};
467 469 requested_attr.bitmapcount = ATTR_BIT_MAP_COUNT;
468 470 requested_attr.commonattr = (ATTR_CMN_NAME | ATTR_CMN_OBJTYPE |
469 471 ATTR_CMN_MODTIME | ATTR_CMN_ACCESSMASK);
470 472 requested_attr.fileattr = ATTR_FILE_DATALENGTH;
471 473
472 474 *fallback = false;
473 475
474 476 if (pathlen >= PATH_MAX) {
475 477 errno = ENAMETOOLONG;
476 478 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
477 479 goto error_value;
478 480 }
479 481
480 482 dfd = open(path, O_RDONLY);
481 483 if (dfd == -1) {
482 484 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
483 485 goto error_value;
484 486 }
485 487
486 488 list = PyList_New(0);
487 489 if (!list)
488 490 goto error_dir;
489 491
490 492 do {
491 493 count = LISTDIR_BATCH_SIZE;
492 494 err = getdirentriesattr(dfd, &requested_attr, &attrbuf,
493 495 sizeof(attrbuf), &count, &basep_unused,
494 496 &new_state, 0);
495 497 if (err < 0) {
496 498 if (errno == ENOTSUP) {
497 499 /* We're on a filesystem that doesn't support
498 500 getdirentriesattr. Fall back to the
499 501 stat-based implementation. */
500 502 *fallback = true;
501 503 } else
502 504 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
503 505 goto error;
504 506 }
505 507
506 508 if (!state_seen) {
507 509 old_state = new_state;
508 510 state_seen = true;
509 511 } else if (old_state != new_state) {
510 512 /* There's an edge case with getdirentriesattr. Consider
511 513 the following initial list of files:
512 514
513 515 a
514 516 b
515 517 <--
516 518 c
517 519 d
518 520
519 521 If the iteration is paused at the arrow, and b is
520 522 deleted before it is resumed, getdirentriesattr will
521 523 not return d at all! Ordinarily we're expected to
522 524 restart the iteration from the beginning. To avoid
523 525 getting stuck in a retry loop here, fall back to
524 526 stat. */
525 527 *fallback = true;
526 528 goto error;
527 529 }
528 530
529 531 entry = (attrbuf_entry *)attrbuf;
530 532
531 533 for (index = 0; index < count; index++) {
532 534 char *filename = ((char *)&entry->name) +
533 535 entry->name.attr_dataoffset;
534 536
535 537 if (!strcmp(filename, ".") || !strcmp(filename, ".."))
536 538 continue;
537 539
538 540 kind = attrkind(entry);
539 541 if (kind == -1) {
540 542 PyErr_Format(PyExc_OSError,
541 543 "unknown object type %u for file "
542 544 "%s%s!",
543 545 entry->obj_type, path, filename);
544 546 goto error;
545 547 }
546 548
547 549 /* quit early? */
548 550 if (skip && kind == S_IFDIR && !strcmp(filename, skip)) {
549 551 ret = PyList_New(0);
550 552 goto error;
551 553 }
552 554
553 555 if (keepstat) {
554 556 /* from the getattrlist(2) man page: "Only the
555 557 permission bits ... are valid". */
556 558 st.st_mode = (entry->access_mask & ~S_IFMT) | kind;
557 559 st.st_mtime = entry->mtime.tv_sec;
558 560 st.st_size = entry->size;
559 561 stat = makestat(&st);
560 562 if (!stat)
561 563 goto error;
562 564 elem = Py_BuildValue("siN", filename, kind, stat);
563 565 } else
564 566 elem = Py_BuildValue("si", filename, kind);
565 567 if (!elem)
566 568 goto error;
567 569 stat = NULL;
568 570
569 571 PyList_Append(list, elem);
570 572 Py_DECREF(elem);
571 573
572 574 entry = (attrbuf_entry *)((char *)entry + entry->length);
573 575 }
574 576 } while (err == 0);
575 577
576 578 ret = list;
577 579 Py_INCREF(ret);
578 580
579 581 error:
580 582 Py_DECREF(list);
581 583 Py_XDECREF(stat);
582 584 error_dir:
583 585 close(dfd);
584 586 error_value:
585 587 return ret;
586 588 }
587 589
588 590 #endif /* __APPLE__ */
589 591
590 592 static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
591 593 {
592 594 #ifdef __APPLE__
593 595 PyObject *ret;
594 596 bool fallback = false;
595 597
596 598 ret = _listdir_batch(path, pathlen, keepstat, skip, &fallback);
597 599 if (ret != NULL || !fallback)
598 600 return ret;
599 601 #endif
600 602 return _listdir_stat(path, pathlen, keepstat, skip);
601 603 }
602 604
603 605 static PyObject *statfiles(PyObject *self, PyObject *args)
604 606 {
605 607 PyObject *names, *stats;
606 608 Py_ssize_t i, count;
607 609
608 610 if (!PyArg_ParseTuple(args, "O:statfiles", &names))
609 611 return NULL;
610 612
611 613 count = PySequence_Length(names);
612 614 if (count == -1) {
613 615 PyErr_SetString(PyExc_TypeError, "not a sequence");
614 616 return NULL;
615 617 }
616 618
617 619 stats = PyList_New(count);
618 620 if (stats == NULL)
619 621 return NULL;
620 622
621 623 for (i = 0; i < count; i++) {
622 624 PyObject *stat, *pypath;
623 625 struct stat st;
624 626 int ret, kind;
625 627 char *path;
626 628
627 629 /* With a large file count or on a slow filesystem,
628 630 don't block signals for long (issue4878). */
629 631 if ((i % 1000) == 999 && PyErr_CheckSignals() == -1)
630 632 goto bail;
631 633
632 634 pypath = PySequence_GetItem(names, i);
633 635 if (!pypath)
634 636 goto bail;
635 637 path = PyBytes_AsString(pypath);
636 638 if (path == NULL) {
637 639 Py_DECREF(pypath);
638 640 PyErr_SetString(PyExc_TypeError, "not a string");
639 641 goto bail;
640 642 }
641 643 ret = lstat(path, &st);
642 644 Py_DECREF(pypath);
643 645 kind = st.st_mode & S_IFMT;
644 646 if (ret != -1 && (kind == S_IFREG || kind == S_IFLNK)) {
645 647 stat = makestat(&st);
646 648 if (stat == NULL)
647 649 goto bail;
648 650 PyList_SET_ITEM(stats, i, stat);
649 651 } else {
650 652 Py_INCREF(Py_None);
651 653 PyList_SET_ITEM(stats, i, Py_None);
652 654 }
653 655 }
654 656
655 657 return stats;
656 658
657 659 bail:
658 660 Py_DECREF(stats);
659 661 return NULL;
660 662 }
661 663
662 664 /*
663 665 * recvfds() simply does not release GIL during blocking io operation because
664 666 * command server is known to be single-threaded.
665 667 *
666 668 * Old systems such as Solaris don't provide CMSG_LEN, msg_control, etc.
667 669 * Currently, recvfds() is not supported on these platforms.
668 670 */
669 671 #ifdef CMSG_LEN
670 672
671 673 static ssize_t recvfdstobuf(int sockfd, int **rfds, void *cbuf, size_t cbufsize)
672 674 {
673 675 char dummy[1];
674 676 struct iovec iov = {dummy, sizeof(dummy)};
675 677 struct msghdr msgh = {0};
676 678 struct cmsghdr *cmsg;
677 679
678 680 msgh.msg_iov = &iov;
679 681 msgh.msg_iovlen = 1;
680 682 msgh.msg_control = cbuf;
681 683 msgh.msg_controllen = (socklen_t)cbufsize;
682 684 if (recvmsg(sockfd, &msgh, 0) < 0)
683 685 return -1;
684 686
685 687 for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg;
686 688 cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
687 689 if (cmsg->cmsg_level != SOL_SOCKET ||
688 690 cmsg->cmsg_type != SCM_RIGHTS)
689 691 continue;
690 692 *rfds = (int *)CMSG_DATA(cmsg);
691 693 return (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
692 694 }
693 695
694 696 *rfds = cbuf;
695 697 return 0;
696 698 }
697 699
698 700 static PyObject *recvfds(PyObject *self, PyObject *args)
699 701 {
700 702 int sockfd;
701 703 int *rfds = NULL;
702 704 ssize_t rfdscount, i;
703 705 char cbuf[256];
704 706 PyObject *rfdslist = NULL;
705 707
706 708 if (!PyArg_ParseTuple(args, "i", &sockfd))
707 709 return NULL;
708 710
709 711 rfdscount = recvfdstobuf(sockfd, &rfds, cbuf, sizeof(cbuf));
710 712 if (rfdscount < 0)
711 713 return PyErr_SetFromErrno(PyExc_OSError);
712 714
713 715 rfdslist = PyList_New(rfdscount);
714 716 if (!rfdslist)
715 717 goto bail;
716 718 for (i = 0; i < rfdscount; i++) {
717 719 PyObject *obj = PyLong_FromLong(rfds[i]);
718 720 if (!obj)
719 721 goto bail;
720 722 PyList_SET_ITEM(rfdslist, i, obj);
721 723 }
722 724 return rfdslist;
723 725
724 726 bail:
725 727 Py_XDECREF(rfdslist);
726 728 return NULL;
727 729 }
728 730
729 731 #endif /* CMSG_LEN */
730 732
731 733 #if defined(HAVE_SETPROCTITLE)
732 734 /* setproctitle is the first choice - available in FreeBSD */
733 735 #define SETPROCNAME_USE_SETPROCTITLE
734 736 #elif (defined(__linux__) || defined(__APPLE__)) && PY_MAJOR_VERSION == 2
735 737 /* rewrite the argv buffer in place - works in Linux and OS X. Py_GetArgcArgv
736 738 * in Python 3 returns the copied wchar_t **argv, thus unsupported. */
737 739 #define SETPROCNAME_USE_ARGVREWRITE
738 740 #else
739 741 #define SETPROCNAME_USE_NONE
740 742 #endif
741 743
742 744 #ifndef SETPROCNAME_USE_NONE
743 745 static PyObject *setprocname(PyObject *self, PyObject *args)
744 746 {
745 747 const char *name = NULL;
746 748 if (!PyArg_ParseTuple(args, "s", &name))
747 749 return NULL;
748 750
749 751 #if defined(SETPROCNAME_USE_SETPROCTITLE)
750 752 setproctitle("%s", name);
751 753 #elif defined(SETPROCNAME_USE_ARGVREWRITE)
752 754 {
753 755 static char *argvstart = NULL;
754 756 static size_t argvsize = 0;
755 757 if (argvstart == NULL) {
756 758 int argc = 0, i;
757 759 char **argv = NULL;
758 760 char *argvend;
759 761 extern void Py_GetArgcArgv(int *argc, char ***argv);
760 762 Py_GetArgcArgv(&argc, &argv);
761 763
762 764 /* Check the memory we can use. Typically, argv[i] and
763 765 * argv[i + 1] are continuous. */
764 766 argvend = argvstart = argv[0];
765 767 for (i = 0; i < argc; ++i) {
766 768 if (argv[i] > argvend || argv[i] < argvstart)
767 769 break; /* not continuous */
768 770 size_t len = strlen(argv[i]);
769 771 argvend = argv[i] + len + 1 /* '\0' */;
770 772 }
771 773 if (argvend > argvstart) /* sanity check */
772 774 argvsize = argvend - argvstart;
773 775 }
774 776
775 777 if (argvstart && argvsize > 1) {
776 778 int n = snprintf(argvstart, argvsize, "%s", name);
777 779 if (n >= 0 && (size_t)n < argvsize)
778 780 memset(argvstart + n, 0, argvsize - n);
779 781 }
780 782 }
781 783 #endif
782 784
783 785 Py_RETURN_NONE;
784 786 }
785 787 #endif /* ndef SETPROCNAME_USE_NONE */
786 788
787 789 #endif /* ndef _WIN32 */
788 790
789 791 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
790 792 {
791 793 PyObject *statobj = NULL; /* initialize - optional arg */
792 794 PyObject *skipobj = NULL; /* initialize - optional arg */
793 795 char *path, *skip = NULL;
794 796 int wantstat, plen;
795 797
796 798 static char *kwlist[] = {"path", "stat", "skip", NULL};
797 799
798 800 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|OO:listdir",
799 801 kwlist, &path, &plen, &statobj, &skipobj))
800 802 return NULL;
801 803
802 804 wantstat = statobj && PyObject_IsTrue(statobj);
803 805
804 806 if (skipobj && skipobj != Py_None) {
805 807 skip = PyBytes_AsString(skipobj);
806 808 if (!skip)
807 809 return NULL;
808 810 }
809 811
810 812 return _listdir(path, plen, wantstat, skip);
811 813 }
812 814
813 815 #ifdef _WIN32
814 816 static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
815 817 {
816 818 static char *kwlist[] = {"name", "mode", "buffering", NULL};
817 819 PyObject *file_obj = NULL;
818 820 char *name = NULL;
819 821 char *mode = "rb";
820 822 DWORD access = 0;
821 823 DWORD creation;
822 824 HANDLE handle;
823 825 int fd, flags = 0;
824 826 int bufsize = -1;
825 827 char m0, m1, m2;
826 828 char fpmode[4];
827 829 int fppos = 0;
828 830 int plus;
829 831 FILE *fp;
830 832
831 833 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist,
832 834 Py_FileSystemDefaultEncoding,
833 835 &name, &mode, &bufsize))
834 836 return NULL;
835 837
836 838 m0 = mode[0];
837 839 m1 = m0 ? mode[1] : '\0';
838 840 m2 = m1 ? mode[2] : '\0';
839 841 plus = m1 == '+' || m2 == '+';
840 842
841 843 fpmode[fppos++] = m0;
842 844 if (m1 == 'b' || m2 == 'b') {
843 845 flags = _O_BINARY;
844 846 fpmode[fppos++] = 'b';
845 847 }
846 848 else
847 849 flags = _O_TEXT;
848 850 if (m0 == 'r' && !plus) {
849 851 flags |= _O_RDONLY;
850 852 access = GENERIC_READ;
851 853 } else {
852 854 /*
853 855 work around http://support.microsoft.com/kb/899149 and
854 856 set _O_RDWR for 'w' and 'a', even if mode has no '+'
855 857 */
856 858 flags |= _O_RDWR;
857 859 access = GENERIC_READ | GENERIC_WRITE;
858 860 fpmode[fppos++] = '+';
859 861 }
860 862 fpmode[fppos++] = '\0';
861 863
862 864 switch (m0) {
863 865 case 'r':
864 866 creation = OPEN_EXISTING;
865 867 break;
866 868 case 'w':
867 869 creation = CREATE_ALWAYS;
868 870 break;
869 871 case 'a':
870 872 creation = OPEN_ALWAYS;
871 873 flags |= _O_APPEND;
872 874 break;
873 875 default:
874 876 PyErr_Format(PyExc_ValueError,
875 877 "mode string must begin with one of 'r', 'w', "
876 878 "or 'a', not '%c'", m0);
877 879 goto bail;
878 880 }
879 881
880 882 handle = CreateFile(name, access,
881 883 FILE_SHARE_READ | FILE_SHARE_WRITE |
882 884 FILE_SHARE_DELETE,
883 885 NULL,
884 886 creation,
885 887 FILE_ATTRIBUTE_NORMAL,
886 888 0);
887 889
888 890 if (handle == INVALID_HANDLE_VALUE) {
889 891 PyErr_SetFromWindowsErrWithFilename(GetLastError(), name);
890 892 goto bail;
891 893 }
892 894
893 895 fd = _open_osfhandle((intptr_t)handle, flags);
894 896
895 897 if (fd == -1) {
896 898 CloseHandle(handle);
897 899 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
898 900 goto bail;
899 901 }
900 902 #ifndef IS_PY3K
901 903 fp = _fdopen(fd, fpmode);
902 904 if (fp == NULL) {
903 905 _close(fd);
904 906 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
905 907 goto bail;
906 908 }
907 909
908 910 file_obj = PyFile_FromFile(fp, name, mode, fclose);
909 911 if (file_obj == NULL) {
910 912 fclose(fp);
911 913 goto bail;
912 914 }
913 915
914 916 PyFile_SetBufSize(file_obj, bufsize);
915 917 #else
916 918 file_obj = PyFile_FromFd(fd, name, mode, bufsize, NULL, NULL, NULL, 1);
917 919 if (file_obj == NULL)
918 920 goto bail;
919 921 #endif
920 922 bail:
921 923 PyMem_Free(name);
922 924 return file_obj;
923 925 }
924 926 #endif
925 927
926 928 #ifdef __APPLE__
927 929 #include <ApplicationServices/ApplicationServices.h>
928 930
929 931 static PyObject *isgui(PyObject *self)
930 932 {
931 933 CFDictionaryRef dict = CGSessionCopyCurrentDictionary();
932 934
933 935 if (dict != NULL) {
934 936 CFRelease(dict);
935 937 Py_RETURN_TRUE;
936 938 } else {
937 939 Py_RETURN_FALSE;
938 940 }
939 941 }
940 942 #endif
941 943
942 944 static char osutil_doc[] = "Native operating system services.";
943 945
944 946 static PyMethodDef methods[] = {
945 947 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
946 948 "list a directory\n"},
947 949 #ifdef _WIN32
948 950 {"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
949 951 "Open a file with POSIX-like semantics.\n"
950 952 "On error, this function may raise either a WindowsError or an IOError."},
951 953 #else
952 954 {"statfiles", (PyCFunction)statfiles, METH_VARARGS | METH_KEYWORDS,
953 955 "stat a series of files or symlinks\n"
954 956 "Returns None for non-existent entries and entries of other types.\n"},
955 957 #ifdef CMSG_LEN
956 958 {"recvfds", (PyCFunction)recvfds, METH_VARARGS,
957 959 "receive list of file descriptors via socket\n"},
958 960 #endif
959 961 #ifndef SETPROCNAME_USE_NONE
960 962 {"setprocname", (PyCFunction)setprocname, METH_VARARGS,
961 963 "set process title (best-effort)\n"},
962 964 #endif
963 965 #endif /* ndef _WIN32 */
964 966 #ifdef __APPLE__
965 967 {
966 968 "isgui", (PyCFunction)isgui, METH_NOARGS,
967 969 "Is a CoreGraphics session available?"
968 970 },
969 971 #endif
970 972 {NULL, NULL}
971 973 };
972 974
973 975 #ifdef IS_PY3K
974 976 static struct PyModuleDef osutil_module = {
975 977 PyModuleDef_HEAD_INIT,
976 978 "osutil",
977 979 osutil_doc,
978 980 -1,
979 981 methods
980 982 };
981 983
982 984 PyMODINIT_FUNC PyInit_osutil(void)
983 985 {
984 986 if (PyType_Ready(&listdir_stat_type) < 0)
985 987 return NULL;
986 988
987 989 return PyModule_Create(&osutil_module);
988 990 }
989 991 #else
990 992 PyMODINIT_FUNC initosutil(void)
991 993 {
992 994 if (PyType_Ready(&listdir_stat_type) == -1)
993 995 return;
994 996
995 997 Py_InitModule3("osutil", methods, osutil_doc);
996 998 }
997 999 #endif
General Comments 0
You need to be logged in to leave comments. Login now