123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693 |
- /*
- opendir -- open a directory stream
-
- last edit: 16-Jun-1987 D A Gwyn
- */
- #include <sys/errno.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include "paxdir.h"
- #ifdef BSD_SYSV
- /*
- <sys/_dir.h> -- definitions for 4.2,4.3BSD directories
-
- last edit: 25-Apr-1987 D A Gwyn
-
- A directory consists of some number of blocks of DIRBLKSIZ bytes each,
- where DIRBLKSIZ is chosen such that it can be transferred to disk in a
- single atomic operation (e.g., 512 bytes on most machines).
-
- Each DIRBLKSIZ-byte block contains some number of directory entry
- structures, which are of variable length. Each directory entry has the
- beginning of a (struct direct) at the front of it, containing its
- filesystem-unique ident number, the length of the entry, and the length
- of the name contained in the entry. These are followed by the NUL-
- terminated name padded to a (long) boundary with 0 bytes. The maximum
- length of a name in a directory is MAXNAMELEN.
-
- The macro DIRSIZ(dp) gives the amount of space required to represent a
- directory entry. Free space in a directory is represented by entries
- that have dp->d_reclen > DIRSIZ(dp). All DIRBLKSIZ bytes in a
- directory block are claimed by the directory entries; this usually
- results in the last entry in a directory having a large dp->d_reclen.
- When entries are deleted from a directory, the space is returned to the
- previous entry in the same directory block by increasing its
- dp->d_reclen. If the first entry of a directory block is free, then
- its dp->d_fileno is set to 0; entries other than the first in a
- directory do not normally have dp->d_fileno set to 0.
-
- prerequisite: <sys/types.h>
- */
- #if defined(accel) || defined(sun) || defined(vax)
- #define DIRBLKSIZ 512 /* size of directory block */
- #else
- #ifdef alliant
- #define DIRBLKSIZ 4096 /* size of directory block */
- #else
- #ifdef gould
- #define DIRBLKSIZ 1024 /* size of directory block */
- #else
- #ifdef ns32000 /* Dynix System V */
- #define DIRBLKSIZ 2600 /* size of directory block */
- #else /* be conservative; multiple blocks are okay
- * but fractions are not */
- #define DIRBLKSIZ 4096 /* size of directory block */
- #endif
- #endif
- #endif
- #endif
- #define MAXNAMELEN 255 /* maximum filename length */
- /* NOTE: not MAXNAMLEN, which has been preempted by SVR3 <dirent.h> */
- struct direct { /* data from read()/_getdirentries() */
- unsigned long d_fileno; /* unique ident of entry */
- unsigned short d_reclen; /* length of this record */
- unsigned short d_namlen; /* length of string in d_name */
- char d_name[MAXNAMELEN + 1]; /* NUL-terminated filename */
- };
- /*
- The DIRSIZ macro gives the minimum record length which will hold the
- directory entry. This requires the amount of space in a (struct
- direct) without the d_name field, plus enough space for the name with a
- terminating NUL character, rounded up to a (long) boundary.
-
- (Note that Berkeley didn't properly compensate for struct padding,
- but we nevertheless have to use the same size as the actual system.)
- */
- #define DIRSIZ( dp ) ((sizeof(struct direct) - (MAXNAMELEN+1) \
- + sizeof(long) + (dp)->d_namlen) \
- / sizeof(long) * sizeof(long))
- #else
- #include <sys/dir.h>
- #ifdef SYSV3
- #undef MAXNAMLEN /* avoid conflict with SVR3 */
- #endif
- /* Good thing we don't need to use the DIRSIZ() macro! */
- #ifdef d_ino /* 4.3BSD/NFS using d_fileno */
- #undef d_ino /* (not absolutely necessary) */
- #else
- #define d_fileno d_ino /* (struct direct) member */
- #endif
- #endif
- #ifdef UNK
- #ifndef UFS
- #include "***** ERROR ***** UNK applies only to UFS"
- /* One could do something similar for getdirentries(), but I didn't bother. */
- #endif
- #include <signal.h>
- #endif
- #if defined(UFS) + defined(BFS) + defined(NFS) != 1 /* sanity check */
- #include "***** ERROR ***** exactly one of UFS, BFS, or NFS must be defined"
- #endif
- #ifdef UFS
- #define RecLen( dp ) (sizeof(struct direct)) /* fixed-length entries */
- #else /* BFS || NFS */
- #define RecLen( dp ) ((dp)->d_reclen) /* variable-length entries */
- #endif
- #ifdef NFS
- #ifdef BSD_SYSV
- #define getdirentries _getdirentries /* package hides this system call */
- #endif
- extern int getdirentries();
- static long dummy; /* getdirentries() needs basep */
- #define GetBlock( fd, buf, n ) getdirentries( fd, buf, (unsigned)n, &dummy )
- #else /* UFS || BFS */
- #ifdef BSD_SYSV
- #define read _read /* avoid emulation overhead */
- #endif
- extern int read();
- #define GetBlock( fd, buf, n ) read( fd, buf, (unsigned)n )
- #endif
- #ifdef UNK
- extern int _getdents(); /* actual system call */
- #endif
- extern char *strncpy();
- extern int fstat();
- extern OFFSET lseek();
- extern int errno;
- #ifndef DIRBLKSIZ
- #define DIRBLKSIZ 4096 /* directory file read buffer size */
- #endif
- #ifndef NULL
- #define NULL 0
- #endif
- #ifndef SEEK_CUR
- #define SEEK_CUR 1
- #endif
- #ifndef S_ISDIR /* macro to test for directory file */
- #define S_ISDIR( mode ) (((mode) & S_IFMT) == S_IFDIR)
- #endif
- #ifndef SEEK_CUR
- #define SEEK_CUR 1
- #endif
- #ifdef BSD_SYSV
- #define open _open /* avoid emulation overhead */
- #endif
- extern int getdents(); /* SVR3 system call, or emulation */
- typedef char *pointer; /* (void *) if you have it */
- extern void free();
- extern pointer malloc();
- extern int
- open(), close(), fstat();
- extern int errno;
- extern OFFSET lseek();
- #ifndef SEEK_SET
- #define SEEK_SET 0
- #endif
- typedef int bool; /* Boolean data type */
- #define false 0
- #define true 1
- #ifndef NULL
- #define NULL 0
- #endif
- #ifndef O_RDONLY
- #define O_RDONLY 0
- #endif
- #ifndef S_ISDIR /* macro to test for directory file */
- #define S_ISDIR( mode ) (((mode) & S_IFMT) == S_IFDIR)
- #endif
- #ifdef __STDC__
- DIR *opendir(char *dirname)
- #else
-
- DIR *opendir(dirname)
- char *dirname; /* name of directory */
- #endif
- {
- register DIR *dirp; /* -> malloc'ed storage */
- register int fd; /* file descriptor for read */
- struct stat sbuf; /* result of fstat() */
- if ((fd = open(dirname, O_RDONLY)) < 0)
- return ((DIR *)NULL); /* errno set by open() */
- if (fstat(fd, &sbuf) != 0 || !S_ISDIR(sbuf.st_mode)) {
- close(fd);
- errno = ENOTDIR;
- return ((DIR *)NULL); /* not a directory */
- }
- if ((dirp = (DIR *) malloc(sizeof(DIR))) == (DIR *)NULL
- || (dirp->dd_buf = (char *) malloc((unsigned) DIRBUF)) == (char *)NULL
- ) {
- register int serrno = errno;
- /* errno set to ENOMEM by sbrk() */
- if (dirp != (DIR *)NULL)
- free((pointer) dirp);
- close(fd);
- errno = serrno;
- return ((DIR *)NULL); /* not enough memory */
- }
- dirp->dd_fd = fd;
- dirp->dd_loc = dirp->dd_size = 0; /* refill needed */
- return dirp;
- }
- /*
- * closedir -- close a directory stream
- *
- * last edit: 11-Nov-1988 D A Gwyn
- */
- #ifdef __STDC__
- int closedir(register DIR *dirp)
- #else
-
- int closedir(dirp)
- register DIR *dirp; /* stream from opendir() */
- #endif
- {
- register int fd;
- if ( dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL ) {
- errno = EFAULT;
- return -1; /* invalid pointer */
- }
- fd = dirp->dd_fd; /* bug fix thanks to R. Salz */
- free( (pointer)dirp->dd_buf );
- free( (pointer)dirp );
- return close( fd );
- }
- /*
- readdir -- read next entry from a directory stream
-
- last edit: 25-Apr-1987 D A Gwyn
- */
- #ifdef __STDC__
- struct dirent *readdir(register DIR *dirp)
- #else
-
- struct dirent *readdir(dirp)
- register DIR *dirp; /* stream from opendir() */
- #endif
- {
- register struct dirent *dp; /* -> directory data */
- if (dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL) {
- errno = EFAULT;
- return (struct dirent *)NULL; /* invalid pointer */
- }
- do {
- if (dirp->dd_loc >= dirp->dd_size) /* empty or obsolete */
- dirp->dd_loc = dirp->dd_size = 0;
- if (dirp->dd_size == 0 /* need to refill buffer */
- && (dirp->dd_size =
- getdents(dirp->dd_fd, dirp->dd_buf, (unsigned) DIRBUF)
- ) <= 0
- )
- return ((struct dirent *)NULL); /* EOF or error */
- dp = (struct dirent *) & dirp->dd_buf[dirp->dd_loc];
- dirp->dd_loc += dp->d_reclen;
- }
- while (dp->d_ino == 0L); /* don't rely on getdents() */
- return dp;
- }
- /*
- seekdir -- reposition a directory stream
-
- last edit: 24-May-1987 D A Gwyn
-
- An unsuccessful seekdir() will in general alter the current
- directory position; beware.
-
- NOTE: 4.nBSD directory compaction makes seekdir() & telldir()
- practically impossible to do right. Avoid using them!
- */
- #ifdef __STDC__
- void seekdir(register DIR *dirp, register OFFSET loc)
- #else
-
- void seekdir(dirp, loc)
- register DIR *dirp; /* stream from opendir() */
- register OFFSET loc; /* position from telldir() */
- #endif
- {
- register bool rewind; /* "start over when stymied" flag */
- if (dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL) {
- errno = EFAULT;
- return; /* invalid pointer */
- }
- /*
- * A (struct dirent)'s d_off is an invented quantity on 4.nBSD
- * NFS-supporting systems, so it is not safe to lseek() to it.
- */
- /* Monotonicity of d_off is heavily exploited in the following. */
- /*
- * This algorithm is tuned for modest directory sizes. For huge
- * directories, it might be more efficient to read blocks until the first
- * d_off is too large, then back up one block, or even to use binary
- * search on the directory blocks. I doubt that the extra code for that
- * would be worthwhile.
- */
- if (dirp->dd_loc >= dirp->dd_size /* invalid index */
- || ((struct dirent *) & dirp->dd_buf[dirp->dd_loc])->d_off > loc
- /* too far along in buffer */
- )
- dirp->dd_loc = 0; /* reset to beginning of buffer */
- /* else save time by starting at current dirp->dd_loc */
- for (rewind = true;;) {
- register struct dirent *dp;
- /* See whether the matching entry is in the current buffer. */
- if ((dirp->dd_loc < dirp->dd_size /* valid index */
- || readdir(dirp) != (struct dirent *)NULL /* next buffer read */
- && (dirp->dd_loc = 0, true) /* beginning of buffer set */
- )
- && (dp = (struct dirent *) & dirp->dd_buf[dirp->dd_loc])->d_off
- <= loc /* match possible in this buffer */
- ) {
- for ( /* dp initialized above */ ;
- (char *) dp < &dirp->dd_buf[dirp->dd_size];
- dp = (struct dirent *) ((char *) dp + dp->d_reclen)
- )
- if (dp->d_off == loc) { /* found it! */
- dirp->dd_loc =
- (char *) dp - dirp->dd_buf;
- return;
- }
- rewind = false; /* no point in backing up later */
- dirp->dd_loc = dirp->dd_size; /* set end of buffer */
- } else
- /* whole buffer past matching entry */ if (!rewind) { /* no point in searching
- * further */
- errno = EINVAL;
- return; /* no entry at specified loc */
- } else { /* rewind directory and start over */
- rewind = false; /* but only once! */
- dirp->dd_loc = dirp->dd_size = 0;
- if (lseek(dirp->dd_fd, (OFFSET) 0, SEEK_SET)
- != 0
- )
- return; /* errno already set (EBADF) */
- if (loc == 0)
- return; /* save time */
- }
- }
- }
- /* telldir - report directory stream position
- *
- * DESCRIPTION
- *
- * Returns the offset of the next directory entry in the
- * directory associated with dirp.
- *
- * NOTE: 4.nBSD directory compaction makes seekdir() & telldir()
- * practically impossible to do right. Avoid using them!
- *
- * PARAMETERS
- *
- * DIR *dirp - stream from opendir()
- *
- * RETURNS
- *
- * Return offset of next entry
- */
- #ifdef __STDC__
- OFFSET telldir(DIR *dirp)
- #else
-
- OFFSET telldir(dirp)
- DIR *dirp; /* stream from opendir() */
- #endif
- {
- if (dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL) {
- errno = EFAULT;
- return -1; /* invalid pointer */
- }
- if (dirp->dd_loc < dirp->dd_size) /* valid index */
- return ((struct dirent *) & dirp->dd_buf[dirp->dd_loc])->d_off;
- else /* beginning of next directory block */
- return lseek(dirp->dd_fd, (OFFSET) 0, SEEK_CUR);
- }
- #ifdef UFS
- /*
- The following routine is necessary to handle DIRSIZ-long entry names.
- Thanks to Richard Todd for pointing this out.
- */
- /* return # chars in embedded name */
- #ifdef __STDC__
- static int NameLen(char *name)
- #else
-
- static int NameLen(name)
- char *name; /* -> name embedded in struct direct */
- #endif
- {
- register char *s; /* -> name[.] */
- register char *stop = &name[DIRSIZ]; /* -> past end of name field */
- for (s = &name[1]; /* (empty names are impossible) */
- *s != '\0' /* not NUL terminator */
- && ++s < stop; /* < DIRSIZ characters scanned */
- );
- return s - name; /* # valid characters in name */
- }
- #else /* BFS || NFS */
- extern int strlen();
- #define NameLen( name ) strlen( name ) /* names are always NUL-terminated */
- #endif
- #ifdef UNK
- static enum {
- maybe, no, yes
- } state = maybe;
- /* sig_catch - used to catch signals
- *
- * DESCRIPTION
- *
- * Used to catch signals.
- */
- /*ARGSUSED*/
- #ifdef __STDC__
- static void sig_catch(int sig)
- #else
-
- static void sig_catch(sig)
- int sig; /* must be SIGSYS */
- #endif
- {
- state = no; /* attempted _getdents() faulted */
- }
- #endif
- /* getdents - get directory entries
- *
- * DESCRIPTION
- *
- * Gets directory entries from the filesystem in an implemenation
- * defined way.
- *
- * PARAMETERS
- *
- * int fildes - directory file descriptor
- * char *buf - where to put the (struct dirent)s
- * unsigned nbyte - size of buf[]
- *
- * RETURNS
- *
- * Returns number of bytes read; 0 on EOF, -1 on error
- */
- #ifdef __STDC__
- int getdents(int fildes, char *buf, unsigned nbyte)
- #else
-
- int getdents(fildes, buf, nbyte)
- int fildes; /* directory file descriptor */
- char *buf; /* where to put the (struct dirent)s */
- unsigned nbyte; /* size of buf[] */
- #endif
- {
- int serrno; /* entry errno */
- OFFSET offset; /* initial directory file offset */
- struct stat statb; /* fstat() info */
- union {
- /* directory file block buffer */
- #ifdef UFS
- char dblk[DIRBLKSIZ + 1];
- #else
- char dblk[DIRBLKSIZ];
- #endif
- struct direct dummy; /* just for alignment */
- } u; /* (avoids having to malloc()) */
- register struct direct *dp; /* -> u.dblk[.] */
- register struct dirent *bp; /* -> buf[.] */
- #ifdef UNK
- switch (state) {
- SIG_T (*shdlr)(); /* entry SIGSYS handler */
- register int retval; /* return from _getdents() if any */
- case yes: /* _getdents() is known to work */
- return _getdents(fildes, buf, nbyte);
- case maybe: /* first time only */
- shdlr = signal(SIGSYS, sig_catch);
- retval = _getdents(fildes, buf, nbyte); /* try it */
- signal(SIGSYS, shdlr);
- if (state == maybe) { /* SIGSYS did not occur */
- state = yes; /* so _getdents() must have worked */
- return retval;
- }
- /* else fall through into emulation */
- /* case no: /* fall through into emulation */
- }
- #endif
- if (buf == (char *)NULL
- #ifdef ATT_SPEC
- || (unsigned long) buf % sizeof(long) != 0 /* ugh */
- #endif
- ) {
- errno = EFAULT; /* invalid pointer */
- return -1;
- }
- if (fstat(fildes, &statb) != 0) {
- return -1; /* errno set by fstat() */
- }
- if (!S_ISDIR(statb.st_mode)) {
- errno = ENOTDIR; /* not a directory */
- return -1;
- }
- if ((offset = lseek(fildes, (OFFSET) 0, SEEK_CUR)) < 0) {
- return -1; /* errno set by lseek() */
- }
- #ifdef BFS /* no telling what remote hosts do */
- if ((unsigned long) offset % DIRBLKSIZ != 0) {
- errno = ENOENT; /* file pointer probably misaligned */
- return -1;
- }
- #endif
- serrno = errno; /* save entry errno */
- for (bp = (struct dirent *) buf; bp == (struct dirent *) buf;) {
- /* convert next directory block */
- int size;
- do {
- size = GetBlock(fildes, u.dblk, DIRBLKSIZ);
- } while (size == -1 && errno == EINTR);
- if (size <= 0) {
- return size; /* EOF or error (EBADF) */
- }
- for (dp = (struct direct *) u.dblk;
- (char *) dp < &u.dblk[size];
- dp = (struct direct *) ((char *) dp + RecLen(dp))
- ) {
- #ifndef UFS
- if (dp->d_reclen <= 0) {
- errno = EIO; /* corrupted directory */
- return -1;
- }
- #endif
- if (dp->d_fileno != 0) { /* non-empty; copy to user buffer */
- register int reclen =
- DIRENTSIZ(NameLen(dp->d_name));
- if ((char *) bp + reclen > &buf[nbyte]) {
- errno = EINVAL;
- return -1; /* buf too small */
- }
- bp->d_ino = dp->d_fileno;
- bp->d_off = offset + ((char *) dp - u.dblk);
- bp->d_reclen = reclen;
- {
- #ifdef UFS
- /* Is the following kludge ugly? You bet. */
- register char save = dp->d_name[DIRSIZ];
- /* save original data */
- dp->d_name[DIRSIZ] = '\0';
- /* ensure NUL termination */
- #endif
- /* adds NUL padding */
- strncpy(bp->d_name, dp->d_name, reclen - DIRENTBASESIZ);
- #ifdef UFS
- dp->d_name[DIRSIZ] = save;
- /* restore original data */
- #endif
- }
- bp = (struct dirent *) ((char *) bp + reclen);
- }
- }
- #ifndef BFS /* 4.2BSD screwed up; fixed in 4.3BSD */
- if ((char *) dp > &u.dblk[size]) {
- errno = EIO; /* corrupted directory */
- return -1;
- }
- #endif
- }
- errno = serrno; /* restore entry errno */
- return (char *) bp - buf; /* return # bytes read */
- }
|