| 1 | /* |
| 2 | * du.c: implementation of du.h. |
| 3 | */ |
| 4 | |
| 5 | #define _GNU_SOURCE |
| 6 | #include <features.h> |
| 7 | |
| 8 | #include <stdio.h> |
| 9 | #include <string.h> |
| 10 | #include <stdlib.h> |
| 11 | #include <errno.h> |
| 12 | |
| 13 | #include <sys/types.h> |
| 14 | #include <sys/stat.h> |
| 15 | #include <unistd.h> |
| 16 | |
| 17 | #include "du.h" |
| 18 | #include "malloc.h" |
| 19 | |
| 20 | #ifdef __linux__ |
| 21 | #define __KERNEL__ |
| 22 | #include <unistd.h> |
| 23 | #include <fcntl.h> |
| 24 | #include <linux/types.h> |
| 25 | #include <linux/dirent.h> |
| 26 | #include <linux/unistd.h> |
| 27 | typedef int dirhandle; |
| 28 | typedef struct { |
| 29 | struct dirent data[32]; |
| 30 | struct dirent *curr; |
| 31 | int pos, endpos; |
| 32 | } direntry; |
| 33 | _syscall3(int, getdents, uint, fd, struct dirent *, dirp, uint, count) |
| 34 | #define OPENDIR(f) open(f, O_RDONLY | O_NOATIME | O_DIRECTORY) |
| 35 | #define DIRVALID(dh) ((dh) >= 0) |
| 36 | #define READDIR(dh,de) ((de).curr = (de).data, (de).pos = 0, \ |
| 37 | ((de).endpos = getdents((dh), (de).data, sizeof((de).data))) > 0) |
| 38 | #define DENAME(de) ((de).curr->d_name) |
| 39 | #define DEDONE(de) ((de).pos >= (de).endpos) |
| 40 | #define DEADVANCE(de) ((de).pos += (de).curr->d_reclen, \ |
| 41 | (de).curr = (struct dirent *)((char *)(de).data + (de).pos)) |
| 42 | #define CLOSEDIR(dh) close(dh) |
| 43 | #else |
| 44 | #include <dirent.h> |
| 45 | typedef DIR *dirhandle; |
| 46 | typedef struct dirent *direntry; |
| 47 | #define OPENDIR(f) opendir(f) |
| 48 | #define DIRVALID(dh) ((dh) != NULL) |
| 49 | #define READDIR(dh,de) (((de) = readdir(dh)) ? 1 : 0) |
| 50 | #define DENAME(de) ((de)->d_name) |
| 51 | #define DEDONE(de) ((de) == NULL) |
| 52 | #define DEADVANCE(de) ((de) = NULL) |
| 53 | #define CLOSEDIR(dh) closedir(dh) |
| 54 | #endif |
| 55 | |
| 56 | static int str_cmp(const void *av, const void *bv) |
| 57 | { |
| 58 | return strcmp(*(const char **)av, *(const char **)bv); |
| 59 | } |
| 60 | |
| 61 | static void du_recurse(char **path, size_t pathlen, size_t *pathsize, |
| 62 | gotdata_fn_t gotdata, void *gotdata_ctx) |
| 63 | { |
| 64 | direntry de; |
| 65 | dirhandle d; |
| 66 | struct stat64 st; |
| 67 | char **names; |
| 68 | size_t i, nnames, namesize; |
| 69 | |
| 70 | if (lstat64(*path, &st) < 0) { |
| 71 | fprintf(stderr, "%s: lstat: %s\n", *path, strerror(errno)); |
| 72 | return; |
| 73 | } |
| 74 | |
| 75 | if (!gotdata(gotdata_ctx, *path, &st)) |
| 76 | return; |
| 77 | |
| 78 | if (!S_ISDIR(st.st_mode)) |
| 79 | return; |
| 80 | |
| 81 | names = NULL; |
| 82 | nnames = namesize = 0; |
| 83 | |
| 84 | d = OPENDIR(*path); |
| 85 | if (!DIRVALID(d)) { |
| 86 | fprintf(stderr, "%s: opendir: %s\n", *path, strerror(errno)); |
| 87 | return; |
| 88 | } |
| 89 | while (READDIR(d, de)) { |
| 90 | do { |
| 91 | const char *name = DENAME(de); |
| 92 | if (name[0] == '.' && (!name[1] || (name[1] == '.' && !name[2]))) { |
| 93 | /* do nothing - we skip "." and ".." */ |
| 94 | } else { |
| 95 | if (nnames >= namesize) { |
| 96 | namesize = nnames * 3 / 2 + 64; |
| 97 | names = sresize(names, namesize, char *); |
| 98 | } |
| 99 | names[nnames++] = dupstr(name); |
| 100 | } |
| 101 | DEADVANCE(de); |
| 102 | } while (!DEDONE(de)); |
| 103 | } |
| 104 | CLOSEDIR(d); |
| 105 | |
| 106 | if (nnames == 0) |
| 107 | return; |
| 108 | |
| 109 | qsort(names, nnames, sizeof(*names), str_cmp); |
| 110 | |
| 111 | for (i = 0; i < nnames; i++) { |
| 112 | size_t newpathlen = pathlen + 1 + strlen(names[i]); |
| 113 | if (*pathsize <= newpathlen) { |
| 114 | *pathsize = newpathlen * 3 / 2 + 256; |
| 115 | *path = sresize(*path, *pathsize, char); |
| 116 | } |
| 117 | /* |
| 118 | * Avoid duplicating a slash if we got a trailing one to |
| 119 | * begin with (i.e. if we're starting the scan in '/' itself). |
| 120 | */ |
| 121 | if (pathlen > 0 && (*path)[pathlen-1] == '/') { |
| 122 | strcpy(*path + pathlen, names[i]); |
| 123 | newpathlen--; |
| 124 | } else { |
| 125 | sprintf(*path + pathlen, "/%s", names[i]); |
| 126 | } |
| 127 | |
| 128 | du_recurse(path, newpathlen, pathsize, gotdata, gotdata_ctx); |
| 129 | |
| 130 | sfree(names[i]); |
| 131 | } |
| 132 | sfree(names); |
| 133 | } |
| 134 | |
| 135 | void du(const char *inpath, gotdata_fn_t gotdata, void *gotdata_ctx) |
| 136 | { |
| 137 | char *path; |
| 138 | size_t pathlen, pathsize; |
| 139 | |
| 140 | pathlen = strlen(inpath); |
| 141 | pathsize = pathlen + 256; |
| 142 | path = snewn(pathsize, char); |
| 143 | strcpy(path, inpath); |
| 144 | |
| 145 | du_recurse(&path, pathlen, &pathsize, gotdata, gotdata_ctx); |
| 146 | } |