##// END OF EJS Templates
osutil: use getdirentriesattr on OS X if possible...
Siddharth Agarwal -
r24461:05ccfe67 default
parent child Browse files
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