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 #ifdef __STDC__
201
opendir(char * dirname)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 ((DIR *)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 ((DIR *)NULL); /* not a directory */
222 }
223 if ((dirp = (DIR *) malloc(sizeof(DIR))) == (DIR *)NULL
224 || (dirp->dd_buf = (char *) malloc((unsigned) DIRBUF)) == (char *)NULL
225 ) {
226 register int serrno = errno;
227 /* errno set to ENOMEM by sbrk() */
228
229 if (dirp != (DIR *)NULL)
230 free((pointer) dirp);
231
232 close(fd);
233 errno = serrno;
234 return ((DIR *)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 #ifdef __STDC__
250
closedir(register DIR * dirp)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 == (DIR *)NULL || dirp->dd_buf == (char *)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 #ifdef __STDC__
281
readdir(register DIR * dirp)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 == (DIR *)NULL || dirp->dd_buf == (char *)NULL) {
294 errno = EFAULT;
295 return (struct dirent *)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 ((struct dirent *)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 #ifdef __STDC__
330
seekdir(register DIR * dirp,register OFFSET loc)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 == (DIR *)NULL || dirp->dd_buf == (char *)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) != (struct dirent *)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 #ifdef __STDC__
435
telldir(DIR * dirp)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 == (DIR *)NULL || dirp->dd_buf == (char *)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 #ifdef __STDC__
467
NameLen(char * name)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 #ifdef __STDC__
512
sig_catch(int sig)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 #ifdef __STDC__
546
getdents(int fildes,char * buf,unsigned nbyte)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 == (char *)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 }
694