Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
}