ack/lang/cem/libcc.ansi/misc/getdents.c

293 lines
7.3 KiB
C
Raw Normal View History

1989-12-18 14:40:54 +00:00
/*
getdents -- get directory entries in a file system independent format
(SVR3 system call emulation)
last edit: 06-Jul-1987 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 that are pertinent:
ATT_SPEC check user buffer address for longword alignment
BSD_SYSV BRL UNIX System V emulation environment on 4.nBSD
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.
*/
2018-06-21 20:33:47 +00:00
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/errno.h>
#include <sys/types.h>
1989-12-18 14:40:54 +00:00
#ifdef BSD_SYSV
2018-06-21 20:33:47 +00:00
#include <sys/_dir.h> /* BSD flavor, not System V */
1989-12-18 14:40:54 +00:00
#else
2018-06-21 20:33:47 +00:00
#if defined(UFS)
#define DIRSIZ 14 /* 14 char filename in Version 7 */
1989-12-18 14:40:54 +00:00
#endif
2018-06-21 20:33:47 +00:00
#define MAXNAMLEN 255
struct direct
{
off_t d_off; /* offset of next disk directory entry */
u_long d_fileno; /* file number of entry */
u_short d_reclen; /* length of this record */
u_short d_namlen; /* length of string in d_name */
char d_name[MAXNAMLEN + 1]; /* name (up to MAXNAMLEN + 1) */
1989-12-18 14:40:54 +00:00
};
2018-06-21 20:33:47 +00:00
#undef MAXNAMLEN /* avoid conflict with SVR3 */
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
#define d_ino d_fileno /* compatability */
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
#ifdef d_ino /* 4.3BSD/NFS using d_fileno */
#undef d_ino /* (not absolutely necessary) */
1989-12-18 14:40:54 +00:00
#else
2018-06-21 20:33:47 +00:00
#define d_fileno d_ino /* (struct direct) member */
1989-12-18 14:40:54 +00:00
#endif
#endif
2018-06-21 20:33:47 +00:00
#include <sys/dirent.h>
#include <sys/stat.h>
1989-12-18 14:40:54 +00:00
#ifdef UNK
#ifndef UFS
2018-06-21 20:33:47 +00:00
#error UNK applies only to UFS
1989-12-18 14:40:54 +00:00
/* One could do something similar for getdirentries(), but I didn't bother. */
#endif
2018-06-21 20:33:47 +00:00
#include <signal.h>
1989-12-18 14:40:54 +00:00
#endif
2018-06-21 20:33:47 +00:00
#if defined(UFS) + defined(BFS) + defined(NFS) != 1 /* sanity check */
#error exactly one of UFS, BFS, or NFS must be defined
1989-12-18 14:40:54 +00:00
#endif
#ifdef UFS
2018-06-21 20:33:47 +00:00
#define RecLen(dp) (sizeof(struct direct)) /* fixed-length entries */
#else /* BFS || NFS */
#define RecLen(dp) ((dp)->d_reclen) /* variable-length entries */
1989-12-18 14:40:54 +00:00
#endif
#ifdef NFS
#ifdef BSD_SYSV
2018-06-21 20:33:47 +00:00
#define getdirentries _getdirentries /* package hides this system call */
1989-12-18 14:40:54 +00:00
#endif
2018-06-21 20:33:47 +00:00
extern int getdirentries(int fd, char* buf, int nbytes, long* basep);
static long dummy; /* getdirentries() needs basep */
#define GetBlock(fd, buf, n) getdirentries(fd, buf, (unsigned)n, &dummy)
#else /* UFS || BFS */
1989-12-18 14:40:54 +00:00
#ifdef BSD_SYSV
2018-06-21 20:33:47 +00:00
#define read _read /* avoid emulation overhead */
1989-12-18 14:40:54 +00:00
#endif
2018-06-21 20:33:47 +00:00
extern int read();
#define GetBlock(fd, buf, n) read(fd, buf, (unsigned)n)
1989-12-18 14:40:54 +00:00
#endif
#ifdef UNK
2018-06-21 20:33:47 +00:00
extern int _getdents(); /* actual system call */
1989-12-18 14:40:54 +00:00
#endif
2018-06-21 20:33:47 +00:00
extern int _fstat(int fd, struct stat* buf);
extern off_t _lseek(int d, int offset, int whence);
1989-12-18 14:40:54 +00:00
#ifndef DIRBLKSIZ
2018-06-21 20:33:47 +00:00
#define DIRBLKSIZ 4096 /* directory file read buffer size */
1989-12-18 14:40:54 +00:00
#endif
#ifndef NULL
2018-06-21 20:33:47 +00:00
#define NULL 0
1989-12-18 14:40:54 +00:00
#endif
#ifndef SEEK_CUR
2018-06-21 20:33:47 +00:00
#define SEEK_CUR 1
1989-12-18 14:40:54 +00:00
#endif
2018-06-21 20:33:47 +00:00
#ifndef S_ISDIR /* macro to test for directory file */
#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR)
1989-12-18 14:40:54 +00:00
#endif
#ifdef UFS
/*
The following routine is necessary to handle DIRSIZ-long entry names.
Thanks to Richard Todd for pointing this out.
*/
static int
2018-06-21 20:33:47 +00:00
NameLen(char name[]) /* return # chars in embedded name */
/* -> name embedded in struct direct */
1989-12-18 14:40:54 +00:00
{
2018-06-21 20:33:47 +00:00
register char* s; /* -> name[.] */
register char* stop = &name[DIRSIZ]; /* -> past end of name field */
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
for (s = &name[1]; /* (empty names are impossible) */
*s != '\0' /* not NUL terminator */
&& ++s < stop; /* < DIRSIZ characters scanned */
1989-12-18 14:40:54 +00:00
)
;
2018-06-21 20:33:47 +00:00
return s - name; /* # valid characters in name */
1989-12-18 14:40:54 +00:00
}
2018-06-21 20:33:47 +00:00
#else /* BFS || NFS */
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
#define NameLen(name) strlen(name) /* names are always NUL-terminated */
1989-12-18 14:40:54 +00:00
#endif
#ifdef UNK
2018-06-21 20:33:47 +00:00
static enum { maybe,
no,
yes } state
= maybe;
/* does _getdents() work? */
1989-12-18 14:40:54 +00:00
/*ARGSUSED*/
static void
2018-06-21 20:33:47 +00:00
sig_catch(int sig) /* sig must be SIGSYS */
1989-12-18 14:40:54 +00:00
{
2018-06-21 20:33:47 +00:00
state = no; /* attempted _getdents() faulted */
1989-12-18 14:40:54 +00:00
}
#endif
2018-06-21 20:33:47 +00:00
int getdents(int fildes, char* buf, unsigned nbyte) /* returns # bytes read;
1989-12-18 14:40:54 +00:00
0 on EOF, -1 on error */
/* fildes == directory file descriptor */
/* *buf == where to put the (struct dirent)s */
/* nbyte == size of buf[] */
{
2018-06-21 20:33:47 +00:00
int serrno; /* entry errno */
off_t offset; /* initial directory file offset */
struct stat statb; /* fstat() info */
union {
char dblk[DIRBLKSIZ];
/* 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[.] */
1989-12-18 14:40:54 +00:00
#ifdef UNK
2018-06-21 20:33:47 +00:00
switch (state)
{
void (*shdlr)(); /* entry SIGSYS handler */
register int retval; /* return from _getdents() if any */
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
case yes: /* _getdents() is known to work */
return _getdents(fildes, buf, nbyte);
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
case maybe: /* first time only */
shdlr = signal(SIGSYS, sig_catch);
retval = _getdents(fildes, buf, nbyte); /* try it */
(void)signal(SIGSYS, shdlr);
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
if (state == maybe) /* SIGSYS did not occur */
1989-12-18 14:40:54 +00:00
{
2018-06-21 20:33:47 +00:00
state = yes; /* so _getdents() must have worked */
return retval;
1989-12-18 14:40:54 +00:00
}
2018-06-21 20:33:47 +00:00
/* else fall through into emulation */
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
/* case no:*/ /* fall through into emulation */
}
1989-12-18 14:40:54 +00:00
#endif
2018-06-21 20:33:47 +00:00
if (buf == NULL
1989-12-18 14:40:54 +00:00
#ifdef ATT_SPEC
2018-06-21 20:33:47 +00:00
|| (unsigned long)buf % sizeof(long) != 0 /* ugh */
1989-12-18 14:40:54 +00:00
#endif
2018-06-21 20:33:47 +00:00
)
{
errno = EFAULT; /* invalid pointer */
1989-12-18 14:40:54 +00:00
return -1;
2018-06-21 20:33:47 +00:00
}
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
if (_fstat(fildes, &statb) != 0)
return -1; /* errno set by fstat() */
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
if (!S_ISDIR(statb.st_mode))
{
errno = ENOTDIR; /* not a directory */
1989-12-18 14:40:54 +00:00
return -1;
2018-06-21 20:33:47 +00:00
}
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
if ((offset = _lseek(fildes, (off_t)0, SEEK_CUR)) < 0)
return -1; /* errno set by lseek() */
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
#ifdef BFS /* no telling what remote hosts do */
if ((unsigned long)offset % DIRBLKSIZ != 0)
{
errno = ENOENT; /* file pointer probably misaligned */
1989-12-18 14:40:54 +00:00
return -1;
2018-06-21 20:33:47 +00:00
}
1989-12-18 14:40:54 +00:00
#endif
2018-06-21 20:33:47 +00:00
serrno = errno; /* save entry errno */
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
for (bp = (struct dirent*)buf; bp == (struct dirent*)buf;)
{ /* convert next directory block */
int size;
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
do
size = GetBlock(fildes, u.dblk, DIRBLKSIZ);
while (size == -1 && errno == EINTR);
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
if (size <= 0)
return size; /* EOF or error (EBADF) */
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
for (dp = (struct direct*)u.dblk;
(char*)dp < &u.dblk[size];
dp = (struct direct*)((char*)dp + RecLen(dp)))
{
1989-12-18 14:40:54 +00:00
#ifndef UFS
2018-06-21 20:33:47 +00:00
if (dp->d_reclen <= 0)
{
errno = EIO; /* corrupted directory */
1989-12-18 14:40:54 +00:00
return -1;
2018-06-21 20:33:47 +00:00
}
1989-12-18 14:40:54 +00:00
#endif
2018-06-21 20:33:47 +00:00
if (dp->d_fileno != 0)
{ /* non-empty; copy to user buffer */
register int reclen = DIRENTSIZ(NameLen(dp->d_name));
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
if ((char*)bp + reclen > &buf[nbyte])
{
1989-12-18 14:40:54 +00:00
errno = EINVAL;
2018-06-21 20:33:47 +00:00
return -1; /* buf too small */
}
1989-12-18 14:40:54 +00:00
bp->d_ino = dp->d_fileno;
2018-06-21 20:33:47 +00:00
bp->d_off = offset + ((char*)dp - u.dblk);
1989-12-18 14:40:54 +00:00
bp->d_reclen = reclen;
2018-06-21 20:33:47 +00:00
(void)strncpy(bp->d_name, dp->d_name,
reclen - DIRENTBASESIZ); /* adds NUL padding */
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
bp = (struct dirent*)((char*)bp + reclen);
1989-12-18 14:40:54 +00:00
}
2018-06-21 20:33:47 +00:00
}
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
#ifndef BFS /* 4.2BSD screwed up; fixed in 4.3BSD */
if ((char*)dp > &u.dblk[size])
{
errno = EIO; /* corrupted directory */
1989-12-18 14:40:54 +00:00
return -1;
}
2018-06-21 20:33:47 +00:00
#endif
}
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
errno = serrno; /* restore entry errno */
return (char*)bp - buf; /* return # bytes read */
1989-12-18 14:40:54 +00:00
}