2 getdents -- get directory entries in a file system independent format
3 (SVR3 system call emulation)
5 last edit: 06-Jul-1987 D A Gwyn
7 This single source file supports several different methods of
8 getting directory entries from the operating system. Define
9 whichever one of the following describes your system:
11 UFS original UNIX filesystem (14-character name limit)
12 BFS 4.2BSD (also 4.3BSD) native filesystem (long names)
13 NFS getdirentries() system call
15 Also define any of the following that are pertinent:
17 ATT_SPEC check user buffer address for longword alignment
18 BSD_SYSV BRL UNIX System V emulation environment on 4.nBSD
19 UNK have _getdents() system call, but kernel may not
22 If your C library has a getdents() system call interface, but you
23 can't count on all kernels on which your application binaries may
24 run to support it, change the system call interface name to
25 _getdents() and define "UNK" to enable the system-call validity
26 test in this "wrapper" around _getdents().
28 If your system has a getdents() system call that is guaranteed
29 to always work, you shouldn't be using this source file at all.
35 #include <sys/errno.h>
36 #include <sys/types.h>
38 #include <sys/_dir.h> /* BSD flavor, not System V */
41 #define DIRSIZ 14 /* 14 char filename in Version 7 */
45 off_t d_off; /* offset of next disk directory entry */
46 u_long d_fileno; /* file number of entry */
47 u_short d_reclen; /* length of this record */
48 u_short d_namlen; /* length of string in d_name */
49 char d_name[MAXNAMLEN + 1]; /* name (up to MAXNAMLEN + 1) */
51 #undef MAXNAMLEN /* avoid conflict with SVR3 */
53 #define d_ino d_fileno /* compatability */
55 #ifdef d_ino /* 4.3BSD/NFS using d_fileno */
56 #undef d_ino /* (not absolutely necessary) */
58 #define d_fileno d_ino /* (struct direct) member */
61 #include <sys/dirent.h>
65 #error UNK applies only to UFS
66 /* One could do something similar for getdirentries(), but I didn't bother. */
71 #if defined(UFS) + defined(BFS) + defined(NFS) != 1 /* sanity check */
72 #error exactly one of UFS, BFS, or NFS must be defined
76 #define RecLen( dp ) (sizeof(struct direct)) /* fixed-length entries */
77 #else /* BFS || NFS */
78 #define RecLen( dp ) ((dp)->d_reclen) /* variable-length entries */
83 #define getdirentries _getdirentries /* package hides this system call */
85 extern int getdirentries(int fd, char *buf, int nbytes, long *basep);
86 static long dummy; /* getdirentries() needs basep */
87 #define GetBlock( fd, buf, n ) getdirentries( fd, buf, (unsigned)n, &dummy )
88 #else /* UFS || BFS */
90 #define read _read /* avoid emulation overhead */
93 #define GetBlock( fd, buf, n ) read( fd, buf, (unsigned)n )
97 extern int _getdents(); /* actual system call */
100 extern int _fstat(int fd, struct stat *buf);
101 extern off_t _lseek(int d, int offset, int whence);
104 #define DIRBLKSIZ 4096 /* directory file read buffer size */
115 #ifndef S_ISDIR /* macro to test for directory file */
116 #define S_ISDIR( mode ) (((mode) & S_IFMT) == S_IFDIR)
122 The following routine is necessary to handle DIRSIZ-long entry names.
123 Thanks to Richard Todd for pointing this out.
127 NameLen( char name[] ) /* return # chars in embedded name */
128 /* -> name embedded in struct direct */
130 register char *s; /* -> name[.] */
131 register char *stop = &name[DIRSIZ]; /* -> past end of name field */
133 for ( s = &name[1]; /* (empty names are impossible) */
134 *s != '\0' /* not NUL terminator */
135 && ++s < stop; /* < DIRSIZ characters scanned */
139 return s - name; /* # valid characters in name */
142 #else /* BFS || NFS */
144 #define NameLen( name ) strlen( name ) /* names are always NUL-terminated */
149 static enum { maybe, no, yes } state = maybe;
150 /* does _getdents() work? */
154 sig_catch(int sig) /* sig must be SIGSYS */
156 state = no; /* attempted _getdents() faulted */
161 getdents(int fildes, char *buf, unsigned nbyte) /* returns # bytes read;
162 0 on EOF, -1 on error */
163 /* fildes == directory file descriptor */
164 /* *buf == where to put the (struct dirent)s */
165 /* nbyte == size of buf[] */
167 int serrno; /* entry errno */
168 off_t offset; /* initial directory file offset */
169 struct stat statb; /* fstat() info */
171 char dblk[DIRBLKSIZ];
172 /* directory file block buffer */
173 struct direct dummy; /* just for alignment */
174 } u; /* (avoids having to malloc()) */
175 register struct direct *dp; /* -> u.dblk[.] */
176 register struct dirent *bp; /* -> buf[.] */
181 void (*shdlr)(); /* entry SIGSYS handler */
182 register int retval; /* return from _getdents() if any */
184 case yes: /* _getdents() is known to work */
185 return _getdents( fildes, buf, nbyte );
187 case maybe: /* first time only */
188 shdlr = signal( SIGSYS, sig_catch );
189 retval = _getdents( fildes, buf, nbyte ); /* try it */
190 (void)signal( SIGSYS, shdlr );
192 if ( state == maybe ) /* SIGSYS did not occur */
194 state = yes; /* so _getdents() must have worked */
197 /* else fall through into emulation */
199 /* case no:*/ /* fall through into emulation */
205 || (unsigned long)buf % sizeof(long) != 0 /* ugh */
208 errno = EFAULT; /* invalid pointer */
212 if ( _fstat( fildes, &statb ) != 0 )
213 return -1; /* errno set by fstat() */
215 if ( !S_ISDIR( statb.st_mode ) )
217 errno = ENOTDIR; /* not a directory */
221 if ( (offset = _lseek( fildes, (off_t)0, SEEK_CUR )) < 0 )
222 return -1; /* errno set by lseek() */
224 #ifdef BFS /* no telling what remote hosts do */
225 if ( (unsigned long)offset % DIRBLKSIZ != 0 )
227 errno = ENOENT; /* file pointer probably misaligned */
232 serrno = errno; /* save entry errno */
234 for ( bp = (struct dirent *)buf; bp == (struct dirent *)buf; )
235 { /* convert next directory block */
238 do size = GetBlock( fildes, u.dblk, DIRBLKSIZ );
239 while ( size == -1 && errno == EINTR );
242 return size; /* EOF or error (EBADF) */
244 for ( dp = (struct direct *)u.dblk;
245 (char *)dp < &u.dblk[size];
246 dp = (struct direct *)((char *)dp + RecLen( dp ))
249 if ( dp->d_reclen <= 0 )
251 errno = EIO; /* corrupted directory */
256 if ( dp->d_fileno != 0 )
257 { /* non-empty; copy to user buffer */
258 register int reclen =
259 DIRENTSIZ( NameLen( dp->d_name ) );
261 if ( (char *)bp + reclen > &buf[nbyte] )
264 return -1; /* buf too small */
267 bp->d_ino = dp->d_fileno;
268 bp->d_off = offset + ((char *)dp - u.dblk);
269 bp->d_reclen = reclen;
270 (void)strncpy( bp->d_name, dp->d_name,
271 reclen - DIRENTBASESIZ
272 ); /* adds NUL padding */
274 bp = (struct dirent *)((char *)bp + reclen);
278 #ifndef BFS /* 4.2BSD screwed up; fixed in 4.3BSD */
279 if ( (char *)dp > &u.dblk[size] )
281 errno = EIO; /* corrupted directory */
287 errno = serrno; /* restore entry errno */
288 return (char *)bp - buf; /* return # bytes read */