Minix1.5/lib/other/getdents.c
#include <lib.h>
/*
(SVR3 system call emulation)
last edit: 27-Oct-1988 D A Gwyn
This single source file supports several different methods of
getting directory entries from the operating system. Define
whichever one of the following describes your system:
UFS original UNIX filesystem (14-character name limit)
BFS 4.2BSD (also 4.3BSD) native filesystem (long names)
NFS getdirentries() system call
Also define any of the following flags that are pertinent:
ATT_SPEC check user buffer address for longword alignment
BSD_SYSV BRL UNIX System V emulation environment on 4.nBSD
INT_SIGS <signal.h> thinks that signal handlers have
return type int (rather than the standard void)
NEG_DELS deleted entries have inode number -1 rather than 0
UNK have _getdents() system call, but kernel may not
support it
If your C library has a getdents() system call interface, but you
can't count on all kernels on which your application binaries may
run to support it, change the system call interface name to
_getdents() and define "UNK" to enable the system-call validity
test in this "wrapper" around _getdents().
If your system has a getdents() system call that is guaranteed
to always work, you shouldn't be using this source file at all.
*/
#define UFS
#include <string.h>
#include <unistd.h>
#ifdef BSD_SYSV
#include <sys/_dir.h> /* BSD flavor, not System V */
#else
#include <sys/dir.h>
#undef MAXNAMLEN /* avoid conflict with SVR3 */
/* Good thing we don't need to use the DIRSIZ() macro! */
#ifdef d_ino /* 4.3BSD/NFS using d_fileno */
#undef d_ino /* (not absolutely necessary) */
#else
#define d_fileno d_ino /* (struct direct) member */
#endif
#endif
#include <dirent.h>
/*==========================================================================*/
/* This was in the original sys/dirent.h. The local definitions don't work. */
#ifdef BSD_SYSV /* (e.g., when compiling getdents.c) */
extern struct dirent __dirent; /* (not actually used) */
/* The following is portable, although rather silly. */
#define DIRENTBASESIZ (__dirent.d_name - (char *)&__dirent.d_ino)
#else
/* The following nonportable ugliness could have been avoided by defining
DIRENTSIZ and DIRENTBASESIZ to also have (struct dirent *) arguments.
There shouldn't be any problem if you avoid using the DIRENTSIZ() macro. */
#define DIRENTBASESIZ (((struct dirent *)0)->d_name \
- (char *)&((struct dirent *)0)->d_ino)
#endif
#define DIRENTSIZ( namlen ) ((DIRENTBASESIZ + sizeof(long) + (namlen)) \
/ sizeof(long) * sizeof(long))
/*==========================================================================*/
#include <sys/stat.h>
#ifdef UNK
#ifndef UFS
#include "***** ERROR ***** UNK applies only to UFS"
/* One could do something similar for getdirentries(), but I didn't bother. */
#endif
#include <signal.h>
#endif
#ifdef BSD_SYSV
struct dirent __dirent; /* (just for the DIRENTBASESIZ macro) */
#endif
#ifdef UFS
#define RecLen( dp ) (sizeof(struct direct)) /* fixed-length entries */
#else /* BFS || NFS */
#define RecLen( dp ) ((dp)->d_reclen) /* variable-length entries */
#endif
#ifdef NFS
#ifdef BSD_SYSV
#define getdirentries _getdirentries /* package hides this system call */
#endif
extern int getdirentries();
PRIVATE long dummy; /* getdirentries() needs basep */
#define GetBlock( fd, buf, n ) getdirentries( fd, buf, (unsigned)n, &dummy )
#else /* UFS || BFS */
#ifdef BSD_SYSV
#define read _read /* avoid emulation overhead */
#endif
#define GetBlock( fd, buf, n ) read( fd, buf, n )
#endif
#ifdef UNK
extern int _getdents(); /* actual system call */
#endif
#ifdef NEG_DELS
#define DELETED (-1)
#else
#define DELETED 0
#endif
#ifndef DIRENTSIZ
#define DIRENTSIZ(x) (x)
#endif
#ifndef DIRENTBASESIZ
#define DIRENTBASESIZ 0
#endif
#ifndef DIRBLKSIZ
#define DIRBLKSIZ 4096 /* directory file read buffer size */
#endif
#ifndef NULL
#define NULL 0
#endif
#ifndef SEEK_CUR
#define SEEK_CUR 1
#endif
#ifndef S_ISDIR /* macro to test for directory file */
#define S_ISDIR( mode ) (((mode) & S_IFMT) == S_IFDIR)
#endif
#ifdef UFS
/*
The following routine is necessary to handle DIRSIZ-long entry names.
Thanks to Richard Todd for pointing this out.
*/
PRIVATE _PROTOTYPE( int NameLen, (char *name));
PRIVATE int NameLen(name) /* return # chars in embedded name */
char name[]; /* -> name embedded in struct direct */
{
register char *s; /* -> name[.] */
register char *stop = &name[NAME_MAX]; /* -> past end of name field */
for (s = &name[1]; /* (empty names are impossible) */
*s != '\0' /* not NUL terminator */
&& ++s < stop; /* < DIRSIZ characters scanned */
);
return (s - name); /* # valid characters in name */
}
#else /* BFS || NFS */
#define NameLen( name ) strlen( name ) /* names are always NUL-terminated */
#endif
#ifdef UNK
PRIVATE enum {
maybe, no, yes
} state = maybe;
/* Does _getdents() work? */
#ifdef INT_SIGS
#define RET_SIG int
#else
#define RET_SIG void
#endif
/*ARGSUSED*/
PRIVATE RET_SIG
sig_catch(sig)
int sig; /* must be SIGSYS */
{
state = no; /* attempted _getdents() faulted */
#ifdef INT_SIGS
return (0); /* telling lies */
#endif
}
#endif /* UNK */
int getdents(fildes, buf, nbyte)/* returns # bytes read; 0 on EOF, -1 on
* error */
int fildes; /* directory file descriptor */
char *buf; /* where to put the (struct dirent)s */
unsigned nbyte; /* size of buf[] */
{
int serrno; /* entry errno */
off_t offset; /* initial directory file offset */
/* The following are PRIVATE just to keep the stack small. */
PRIVATE struct stat statb; /* fstat() info */
PRIVATE union {
char dblk[DIRBLKSIZ
#ifdef UFS
+ 1 /* for last entry name terminator */
#endif
];
/* Directory file block buffer */
struct direct dummy; /* just for alignment */
} u; /* (avoids having to malloc()) */
register struct direct *dp; /* -> u.dblk[.] */
register struct dirent *bp; /* -> buf[.] */
#ifdef UNK
if (state == yes) /* _getdents() is known to work */
return (_getdents(fildes, buf, nbyte));
if (state == maybe) { /* first time only */
RET_SIG(*shdlr) (); /* entry SIGSYS handler */
register int retval; /* return from _getdents() if any */
shdlr = signal(SIGSYS, sig_catch);
retval = _getdents(fildes, buf, nbyte); /* try it */
(void) signal(SIGSYS, shdlr);
if (state == maybe) { /* SIGSYS did not occur */
state = yes; /* so _getdents() must have worked */
return (retval);
}
}
/* State == no; perform emulation */
#endif
if (buf == (char *)NULL
#ifdef ATT_SPEC
|| (unsigned long) buf % sizeof(long) != 0 /* ugh */
#endif
) {
errno = EFAULT; /* invalid pointer */
return (-1);
}
if (fstat(fildes, &statb) != 0) return (-1); /* errno set by fstat() */
if (!S_ISDIR(statb.st_mode)) {
errno = ENOTDIR; /* not a directory */
return (-1);
}
if ((offset = lseek(fildes, (off_t) 0, SEEK_CUR)) < 0)
return (-1); /* errno set by lseek() */
#ifdef BFS /* no telling what remote hosts do */
if ((unsigned long) offset % DIRBLKSIZ != 0) {
errno = ENOENT; /* file pointer probably misaligned */
return (-1);
}
#endif
serrno = errno; /* save entry errno */
for (bp = (struct dirent *) buf; bp == (struct dirent *) buf;) { /* convert next
* directory block */
int size;
do
size = GetBlock(fildes, u.dblk, DIRBLKSIZ);
while (size == -1 && errno == EINTR);
if (size <= 0) return (size); /* EOF or error (EBADF) */
for (dp = (struct direct *) u.dblk;
(char *) dp < &u.dblk[size];
dp = (struct direct *) ((char *) dp + RecLen(dp))
) {
#ifndef UFS
if (dp->d_reclen <= 0) {
errno = EIO; /* corrupted directory */
return (-1);
}
#endif
if (dp->d_fileno != DELETED) { /* non-empty; copy to
* user buffer */
register int reclen =
DIRENTSIZ(NameLen(dp->d_name));
if ((char *) bp + reclen > &buf[nbyte]) {
errno = EINVAL;
return (-1); /* buf too small */
}
bp->d_ino = dp->d_fileno;
bp->d_off = offset + ((char *) dp - u.dblk);
bp->d_reclen = reclen;
{
#ifdef UFS
/* Is the following kludge ugly? You bet. */
register char save = dp->d_name[NAME_MAX];
/* Save original data */
dp->d_name[NAME_MAX] = '\0';
/* Ensure NUL termination */
#endif
(void) strncpy(bp->d_name, dp->d_name,
reclen - DIRENTBASESIZ
); /* adds NUL padding */
#ifdef UFS
dp->d_name[NAME_MAX] = save;
/* Restore original data */
#endif
}
bp = (struct dirent *) ((char *) bp + reclen);
}
}
if ((char *) dp > &u.dblk[size]) {
errno = EIO; /* corrupted directory */
return (-1);
}
}
errno = serrno; /* restore entry errno */
return((char *) bp - buf); /* return # bytes read */
}