paxdir.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. /*
  2. opendir -- open a directory stream
  3. last edit: 16-Jun-1987 D A Gwyn
  4. */
  5. #include <sys/errno.h>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include "paxdir.h"
  9. #ifdef BSD_SYSV
  10. /*
  11. <sys/_dir.h> -- definitions for 4.2,4.3BSD directories
  12. last edit: 25-Apr-1987 D A Gwyn
  13. A directory consists of some number of blocks of DIRBLKSIZ bytes each,
  14. where DIRBLKSIZ is chosen such that it can be transferred to disk in a
  15. single atomic operation (e.g., 512 bytes on most machines).
  16. Each DIRBLKSIZ-byte block contains some number of directory entry
  17. structures, which are of variable length. Each directory entry has the
  18. beginning of a (struct direct) at the front of it, containing its
  19. filesystem-unique ident number, the length of the entry, and the length
  20. of the name contained in the entry. These are followed by the NUL-
  21. terminated name padded to a (long) boundary with 0 bytes. The maximum
  22. length of a name in a directory is MAXNAMELEN.
  23. The macro DIRSIZ(dp) gives the amount of space required to represent a
  24. directory entry. Free space in a directory is represented by entries
  25. that have dp->d_reclen > DIRSIZ(dp). All DIRBLKSIZ bytes in a
  26. directory block are claimed by the directory entries; this usually
  27. results in the last entry in a directory having a large dp->d_reclen.
  28. When entries are deleted from a directory, the space is returned to the
  29. previous entry in the same directory block by increasing its
  30. dp->d_reclen. If the first entry of a directory block is free, then
  31. its dp->d_fileno is set to 0; entries other than the first in a
  32. directory do not normally have dp->d_fileno set to 0.
  33. prerequisite: <sys/types.h>
  34. */
  35. #if defined(accel) || defined(sun) || defined(vax)
  36. #define DIRBLKSIZ 512 /* size of directory block */
  37. #else
  38. #ifdef alliant
  39. #define DIRBLKSIZ 4096 /* size of directory block */
  40. #else
  41. #ifdef gould
  42. #define DIRBLKSIZ 1024 /* size of directory block */
  43. #else
  44. #ifdef ns32000 /* Dynix System V */
  45. #define DIRBLKSIZ 2600 /* size of directory block */
  46. #else /* be conservative; multiple blocks are okay
  47. * but fractions are not */
  48. #define DIRBLKSIZ 4096 /* size of directory block */
  49. #endif
  50. #endif
  51. #endif
  52. #endif
  53. #define MAXNAMELEN 255 /* maximum filename length */
  54. /* NOTE: not MAXNAMLEN, which has been preempted by SVR3 <dirent.h> */
  55. struct direct { /* data from read()/_getdirentries() */
  56. unsigned long d_fileno; /* unique ident of entry */
  57. unsigned short d_reclen; /* length of this record */
  58. unsigned short d_namlen; /* length of string in d_name */
  59. char d_name[MAXNAMELEN + 1]; /* NUL-terminated filename */
  60. };
  61. /*
  62. The DIRSIZ macro gives the minimum record length which will hold the
  63. directory entry. This requires the amount of space in a (struct
  64. direct) without the d_name field, plus enough space for the name with a
  65. terminating NUL character, rounded up to a (long) boundary.
  66. (Note that Berkeley didn't properly compensate for struct padding,
  67. but we nevertheless have to use the same size as the actual system.)
  68. */
  69. #define DIRSIZ( dp ) ((sizeof(struct direct) - (MAXNAMELEN+1) \
  70. + sizeof(long) + (dp)->d_namlen) \
  71. / sizeof(long) * sizeof(long))
  72. #else
  73. #include <sys/dir.h>
  74. #ifdef SYSV3
  75. #undef MAXNAMLEN /* avoid conflict with SVR3 */
  76. #endif
  77. /* Good thing we don't need to use the DIRSIZ() macro! */
  78. #ifdef d_ino /* 4.3BSD/NFS using d_fileno */
  79. #undef d_ino /* (not absolutely necessary) */
  80. #else
  81. #define d_fileno d_ino /* (struct direct) member */
  82. #endif
  83. #endif
  84. #ifdef UNK
  85. #ifndef UFS
  86. #include "***** ERROR ***** UNK applies only to UFS"
  87. /* One could do something similar for getdirentries(), but I didn't bother. */
  88. #endif
  89. #include <signal.h>
  90. #endif
  91. #if defined(UFS) + defined(BFS) + defined(NFS) != 1 /* sanity check */
  92. #include "***** ERROR ***** exactly one of UFS, BFS, or NFS must be defined"
  93. #endif
  94. #ifdef UFS
  95. #define RecLen( dp ) (sizeof(struct direct)) /* fixed-length entries */
  96. #else /* BFS || NFS */
  97. #define RecLen( dp ) ((dp)->d_reclen) /* variable-length entries */
  98. #endif
  99. #ifdef NFS
  100. #ifdef BSD_SYSV
  101. #define getdirentries _getdirentries /* package hides this system call */
  102. #endif
  103. extern int getdirentries();
  104. static long dummy; /* getdirentries() needs basep */
  105. #define GetBlock( fd, buf, n ) getdirentries( fd, buf, (unsigned)n, &dummy )
  106. #else /* UFS || BFS */
  107. #ifdef BSD_SYSV
  108. #define read _read /* avoid emulation overhead */
  109. #endif
  110. extern int read();
  111. #define GetBlock( fd, buf, n ) read( fd, buf, (unsigned)n )
  112. #endif
  113. #ifdef UNK
  114. extern int _getdents(); /* actual system call */
  115. #endif
  116. extern char *strncpy();
  117. extern int fstat();
  118. extern OFFSET lseek();
  119. extern int errno;
  120. #ifndef DIRBLKSIZ
  121. #define DIRBLKSIZ 4096 /* directory file read buffer size */
  122. #endif
  123. #ifndef NULL
  124. #define NULL 0
  125. #endif
  126. #ifndef SEEK_CUR
  127. #define SEEK_CUR 1
  128. #endif
  129. #ifndef S_ISDIR /* macro to test for directory file */
  130. #define S_ISDIR( mode ) (((mode) & S_IFMT) == S_IFDIR)
  131. #endif
  132. #ifndef SEEK_CUR
  133. #define SEEK_CUR 1
  134. #endif
  135. #ifdef BSD_SYSV
  136. #define open _open /* avoid emulation overhead */
  137. #endif
  138. extern int getdents(); /* SVR3 system call, or emulation */
  139. typedef char *pointer; /* (void *) if you have it */
  140. extern void free();
  141. extern pointer malloc();
  142. extern int
  143. open(), close(), fstat();
  144. extern int errno;
  145. extern OFFSET lseek();
  146. #ifndef SEEK_SET
  147. #define SEEK_SET 0
  148. #endif
  149. typedef int bool; /* Boolean data type */
  150. #define false 0
  151. #define true 1
  152. #ifndef NULL
  153. #define NULL 0
  154. #endif
  155. #ifndef O_RDONLY
  156. #define O_RDONLY 0
  157. #endif
  158. #ifndef S_ISDIR /* macro to test for directory file */
  159. #define S_ISDIR( mode ) (((mode) & S_IFMT) == S_IFDIR)
  160. #endif
  161. #ifdef __STDC__
  162. DIR *opendir(char *dirname)
  163. #else
  164. DIR *opendir(dirname)
  165. char *dirname; /* name of directory */
  166. #endif
  167. {
  168. register DIR *dirp; /* -> malloc'ed storage */
  169. register int fd; /* file descriptor for read */
  170. struct stat sbuf; /* result of fstat() */
  171. if ((fd = open(dirname, O_RDONLY)) < 0)
  172. return ((DIR *)NULL); /* errno set by open() */
  173. if (fstat(fd, &sbuf) != 0 || !S_ISDIR(sbuf.st_mode)) {
  174. close(fd);
  175. errno = ENOTDIR;
  176. return ((DIR *)NULL); /* not a directory */
  177. }
  178. if ((dirp = (DIR *) malloc(sizeof(DIR))) == (DIR *)NULL
  179. || (dirp->dd_buf = (char *) malloc((unsigned) DIRBUF)) == (char *)NULL
  180. ) {
  181. register int serrno = errno;
  182. /* errno set to ENOMEM by sbrk() */
  183. if (dirp != (DIR *)NULL)
  184. free((pointer) dirp);
  185. close(fd);
  186. errno = serrno;
  187. return ((DIR *)NULL); /* not enough memory */
  188. }
  189. dirp->dd_fd = fd;
  190. dirp->dd_loc = dirp->dd_size = 0; /* refill needed */
  191. return dirp;
  192. }
  193. /*
  194. * closedir -- close a directory stream
  195. *
  196. * last edit: 11-Nov-1988 D A Gwyn
  197. */
  198. #ifdef __STDC__
  199. int closedir(register DIR *dirp)
  200. #else
  201. int closedir(dirp)
  202. register DIR *dirp; /* stream from opendir() */
  203. #endif
  204. {
  205. register int fd;
  206. if ( dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL ) {
  207. errno = EFAULT;
  208. return -1; /* invalid pointer */
  209. }
  210. fd = dirp->dd_fd; /* bug fix thanks to R. Salz */
  211. free( (pointer)dirp->dd_buf );
  212. free( (pointer)dirp );
  213. return close( fd );
  214. }
  215. /*
  216. readdir -- read next entry from a directory stream
  217. last edit: 25-Apr-1987 D A Gwyn
  218. */
  219. #ifdef __STDC__
  220. struct dirent *readdir(register DIR *dirp)
  221. #else
  222. struct dirent *readdir(dirp)
  223. register DIR *dirp; /* stream from opendir() */
  224. #endif
  225. {
  226. register struct dirent *dp; /* -> directory data */
  227. if (dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL) {
  228. errno = EFAULT;
  229. return (struct dirent *)NULL; /* invalid pointer */
  230. }
  231. do {
  232. if (dirp->dd_loc >= dirp->dd_size) /* empty or obsolete */
  233. dirp->dd_loc = dirp->dd_size = 0;
  234. if (dirp->dd_size == 0 /* need to refill buffer */
  235. && (dirp->dd_size =
  236. getdents(dirp->dd_fd, dirp->dd_buf, (unsigned) DIRBUF)
  237. ) <= 0
  238. )
  239. return ((struct dirent *)NULL); /* EOF or error */
  240. dp = (struct dirent *) & dirp->dd_buf[dirp->dd_loc];
  241. dirp->dd_loc += dp->d_reclen;
  242. }
  243. while (dp->d_ino == 0L); /* don't rely on getdents() */
  244. return dp;
  245. }
  246. /*
  247. seekdir -- reposition a directory stream
  248. last edit: 24-May-1987 D A Gwyn
  249. An unsuccessful seekdir() will in general alter the current
  250. directory position; beware.
  251. NOTE: 4.nBSD directory compaction makes seekdir() & telldir()
  252. practically impossible to do right. Avoid using them!
  253. */
  254. #ifdef __STDC__
  255. void seekdir(register DIR *dirp, register OFFSET loc)
  256. #else
  257. void seekdir(dirp, loc)
  258. register DIR *dirp; /* stream from opendir() */
  259. register OFFSET loc; /* position from telldir() */
  260. #endif
  261. {
  262. register bool rewind; /* "start over when stymied" flag */
  263. if (dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL) {
  264. errno = EFAULT;
  265. return; /* invalid pointer */
  266. }
  267. /*
  268. * A (struct dirent)'s d_off is an invented quantity on 4.nBSD
  269. * NFS-supporting systems, so it is not safe to lseek() to it.
  270. */
  271. /* Monotonicity of d_off is heavily exploited in the following. */
  272. /*
  273. * This algorithm is tuned for modest directory sizes. For huge
  274. * directories, it might be more efficient to read blocks until the first
  275. * d_off is too large, then back up one block, or even to use binary
  276. * search on the directory blocks. I doubt that the extra code for that
  277. * would be worthwhile.
  278. */
  279. if (dirp->dd_loc >= dirp->dd_size /* invalid index */
  280. || ((struct dirent *) & dirp->dd_buf[dirp->dd_loc])->d_off > loc
  281. /* too far along in buffer */
  282. )
  283. dirp->dd_loc = 0; /* reset to beginning of buffer */
  284. /* else save time by starting at current dirp->dd_loc */
  285. for (rewind = true;;) {
  286. register struct dirent *dp;
  287. /* See whether the matching entry is in the current buffer. */
  288. if ((dirp->dd_loc < dirp->dd_size /* valid index */
  289. || readdir(dirp) != (struct dirent *)NULL /* next buffer read */
  290. && (dirp->dd_loc = 0, true) /* beginning of buffer set */
  291. )
  292. && (dp = (struct dirent *) & dirp->dd_buf[dirp->dd_loc])->d_off
  293. <= loc /* match possible in this buffer */
  294. ) {
  295. for ( /* dp initialized above */ ;
  296. (char *) dp < &dirp->dd_buf[dirp->dd_size];
  297. dp = (struct dirent *) ((char *) dp + dp->d_reclen)
  298. )
  299. if (dp->d_off == loc) { /* found it! */
  300. dirp->dd_loc =
  301. (char *) dp - dirp->dd_buf;
  302. return;
  303. }
  304. rewind = false; /* no point in backing up later */
  305. dirp->dd_loc = dirp->dd_size; /* set end of buffer */
  306. } else
  307. /* whole buffer past matching entry */ if (!rewind) { /* no point in searching
  308. * further */
  309. errno = EINVAL;
  310. return; /* no entry at specified loc */
  311. } else { /* rewind directory and start over */
  312. rewind = false; /* but only once! */
  313. dirp->dd_loc = dirp->dd_size = 0;
  314. if (lseek(dirp->dd_fd, (OFFSET) 0, SEEK_SET)
  315. != 0
  316. )
  317. return; /* errno already set (EBADF) */
  318. if (loc == 0)
  319. return; /* save time */
  320. }
  321. }
  322. }
  323. /* telldir - report directory stream position
  324. *
  325. * DESCRIPTION
  326. *
  327. * Returns the offset of the next directory entry in the
  328. * directory associated with dirp.
  329. *
  330. * NOTE: 4.nBSD directory compaction makes seekdir() & telldir()
  331. * practically impossible to do right. Avoid using them!
  332. *
  333. * PARAMETERS
  334. *
  335. * DIR *dirp - stream from opendir()
  336. *
  337. * RETURNS
  338. *
  339. * Return offset of next entry
  340. */
  341. #ifdef __STDC__
  342. OFFSET telldir(DIR *dirp)
  343. #else
  344. OFFSET telldir(dirp)
  345. DIR *dirp; /* stream from opendir() */
  346. #endif
  347. {
  348. if (dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL) {
  349. errno = EFAULT;
  350. return -1; /* invalid pointer */
  351. }
  352. if (dirp->dd_loc < dirp->dd_size) /* valid index */
  353. return ((struct dirent *) & dirp->dd_buf[dirp->dd_loc])->d_off;
  354. else /* beginning of next directory block */
  355. return lseek(dirp->dd_fd, (OFFSET) 0, SEEK_CUR);
  356. }
  357. #ifdef UFS
  358. /*
  359. The following routine is necessary to handle DIRSIZ-long entry names.
  360. Thanks to Richard Todd for pointing this out.
  361. */
  362. /* return # chars in embedded name */
  363. #ifdef __STDC__
  364. static int NameLen(char *name)
  365. #else
  366. static int NameLen(name)
  367. char *name; /* -> name embedded in struct direct */
  368. #endif
  369. {
  370. register char *s; /* -> name[.] */
  371. register char *stop = &name[DIRSIZ]; /* -> past end of name field */
  372. for (s = &name[1]; /* (empty names are impossible) */
  373. *s != '\0' /* not NUL terminator */
  374. && ++s < stop; /* < DIRSIZ characters scanned */
  375. );
  376. return s - name; /* # valid characters in name */
  377. }
  378. #else /* BFS || NFS */
  379. extern int strlen();
  380. #define NameLen( name ) strlen( name ) /* names are always NUL-terminated */
  381. #endif
  382. #ifdef UNK
  383. static enum {
  384. maybe, no, yes
  385. } state = maybe;
  386. /* sig_catch - used to catch signals
  387. *
  388. * DESCRIPTION
  389. *
  390. * Used to catch signals.
  391. */
  392. /*ARGSUSED*/
  393. #ifdef __STDC__
  394. static void sig_catch(int sig)
  395. #else
  396. static void sig_catch(sig)
  397. int sig; /* must be SIGSYS */
  398. #endif
  399. {
  400. state = no; /* attempted _getdents() faulted */
  401. }
  402. #endif
  403. /* getdents - get directory entries
  404. *
  405. * DESCRIPTION
  406. *
  407. * Gets directory entries from the filesystem in an implemenation
  408. * defined way.
  409. *
  410. * PARAMETERS
  411. *
  412. * int fildes - directory file descriptor
  413. * char *buf - where to put the (struct dirent)s
  414. * unsigned nbyte - size of buf[]
  415. *
  416. * RETURNS
  417. *
  418. * Returns number of bytes read; 0 on EOF, -1 on error
  419. */
  420. #ifdef __STDC__
  421. int getdents(int fildes, char *buf, unsigned nbyte)
  422. #else
  423. int getdents(fildes, buf, nbyte)
  424. int fildes; /* directory file descriptor */
  425. char *buf; /* where to put the (struct dirent)s */
  426. unsigned nbyte; /* size of buf[] */
  427. #endif
  428. {
  429. int serrno; /* entry errno */
  430. OFFSET offset; /* initial directory file offset */
  431. struct stat statb; /* fstat() info */
  432. union {
  433. /* directory file block buffer */
  434. #ifdef UFS
  435. char dblk[DIRBLKSIZ + 1];
  436. #else
  437. char dblk[DIRBLKSIZ];
  438. #endif
  439. struct direct dummy; /* just for alignment */
  440. } u; /* (avoids having to malloc()) */
  441. register struct direct *dp; /* -> u.dblk[.] */
  442. register struct dirent *bp; /* -> buf[.] */
  443. #ifdef UNK
  444. switch (state) {
  445. SIG_T (*shdlr)(); /* entry SIGSYS handler */
  446. register int retval; /* return from _getdents() if any */
  447. case yes: /* _getdents() is known to work */
  448. return _getdents(fildes, buf, nbyte);
  449. case maybe: /* first time only */
  450. shdlr = signal(SIGSYS, sig_catch);
  451. retval = _getdents(fildes, buf, nbyte); /* try it */
  452. signal(SIGSYS, shdlr);
  453. if (state == maybe) { /* SIGSYS did not occur */
  454. state = yes; /* so _getdents() must have worked */
  455. return retval;
  456. }
  457. /* else fall through into emulation */
  458. /* case no: /* fall through into emulation */
  459. }
  460. #endif
  461. if (buf == (char *)NULL
  462. #ifdef ATT_SPEC
  463. || (unsigned long) buf % sizeof(long) != 0 /* ugh */
  464. #endif
  465. ) {
  466. errno = EFAULT; /* invalid pointer */
  467. return -1;
  468. }
  469. if (fstat(fildes, &statb) != 0) {
  470. return -1; /* errno set by fstat() */
  471. }
  472. if (!S_ISDIR(statb.st_mode)) {
  473. errno = ENOTDIR; /* not a directory */
  474. return -1;
  475. }
  476. if ((offset = lseek(fildes, (OFFSET) 0, SEEK_CUR)) < 0) {
  477. return -1; /* errno set by lseek() */
  478. }
  479. #ifdef BFS /* no telling what remote hosts do */
  480. if ((unsigned long) offset % DIRBLKSIZ != 0) {
  481. errno = ENOENT; /* file pointer probably misaligned */
  482. return -1;
  483. }
  484. #endif
  485. serrno = errno; /* save entry errno */
  486. for (bp = (struct dirent *) buf; bp == (struct dirent *) buf;) {
  487. /* convert next directory block */
  488. int size;
  489. do {
  490. size = GetBlock(fildes, u.dblk, DIRBLKSIZ);
  491. } while (size == -1 && errno == EINTR);
  492. if (size <= 0) {
  493. return size; /* EOF or error (EBADF) */
  494. }
  495. for (dp = (struct direct *) u.dblk;
  496. (char *) dp < &u.dblk[size];
  497. dp = (struct direct *) ((char *) dp + RecLen(dp))
  498. ) {
  499. #ifndef UFS
  500. if (dp->d_reclen <= 0) {
  501. errno = EIO; /* corrupted directory */
  502. return -1;
  503. }
  504. #endif
  505. if (dp->d_fileno != 0) { /* non-empty; copy to user buffer */
  506. register int reclen =
  507. DIRENTSIZ(NameLen(dp->d_name));
  508. if ((char *) bp + reclen > &buf[nbyte]) {
  509. errno = EINVAL;
  510. return -1; /* buf too small */
  511. }
  512. bp->d_ino = dp->d_fileno;
  513. bp->d_off = offset + ((char *) dp - u.dblk);
  514. bp->d_reclen = reclen;
  515. {
  516. #ifdef UFS
  517. /* Is the following kludge ugly? You bet. */
  518. register char save = dp->d_name[DIRSIZ];
  519. /* save original data */
  520. dp->d_name[DIRSIZ] = '\0';
  521. /* ensure NUL termination */
  522. #endif
  523. /* adds NUL padding */
  524. strncpy(bp->d_name, dp->d_name, reclen - DIRENTBASESIZ);
  525. #ifdef UFS
  526. dp->d_name[DIRSIZ] = save;
  527. /* restore original data */
  528. #endif
  529. }
  530. bp = (struct dirent *) ((char *) bp + reclen);
  531. }
  532. }
  533. #ifndef BFS /* 4.2BSD screwed up; fixed in 4.3BSD */
  534. if ((char *) dp > &u.dblk[size]) {
  535. errno = EIO; /* corrupted directory */
  536. return -1;
  537. }
  538. #endif
  539. }
  540. errno = serrno; /* restore entry errno */
  541. return (char *) bp - buf; /* return # bytes read */
  542. }