#include #include #include #include #include #include #include #include #include #include #include #define DT_DIR 4 #include "list.h" /* * XXX: * - error handling * - openat dir */ LIST_HEAD(pending_work); #define die(fmt , args...) do { \ printf(fmt , ##args); \ exit(1); \ } while (0) int verbose = 0; #define verbosef(fmt , args...) do { \ if (verbose) \ printf(fmt , ##args); \ } while (0) #define declare_alloced_ptr(type, name) \ type *name = malloc(sizeof(type)) struct syscall; typedef void (*handler_t)(struct syscall *call, int ret); struct syscall { struct list_head list; int nr; unsigned nr_args; unsigned long args[6]; handler_t handler; }; struct asys_input { int syscall_nr; unsigned long cookie; unsigned long nr_args; unsigned long *args; }; struct asys_completion { long return_code; unsigned long cookie; }; static void __submit_syscall(struct syscall *call, handler_t handler, int syscall_nr, int nr_args, ...) { va_list ap; unsigned long i; call->nr = syscall_nr; call->nr_args = nr_args; call->handler = handler; va_start(ap, nr_args); for(i = 0; i < nr_args; i++) call->args[i] = va_arg(ap, unsigned long); va_end(ap); list_add_tail(&call->list, &pending_work); } #define submit_syscall(call, hand, nr, nr_args, one, two, three) \ __submit_syscall(call, hand, nr, nr_args, (unsigned long)one, \ (unsigned long)two, (unsigned long)three) \ struct a_getdents { int fd; struct dirent64 *dents; unsigned int count; struct syscall call; }; static void start_openat(int fd, char *dir_name); void handle_getdents(struct syscall *call, int ret) { struct a_getdents *agetdents; struct dirent64 *dent; agetdents = list_entry(call, struct a_getdents, call); if (ret < 0) printf("getdents(%d) = %d\n", agetdents->fd, ret); else verbosef("getdents(%d) = %d\n", agetdents->fd, ret); for (dent = agetdents->dents; ret > 0 && dent->d_reclen && ret >= dent->d_reclen; ret -= dent->d_reclen, dent = (void *)dent + dent->d_reclen) { if (dent->d_type & DT_DIR && strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) start_openat(agetdents->fd, dent->d_name); } free(agetdents->dents); free(agetdents); } static void start_getdents(int fd, unsigned int size) { declare_alloced_ptr(struct a_getdents, agetdents); agetdents->fd = fd; agetdents->dents = malloc(size); agetdents->count = size; submit_syscall(&agetdents->call, handle_getdents, SYS_getdents64, 3, agetdents->fd, agetdents->dents, agetdents->count); } struct a_fstat { int fd; struct stat st; struct syscall call; }; void handle_fstat(struct syscall *call, int ret) { struct a_fstat *afstat; afstat = list_entry(call, struct a_fstat, call); if (ret < 0) printf("fstat(%d) = %d\n", afstat->fd, ret); else verbosef("fstat(%d) = %d\n", afstat->fd, ret); if (ret == 0) start_getdents(afstat->fd, afstat->st.st_size * 4); free(afstat); } static void start_fstat(int fd) { declare_alloced_ptr(struct a_fstat, afstat); afstat->fd = fd; submit_syscall(&afstat->call, handle_fstat, SYS_fstat64, 2, afstat->fd, &afstat->st, 0); } struct a_openat { int fd; char *path; int flags; struct syscall call; }; void handle_openat(struct syscall *call, int ret) { struct a_openat *aopenat; aopenat = list_entry(call, struct a_openat, call); if (ret < 0) printf("openat(%d, %s) = %d\n", aopenat->fd, aopenat->path, ret); else verbosef("openat(%d, %s) = %d\n", aopenat->fd, aopenat->path, ret); if (ret >= 0) start_fstat(ret); free(aopenat->path); free(aopenat); } static void start_openat(int fd, char *dir_name) { declare_alloced_ptr(struct a_openat, aopenat); aopenat->fd = fd; aopenat->path = strdup(dir_name); aopenat->flags = O_RDONLY; submit_syscall(&aopenat->call, handle_openat, SYS_openat, 3, aopenat->fd, aopenat->path, aopenat->flags); } static void process_sync(void) { struct syscall *call; int ret; while (!list_empty(&pending_work)) { call = list_entry(pending_work.next, struct syscall, list); list_del_init(&call->list); switch(call->nr_args) { case 1: ret = syscall(call->nr, call->args[0]); break; case 2: ret = syscall(call->nr, call->args[0], call->args[1]); break; case 3: ret = syscall(call->nr, call->args[0], call->args[1], call->args[2]); break; } call->handler(call, ret); } } static void process_asys(void) { struct syscall *call; int ret; struct asys_input *input = NULL, *inp; struct asys_completion comp; unsigned nr_calls = 0; unsigned in_flight = 0; unsigned nr_alloced = 0; struct list_head *pos, *n; do { list_for_each_safe(pos, n, &pending_work) { call = list_entry(pos, struct syscall, list); list_del_init(&call->list); if (nr_calls == nr_alloced) { nr_alloced = nr_alloced + 128; input = realloc(input, nr_alloced * sizeof(*inp)); if (input == NULL) die("input alloc failed\n"); } inp = &input[nr_calls++]; inp->syscall_nr = call->nr; inp->cookie = (unsigned long)call; inp->nr_args = call->nr_args; inp->args = call->args; } if (nr_calls) { ret = syscall(320, input, nr_calls); if (ret <= 0) die("sys_asys_submit(..., %u) = %d\n", nr_calls, ret); else verbosef("sys_asys_submit(..., %u) = %d\n", nr_calls, ret); in_flight += ret; if (ret < nr_calls) memmove(input, input + ret, (nr_calls - ret) * (sizeof *inp)); nr_calls -= ret; } ret = syscall(321, &comp); if (ret < 0) die("collection failed\n"); else verbosef("sys_asys_collection(...) = %d\n", ret); call = (struct syscall *)comp.cookie; call->handler(call, comp.return_code); in_flight--; } while (!list_empty(&pending_work) || in_flight); } static void usage(void) { printf(" -d directory to start from\n" " -h output this usage message and exit\n" " -v output system calls as they complete\n" " -s perform system calls synchronously\n" " -a use 'asys' to issue system calls asynchronously\n"); } int main(int argc, char **argv) { int cwd; int c; char *dir = NULL; int mode = 0; while(1) { c = getopt(argc, argv, "+ad:hsv"); if (c == -1) break; switch(c) { case 'a': mode = 1; break; case 'd': dir = strdup(optarg); break; case 's': mode = 0; break; case 'v': verbose = 1; break; case 'h': case '?': default: usage(); exit(1); break; } } if (dir == NULL) die("need to specify a starting directory with -d\n"); cwd = open(".", O_RDONLY); if (cwd < 0) die("failed to open '.'\n"); start_openat(cwd, dir); if (mode) process_asys(); else process_sync(); return 0; }