xref: /plan9/sys/src/ape/cmd/pax/paxdir.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
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