ncpascal@ndsuvax.UUCP (Freeman Pascal) (12/17/87)
Hello, Here I am again. This time I have the directory(3) library calls. I have followed the BSD description as closely as the BSD 4.3 PRM allows me to (some entries have so little info in them). I hope they can be of some use. N O T E ------- I would not suggest using these routines if you already have a tried and tested set of directory functions that work for you. Use these as a last resort (I have such confidence in my code). I know the readdir(), opendir(), and closedir() work properly. Although, I had to make some assumptions on how to implement telldir() and seekdir(). I basicly decided to pass and returned what was needed for the lseek() call. I hope this was correct. If anyone knows the correct specs on these calls or know where to point me to please do. Enjoy. Freeman P. Pascal IV ncpascal@ndsuvax # /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 1 (of 1)." # Contents: INSTALL Makefile dir.c dir.h makelibc tst.c # Wrapped by ncpascal@ndsuvax on Wed Dec 16 22:27:00 1987 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f INSTALL -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"INSTALL\" else echo shar: Extracting \"INSTALL\" \(1907 characters\) sed "s/^X//" >INSTALL <<'END_OF_INSTALL' XManifest: X-------- X INSTALL this file X Makefile makefile for tty X makelibc shell script to make C library X tst.c test file for directory(3) X dir.c library function to return tty name (see directory(3)) X XInstallation instructions: X------------------------- X X1. install dir.h in /usr/include/sys. X X2. Add "typedef unshort ino_t" or "#define ino_t inode_nr" to X /usr/include/sys/types.h depending on your thinking how this X declaration should be handled. X X3. Compile dir.c as a library routine and place in /usr/lib/libc.a. X If you are using the makelibc shell script that was posted quite X awhile back just append "ar av libc.a dir.s" to the beginning and run. X I am including my version if you don't have it. X X4. Run makefile to compile tst.c and run it to test directory(3). X X5. Enjoy X XNotes: X----- X X I'm not sure on the exact operations performed by telldir() and Xseekdir(). I set up telldir() to return the current position (long int Xlike seek()) within the opened directory. Seekdir() was set up likewise, Xit will set the file postion within the opened directory without regard Xif it entry boundries or if the entry is active (d_ino entry is non-zero). X X In all actuality, I would suggest that if you use the POSIX version of Xdirectory(3) that came across the net lately. At the time I wrote these Xroutines I was attempting to get the BSD version of arc to work under MINIX. XI am now leaning towards using the POSIX implementation. I am including Xthese only for the sake of completeness for my scandir(3) routines I'm also Xposting. X X I must also state I don't garentee that theses routines work correctly. XI have tested them and found them to work as I need them to. I would Xsuggest you run them through your own tests to decide if they work for Xyour own purposes. (Gee - I have such confidence in my own code :-) X X Freeman P. Pascal IV X ncpascal@ndsuvax END_OF_INSTALL if test 1907 -ne `wc -c <INSTALL`; then echo shar: \"INSTALL\" unpacked with wrong size! fi # end of overwriting check fi if test -f Makefile -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"Makefile\" else echo shar: Extracting \"Makefile\" \(125 characters\) sed "s/^X//" >Makefile <<'END_OF_Makefile' XCFLAGS=-T. -i X Xall: tst dir.s X Xtst: tst.s dir.s X @cc -o tst $(CFLAGS) tst.s dir.s X @echo "Done." X Xtst.s: tst.c X Xdir.s: dir.c END_OF_Makefile if test 125 -ne `wc -c <Makefile`; then echo shar: \"Makefile\" unpacked with wrong size! fi # end of overwriting check fi if test -f dir.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"dir.c\" else echo shar: Extracting \"dir.c\" \(3423 characters\) sed "s/^X//" >dir.c <<'END_OF_dir.c' X/* X * dirs.c - BSD 4.x compatible directory functions X * X * Freeman P. Pascal IV X */ X/* X * History: X * X * 04 Dec 87 fpp Creation X */ X#include <errno.h> X#include <sys/types.h> X#include <sys/dir.h> X#include <sys/stat.h> X#include <sys/file.h> X X#ifndef VOID X#define VOID void X#endif X X#ifndef PUBLIC X#define PUBLIC X#endif X X#ifndef NULL X#define NULL 0 X#endif X X#define loop while( 1 ) /* loop forever */ X Xstruct direct __dir; Xextern int errno; X X/*========================================================================*\ X** opendir() ** X\*========================================================================*/ XPUBLIC DIR * Xopendir( path ) Xchar *path; X{ X/* X * Open a directory for reading X */ X struct stat sp; X DIR *dirp; X X if (( dirp = (DIR *) malloc( sizeof( DIR ))) == (DIR *) NULL ) { X errno = ENOMEM; X return( NULL ); X } X if (( dirp->dd_fd = open( path, O_RDONLY )) == -1 ) { X#ifdef DIR_DEBUG X perror( "opendir() (open)" ); X#endif X return( NULL ); X } X if ( fstat( dirp->dd_fd, &sp ) == -1 ) { X#ifdef DIR_DEBUG X perror( "opendir() (fstat)" ); X#endif X return( NULL ); X } X dirp->dd_loc = 0; X return( dirp ); X} X X/*========================================================================*\ X** readdir() ** X\*========================================================================*/ XPUBLIC struct direct * Xreaddir( dirp ) Xregister DIR *dirp; X{ X/* X * Read and return the next directory structure X * referenced by *dirp X */ X register struct direct *dp; X X loop { X if (dirp->dd_loc == 0) { X dirp->dd_size = read( dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ ); X if (dirp->dd_size <= 0) X return( NULL ); X } X if (dirp->dd_loc >= dirp->dd_size) { X dirp->dd_loc = 0; /* reload buffer */ X continue; X } X dp = (struct direct *) (dirp->dd_buf + dirp->dd_loc); X dirp->dd_loc += sizeof( struct direct ); X if (dp->d_ino == 0) X continue; /* skip empty entries */ X __dir.d_ino = dp->d_ino; X strncpy( __dir.d_name, dp->d_name, DIRSIZ ); X __dir.d_name[ DIRSIZ ] = '\0'; X return( &__dir ); X } X} X X/*========================================================================*\ X** telldir() ** X\*========================================================================*/ XPUBLIC long Xtelldir( dirp ) XDIR *dirp; X{ X/* X * Return current position within directory stream. X */ X return( lseek( dirp->dd_fd, 0L, L_INCR )); X} X X/*========================================================================*\ X** seekdir() ** X\*========================================================================*/ XPUBLIC int Xseekdir( dirp, loc ) XDIR *dirp; Xlong loc; X{ X/* X * Set position within directory stream. X */ X long pos; X X dirp->dd_loc = 0; /* force readdir() read into dd_buf */ X return( lseek( dirp->dd_fd, loc, L_SET )); X} X X/*========================================================================*\ X** rewinddir() ** X\*========================================================================*/ XPUBLIC int Xrewinddir( dirp ) XDIR *dirp; X{ X/* X * Resets the position of the directory stream to the beginning. X */ X return( seekdir( dirp, 0L )); X} X X/*========================================================================*\ X** closedir() ** X\*========================================================================*/ XPUBLIC int Xclosedir( dirp ) XDIR *dirp; X{ X/* X * Close given directory and free memory used by *dirp X */ X close( dirp->dd_fd ); X free(( char * ) dirp ); X return( 0 ); X} X END_OF_dir.c if test 3423 -ne `wc -c <dir.c`; then echo shar: \"dir.c\" unpacked with wrong size! fi # end of overwriting check fi if test -f dir.h -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"dir.h\" else echo shar: Extracting \"dir.h\" \(741 characters\) sed "s/^X//" >dir.h <<'END_OF_dir.h' X/* X * dir.h X * X * Minix directory structures X * X */ X/* X * History: X * X * 16 Dec 87 fpp Changed type of d_ino in direct structure X * from "inode_nr" (from orginal types.h) X * to "ino_t" (added to types.h for compatibl- X * ity) X * 29 July 87 fpp Creation X */ X#define DIRSIZ 14 X#define DIRBLKSIZ 512 /* read this much in at a time */ X Xstruct direct { X ino_t d_ino; /* inode of file */ X char d_name[ DIRSIZ ]; /* file name */ X}; X Xtypedef struct _dirdesc X{ X int dd_fd; /* directory file descriptor */ X long dd_loc; /* current location in dd_buf */ X long dd_size; /* size of last read */ X char dd_buf[ DIRBLKSIZ ]; /* read buffer */ X X} DIR; X XDIR *opendir(); Xstruct direct *readdir(); Xlong telldir(); X END_OF_dir.h if test 741 -ne `wc -c <dir.h`; then echo shar: \"dir.h\" unpacked with wrong size! fi # end of overwriting check fi if test -f makelibc -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"makelibc\" else echo shar: Extracting \"makelibc\" \(2138 characters\) sed "s/^X//" >makelibc <<'END_OF_makelibc' X# X# makelibc - Make C library X# X# - NOTE - X# X# This shell script will -REMOVE- the old version of libc.a if it X# exists in the current directory. It will also -REPLACE- the old X# /usr/lib/libc.a with version just packaged. X# Xif (test -f ./libc.a) # remove old libc.a if it exists X then rm ./libc.a Xfi Xar av libc.a getwd.s rename.s # getwd(3) rename(2) Xar av libc.a dir.s scandir.s # directory(3), scandir(3) Xar av libc.a getgrp.s # process groups Xar av libc.a termcap.s gtty.s stty.s # v1.2 update Xar av libc.a popen.s ctime.s system.s qsort.s # v1.2 upgrade Xar av libc.a regexp.s regsub.s Xar av libc.a getopt.s getgrent.s getpwent.s crypt.s Xar av libc.a fdopen.s Xar av libc.a fgets.s fprintf.s fputs.s fread.s freopen.s fclose.s Xar av libc.a fopen.s fseek.s ftell.s fwrite.s gets.s scanf.s getc.s printdat.s Xar av libc.a fflush.s setbuf.s sprintf.s doprintf.s putc.s ungetc.s strcmp.s Xar av libc.a access.s chdir.s chmod.s chown.s chroot.s creat.s dup.s dup2.s Xar av libc.a exec.s exit.s cleanup.s fork.s isatty.s fstat.s getegid.s getenv.s Xar av libc.a geteuid.s getgid.s getpass.s close.s getuid.s ioctl.s kill.s Xar av libc.a link.s lseek.s malloc.s brk.s brk2.s brksize.s mknod.s mktemp.s Xar av libc.a getpid.s mount.s open.s perror.s pipe.s prints.s read.s setgid.s Xar av libc.a setuid.s sleep.s alarm.s pause.s signal.s catchsig.s stat.s Xar av libc.a stime.s strcat.s strcpy.s strlen.s strncat.s strncmp.s strncpy.s Xar av libc.a ftime.s Xar av libc.a sync.s time.s times.s umask.s umount.s unlink.s utime.s wait.s Xar av libc.a stderr.s write.s syslib.s call.s atoi.s message.s sendrec.s Xar av libc.a printk.s abort.s itoa.s stb.s abs.s atol.s ctype.s index.s bcopy.s Xar av libc.a getutil.s rand.s rindex.s adi.s and.s cii.s cms.s cmu4.s com.s Xar av libc.a csa2.s csb2.s cuu.s .dup.s dvi.s dvi4.s dvu.s dvu4.s exg.s fakfp.s Xar av libc.a gto.s iaar.s ilar.s inn.s ior.s isar.s lar2.s loi.s mli.s mli4.s Xar av libc.a ngi.s nop.s rck.s rmi.s rmi4.s rmu.s rmu4.s rol.s ror.s sar2.s Xar av libc.a sbi.s set.s sli.s sri.s sti.s xor.s error.s unknown.s trp.s Xar av libc.a setjmp.s X Xcp libc.a /usr/lib Xecho Xecho "Done." Xecho X END_OF_makelibc if test 2138 -ne `wc -c <makelibc`; then echo shar: \"makelibc\" unpacked with wrong size! fi # end of overwriting check fi if test -f tst.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"tst.c\" else echo shar: Extracting \"tst.c\" \(765 characters\) sed "s/^X//" >tst.c <<'END_OF_tst.c' X#include <sys/types.h> X#include <sys/dir.h> X X#define NULL (DIR *) 0 X#define PRDIR( dp ) printf( "%14s\t%d\n", dp->d_name, dp->d_ino ) X Xmain() X{ X DIR *dirp; X struct direct *dp; X X if (( dirp = opendir( "." ) ) == NULL ) { X perror( "\".\"" ); X exit( 1 ); X } X while(( dp = readdir( dirp )) != NULL ) X PRDIR( dp ); X X prints( "\nseekdir: " ); X seekdir( dirp, 45 ); dp = readdir( dirp ); PRDIR( dp ); X X printf( "\ntelldir(): %20s %D\n", dp->d_name, telldir( dirp )); X X prints( "\nrewind(): " ); X rewinddir( dirp ); dp = readdir( dirp ); PRDIR( dp ); X X printf( "\ntelldir(): %20s %D\n", dp->d_name, telldir( dirp )); X X prints( "\nseekdir: " ); X seekdir( dirp, 4 * sizeof( struct direct )); X dp = readdir( dirp ); PRDIR( dp ); X X closedir( dirp ); X} END_OF_tst.c if test 765 -ne `wc -c <tst.c`; then echo shar: \"tst.c\" unpacked with wrong size! fi # end of overwriting check fi echo shar: End of archive 1 \(of 1\). cp /dev/null ark1isdone MISSING="" for I in 1 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 1 archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0
tholm@uvicctr.UUCP (Terrence W. Holm) (12/13/88)
EFTH MINIX report #62 - December 1988 - directory(3) This posting includes Douglas Gwyn's POSIX-compatible directory routines. If you have been using his routines for a while, please tell me what changes you have found necessary, so that I can keep a current copy of the POSIX library. If you have not been using Doug's library, then this is your chance to install it! Please find below the 13 files: HEADERS /usr/include/dirent.h /usr/include/sys/dir.h /usr/include/sys/dirent.h LIBRARY closedir.c getdents.c opendir.c readdir.c rewinddir.c seekdir.c telldir.c MAN PAGES getdents.3 directory.3 dirent.4 Instructions, 1) Install the header files, link in sys/errno.h. 2) Put the man pages under /usr/man/cat3 and cat4. 3) Compile and install the library routines. Note that "seekdir.s" must precede "readdir.s", which must precede "getdents.s" in libc.a ---------------------------------------------------------- echo x - directory.3 gres '^X' '' > directory.3 << '/' XSUBROUTINES X directory(3) - directory operations X XINVOCATION X #include <sys/types.h> X #include <dirent.h> X X DIR *opendir( dirname ) X char *dirname; X X struct dirent *readdir( dirp ) X DIR *dirp; X X off_t telldir( dirp ) X DIR *dirp; X X void seekdir( dirp, loc ) X DIR *dirp; X off_t loc; X X void rewinddir( dirp ) X DIR *dirp; X X int closedir( dirp ) X DIR *dirp; X XEXPLANATION X Opendir(3) establishes a connection between the directory named X by <dirname> and a unique object of type DIR known as a X directory stream that it creates. Opendir(3) returns a pointer X to be used to identify the directory stream in subsequent X operations. A NULL pointer is returned if <dirname> cannot be X accessed or is not a directory, or if opendir(3) is unable to X create the DIR object (perhaps due to insufficient memory). X X Readdir(3) returns a pointer to an internal structure containing X information about the next active directory entry. No inactive X entries are reported. The internal structure may be overwritten X by another operation on the same directory stream; the amount of X storage needed to hold a copy of the internal structure is given X by the value of a macro, DIRENTSIZ(strlen(direntp->d_name)), not X by sizeof(struct dirent) as one might expect. A NULL pointer is X returned upon reaching the end of the directory, upon detecting X an invalid location in the directory, or upon occurrence of an X error while reading the directory. X X Telldir(3) returns the current position associated with the named X directory stream for later use as an argument to seekdir(3). X X Seekdir(3) sets the position of the next readdir(3) operation on X the named directory stream. The new position reverts to the one X associated with the directory stream when the telldir(3) operation X from which <loc> was obtained was performed. X X Rewinddir(3) resets the position of the named directory stream to X the beginning of the directory. All buffered data for the directory X stream is discarded, thereby guaranteeing that the actual file X system directory will be referred to for the next readdir(3) on the X directory stream. X X Closedir(3) closes the named directory stream; internal resources X used for the directory stream are liberated, and subsequent use of X the associated DIR object is no longer valid. Closedir(3) returns X a value of zero if no error occurs, -1 otherwise. There are several X possible errors that can occur as a result of these operations; the X external integer variable <errno> is set to indicate the specific X error. (Readdir's detection of the normal end of a directory is not X considered to be an error.) X XEXAMPLE X Sample code which searches the current working directory for X an entry. Usage: "a.out directory filename". X X #include <sys/types.h> X #include <dirent.h> X #include <stdio.h> X X main( argc, argv ) X int argc; X char *argv[]; X X { X DIR *dirp; X struct dirent *dp; X X if ( (dirp = opendir( argv[1] )) == NULL ) X { X fprintf( stderr, "Cannot open directory\n" ); X exit( 1 ); X } X X while ( (dp = readdir( dirp )) != NULL ) X if ( strcmp( dp->d_name, argv[2] ) == 0 ) X { X (void) closedir( dirp ); X printf( "Found\n" ); X exit( 0 ); X } X X (void) closedir( dirp ); X printf( "Not found\n" ); X exit( 1 ); X } X XREFERENCES X dirent(4) X XWARNINGS X The value returned by telldir(3) need not have any simple X interpretation and should only be used as an argument to X seekdir(3). Similarly, the <loc> argument to seekdir(3) must X be obtained from a previous telldir(3) operation on the same X directory stream. Telldir(3) and seekdir(3) are unreliable X when the directory stream has been closed and reopened. It X is best to avoid using telldir(3) and seekdir(3) altogether. X X The exact set of <errno> values and meanings may vary among X implementations. X X Because directory entries can dynamically appear and disappear, X and because directory contents are buffered by these routines, X an application may need to continually rescan a directory to X maintain an accurate picture of its active entries. X XAUTHOR X Douglas A. Gwyn / echo x - getdents.3 gres '^X' '' > getdents.3 << '/' XSUBROUTINES X getdents(3) - get directory entries X XINVOCATION X int getdents( fildes, buf, nbyte ) X int fildes; X char *buf; X unsigned nbyte; X XEXPLANATION X Getdents(3) reads directory entries in a file system independent X format. This routine is only used by readdir(3) and should X never be used directly by a user's program. X XREFERENCES X directory(3), dirent(4) / echo x - dirent.4 gres '^X' '' > dirent.4 << '/' XSTRUCTURES X dirent(4) - file system independent directory entry X XEXPLANATION X Different file system types may have different directory X entries. The <dirent> structure defines a file system X independent directory entry, which contains information X common to directory entries in different file system types. X A set of these structures is returned by the getdents(3) X subroutine. The <dirent> structure is defined below. X X struct dirent X { X long d_ino; X off_t d_off; X unsigned short d_reclen; X char d_name[1]; X }; X X The field d_ino is a number which is unique for each file in X the file system. The field d_off represents an offset of that X directory entry in the actual file system directory. The field X d_name is the beginning of the character array giving the name X of the directory entry. This name is null terminated and may X have at most {NAME_MAX} characters in addition to the null X terminator. This results in file system independent directory X entries being variable-length entities. The value of d_reclen X is the record length of this entry. This length is defined to X be the number of bytes between the beginning of the current entry X and the next one, adjusted so that the next entry will start on X a long boundary. X XFILES X /usr/include/sys/dirent.h X XREFERENCES X getdents(3) X XWARNING X The field d_off does not have a simple interpretation for X some file system types and should not be used directly by X applications. / echo x - dir.h gres '^X' '' > dir.h << '/' X#define DIRBLKSIZ 512 /* size of directory block */ X X#ifndef DIRSIZ X#define DIRSIZ 14 X#endif X Xstruct direct { X ino_t d_ino; X char d_name[DIRSIZ]; X}; / echo x - dirent.h gres '^X' '' > dirent.h << '/' X/* X <dirent.h> -- definitions for SVR3 directory access routines X X last edit: 25-Apr-1987 D A Gwyn X X Prerequisite: <sys/types.h> X*/ X X#include <sys/dirent.h> X X#define DIRBUF 1024 /* buffer size for fs-indep. dirs */ X /* must in general be larger than the filesystem buffer size */ X Xtypedef struct X { X int dd_fd; /* file descriptor */ X int dd_loc; /* offset in block */ X int dd_size; /* amount of valid data */ X char *dd_buf; /* -> directory block */ X } DIR; /* stream data from opendir() */ X Xextern DIR *opendir(); Xextern struct dirent *readdir(); Xextern off_t telldir(); Xextern void seekdir(); Xextern void rewinddir(); Xextern int closedir(); X X#ifndef NULL X#define NULL 0 /* DAG -- added for convenience */ X#endif / echo x - sys_dirent.h gres '^X' '' > sys_dirent.h << '/' X/* X <sys/dirent.h> -- file system independent directory entry (SVR3) X X last edit: 27-Oct-1988 D A Gwyn X X prerequisite: <sys/types.h> X*/ X Xstruct dirent /* data from getdents()/readdir() */ X { X long d_ino; /* inode number of entry */ X off_t d_off; /* offset of disk directory entry */ X unsigned short d_reclen; /* length of this record */ X char d_name[1]; /* name of file */ /* non-ANSI */ X }; X X#ifdef BSD_SYSV /* (e.g., when compiling getdents.c) */ Xextern struct dirent __dirent; /* (not actually used) */ X/* The following is portable, although rather silly. */ X#define DIRENTBASESIZ (__dirent.d_name - (char *)&__dirent.d_ino) X X#else X/* The following nonportable ugliness could have been avoided by defining X DIRENTSIZ and DIRENTBASESIZ to also have (struct dirent *) arguments. X There shouldn't be any problem if you avoid using the DIRENTSIZ() macro. */ X X#define DIRENTBASESIZ (((struct dirent *)0)->d_name \ X - (char *)&((struct dirent *)0)->d_ino) X#endif X X#define DIRENTSIZ( namlen ) ((DIRENTBASESIZ + sizeof(long) + (namlen)) \ X / sizeof(long) * sizeof(long)) X X/* DAG -- the following was moved from <dirent.h>, which was the wrong place */ X#define MAXNAMLEN 512 /* maximum filename length */ X X#ifndef NAME_MAX X#define NAME_MAX (MAXNAMLEN - 1) /* DAG -- added for POSIX */ X#endif / echo x - closedir.c gres '^X' '' > closedir.c << '/' X/* X closedir -- close a directory stream X X last edit: 11-Nov-1988 D A Gwyn X*/ X X#include <sys/errno.h> X#include <sys/types.h> X#include <dirent.h> X Xtypedef char *pointer; /* (void *) if you have it */ X Xextern void free(); Xextern int close(); X Xextern int errno; X X#ifndef NULL X#define NULL 0 X#endif X Xint Xclosedir( dirp ) X register DIR *dirp; /* stream from opendir() */ X { X register int fd; X X if ( dirp == NULL || dirp->dd_buf == NULL ) X { X errno = EFAULT; X return -1; /* invalid pointer */ X } X X fd = dirp->dd_fd; /* bug fix thanks to R. Salz */ X free( (pointer)dirp->dd_buf ); X free( (pointer)dirp ); X return close( fd ); X } / echo x - getdents.c gres '^X' '' > getdents.c << '/' X/* X getdents -- get directory entries in a file system independent format X (SVR3 system call emulation) X X last edit: 27-Oct-1988 D A Gwyn X X This single source file supports several different methods of X getting directory entries from the operating system. Define X whichever one of the following describes your system: X X UFS original UNIX filesystem (14-character name limit) X BFS 4.2BSD (also 4.3BSD) native filesystem (long names) X NFS getdirentries() system call X X Also define any of the following flags that are pertinent: X X ATT_SPEC check user buffer address for longword alignment X BSD_SYSV BRL UNIX System V emulation environment on 4.nBSD X INT_SIGS <signal.h> thinks that signal handlers have X return type int (rather than the standard void) X NEG_DELS deleted entries have inode number -1 rather than 0 X UNK have _getdents() system call, but kernel may not X support it X X If your C library has a getdents() system call interface, but you X can't count on all kernels on which your application binaries may X run to support it, change the system call interface name to X _getdents() and define "UNK" to enable the system-call validity X test in this "wrapper" around _getdents(). X X If your system has a getdents() system call that is guaranteed X to always work, you shouldn't be using this source file at all. X*/ X X#define UFS X X#include <sys/errno.h> X#include <sys/types.h> X#ifdef BSD_SYSV X#include <sys/_dir.h> /* BSD flavor, not System V */ X#else X#include <sys/dir.h> X#undef MAXNAMLEN /* avoid conflict with SVR3 */ X /* Good thing we don't need to use the DIRSIZ() macro! */ X#ifdef d_ino /* 4.3BSD/NFS using d_fileno */ X#undef d_ino /* (not absolutely necessary) */ X#else X#define d_fileno d_ino /* (struct direct) member */ X#endif X#endif X#include <sys/dirent.h> X#include <sys/stat.h> X#ifdef UNK X#ifndef UFS X#include "***** ERROR ***** UNK applies only to UFS" X/* One could do something similar for getdirentries(), but I didn't bother. */ X#endif X#include <signal.h> X#endif X X#ifdef BSD_SYSV Xstruct dirent __dirent; /* (just for the DIRENTBASESIZ macro) */ X#endif X X#ifdef UFS X#define RecLen( dp ) (sizeof(struct direct)) /* fixed-length entries */ X#else /* BFS || NFS */ X#define RecLen( dp ) ((dp)->d_reclen) /* variable-length entries */ X#endif X X#ifdef NFS X#ifdef BSD_SYSV X#define getdirentries _getdirentries /* package hides this system call */ X#endif Xextern int getdirentries(); Xstatic long dummy; /* getdirentries() needs basep */ X#define GetBlock( fd, buf, n ) getdirentries( fd, buf, (unsigned)n, &dummy ) X#else /* UFS || BFS */ X#ifdef BSD_SYSV X#define read _read /* avoid emulation overhead */ X#endif Xextern int read(); X#define GetBlock( fd, buf, n ) read( fd, buf, (unsigned)n ) X#endif X X#ifdef UNK Xextern int _getdents(); /* actual system call */ X#endif X Xextern char *strncpy(); Xextern int fstat(); Xextern off_t lseek(); X Xextern int errno; X X#ifdef NEG_DELS X#define DELETED (-1) X#else X#define DELETED 0 X#endif X X#ifndef DIRBLKSIZ X#define DIRBLKSIZ 4096 /* directory file read buffer size */ X#endif X X#ifndef NULL X#define NULL 0 X#endif X X#ifndef SEEK_CUR X#define SEEK_CUR 1 X#endif X X#ifndef S_ISDIR /* macro to test for directory file */ X#define S_ISDIR( mode ) (((mode) & S_IFMT) == S_IFDIR) X#endif X X#ifdef UFS X X/* X The following routine is necessary to handle DIRSIZ-long entry names. X Thanks to Richard Todd for pointing this out. X*/ X Xstatic int XNameLen( name ) /* return # chars in embedded name */ X char name[]; /* -> name embedded in struct direct */ X { X register char *s; /* -> name[.] */ X register char *stop = &name[DIRSIZ]; /* -> past end of name field */ X X for ( s = &name[1]; /* (empty names are impossible) */ X *s != '\0' /* not NUL terminator */ X && ++s < stop; /* < DIRSIZ characters scanned */ X ) X ; X X return s - name; /* # valid characters in name */ X } X X#else /* BFS || NFS */ X Xextern int strlen(); X X#define NameLen( name ) strlen( name ) /* names are always NUL-terminated */ X X#endif X X#ifdef UNK Xstatic enum { maybe, no, yes } state = maybe; X /* does _getdents() work? */ X X#ifdef INT_SIGS X#define RET_SIG int X#else X#define RET_SIG void X#endif X X/*ARGSUSED*/ Xstatic RET_SIG Xsig_catch( sig ) X int sig; /* must be SIGSYS */ X { X state = no; /* attempted _getdents() faulted */ X#ifdef INT_SIGS X return 0; /* telling lies */ X#endif X } X#endif /* UNK */ X Xint Xgetdents( fildes, buf, nbyte ) /* returns # bytes read; X 0 on EOF, -1 on error */ X int fildes; /* directory file descriptor */ X char *buf; /* where to put the (struct dirent)s */ X unsigned nbyte; /* size of buf[] */ X { X int serrno; /* entry errno */ X off_t offset; /* initial directory file offset */ X /* The following are static just to keep the stack small. */ X static struct stat statb; /* fstat() info */ X static union X { X char dblk[DIRBLKSIZ X#ifdef UFS X +1 /* for last entry name terminator */ X#endif X ]; X /* directory file block buffer */ X struct direct dummy; /* just for alignment */ X } u; /* (avoids having to malloc()) */ X register struct direct *dp; /* -> u.dblk[.] */ X register struct dirent *bp; /* -> buf[.] */ X X#ifdef UNK X if ( state == yes ) /* _getdents() is known to work */ X return _getdents( fildes, buf, nbyte ); X X if ( state == maybe ) /* first time only */ X { X RET_SIG (*shdlr)(); /* entry SIGSYS handler */ X register int retval; /* return from _getdents() if any */ X X shdlr = signal( SIGSYS, sig_catch ); X retval = _getdents( fildes, buf, nbyte ); /* try it */ X (void)signal( SIGSYS, shdlr ); X X if ( state == maybe ) /* SIGSYS did not occur */ X { X state = yes; /* so _getdents() must have worked */ X return retval; X } X } X X /* state == no; perform emulation */ X#endif X X if ( buf == NULL X#ifdef ATT_SPEC X || (unsigned long)buf % sizeof(long) != 0 /* ugh */ X#endif X ) { X errno = EFAULT; /* invalid pointer */ X return -1; X } X X if ( fstat( fildes, &statb ) != 0 ) X return -1; /* errno set by fstat() */ X X if ( !S_ISDIR( statb.st_mode ) ) X { X errno = ENOTDIR; /* not a directory */ X return -1; X } X X if ( (offset = lseek( fildes, (off_t)0, SEEK_CUR )) < 0 ) X return -1; /* errno set by lseek() */ X X#ifdef BFS /* no telling what remote hosts do */ X if ( (unsigned long)offset % DIRBLKSIZ != 0 ) X { X errno = ENOENT; /* file pointer probably misaligned */ X return -1; X } X#endif X X serrno = errno; /* save entry errno */ X X for ( bp = (struct dirent *)buf; bp == (struct dirent *)buf; ) X { /* convert next directory block */ X int size; X X do size = GetBlock( fildes, u.dblk, DIRBLKSIZ ); X while ( size == -1 && errno == EINTR ); X X if ( size <= 0 ) X return size; /* EOF or error (EBADF) */ X X for ( dp = (struct direct *)u.dblk; X (char *)dp < &u.dblk[size]; X dp = (struct direct *)((char *)dp + RecLen( dp )) X ) { X#ifndef UFS X if ( dp->d_reclen <= 0 ) X { X errno = EIO; /* corrupted directory */ X return -1; X } X#endif X X if ( dp->d_fileno != DELETED ) X { /* non-empty; copy to user buffer */ X register int reclen = X DIRENTSIZ( NameLen( dp->d_name ) ); X X if ( (char *)bp + reclen > &buf[nbyte] ) X { X errno = EINVAL; X return -1; /* buf too small */ X } X X bp->d_ino = dp->d_fileno; X bp->d_off = offset + ((char *)dp - u.dblk); X bp->d_reclen = reclen; X X { X#ifdef UFS X /* Is the following kludge ugly? You bet. */ X X register char save = dp->d_name[DIRSIZ]; X /* save original data */ X X dp->d_name[DIRSIZ] = '\0'; X /* ensure NUL termination */ X#endif X (void)strncpy( bp->d_name, dp->d_name, X reclen - DIRENTBASESIZ X ); /* adds NUL padding */ X#ifdef UFS X dp->d_name[DIRSIZ] = save; X /* restore original data */ X#endif X } X X bp = (struct dirent *)((char *)bp + reclen); X } X } X X if ( (char *)dp > &u.dblk[size] ) X { X errno = EIO; /* corrupted directory */ X return -1; X } X } X X errno = serrno; /* restore entry errno */ X return (char *)bp - buf; /* return # bytes read */ X } / echo x - opendir.c gres '^X' '' > opendir.c << '/' X/* X opendir -- open a directory stream X X last edit: 27-Oct-1988 D A Gwyn X*/ X X#include <sys/errno.h> X#include <sys/types.h> X#include <sys/stat.h> X#include <dirent.h> X X#ifdef BSD_SYSV X#define open _open /* avoid emulation overhead */ X#endif X Xtypedef char *pointer; /* (void *) if you have it */ X Xextern void free(); Xextern pointer malloc(); Xextern int open(), close(), fstat(); X Xextern int errno; X X#ifndef NULL X#define NULL 0 X#endif X X#ifndef O_RDONLY X#define O_RDONLY 0 X#endif X X#ifndef S_ISDIR /* macro to test for directory file */ X#define S_ISDIR( mode ) (((mode) & S_IFMT) == S_IFDIR) X#endif X XDIR * Xopendir( dirname ) X char *dirname; /* name of directory */ X { X register DIR *dirp; /* -> malloc'ed storage */ X register int fd; /* file descriptor for read */ X /* The following is static just to keep the stack small. */ X static struct stat sbuf; /* result of fstat() */ X X if ( (fd = open( dirname, O_RDONLY )) < 0 ) X return NULL; /* errno set by open() */ X X if ( fstat( fd, &sbuf ) != 0 || !S_ISDIR( sbuf.st_mode ) ) X { X (void)close( fd ); X errno = ENOTDIR; X return NULL; /* not a directory */ X } X X if ( (dirp = (DIR *)malloc( sizeof(DIR) )) == NULL X || (dirp->dd_buf = (char *)malloc( (unsigned)DIRBUF )) == NULL X ) { X register int serrno = errno; X /* errno set to ENOMEM by sbrk() */ X X if ( dirp != NULL ) X free( (pointer)dirp ); X X (void)close( fd ); X errno = serrno; X return NULL; /* not enough memory */ X } X X dirp->dd_fd = fd; X dirp->dd_loc = dirp->dd_size = 0; /* refill needed */ X X return dirp; X } / echo x - readdir.c gres '^X' '' > readdir.c << '/' X/* X readdir -- read next entry from a directory stream X X last edit: 25-Apr-1987 D A Gwyn X*/ X X#include <sys/errno.h> X#include <sys/types.h> X#include <dirent.h> X Xextern int getdents(); /* SVR3 system call, or emulation */ X Xextern int errno; X X#ifndef NULL X#define NULL 0 X#endif X Xstruct dirent * Xreaddir( dirp ) X register DIR *dirp; /* stream from opendir() */ X { X register struct dirent *dp; /* -> directory data */ X X if ( dirp == NULL || dirp->dd_buf == NULL ) X { X errno = EFAULT; X return NULL; /* invalid pointer */ X } X X do { X if ( dirp->dd_loc >= dirp->dd_size ) /* empty or obsolete */ X dirp->dd_loc = dirp->dd_size = 0; X X if ( dirp->dd_size == 0 /* need to refill buffer */ X && (dirp->dd_size = X getdents( dirp->dd_fd, dirp->dd_buf, (unsigned)DIRBUF ) X ) <= 0 X ) X return NULL; /* EOF or error */ X X dp = (struct dirent *)&dirp->dd_buf[dirp->dd_loc]; X dirp->dd_loc += dp->d_reclen; X } X while ( dp->d_ino == 0L ); /* don't rely on getdents() */ X X return dp; X } / echo x - rewinddir.c gres '^X' '' > rewinddir.c << '/' X/* X rewinddir -- rewind a directory stream X X last edit: 25-Apr-1987 D A Gwyn X X This is not simply a call to seekdir(), because seekdir() X will use the current buffer whenever possible and we need X rewinddir() to forget about buffered data. X*/ X X#include <sys/errno.h> X#include <sys/types.h> X#include <dirent.h> X Xextern off_t lseek(); X Xextern int errno; X X#ifndef NULL X#define NULL 0 X#endif X X#ifndef SEEK_SET X#define SEEK_SET 0 X#endif X Xvoid Xrewinddir( dirp ) X register DIR *dirp; /* stream from opendir() */ X { X if ( dirp == NULL || dirp->dd_buf == NULL ) X { X errno = EFAULT; X return; /* invalid pointer */ X } X X dirp->dd_loc = dirp->dd_size = 0; /* invalidate buffer */ X (void)lseek( dirp->dd_fd, (off_t)0, SEEK_SET ); /* may set errno */ X } / echo x - seekdir.c gres '^X' '' > seekdir.c << '/' X/* X seekdir -- reposition a directory stream X X last edit: 24-May-1987 D A Gwyn X X An unsuccessful seekdir() will in general alter the current X directory position; beware. X X NOTE: 4.nBSD directory compaction makes seekdir() & telldir() X practically impossible to do right. Avoid using them! X*/ X X#include <sys/errno.h> X#include <sys/types.h> X#include <dirent.h> X Xextern off_t lseek(); X Xextern int errno; X X#ifndef NULL X#define NULL 0 X#endif X X#ifndef SEEK_SET X#define SEEK_SET 0 X#endif X Xtypedef int bool; /* Boolean data type */ X#define false 0 X#define true 1 X Xvoid Xseekdir( dirp, loc ) X register DIR *dirp; /* stream from opendir() */ X register off_t loc; /* position from telldir() */ X { X register bool rewind; /* "start over when stymied" flag */ X X if ( dirp == NULL || dirp->dd_buf == NULL ) X { X errno = EFAULT; X return; /* invalid pointer */ X } X X /* A (struct dirent)'s d_off is an invented quantity on 4.nBSD X NFS-supporting systems, so it is not safe to lseek() to it. */ X X /* Monotonicity of d_off is heavily exploited in the following. */ X X /* This algorithm is tuned for modest directory sizes. For X huge directories, it might be more efficient to read blocks X until the first d_off is too large, then back up one block, X or even to use binary search on the directory blocks. I X doubt that the extra code for that would be worthwhile. */ X X if ( dirp->dd_loc >= dirp->dd_size /* invalid index */ X || ((struct dirent *)&dirp->dd_buf[dirp->dd_loc])->d_off > loc X /* too far along in buffer */ X ) X dirp->dd_loc = 0; /* reset to beginning of buffer */ X /* else save time by starting at current dirp->dd_loc */ X X for ( rewind = true; ; ) X { X register struct dirent *dp; X X /* See whether the matching entry is in the current buffer. */ X X if ( (dirp->dd_loc < dirp->dd_size /* valid index */ X || readdir( dirp ) != NULL /* next buffer read */ X && (dirp->dd_loc = 0, true) /* beginning of buffer set */ X ) X && (dp = (struct dirent *)&dirp->dd_buf[dirp->dd_loc])->d_off X <= loc /* match possible in this buffer */ X ) { X for ( /* dp initialized above */ ; X (char *)dp < &dirp->dd_buf[dirp->dd_size]; X dp = (struct dirent *)((char *)dp + dp->d_reclen) X ) X if ( dp->d_off == loc ) X { /* found it! */ X dirp->dd_loc = X (char *)dp - dirp->dd_buf; X return; X } X X rewind = false; /* no point in backing up later */ X dirp->dd_loc = dirp->dd_size; /* set end of buffer */ X } X else /* whole buffer past matching entry */ X if ( !rewind ) X { /* no point in searching further */ X errno = EINVAL; X return; /* no entry at specified loc */ X } X else { /* rewind directory and start over */ X rewind = false; /* but only once! */ X X dirp->dd_loc = dirp->dd_size = 0; X X if ( lseek( dirp->dd_fd, (off_t)0, SEEK_SET ) X != 0 X ) X return; /* errno already set (EBADF) */ X X if ( loc == 0 ) X return; /* save time */ X } X } X } / echo x - telldir.c gres '^X' '' > telldir.c << '/' X/* X telldir -- report directory stream position X X last edit: 25-Apr-1987 D A Gwyn X X NOTE: 4.nBSD directory compaction makes seekdir() & telldir() X practically impossible to do right. Avoid using them! X*/ X X#include <sys/errno.h> X#include <sys/types.h> X#include <dirent.h> X Xextern off_t lseek(); X Xextern int errno; X X#ifndef SEEK_CUR X#define SEEK_CUR 1 X#endif X Xoff_t Xtelldir( dirp ) /* return offset of next entry */ X DIR *dirp; /* stream from opendir() */ X { X if ( dirp == NULL || dirp->dd_buf == NULL ) X { X errno = EFAULT; X return -1; /* invalid pointer */ X } X X if ( dirp->dd_loc < dirp->dd_size ) /* valid index */ X return ((struct dirent *)&dirp->dd_buf[dirp->dd_loc])->d_off; X else /* beginning of next directory block */ X return lseek( dirp->dd_fd, (off_t)0, SEEK_CUR ); X } / exit ---------------------------------------------------------- Terrence W. Holm uunet!uw-beaver!uvicctr!tholm tholm%uvunix.bitnet tholm@uvicctr.UVic.ca
housel@en.ecn.purdue.edu (Peter S. Housel) (12/17/88)
There is a problem with these routines as distributed here. readdir() will always fail in a large directory (approx. 1K or larger). The reason is that getdents() can't be allowed to overflow the per-DIR buffer (which is of size DIRBUF) by reading one directory block of size DIRBLKSIZ. By default, DIRBLKSIZ is 4096, so it is possible to read in more directory data in one read() than can be converted in one step. getdents() cannot return having only converted part of a directory block. (The comment in dirent.h says that DIRBUF should be larger than the "filesystem buffer size," but it doesn't explain why.) Probably the best solution is to compile getdents.c with a "-DDIRBLKSIZ=512" compiler flag. This also has the effect of reducing the memory requirements of programs that call these routines. By the way, when and where was this latest version posted? I must have missed it... -Peter S. Housel- housel@en.ecn.purdue.edu ...!pur-ee!housel
tholm@uvicctr.UUCP (Terrence W. Holm) (12/20/88)
In article <7488@ea.ecn.purdue.edu> housel@en.ecn.purdue.edu (Peter S. Housel) writes: > > There is a problem with these routines as distributed here. OK, did Terrence not test this code well enough before posting? Or, was Peter so anxious to post, that he didn't install and test exactly what I posted? > ... (The comment in dirent.h says that DIRBUF should be >larger than the "filesystem buffer size," but it doesn't explain why.) It must be larger because directory entries are expanded to the system-independent format "struct dirent". For version 7, a 16 byte directory entry may be expanded to as much as 28 bytes, ie. almost twice as large. > Probably the best solution is to compile getdents.c with a >"-DDIRBLKSIZ=512" compiler flag. See /usr/include/sys/dir.h Terrence W. Holm uunet!uw-beaver!uvicctr!tholm tholm%uvunix.bitnet tholm@uvicctr.UVic.ca
glenn@extro.ucc.su.oz (G. Geers [ext 3241]) (05/12/89)
Does anyone have a public domain implementation of directory(3). I need it for minix and for installation on a 68020 box running sysV.2. Glenn glenn@extro.ucc.su.oz