Skip to content

Instantly share code, notes, and snippets.

Created May 27, 2015 08:58
Show Gist options
  • Save anonymous/8f92e5c5b67133cbcc86 to your computer and use it in GitHub Desktop.
Save anonymous/8f92e5c5b67133cbcc86 to your computer and use it in GitHub Desktop.
A sample on how to properly use getattrlistbulk(2)
//
// A multithreaded directory size measurer with various bugs, flaws and defects
// based on `man getattrlistbulk`
// All modifications against the original example code must be distributed under GPLv3 license.
//
#include <stdlib.h>
#include <sys/syscall.h>
#include <sys/attr.h>
#include <sys/errno.h>
#include <sys/vnode.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <assert.h>
#include <stddef.h>
#include <string.h>
#include <pthread.h>
#include <libkern/OSAtomic.h>
#include <string>
#include <vector>
#include <algorithm>
const int kAttrBufSize = 25600;
typedef struct val_attrs {
uint32_t length;
attribute_set_t returned;
attrreference_t name_info;
char *name;
fsobj_type_t obj_type;
uint64_t size;
uint32_t entries;
} val_attrs_t;
struct val_entry {
std::string name;
int64_t size;
std::vector<val_entry*> children;
val_entry* parent;
val_entry() : size(0), parent(NULL) {}
};
int64_t totalSize = 0;
int activeThreads = 0;
int totalFiles = 0;
void* traverse(void *p) {
char *attrBuf = (char*) malloc(kAttrBufSize);
val_entry* parent = (val_entry*) p;
struct attrlist attrList = {
.bitmapcount = ATTR_BIT_MAP_COUNT,
.commonattr = ATTR_CMN_RETURNED_ATTRS | ATTR_CMN_NAME | ATTR_CMN_OBJTYPE,
.fileattr = ATTR_FILE_TOTALSIZE,
.dirattr = ATTR_DIR_ENTRYCOUNT
};
int dirfd = open(parent->name.c_str(), O_RDONLY, 0);
if (dirfd < 0) {
fprintf(stderr, "Could not open directory \"%s\". ", parent->name.c_str());
perror("Error was ");
} else {
for (;;) {
int retcount = getattrlistbulk(dirfd, &attrList, attrBuf, kAttrBufSize, 1);
if (retcount == -1) {
perror("Error returned: ");
break;
} else if (retcount == 0) {
break;
} else {
char *entry_start = attrBuf;
for (int index = 0; index < retcount; index++) {
val_attrs_t attrs = {0};
char *field = entry_start;
attrs.length = *(uint32_t *)field;
field += sizeof(uint32_t);
entry_start += attrs.length;
attrs.returned = *(attribute_set_t *)field;
field += sizeof(attribute_set_t);
if (attrs.returned.commonattr & ATTR_CMN_NAME) {
attrs.name = field;
attrs.name_info = *(attrreference_t *)field;
field += sizeof(attrreference_t);
}
if (attrs.returned.commonattr & ATTR_CMN_OBJTYPE) {
attrs.obj_type = *(fsobj_type_t *)field;
field += sizeof(fsobj_type_t);
}
if (attrs.returned.fileattr & ATTR_FILE_TOTALSIZE) {
attrs.size = *(uint64_t *)field;
field += sizeof(uint64_t);
OSAtomicAdd64(attrs.size, &totalSize);
for (val_entry* e = parent; e; e = e->parent) {
OSAtomicAdd64(attrs.size, &e->size);
}
}
if (attrs.returned.dirattr & ATTR_DIR_ENTRYCOUNT) {
attrs.entries = *(uint32_t *)field;
field += sizeof(uint32_t);
}
OSAtomicIncrement32(&totalFiles);
val_entry* e = new val_entry();
e->name = parent->name + '/' + (attrs.name + attrs.name_info.attr_dataoffset);
e->size = attrs.size;
e->parent = parent;
parent->children.push_back(e);
switch (attrs.obj_type) {
case VREG:
break;
case VDIR:
OSAtomicIncrement32(&activeThreads);
if (attrs.entries > 32 && activeThreads < 128) {
pthread_t tid;
pthread_create(&tid, NULL, traverse, e);
} else {
traverse(e);
}
break;
default:
break;
}
}
}
}
close(dirfd);
}
free(attrBuf);
OSAtomicDecrement32(&activeThreads);
return NULL;
}
int clean_entries(val_entry* b) {
int r = 0;
for (const auto& c : b->children) {
if (c->children.size()) r += clean_entries(c);
delete c;
++r;
}
return r;
}
int main(int argc, char** argv) {
activeThreads = 1;
val_entry root;
root.name = argc < 2 ? "." : argv[1];
struct timeval tbegin, tend;
gettimeofday(&tbegin, NULL);
traverse(&root);
while (activeThreads > 0) usleep(1000);
std::stable_sort(root.children.begin(), root.children.end(),
[](val_entry* a, val_entry* b) {
return a->size > b->size;
}
);
for (const auto& e : root.children) { printf("%*lld\t%s\n", 20, e->size, e->name.c_str()); }
int rc = clean_entries(&root);
gettimeofday(&tend, NULL);
int64_t tms = ((tend.tv_sec * 1000000 + tend.tv_usec) - (tbegin.tv_sec * 1000000 + tbegin.tv_usec)) / 1000;
int64_t tsec = tms / 1000;
int64_t tmsec = tms % 1000;
printf("\nfiles = %d checked = %d\n", totalFiles, rc);
printf("grand total = %lld checked = %lld\n", root.size, totalSize);
printf("seconds elapsed = %lld.%03lld\n", tsec, tmsec);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment