Show More
@@ -24,6 +24,11 b'' | |||||
24 | #include <unistd.h> |
|
24 | #include <unistd.h> | |
25 | #endif |
|
25 | #endif | |
26 |
|
26 | |||
|
27 | #ifdef __APPLE__ | |||
|
28 | #include <sys/attr.h> | |||
|
29 | #include <sys/vnode.h> | |||
|
30 | #endif | |||
|
31 | ||||
27 | #include "util.h" |
|
32 | #include "util.h" | |
28 |
|
33 | |||
29 | /* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */ |
|
34 | /* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */ | |
@@ -392,8 +397,195 b' error_value:' | |||||
392 | return ret; |
|
397 | return ret; | |
393 | } |
|
398 | } | |
394 |
|
399 | |||
|
400 | #ifdef __APPLE__ | |||
|
401 | ||||
|
402 | typedef struct { | |||
|
403 | u_int32_t length; | |||
|
404 | attrreference_t name; | |||
|
405 | fsobj_type_t obj_type; | |||
|
406 | struct timespec mtime; | |||
|
407 | #if __LITTLE_ENDIAN__ | |||
|
408 | mode_t access_mask; | |||
|
409 | uint16_t padding; | |||
|
410 | #else | |||
|
411 | uint16_t padding; | |||
|
412 | mode_t access_mask; | |||
|
413 | #endif | |||
|
414 | off_t size; | |||
|
415 | } __attribute__((packed)) attrbuf_entry; | |||
|
416 | ||||
|
417 | int attrkind(attrbuf_entry *entry) | |||
|
418 | { | |||
|
419 | switch (entry->obj_type) { | |||
|
420 | case VREG: return S_IFREG; | |||
|
421 | case VDIR: return S_IFDIR; | |||
|
422 | case VLNK: return S_IFLNK; | |||
|
423 | case VBLK: return S_IFBLK; | |||
|
424 | case VCHR: return S_IFCHR; | |||
|
425 | case VFIFO: return S_IFIFO; | |||
|
426 | case VSOCK: return S_IFSOCK; | |||
|
427 | } | |||
|
428 | return -1; | |||
|
429 | } | |||
|
430 | ||||
|
431 | /* get these many entries at a time */ | |||
|
432 | #define LISTDIR_BATCH_SIZE 50 | |||
|
433 | ||||
|
434 | static PyObject *_listdir_batch(char *path, int pathlen, int keepstat, | |||
|
435 | char *skip, bool *fallback) | |||
|
436 | { | |||
|
437 | PyObject *list, *elem, *stat = NULL, *ret = NULL; | |||
|
438 | int kind, err; | |||
|
439 | unsigned long index; | |||
|
440 | unsigned int count, old_state, new_state; | |||
|
441 | bool state_seen = false; | |||
|
442 | attrbuf_entry *entry; | |||
|
443 | /* from the getattrlist(2) man page: a path can be no longer than | |||
|
444 | (NAME_MAX * 3 + 1) bytes. Also, "The getattrlist() function will | |||
|
445 | silently truncate attribute data if attrBufSize is too small." So | |||
|
446 | pass in a buffer big enough for the worst case. */ | |||
|
447 | char attrbuf[LISTDIR_BATCH_SIZE * (sizeof(attrbuf_entry) + NAME_MAX * 3 + 1)]; | |||
|
448 | unsigned int basep_unused; | |||
|
449 | ||||
|
450 | struct stat st; | |||
|
451 | int dfd = -1; | |||
|
452 | ||||
|
453 | /* these must match the attrbuf_entry struct, otherwise you'll end up | |||
|
454 | with garbage */ | |||
|
455 | struct attrlist requested_attr = {0}; | |||
|
456 | requested_attr.bitmapcount = ATTR_BIT_MAP_COUNT; | |||
|
457 | requested_attr.commonattr = (ATTR_CMN_NAME | ATTR_CMN_OBJTYPE | | |||
|
458 | ATTR_CMN_MODTIME | ATTR_CMN_ACCESSMASK); | |||
|
459 | requested_attr.fileattr = ATTR_FILE_TOTALSIZE; | |||
|
460 | ||||
|
461 | *fallback = false; | |||
|
462 | ||||
|
463 | if (pathlen >= PATH_MAX) { | |||
|
464 | errno = ENAMETOOLONG; | |||
|
465 | PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); | |||
|
466 | goto error_value; | |||
|
467 | } | |||
|
468 | ||||
|
469 | dfd = open(path, O_RDONLY); | |||
|
470 | if (dfd == -1) { | |||
|
471 | PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); | |||
|
472 | goto error_value; | |||
|
473 | } | |||
|
474 | ||||
|
475 | list = PyList_New(0); | |||
|
476 | if (!list) | |||
|
477 | goto error_dir; | |||
|
478 | ||||
|
479 | do { | |||
|
480 | count = LISTDIR_BATCH_SIZE; | |||
|
481 | err = getdirentriesattr(dfd, &requested_attr, &attrbuf, | |||
|
482 | sizeof(attrbuf), &count, &basep_unused, | |||
|
483 | &new_state, 0); | |||
|
484 | if (err < 0) { | |||
|
485 | if (errno == ENOTSUP) { | |||
|
486 | /* We're on a filesystem that doesn't support | |||
|
487 | getdirentriesattr. Fall back to the | |||
|
488 | stat-based implementation. */ | |||
|
489 | *fallback = true; | |||
|
490 | } else | |||
|
491 | PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); | |||
|
492 | goto error; | |||
|
493 | } | |||
|
494 | ||||
|
495 | if (!state_seen) { | |||
|
496 | old_state = new_state; | |||
|
497 | state_seen = true; | |||
|
498 | } else if (old_state != new_state) { | |||
|
499 | /* There's an edge case with getdirentriesattr. Consider | |||
|
500 | the following initial list of files: | |||
|
501 | ||||
|
502 | a | |||
|
503 | b | |||
|
504 | <-- | |||
|
505 | c | |||
|
506 | d | |||
|
507 | ||||
|
508 | If the iteration is paused at the arrow, and b is | |||
|
509 | deleted before it is resumed, getdirentriesattr will | |||
|
510 | not return d at all! Ordinarily we're expected to | |||
|
511 | restart the iteration from the beginning. To avoid | |||
|
512 | getting stuck in a retry loop here, fall back to | |||
|
513 | stat. */ | |||
|
514 | *fallback = true; | |||
|
515 | goto error; | |||
|
516 | } | |||
|
517 | ||||
|
518 | entry = (attrbuf_entry *)attrbuf; | |||
|
519 | ||||
|
520 | for (index = 0; index < count; index++) { | |||
|
521 | char *filename = ((char *)&entry->name) + | |||
|
522 | entry->name.attr_dataoffset; | |||
|
523 | ||||
|
524 | if (!strcmp(filename, ".") || !strcmp(filename, "..")) | |||
|
525 | continue; | |||
|
526 | ||||
|
527 | kind = attrkind(entry); | |||
|
528 | if (kind == -1) { | |||
|
529 | PyErr_Format(PyExc_OSError, | |||
|
530 | "unknown object type %u for file " | |||
|
531 | "%s%s!", | |||
|
532 | entry->obj_type, path, filename); | |||
|
533 | goto error; | |||
|
534 | } | |||
|
535 | ||||
|
536 | /* quit early? */ | |||
|
537 | if (skip && kind == S_IFDIR && !strcmp(filename, skip)) { | |||
|
538 | ret = PyList_New(0); | |||
|
539 | goto error; | |||
|
540 | } | |||
|
541 | ||||
|
542 | if (keepstat) { | |||
|
543 | /* from the getattrlist(2) man page: "Only the | |||
|
544 | permission bits ... are valid". */ | |||
|
545 | st.st_mode = (entry->access_mask & ~S_IFMT) | kind; | |||
|
546 | st.st_mtime = entry->mtime.tv_sec; | |||
|
547 | st.st_size = entry->size; | |||
|
548 | stat = makestat(&st); | |||
|
549 | if (!stat) | |||
|
550 | goto error; | |||
|
551 | elem = Py_BuildValue("siN", filename, kind, stat); | |||
|
552 | } else | |||
|
553 | elem = Py_BuildValue("si", filename, kind); | |||
|
554 | if (!elem) | |||
|
555 | goto error; | |||
|
556 | stat = NULL; | |||
|
557 | ||||
|
558 | PyList_Append(list, elem); | |||
|
559 | Py_DECREF(elem); | |||
|
560 | ||||
|
561 | entry = (attrbuf_entry *)((char *)entry + entry->length); | |||
|
562 | } | |||
|
563 | } while (err == 0); | |||
|
564 | ||||
|
565 | ret = list; | |||
|
566 | Py_INCREF(ret); | |||
|
567 | ||||
|
568 | error: | |||
|
569 | Py_DECREF(list); | |||
|
570 | Py_XDECREF(stat); | |||
|
571 | error_dir: | |||
|
572 | close(dfd); | |||
|
573 | error_value: | |||
|
574 | return ret; | |||
|
575 | } | |||
|
576 | ||||
|
577 | #endif /* __APPLE__ */ | |||
|
578 | ||||
395 | static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip) |
|
579 | static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip) | |
396 | { |
|
580 | { | |
|
581 | #ifdef __APPLE__ | |||
|
582 | PyObject *ret; | |||
|
583 | bool fallback = false; | |||
|
584 | ||||
|
585 | ret = _listdir_batch(path, pathlen, keepstat, skip, &fallback); | |||
|
586 | if (ret != NULL || !fallback) | |||
|
587 | return ret; | |||
|
588 | #endif | |||
397 | return _listdir_stat(path, pathlen, keepstat, skip); |
|
589 | return _listdir_stat(path, pathlen, keepstat, skip); | |
398 | } |
|
590 | } | |
399 |
|
591 |
General Comments 0
You need to be logged in to leave comments.
Login now