Minix1.5/lib/other/getdents.c

Compare this file to the similar file:
Show the results in this format:

#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 */
}