summaryrefslogtreecommitdiffstats
path: root/lib/libc/gen/opendir.c
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>1997-03-11 11:52:33 +0000
committerpeter <peter@FreeBSD.org>1997-03-11 11:52:33 +0000
commit0b3e1277b4e3cc3bb2575162ec238cac00b15a18 (patch)
tree30be23b5b8ddeb71e0495f1655cf455ef58c410f /lib/libc/gen/opendir.c
parent9234fe208b22e039265244d8a518ff096d630a44 (diff)
downloadFreeBSD-src-0b3e1277b4e3cc3bb2575162ec238cac00b15a18.zip
FreeBSD-src-0b3e1277b4e3cc3bb2575162ec238cac00b15a18.tar.gz
Merge from Lite2:
filesystem include updates, duplicate group suppression, cleanups, filesystem whiteout support (unionfs), bidir popen().
Diffstat (limited to 'lib/libc/gen/opendir.c')
-rw-r--r--lib/libc/gen/opendir.c233
1 files changed, 200 insertions, 33 deletions
diff --git a/lib/libc/gen/opendir.c b/lib/libc/gen/opendir.c
index a3db45b..8c6afc6 100644
--- a/lib/libc/gen/opendir.c
+++ b/lib/libc/gen/opendir.c
@@ -32,11 +32,12 @@
*/
#if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)opendir.c 8.2 (Berkeley) 2/12/94";
+static char sccsid[] = "@(#)opendir.c 8.8 (Berkeley) 5/1/95";
#endif /* LIBC_SCCS and not lint */
#include <sys/param.h>
#include <sys/stat.h>
+#include <sys/mount.h>
#include <dirent.h>
#include <errno.h>
@@ -45,62 +46,228 @@ static char sccsid[] = "@(#)opendir.c 8.2 (Berkeley) 2/12/94";
#include <unistd.h>
/*
- * open a directory.
+ * Open a directory.
*/
DIR *
opendir(name)
const char *name;
{
- register DIR *dirp;
- register int fd;
- int saved_errno;
- struct stat sb;
+ return (__opendir2(name, DTF_HIDEW|DTF_NODUP));
+}
+
+DIR *
+__opendir2(name, flags)
+ const char *name;
+ int flags;
+{
+ DIR *dirp;
+ int fd;
+ int incr;
+ int unionstack;
+ struct stat statb;
/*
* stat() before open() because opening of special files may be
* harmful. fstat() after open because the file may have changed.
*/
- if (stat(name, &sb) != 0)
+ if (stat(name, &statb) != 0)
return NULL;
- if (!S_ISDIR(sb.st_mode)) {
+ if (!S_ISDIR(statb.st_mode)) {
errno = ENOTDIR;
return NULL;
}
if ((fd = open(name, O_RDONLY | O_NONBLOCK)) == -1)
- return NULL;
- dirp = NULL;
- if (fstat(fd, &sb) != 0)
- goto fail;
- if (!S_ISDIR(sb.st_mode)) {
+ return (NULL);
+ if (fstat(fd, &statb) || !S_ISDIR(statb.st_mode)) {
errno = ENOTDIR;
- goto fail;
+ close(fd);
+ return (NULL);
}
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 ||
- (dirp = malloc(sizeof(DIR))) == NULL)
- goto fail;
+ (dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
+ close(fd);
+ return (NULL);
+ }
+
/*
* Use the system page size if that is a multiple of DIRBLKSIZ
- * this could speed things up in some cases we hope
+ * this could speed things up in some cases.
+ * Hopefully this can be a big win someday by allowing page
+ * trades to user space to be done by getdirentries().
*/
- dirp->dd_len = getpagesize();
- if ((dirp->dd_len % DIRBLKSIZ) != 0)
- dirp->dd_len = DIRBLKSIZ;
- dirp->dd_buf = malloc(dirp->dd_len);
- if (dirp->dd_buf == NULL)
- goto fail;
- dirp->dd_fd = fd;
+ incr = getpagesize();
+ if ((incr % DIRBLKSIZ) != 0)
+ incr = DIRBLKSIZ;
+
+ /*
+ * Determine whether this directory is the top of a union stack.
+ */
+ if (flags & DTF_NODUP) {
+ struct statfs sfb;
+
+ if (fstatfs(fd, &sfb) < 0) {
+ free(dirp);
+ close(fd);
+ return (NULL);
+ }
+ unionstack = !strcmp(sfb.f_fstypename, "union");
+ } else {
+ unionstack = 0;
+ }
+
+ if (unionstack) {
+ int len = 0;
+ int space = 0;
+ char *buf = 0;
+ char *ddptr = 0;
+ char *ddeptr;
+ int n;
+ struct dirent **dpv;
+
+ /*
+ * The strategy here is to read all the directory
+ * entries into a buffer, sort the buffer, and
+ * remove duplicate entries by setting the inode
+ * number to zero.
+ */
+
+ do {
+ /*
+ * Always make at least DIRBLKSIZ bytes
+ * available to getdirentries
+ */
+ if (space < DIRBLKSIZ) {
+ space += incr;
+ len += incr;
+ buf = realloc(buf, len);
+ if (buf == NULL) {
+ free(dirp);
+ close(fd);
+ return (NULL);
+ }
+ ddptr = buf + (len - space);
+ }
+
+ n = getdirentries(fd, ddptr, space, &dirp->dd_seek);
+ if (n > 0) {
+ ddptr += n;
+ space -= n;
+ }
+ } while (n > 0);
+
+ ddeptr = ddptr;
+ flags |= __DTF_READALL;
+
+ /*
+ * Re-open the directory.
+ * This has the effect of rewinding back to the
+ * top of the union stack and is needed by
+ * programs which plan to fchdir to a descriptor
+ * which has also been read -- see fts.c.
+ */
+ if (flags & DTF_REWIND) {
+ (void) close(fd);
+ if ((fd = open(name, O_RDONLY)) == -1) {
+ free(buf);
+ free(dirp);
+ return (NULL);
+ }
+ }
+
+ /*
+ * There is now a buffer full of (possibly) duplicate
+ * names.
+ */
+ dirp->dd_buf = buf;
+
+ /*
+ * Go round this loop twice...
+ *
+ * Scan through the buffer, counting entries.
+ * On the second pass, save pointers to each one.
+ * Then sort the pointers and remove duplicate names.
+ */
+ for (dpv = 0;;) {
+ n = 0;
+ ddptr = buf;
+ while (ddptr < ddeptr) {
+ struct dirent *dp;
+
+ dp = (struct dirent *) ddptr;
+ if ((int)dp & 03)
+ break;
+ if ((dp->d_reclen <= 0) ||
+ (dp->d_reclen > (ddeptr + 1 - ddptr)))
+ break;
+ ddptr += dp->d_reclen;
+ if (dp->d_fileno) {
+ if (dpv)
+ dpv[n] = dp;
+ n++;
+ }
+ }
+
+ if (dpv) {
+ struct dirent *xp;
+
+ /*
+ * This sort must be stable.
+ */
+ mergesort(dpv, n, sizeof(*dpv), alphasort);
+
+ dpv[n] = NULL;
+ xp = NULL;
+
+ /*
+ * Scan through the buffer in sort order,
+ * zapping the inode number of any
+ * duplicate names.
+ */
+ for (n = 0; dpv[n]; n++) {
+ struct dirent *dp = dpv[n];
+
+ if ((xp == NULL) ||
+ strcmp(dp->d_name, xp->d_name)) {
+ xp = dp;
+ } else {
+ dp->d_fileno = 0;
+ }
+ if (dp->d_type == DT_WHT &&
+ (flags & DTF_HIDEW))
+ dp->d_fileno = 0;
+ }
+
+ free(dpv);
+ break;
+ } else {
+ dpv = malloc((n+1) * sizeof(struct dirent *));
+ if (dpv == NULL)
+ break;
+ }
+ }
+
+ dirp->dd_len = len;
+ dirp->dd_size = ddptr - dirp->dd_buf;
+ } else {
+ dirp->dd_len = incr;
+ dirp->dd_buf = malloc(dirp->dd_len);
+ if (dirp->dd_buf == NULL) {
+ free(dirp);
+ close (fd);
+ return (NULL);
+ }
+ dirp->dd_seek = 0;
+ flags &= ~DTF_REWIND;
+ }
+
dirp->dd_loc = 0;
- dirp->dd_seek = 0;
+ dirp->dd_fd = fd;
+ dirp->dd_flags = flags;
+
/*
* Set up seek point for rewinddir.
*/
dirp->dd_rewind = telldir(dirp);
- return dirp;
-
-fail:
- saved_errno = errno;
- free(dirp);
- close(fd);
- errno = saved_errno;
- return NULL;
+
+ return (dirp);
}
OpenPOWER on IntegriCloud