#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dir.h" #if 0 /* from sys/dirents.h for reference only */ struct dirent { __uint32_t d_fileno; /* file number of entry */ __uint16_t d_reclen; /* length of this record */ __uint8_t d_type; /* file type, see below */ __uint8_t d_namlen; /* length of string in d_name */ #if __BSD_VISIBLE #define MAXNAMLEN 255 char d_name [MAXNAMLEN + 1]; /* name must be no * longer than this */ #else char d_name [255 + 1]; /* name must be no longer * than this */ #endif }; #endif /* from /usr/src/lib/libc/gen/gen-private.h for reference only */ /*- * Structure describing an open directory. * * NOTE. Change structure layout with care, at least dd_fd field has to * remain unchanged to guarantee backward compatibility. */ struct _dirdesc { int dd_fd; /* file descriptor associated with directory */ long dd_loc; /* offset in current buffer */ long dd_size;/* amount of data returned by getdirentries */ char *dd_buf; /* data buffer */ int dd_len; /* size of data buffer */ long dd_seek;/* magic cookie returned by getdirentries */ int dd_flags; /* flags for readdir */ struct pthread_mutex *dd_lock; /* lock */ //struct _telldir *dd_td; /* telldir position recording */ }; #define _dirfd(dirp) ((dirp)->dd_fd) #define DIR_INIT_LOCK(_dirp) do {dirp->dd_lock = NULL; } while (0) #define DIR_LOCK(_dirp) \ do {if (__isthreaded) pthread_mutex_lock(&dirp->dd_lock); } while (0) #define DIR_UNLOCK(_dirp) \ do {if (__isthreaded) pthread_mutex_unlock(&dirp->dd_lock); } while (0) #define DIR_DESTROY_LOCK(_dirp) \ do {if (__isthreaded) pthread_mutex_destroy(&dirp->dd_lock); } while (0) #define __set_errno(_val) do { errno = (_val); } while (0) /* Read a directory entry from DIRP. */ struct dirent * xreaddir(DIR * dirp) { struct dirent *dp; int saved_errno = errno; size_t reclen; size_t maxread; ssize_t bytes; DIR_LOCK(dirp); do { if (dirp->dd_loc >= dirp->dd_size) { /* We've emptied out our buffer. Refill it. */ maxread = dirp->dd_len; bytes = getdents(dirp->dd_fd, dirp->dd_buf, maxread); if (bytes <= 0) { /* * On some systems getdents fails with ENOENT * when the open directory has been rmdir'd * already. POSIX.1 requires that we treat * this condition like normal EOF. */ if (bytes < 0 && errno == ENOENT) bytes = 0; /* Don't modifiy errno when reaching EOF. */ if (bytes == 0) __set_errno(saved_errno); dp = NULL; break; } dirp->dd_size = (size_t) bytes; /* Reset the offset into the buffer. */ dirp->dd_loc = 0; } dp = (struct dirent *)&dirp->dd_buf[dirp->dd_loc]; reclen = dp->d_reclen; dirp->dd_loc += reclen; dirp->dd_seek += reclen; /* Skip deleted files. */ } while (dp->d_ino == 0); DIR_UNLOCK(dirp); return dp; } /* Seek to position POS in DIRP. */ /* XXX should be __seekdir ? */ void xseekdir(dirp, pos) DIR *dirp; long int pos; { DIR_LOCK(dirp); (void)lseek(dirp->dd_fd, pos, SEEK_SET); dirp->dd_size = 0; dirp->dd_loc = 0; dirp->dd_seek = pos; DIR_UNLOCK(dirp); } long int xtelldir(DIR * dirp) { return dirp->dd_seek; } /* * The st_blksize value of the directory is used as a hint for the size of * the buffer which receives struct dirent values from the kernel. st_blksize * is limited to MAX_DIR_BUFFER_SIZE, in case the file system provides a * bogus value. */ #define MAX_DIR_BUFFER_SIZE 1048576U /* opendir() must not accidentally open something other than a directory. */ /* Open a directory stream on NAME. */ DIR * xopendir(const char *name) { struct stat sb; struct stat *statp = NULL; DIR *dirp; int fd; int incr; if (name[0] == '\0') { /* * POSIX.1-1990 says an empty name gets ENOENT; but `open' * might like it fine. */ __set_errno(ENOENT); return NULL; } /* * We first have to check whether the name is for a directory. We * cannot do this after the open() call since the open/close * operation performed on, say, a tape device might have undesirable * effects. */ if (stat(name, &sb) < 0) return NULL; if (!S_ISDIR(sb.st_mode)) { __set_errno(ENOTDIR); return NULL; } if ((fd = open(name, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC)) == -1) return (NULL); /* * Now make sure this really is a directory and nothing changed since * the `stat' call. */ if ((fstat(fd, &sb) < 0) || (!S_ISDIR(sb.st_mode))) { __set_errno(ENOTDIR); close(fd); return NULL; } statp = &sb; /* Now allocate the actual DIR */ if ((dirp = malloc(sizeof(DIR) /* + sizeof(struct _telldir) */ )) == NULL) return (NULL); dirp->dd_buf = NULL; dirp->dd_fd = fd; //dirp->dd_flags = flags; dirp->dd_loc = 0; dirp->dd_lock = NULL; //dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR)); //LIST_INIT(&dirp->dd_td->td_locq); //dirp->dd_td->td_loccnt = 0; /* * Use the system page size if that is a multiple of DIRBLKSIZ. * Hopefully this can be a big win someday by allowing page trades to * user space to be done by _getdirentries(). */ incr = getpagesize(); if ((incr % sb.st_blksize) != 0) incr = sb.st_blksize; incr = MIN(incr, MAX_DIR_BUFFER_SIZE); printf("blksiz is %d, using %d\n", sb.st_blksize, incr); dirp->dd_len = incr; dirp->dd_buf = malloc(dirp->dd_len); if (dirp->dd_buf == NULL) goto fail; DIR_INIT_LOCK(dirp); dirp->dd_size = 0; dirp->dd_loc = 0; dirp->dd_seek = 0; return dirp; fail: free(dirp); return NULL; } /* * Close the directory stream DIRP. Return 0 if successful, -1 if not. */ int xclosedir(DIR * dirp) { int fd; if (dirp == NULL) { __set_errno(EINVAL); return -1; } /* * We do not try to synchronize access here. If some other thread * still uses this handle it is a big mistake and that thread * deserves all the bad data it gets. */ fd = dirp->dd_fd; DIR_DESTROY_LOCK(dirp); free((void *)dirp); return close(fd); } /* Rewind DIRP to the beginning of the directory. */ void xrewinddir(dirp) DIR *dirp; { DIR_LOCK(dirp); (void)lseek(dirp->dd_fd, (off_t) 0, SEEK_SET); dirp->dd_seek = 0; dirp->dd_loc = 0; dirp->dd_size = 0; DIR_UNLOCK(dirp); }