Show More
@@ -24,6 +24,11 b'' | |||
|
24 | 24 | #include <unistd.h> |
|
25 | 25 | #endif |
|
26 | 26 | |
|
27 | #ifdef __APPLE__ | |
|
28 | #include <sys/attr.h> | |
|
29 | #include <sys/vnode.h> | |
|
30 | #endif | |
|
31 | ||
|
27 | 32 | #include "util.h" |
|
28 | 33 | |
|
29 | 34 | /* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */ |
@@ -392,8 +397,195 b' error_value:' | |||
|
392 | 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 | 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 | 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