9a8a4a3924bb464ed89627df565103a6d7168783
2 * du.c: implementation of du.h.
13 #include <sys/types.h>
21 #if !defined __linux__ || defined HAVE_FDOPENDIR
24 * Wrappers around POSIX opendir, readdir and closedir, which
25 * permit me to replace them with different wrappers in special
30 typedef DIR *dirhandle
;
32 int open_dir(const char *path
, dirhandle
*dh
)
34 #if defined O_NOATIME && defined HAVE_FDOPENDIR
37 * On Linux, we have the O_NOATIME flag. This means we can
38 * read the contents of directories without affecting their
39 * atimes, which enables us to at least try to include them in
40 * the age display rather than exempting them.
42 * Unfortunately, opendir() doesn't let us open a directory
43 * with O_NOATIME. So instead, we have to open the directory
44 * with vanilla open(), and then use fdopendir() to translate
45 * the fd into a POSIX dir handle.
49 fd
= open(path
, O_RDONLY
| O_NONBLOCK
| O_NOCTTY
| O_LARGEFILE
|
50 O_NOATIME
| O_DIRECTORY
);
53 * Opening a file with O_NOATIME is not unconditionally
54 * permitted by the Linux kernel. As far as I can tell,
55 * it's permitted only for files on which the user would
56 * have been able to call utime(2): in other words, files
57 * for which the user could have deliberately set the
58 * atime back to its original value after finishing with
59 * it. Hence, O_NOATIME has no security implications; it's
60 * simply a cleaner, faster and more race-condition-free
61 * alternative to stat(), a normal open(), and a utimes()
64 * The upshot of all of which, for these purposes, is that
65 * we must be prepared to try again without O_NOATIME if
69 fd
= open(path
, O_RDONLY
| O_NONBLOCK
| O_NOCTTY
|
70 O_LARGEFILE
| O_DIRECTORY
);
85 const char *read_dir(dirhandle
*dh
)
87 struct dirent
*de
= readdir(*dh
);
88 return de ? de
->d_name
: NULL
;
91 void close_dir(dirhandle
*dh
)
96 #else /* defined __linux__ && !defined HAVE_FDOPENDIR */
99 * Earlier versions of glibc do not have fdopendir(). Therefore,
100 * if we are on Linux and still wish to make use of O_NOATIME, we
101 * have no option but to talk directly to the kernel system call
102 * interface which underlies the POSIX opendir/readdir machinery.
108 #include <linux/types.h>
109 #include <linux/dirent.h>
110 #include <linux/unistd.h>
112 _syscall3(int, getdents
, uint
, fd
, struct dirent
*, dirp
, uint
, count
)
116 struct dirent data
[32];
121 int open_dir(const char *path
, dirhandle
*dh
)
124 * As above, we try with O_NOATIME and then fall back to
127 dh
->fd
= open(path
, O_RDONLY
| O_NONBLOCK
| O_NOCTTY
| O_LARGEFILE
|
128 O_NOATIME
| O_DIRECTORY
);
131 dh
->fd
= open(path
, O_RDONLY
| O_NONBLOCK
| O_NOCTTY
|
132 O_LARGEFILE
| O_DIRECTORY
);
137 dh
->pos
= dh
->endpos
= 0;
142 const char *read_dir(dirhandle
*dh
)
146 if (dh
->pos
>= dh
->endpos
) {
149 dh
->endpos
= getdents(dh
->fd
, dh
->data
, sizeof(dh
->data
));
154 ret
= dh
->curr
->d_name
;
156 dh
->pos
+= dh
->curr
->d_reclen
;
157 dh
->curr
= (struct dirent
*)((char *)dh
->data
+ dh
->pos
);
162 void close_dir(dirhandle
*dh
)
167 #endif /* !defined __linux__ || defined HAVE_FDOPENDIR */
169 static int str_cmp(const void *av
, const void *bv
)
171 return strcmp(*(const char **)av
, *(const char **)bv
);
174 static void du_recurse(char **path
, size_t pathlen
, size_t *pathsize
,
175 gotdata_fn_t gotdata
, void *gotdata_ctx
)
181 size_t i
, nnames
, namesize
;
183 if (LSTAT(*path
, &st
) < 0) {
184 fprintf(stderr
, "%s: lstat: %s\n", *path
, strerror(errno
));
188 if (!gotdata(gotdata_ctx
, *path
, &st
))
191 if (!S_ISDIR(st
.st_mode
))
195 nnames
= namesize
= 0;
197 if (open_dir(*path
, &d
) < 0) {
198 fprintf(stderr
, "%s: opendir: %s\n", *path
, strerror(errno
));
201 while ((name
= read_dir(&d
)) != NULL
) {
202 if (name
[0] == '.' && (!name
[1] || (name
[1] == '.' && !name
[2]))) {
203 /* do nothing - we skip "." and ".." */
205 if (nnames
>= namesize
) {
206 namesize
= nnames
* 3 / 2 + 64;
207 names
= sresize(names
, namesize
, char *);
209 names
[nnames
++] = dupstr(name
);
217 qsort(names
, nnames
, sizeof(*names
), str_cmp
);
219 for (i
= 0; i
< nnames
; i
++) {
220 size_t newpathlen
= pathlen
+ 1 + strlen(names
[i
]);
221 if (*pathsize
<= newpathlen
) {
222 *pathsize
= newpathlen
* 3 / 2 + 256;
223 *path
= sresize(*path
, *pathsize
, char);
226 * Avoid duplicating a slash if we got a trailing one to
227 * begin with (i.e. if we're starting the scan in '/' itself).
229 if (pathlen
> 0 && (*path
)[pathlen
-1] == '/') {
230 strcpy(*path
+ pathlen
, names
[i
]);
233 sprintf(*path
+ pathlen
, "/%s", names
[i
]);
236 du_recurse(path
, newpathlen
, pathsize
, gotdata
, gotdata_ctx
);
243 void du(const char *inpath
, gotdata_fn_t gotdata
, void *gotdata_ctx
)
246 size_t pathlen
, pathsize
;
248 pathlen
= strlen(inpath
);
249 pathsize
= pathlen
+ 256;
250 path
= snewn(pathsize
, char);
251 strcpy(path
, inpath
);
253 du_recurse(&path
, pathlen
, &pathsize
, gotdata
, gotdata_ctx
);