getdents.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. /*
  2. getdents -- get directory entries in a file system independent format
  3. (SVR3 system call emulation)
  4. last edit: 06-Jul-1987 D A Gwyn
  5. This single source file supports several different methods of
  6. getting directory entries from the operating system. Define
  7. whichever one of the following describes your system:
  8. UFS original UNIX filesystem (14-character name limit)
  9. BFS 4.2BSD (also 4.3BSD) native filesystem (long names)
  10. NFS getdirentries() system call
  11. Also define any of the following that are pertinent:
  12. ATT_SPEC check user buffer address for longword alignment
  13. BSD_SYSV BRL UNIX System V emulation environment on 4.nBSD
  14. UNK have _getdents() system call, but kernel may not
  15. support it
  16. If your C library has a getdents() system call interface, but you
  17. can't count on all kernels on which your application binaries may
  18. run to support it, change the system call interface name to
  19. _getdents() and define "UNK" to enable the system-call validity
  20. test in this "wrapper" around _getdents().
  21. If your system has a getdents() system call that is guaranteed
  22. to always work, you shouldn't be using this source file at all.
  23. */
  24. #include <errno.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #include <sys/errno.h>
  28. #include <sys/types.h>
  29. #ifdef BSD_SYSV
  30. #include <sys/_dir.h> /* BSD flavor, not System V */
  31. #else
  32. #if defined(UFS)
  33. #define DIRSIZ 14 /* 14 char filename in Version 7 */
  34. #endif
  35. #define MAXNAMLEN 255
  36. struct direct {
  37. off_t d_off; /* offset of next disk directory entry */
  38. u_long d_fileno; /* file number of entry */
  39. u_short d_reclen; /* length of this record */
  40. u_short d_namlen; /* length of string in d_name */
  41. char d_name[MAXNAMLEN + 1]; /* name (up to MAXNAMLEN + 1) */
  42. };
  43. #undef MAXNAMLEN /* avoid conflict with SVR3 */
  44. #define d_ino d_fileno /* compatability */
  45. #ifdef d_ino /* 4.3BSD/NFS using d_fileno */
  46. #undef d_ino /* (not absolutely necessary) */
  47. #else
  48. #define d_fileno d_ino /* (struct direct) member */
  49. #endif
  50. #endif
  51. #include <sys/dirent.h>
  52. #include <sys/stat.h>
  53. #ifdef UNK
  54. #ifndef UFS
  55. #error UNK applies only to UFS
  56. /* One could do something similar for getdirentries(), but I didn't bother. */
  57. #endif
  58. #include <signal.h>
  59. #endif
  60. #if defined(UFS) + defined(BFS) + defined(NFS) != 1 /* sanity check */
  61. #error exactly one of UFS, BFS, or NFS must be defined
  62. #endif
  63. #ifdef UFS
  64. #define RecLen( dp ) (sizeof(struct direct)) /* fixed-length entries */
  65. #else /* BFS || NFS */
  66. #define RecLen( dp ) ((dp)->d_reclen) /* variable-length entries */
  67. #endif
  68. #ifdef NFS
  69. #ifdef BSD_SYSV
  70. #define getdirentries _getdirentries /* package hides this system call */
  71. #endif
  72. extern int getdirentries(int fd, char *buf, int nbytes, long *basep);
  73. static long dummy; /* getdirentries() needs basep */
  74. #define GetBlock( fd, buf, n ) getdirentries( fd, buf, (unsigned)n, &dummy )
  75. #else /* UFS || BFS */
  76. #ifdef BSD_SYSV
  77. #define read _read /* avoid emulation overhead */
  78. #endif
  79. extern int read();
  80. #define GetBlock( fd, buf, n ) read( fd, buf, (unsigned)n )
  81. #endif
  82. #ifdef UNK
  83. extern int _getdents(); /* actual system call */
  84. #endif
  85. extern int _fstat(int fd, struct stat *buf);
  86. extern off_t _lseek(int d, int offset, int whence);
  87. #ifndef DIRBLKSIZ
  88. #define DIRBLKSIZ 4096 /* directory file read buffer size */
  89. #endif
  90. #ifndef NULL
  91. #define NULL 0
  92. #endif
  93. #ifndef SEEK_CUR
  94. #define SEEK_CUR 1
  95. #endif
  96. #ifndef S_ISDIR /* macro to test for directory file */
  97. #define S_ISDIR( mode ) (((mode) & S_IFMT) == S_IFDIR)
  98. #endif
  99. #ifdef UFS
  100. /*
  101. The following routine is necessary to handle DIRSIZ-long entry names.
  102. Thanks to Richard Todd for pointing this out.
  103. */
  104. static int
  105. NameLen( char name[] ) /* return # chars in embedded name */
  106. /* -> name embedded in struct direct */
  107. {
  108. register char *s; /* -> name[.] */
  109. register char *stop = &name[DIRSIZ]; /* -> past end of name field */
  110. for ( s = &name[1]; /* (empty names are impossible) */
  111. *s != '\0' /* not NUL terminator */
  112. && ++s < stop; /* < DIRSIZ characters scanned */
  113. )
  114. ;
  115. return s - name; /* # valid characters in name */
  116. }
  117. #else /* BFS || NFS */
  118. #define NameLen( name ) strlen( name ) /* names are always NUL-terminated */
  119. #endif
  120. #ifdef UNK
  121. static enum { maybe, no, yes } state = maybe;
  122. /* does _getdents() work? */
  123. /*ARGSUSED*/
  124. static void
  125. sig_catch(int sig) /* sig must be SIGSYS */
  126. {
  127. state = no; /* attempted _getdents() faulted */
  128. }
  129. #endif
  130. int
  131. getdents(int fildes, char *buf, unsigned nbyte) /* returns # bytes read;
  132. 0 on EOF, -1 on error */
  133. /* fildes == directory file descriptor */
  134. /* *buf == where to put the (struct dirent)s */
  135. /* nbyte == size of buf[] */
  136. {
  137. int serrno; /* entry errno */
  138. off_t offset; /* initial directory file offset */
  139. struct stat statb; /* fstat() info */
  140. union {
  141. char dblk[DIRBLKSIZ];
  142. /* directory file block buffer */
  143. struct direct dummy; /* just for alignment */
  144. } u; /* (avoids having to malloc()) */
  145. register struct direct *dp; /* -> u.dblk[.] */
  146. register struct dirent *bp; /* -> buf[.] */
  147. #ifdef UNK
  148. switch ( state )
  149. {
  150. void (*shdlr)(); /* entry SIGSYS handler */
  151. register int retval; /* return from _getdents() if any */
  152. case yes: /* _getdents() is known to work */
  153. return _getdents( fildes, buf, nbyte );
  154. case maybe: /* first time only */
  155. shdlr = signal( SIGSYS, sig_catch );
  156. retval = _getdents( fildes, buf, nbyte ); /* try it */
  157. (void)signal( SIGSYS, shdlr );
  158. if ( state == maybe ) /* SIGSYS did not occur */
  159. {
  160. state = yes; /* so _getdents() must have worked */
  161. return retval;
  162. }
  163. /* else fall through into emulation */
  164. /* case no:*/ /* fall through into emulation */
  165. }
  166. #endif
  167. if ( buf == NULL
  168. #ifdef ATT_SPEC
  169. || (unsigned long)buf % sizeof(long) != 0 /* ugh */
  170. #endif
  171. ) {
  172. errno = EFAULT; /* invalid pointer */
  173. return -1;
  174. }
  175. if ( _fstat( fildes, &statb ) != 0 )
  176. return -1; /* errno set by fstat() */
  177. if ( !S_ISDIR( statb.st_mode ) )
  178. {
  179. errno = ENOTDIR; /* not a directory */
  180. return -1;
  181. }
  182. if ( (offset = _lseek( fildes, (off_t)0, SEEK_CUR )) < 0 )
  183. return -1; /* errno set by lseek() */
  184. #ifdef BFS /* no telling what remote hosts do */
  185. if ( (unsigned long)offset % DIRBLKSIZ != 0 )
  186. {
  187. errno = ENOENT; /* file pointer probably misaligned */
  188. return -1;
  189. }
  190. #endif
  191. serrno = errno; /* save entry errno */
  192. for ( bp = (struct dirent *)buf; bp == (struct dirent *)buf; )
  193. { /* convert next directory block */
  194. int size;
  195. do size = GetBlock( fildes, u.dblk, DIRBLKSIZ );
  196. while ( size == -1 && errno == EINTR );
  197. if ( size <= 0 )
  198. return size; /* EOF or error (EBADF) */
  199. for ( dp = (struct direct *)u.dblk;
  200. (char *)dp < &u.dblk[size];
  201. dp = (struct direct *)((char *)dp + RecLen( dp ))
  202. ) {
  203. #ifndef UFS
  204. if ( dp->d_reclen <= 0 )
  205. {
  206. errno = EIO; /* corrupted directory */
  207. return -1;
  208. }
  209. #endif
  210. if ( dp->d_fileno != 0 )
  211. { /* non-empty; copy to user buffer */
  212. register int reclen =
  213. DIRENTSIZ( NameLen( dp->d_name ) );
  214. if ( (char *)bp + reclen > &buf[nbyte] )
  215. {
  216. errno = EINVAL;
  217. return -1; /* buf too small */
  218. }
  219. bp->d_ino = dp->d_fileno;
  220. bp->d_off = offset + ((char *)dp - u.dblk);
  221. bp->d_reclen = reclen;
  222. (void)strncpy( bp->d_name, dp->d_name,
  223. reclen - DIRENTBASESIZ
  224. ); /* adds NUL padding */
  225. bp = (struct dirent *)((char *)bp + reclen);
  226. }
  227. }
  228. #ifndef BFS /* 4.2BSD screwed up; fixed in 4.3BSD */
  229. if ( (char *)dp > &u.dblk[size] )
  230. {
  231. errno = EIO; /* corrupted directory */
  232. return -1;
  233. }
  234. #endif
  235. }
  236. errno = serrno; /* restore entry errno */
  237. return (char *)bp - buf; /* return # bytes read */
  238. }