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