X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/agedu/blobdiff_plain/704fafa3ca85f378c91f8b1fdd3dfd9f34d02c8c..1d3a7ff684c36162124901504e62a4c6d48ead2f:/du.c diff --git a/du.c b/du.c index 957c6e7..6d26c34 100644 --- a/du.c +++ b/du.c @@ -2,58 +2,53 @@ * du.c: implementation of du.h. */ -#define _GNU_SOURCE -#include - -#include -#include -#include -#include - -#include -#include -#include - +#include "agedu.h" #include "du.h" -#include "malloc.h" +#include "alloc.h" -#ifdef __linux__ +#if !defined __linux__ || !defined O_NOATIME || defined HAVE_FDOPENDIR + +#ifdef HAVE_DIRENT_H +# include +#endif +#ifdef HAVE_NDIR_H +# include +#endif +#ifdef HAVE_SYS_DIR_H +# include +#endif +#ifdef HAVE_SYS_NDIR_H +# include +#endif /* - * On Linux, we have the O_NOATIME flag. This means we can read - * the contents of directories without affecting their atimes, - * which enables us to at least try to include them in the age - * display rather than exempting them. - * - * Unfortunately, opendir() doesn't let us open a directory with - * O_NOATIME. In later glibcs we can open one manually using - * open() and then use fdopendir() to translate the fd into a - * POSIX dir handle; in earlier glibcs fdopendir() is not - * available, so we have no option but to talk directly to the - * kernel system call interface which underlies the POSIX - * opendir/readdir machinery. + * Wrappers around POSIX opendir, readdir and closedir, which + * permit me to replace them with different wrappers in special + * circumstances. */ -#define __KERNEL__ -#include -#include -#include -#include -#include - -_syscall3(int, getdents, uint, fd, struct dirent *, dirp, uint, count) - -typedef struct { - int fd; - struct dirent data[32]; - struct dirent *curr; - int pos, endpos; -} dirhandle; +typedef DIR *dirhandle; int open_dir(const char *path, dirhandle *dh) { - dh->fd = open(path, O_RDONLY | O_NOATIME | O_DIRECTORY); - if (dh->fd < 0) { +#if defined O_NOATIME && defined HAVE_FDOPENDIR + + /* + * On Linux, we have the O_NOATIME flag. This means we can + * read the contents of directories without affecting their + * atimes, which enables us to at least try to include them in + * the age display rather than exempting them. + * + * Unfortunately, opendir() doesn't let us open a directory + * with O_NOATIME. So instead, we have to open the directory + * with vanilla open(), and then use fdopendir() to translate + * the fd into a POSIX dir handle. + */ + int fd; + + fd = open(path, O_RDONLY | O_NONBLOCK | O_NOCTTY | O_LARGEFILE | + O_NOATIME | O_DIRECTORY); + if (fd < 0) { /* * Opening a file with O_NOATIME is not unconditionally * permitted by the Linux kernel. As far as I can tell, @@ -71,7 +66,68 @@ int open_dir(const char *path, dirhandle *dh) * we receive EPERM. */ if (errno == EPERM) - dh->fd = open(path, O_RDONLY | O_DIRECTORY); + fd = open(path, O_RDONLY | O_NONBLOCK | O_NOCTTY | + O_LARGEFILE | O_DIRECTORY); + if (fd < 0) + return -1; + } + + *dh = fdopendir(fd); +#else + *dh = opendir(path); +#endif + + if (!*dh) + return -1; + return 0; +} + +const char *read_dir(dirhandle *dh) +{ + struct dirent *de = readdir(*dh); + return de ? de->d_name : NULL; +} + +void close_dir(dirhandle *dh) +{ + closedir(*dh); +} + +#else /* defined __linux__ && !defined HAVE_FDOPENDIR */ + +/* + * Earlier versions of glibc do not have fdopendir(). Therefore, + * if we are on Linux and still wish to make use of O_NOATIME, we + * have no option but to talk directly to the kernel system call + * interface which underlies the POSIX opendir/readdir machinery. + */ + +#define __KERNEL__ +#include +#include +#include + +_syscall3(int, getdents, uint, fd, struct dirent *, dirp, uint, count) + +typedef struct { + int fd; + struct dirent data[32]; + struct dirent *curr; + int pos, endpos; +} dirhandle; + +int open_dir(const char *path, dirhandle *dh) +{ + /* + * As above, we try with O_NOATIME and then fall back to + * trying without it. + */ + dh->fd = open(path, O_RDONLY | O_NONBLOCK | O_NOCTTY | O_LARGEFILE | + O_NOATIME | O_DIRECTORY); + if (dh->fd < 0) { + if (errno == EPERM) + dh->fd = open(path, O_RDONLY | O_NONBLOCK | O_NOCTTY | + O_LARGEFILE | O_DIRECTORY); if (dh->fd < 0) return -1; } @@ -106,36 +162,7 @@ void close_dir(dirhandle *dh) close(dh->fd); } -#else /* __linux__ */ - -/* - * This branch of the ifdef is a simple exercise of ordinary POSIX - * opendir/readdir. - */ - -#include -typedef DIR *dirhandle; - -int open_dir(const char *path, dirhandle *dh) -{ - *dh = opendir(path); - if (!*dh) - return -1; - return 0; -} - -const char *read_dir(dirhandle *dh) -{ - struct dirent *de = readdir(*dh); - return de ? de->d_name : NULL; -} - -void close_dir(dirhandle *dh) -{ - closedir(*dh); -} - -#endif +#endif /* !defined __linux__ || defined HAVE_FDOPENDIR */ static int str_cmp(const void *av, const void *bv) { @@ -143,16 +170,26 @@ static int str_cmp(const void *av, const void *bv) } static void du_recurse(char **path, size_t pathlen, size_t *pathsize, - gotdata_fn_t gotdata, void *gotdata_ctx) + gotdata_fn_t gotdata, err_fn_t err, void *gotdata_ctx, + int toplevel) { const char *name; dirhandle d; - struct stat64 st; + STRUCT_STAT st; char **names; size_t i, nnames, namesize; - - if (lstat64(*path, &st) < 0) { - fprintf(stderr, "%s: lstat: %s\n", *path, strerror(errno)); + int statret; + + /* + * Special case: at the very top of the scan, we follow a + * symlink. + */ + if (toplevel) + statret = STAT(*path, &st); + else + statret = LSTAT(*path, &st); + if (statret < 0) { + err(gotdata_ctx, "%s: lstat: %s\n", *path, strerror(errno)); return; } @@ -166,7 +203,7 @@ static void du_recurse(char **path, size_t pathlen, size_t *pathsize, nnames = namesize = 0; if (open_dir(*path, &d) < 0) { - fprintf(stderr, "%s: opendir: %s\n", *path, strerror(errno)); + err(gotdata_ctx, "%s: opendir: %s\n", *path, strerror(errno)); return; } while ((name = read_dir(&d)) != NULL) { @@ -204,14 +241,15 @@ static void du_recurse(char **path, size_t pathlen, size_t *pathsize, sprintf(*path + pathlen, "/%s", names[i]); } - du_recurse(path, newpathlen, pathsize, gotdata, gotdata_ctx); + du_recurse(path, newpathlen, pathsize, gotdata, err, gotdata_ctx, 0); sfree(names[i]); } sfree(names); } -void du(const char *inpath, gotdata_fn_t gotdata, void *gotdata_ctx) +void du(const char *inpath, gotdata_fn_t gotdata, err_fn_t err, + void *gotdata_ctx) { char *path; size_t pathlen, pathsize; @@ -221,5 +259,5 @@ void du(const char *inpath, gotdata_fn_t gotdata, void *gotdata_ctx) path = snewn(pathsize, char); strcpy(path, inpath); - du_recurse(&path, pathlen, &pathsize, gotdata, gotdata_ctx); + du_recurse(&path, pathlen, &pathsize, gotdata, err, gotdata_ctx, 1); }