1a375bf9adde7bbab6f2439bf21711f25657d1f5
2 * du.c: implementation of du.h.
5 #include "agedu.h" /* for config.h */
17 #include <sys/types.h>
24 #if !defined __linux__ || defined HAVE_FDOPENDIR
27 * Wrappers around POSIX opendir, readdir and closedir, which
28 * permit me to replace them with different wrappers in special
33 typedef DIR *dirhandle
;
35 int open_dir(const char *path
, dirhandle
*dh
)
37 #if defined O_NOATIME && defined HAVE_FDOPENDIR
40 * On Linux, we have the O_NOATIME flag. This means we can
41 * read the contents of directories without affecting their
42 * atimes, which enables us to at least try to include them in
43 * the age display rather than exempting them.
45 * Unfortunately, opendir() doesn't let us open a directory
46 * with O_NOATIME. So instead, we have to open the directory
47 * with vanilla open(), and then use fdopendir() to translate
48 * the fd into a POSIX dir handle.
52 fd
= open(path
, O_RDONLY
| O_NONBLOCK
| O_NOCTTY
| O_LARGEFILE
|
53 O_NOATIME
| O_DIRECTORY
);
56 * Opening a file with O_NOATIME is not unconditionally
57 * permitted by the Linux kernel. As far as I can tell,
58 * it's permitted only for files on which the user would
59 * have been able to call utime(2): in other words, files
60 * for which the user could have deliberately set the
61 * atime back to its original value after finishing with
62 * it. Hence, O_NOATIME has no security implications; it's
63 * simply a cleaner, faster and more race-condition-free
64 * alternative to stat(), a normal open(), and a utimes()
67 * The upshot of all of which, for these purposes, is that
68 * we must be prepared to try again without O_NOATIME if
72 fd
= open(path
, O_RDONLY
| O_NONBLOCK
| O_NOCTTY
|
73 O_LARGEFILE
| O_DIRECTORY
);
88 const char *read_dir(dirhandle
*dh
)
90 struct dirent
*de
= readdir(*dh
);
91 return de ? de
->d_name
: NULL
;
94 void close_dir(dirhandle
*dh
)
99 #else /* defined __linux__ && !defined HAVE_FDOPENDIR */
102 * Earlier versions of glibc do not have fdopendir(). Therefore,
103 * if we are on Linux and still wish to make use of O_NOATIME, we
104 * have no option but to talk directly to the kernel system call
105 * interface which underlies the POSIX opendir/readdir machinery.
111 #include <linux/types.h>
112 #include <linux/dirent.h>
113 #include <linux/unistd.h>
115 _syscall3(int, getdents
, uint
, fd
, struct dirent
*, dirp
, uint
, count
)
119 struct dirent data
[32];
124 int open_dir(const char *path
, dirhandle
*dh
)
127 * As above, we try with O_NOATIME and then fall back to
130 dh
->fd
= open(path
, O_RDONLY
| O_NONBLOCK
| O_NOCTTY
| O_LARGEFILE
|
131 O_NOATIME
| O_DIRECTORY
);
134 dh
->fd
= open(path
, O_RDONLY
| O_NONBLOCK
| O_NOCTTY
|
135 O_LARGEFILE
| O_DIRECTORY
);
140 dh
->pos
= dh
->endpos
= 0;
145 const char *read_dir(dirhandle
*dh
)
149 if (dh
->pos
>= dh
->endpos
) {
152 dh
->endpos
= getdents(dh
->fd
, dh
->data
, sizeof(dh
->data
));
157 ret
= dh
->curr
->d_name
;
159 dh
->pos
+= dh
->curr
->d_reclen
;
160 dh
->curr
= (struct dirent
*)((char *)dh
->data
+ dh
->pos
);
165 void close_dir(dirhandle
*dh
)
170 #endif /* !defined __linux__ || defined HAVE_FDOPENDIR */
172 static int str_cmp(const void *av
, const void *bv
)
174 return strcmp(*(const char **)av
, *(const char **)bv
);
177 static void du_recurse(char **path
, size_t pathlen
, size_t *pathsize
,
178 gotdata_fn_t gotdata
, void *gotdata_ctx
)
184 size_t i
, nnames
, namesize
;
186 if (LSTAT(*path
, &st
) < 0) {
187 fprintf(stderr
, "%s: lstat: %s\n", *path
, strerror(errno
));
191 if (!gotdata(gotdata_ctx
, *path
, &st
))
194 if (!S_ISDIR(st
.st_mode
))
198 nnames
= namesize
= 0;
200 if (open_dir(*path
, &d
) < 0) {
201 fprintf(stderr
, "%s: opendir: %s\n", *path
, strerror(errno
));
204 while ((name
= read_dir(&d
)) != NULL
) {
205 if (name
[0] == '.' && (!name
[1] || (name
[1] == '.' && !name
[2]))) {
206 /* do nothing - we skip "." and ".." */
208 if (nnames
>= namesize
) {
209 namesize
= nnames
* 3 / 2 + 64;
210 names
= sresize(names
, namesize
, char *);
212 names
[nnames
++] = dupstr(name
);
220 qsort(names
, nnames
, sizeof(*names
), str_cmp
);
222 for (i
= 0; i
< nnames
; i
++) {
223 size_t newpathlen
= pathlen
+ 1 + strlen(names
[i
]);
224 if (*pathsize
<= newpathlen
) {
225 *pathsize
= newpathlen
* 3 / 2 + 256;
226 *path
= sresize(*path
, *pathsize
, char);
229 * Avoid duplicating a slash if we got a trailing one to
230 * begin with (i.e. if we're starting the scan in '/' itself).
232 if (pathlen
> 0 && (*path
)[pathlen
-1] == '/') {
233 strcpy(*path
+ pathlen
, names
[i
]);
236 sprintf(*path
+ pathlen
, "/%s", names
[i
]);
239 du_recurse(path
, newpathlen
, pathsize
, gotdata
, gotdata_ctx
);
246 void du(const char *inpath
, gotdata_fn_t gotdata
, void *gotdata_ctx
)
249 size_t pathlen
, pathsize
;
251 pathlen
= strlen(inpath
);
252 pathsize
= pathlen
+ 256;
253 path
= snewn(pathsize
, char);
254 strcpy(path
, inpath
);
256 du_recurse(&path
, pathlen
, &pathsize
, gotdata
, gotdata_ctx
);