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:
GETDENTS getdents() system call native, this library is unnecessary
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
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.
#ifndef lint /* for strings... */
static char *rcsid
="$Header: /f/osi/dirent/RCS/getdents.c,v 7.2 91/02/22 09:17:25 mrose Interim $";
#if !defined(SVR3) && !defined(apollo) && !defined(GETDENTS)
#include "sys._dir.h" /* BSD flavor, not System V */
#include "/usr/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) */
#define d_fileno d_ino /* (struct direct) member */
#include "***** ERROR ***** UNK applies only to UFS"
/* One could do something similar for getdirentries(), but I didn't bother. */
#if defined(UFS) + defined(BFS) + defined(NFS) != 1 /* sanity check */
#include "***** ERROR ***** exactly one of UFS, BFS, or NFS must be defined"
#define RecLen( dp ) (sizeof(struct direct)) /* fixed-length entries */
#define RecLen( dp ) ((dp)->d_reclen) /* variable-length entries */
#define getdirentries _getdirentries /* package hides this system call */
extern int getdirentries();
static long dummy
; /* getdirentries() needs basep */
#define GetBlock( fd, buf, n ) getdirentries( fd, buf, (int)n, &dummy )
#define read _read /* avoid emulation overhead */
#define GetBlock( fd, buf, n ) read( fd, buf, (unsigned)n )
extern int _getdents(); /* actual system call */
#define DIRBLKSIZ 4096 /* directory file read buffer size */
#ifndef S_ISDIR /* macro to test for directory file */
#define S_ISDIR( mode ) (((mode) & S_IFMT) == S_IFDIR)
The following routine is necessary to handle DIRSIZ-long entry names.
Thanks to Richard Todd for pointing this out.
NameLen( name
) /* return # chars in embedded name */
char name
[]; /* -> name embedded in struct direct */
register char *s
; /* -> name[.] */
register char *stop
= &name
[DIRSIZ
]; /* -> 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 */
#define NameLen( name ) strlen( name ) /* names are always NUL-terminated */
static enum { maybe
, no
, yes
} state
= maybe
;
/* does _getdents() work? */
int sig
; /* must be SIGSYS */
state
= no
; /* attempted _getdents() faulted */
getdents( fildes
, buf
, nbyte
) /* returns # bytes read;
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 */
struct stat statb
; /* fstat() info */
/* 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[.] */
void (*shdlr
)(); /* entry SIGSYS handler */
register int retval
; /* return from _getdents() if any */
case yes
: /* _getdents() is known to work */
return _getdents( fildes
, buf
, nbyte
);
case maybe
: /* first time only */
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 */
/* else fall through into emulation */
/* case no: /* fall through into emulation */
|| (unsigned long)buf
% sizeof(long) != 0 /* ugh */
errno
= EFAULT
; /* invalid pointer */
if ( fstat( fildes
, &statb
) != 0 )
return -1; /* errno set by fstat() */
if ( !S_ISDIR( statb
.st_mode
) )
errno
= ENOTDIR
; /* not a directory */
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 */
serrno
= errno
; /* save entry errno */
for ( bp
= (struct dirent
*)buf
; bp
== (struct dirent
*)buf
; )
{ /* convert next directory block */
do size
= GetBlock( fildes
, u
.dblk
, DIRBLKSIZ
);
while ( size
== -1 && errno
== EINTR
);
return size
; /* EOF or error (EBADF) */
for ( dp
= (struct direct
*)u
.dblk
;
(char *)dp
< &u
.dblk
[size
];
dp
= (struct direct
*)((char *)dp
+ RecLen( dp
))
errno
= EIO
; /* corrupted directory */
{ /* non-empty; copy to user buffer */
DIRENTSIZ( NameLen( dp
->d_name
) );
if ( (char *)bp
+ reclen
> &buf
[nbyte
] )
return -1; /* buf too small */
bp
->d_ino
= dp
->d_fileno
;
bp
->d_off
= offset
+ ((char *)dp
- u
.dblk
);
(void)strncpy( bp
->d_name
, dp
->d_name
,
/* be sure d_name is NULL-terminated */
bp
->d_name
[DIRSIZ
] = NULL
;
reclen
- DIRENTBASESIZ
);
bp
= (struct dirent
*)((char *)bp
+ reclen
);
#ifndef BFS /* 4.2BSD screwed up; fixed in 4.3BSD */
if ( (char *)dp
> &u
.dblk
[size
] )
errno
= EIO
; /* corrupted directory */
errno
= serrno
; /* restore entry errno */
return (char *)bp
- buf
; /* return # bytes read */
int _getdents_stub () {};