70322ae3 |
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 | } |
256c29a2 |
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 | } |
70322ae3 |
127 | |
128 | du_recurse(path, newpathlen, pathsize, gotdata, gotdata_ctx); |
129 | |
130 | sfree(names[i]); |
131 | } |
132 | sfree(names); |
133 | } |
134 | |
135 | void du(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 | } |