lln - a routine to list all links to a file

Dennis Bednar dennis at rlgvax.UUCP
Tue Jan 6 15:43:48 AEST 1987


I have added a couple of #ifdef's so that the lln.c tool posted
to net.sources should work on 4.2 machines in addition to Sys 5
machines.  Theoretically, all you have to do is define one of
USG, BSD4X, or CCI632, and it should work.  I haved tested it
on the cci632, but have not tried it on a pure 4.2 or 4.3 system.
If you have problems with it, I also added some #ifdef DEBUG code,
which may be enabled by changing the "#undef DEBUG" to "#define
DEBUG 1".

In summary, the changes I made were:

	- the cci632 uses <mtab.h> and /etc/mtab instead of <mnttab.h>
	  and /etc/mnttab.  I assume that the former is 4.2, and the
	  later is pure S5. (Our machine is a Sys5/4.2 hybrid,
	  Sys 5 at the cmd(1) level, some 4.2'isms still hiding under
	  the hood in the kernel.)

	- the cci632 uses 4.2 filesystem with variable length directory
	  entries, instead of 14 char fixed length entries.

	- the cci632 has symbolic links, normally found in 4.2.

Below is only the lln.c file.  Don't forget to remove the trailer
(USENET trailer decapsulation protocol :-) )

------ CUT HERE ---------
/* define your OS type:
 * USG			S3 or S5
 * BSD4X		V7 or 4.2 or 4.3
 * CCI632		Sys5 r2 on CCI Power 6/32
 */
#define CCI632	1

/* the OS type, in turn, defines the lower level #defines that
 * are typical for that system.  If your system is usual,
 * add its type either alongside USG or BSD4X, otherwise if
 * your machine is weird, add it near last #else.
 */
#ifdef USG
#	undef MTAB	/* use /etc/mnttab and mnttab.h */
#	undef BSD_FILESYS	/* use 14 char fix len dir entries */
#	undef BSD_SYMLINK	/* don't have symbolic links */
#else	/* !USG */
/* CCI632 Sys5r2 really has a 4.2 engine under the hood :-) */
#if defined(BSD4X) || defined(CCI632)
#	define MTAB 1		/* have /etc/mtab and mtab.h instead of /etc/mnttab and mnttab.h */
#	define BSD_FILESYS 1	/* have BSD4_2 file system */
#else	/* !BSD4X && !CCI632 && !USG */
	Unknown System.  This causes a compiler error.
#endif /* BSD4X */
#endif /* USG */

#undef DEBUG		/* enables debug code */
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#if defined(MTAB)
#include <fstab.h>
#include <sys/param.h>	/* NMOUNT */
#include <mtab.h>
#define	MNTTAB	mtab
#define MT_FILSYS	m_path
/* MT_FILSYS is field in mtab with directory name */
#else
#include <mnttab.h>
#define MNTTAB	mnttab
#define MT_FILSYS	mt_filsys
#endif
#ifdef BSD_SYMLINK
#define	STAT	lstat
	/* special stat() to handle regular and symbolically linked files */
#else
#define STAT	stat	/* regular old stat() sys call */
#endif
#define BOOL	char


/*
	lln: list links for files

	not (c) 1986 Paul Heffner @ AT&T-IS Altamonte Springs, FL.
		 use it however you wish.

	rlgvax!dennis 01 06 86 Added MTAB, and BSD_FILESYS,
	and BSD_SYMLINK #ifdefs to work on CCI632.
	Enquiries, advice, and calm criticism to:

	ihnp4   \
	akgua   - bsdpkh!heff
	attmail /
*/

#ifdef MTAB
#define MTE_SIZE	sizeof(struct mtab)
#else
#define MTE_SIZE	sizeof(struct mnttab)
#endif
#define DIRESIZ		sizeof(struct direct)
#define PATHMAX	512
#define STR_SAME	!strcmp
#if defined(BSD_FILESYS)
#if !defined(DEBUG)
#define OPENDIR	opendir
#define CLOSEDIR closedir
#else /* debug */
/* use OPENDIR(), CLOSEDIR() debug routines that are default */
/* don't redefine OPENDIR as OPENDIR, because macro recursion expansion */
DIR	*OPENDIR();	/* fw ref */
void	CLOSEDIR();	/* fw ref */
#endif
#endif
char	*u_errmesg();	/* fw ref - return perror() like string */

extern int errno;

/*
	program globals
*/
static int inn;		/* inode number of file being searched for... */
static int found;	/* count of found directory entries.. */
static int need;	/* count of directory entries to find.. */
static int curdev;	/* device # of disk holding fs of objective file. */
static char pathbuf[PATHMAX];	/* holds current search path */

main(argc,argv)
int argc;
char **argv;
{
#ifdef MTAB
static char *mtfil = "/etc/mtab";
#else
static char *mtfil = "/etc/mnttab";
#endif
	struct stat ssfil, junk1, junk2, ssfs;
	/* junk1, junk2 are dummy vars because lstat() would write
	 * past end of ssfil into ssfs, and corrupt it otherwise.
	 * lstat(2) bug on rlgvax!!!
	 */
	struct MNTTAB mte;
	int i, mth, x;
#ifdef	BSD_FILESYS
	DIR	*dirfd;
#else
	int	dirfd;		/* 14 char fixed length directory entries */
#endif

	if (argc < 2) exit(0);

	fclose(stdin);	/* free up an extra fd (just in case) */

	if ((mth = open(mtfil,O_RDONLY)) == -1)
	{	fprintf(stderr,"%s: errno %d, cannot open %s\n",argv[0],errno,mtfil);
		exit(16);
	}
#ifdef DEBUG
	printf("opened mount table file okay\n");
#endif
	for (i = 1; i < argc; ++i)
	{
		if (STAT(argv[i],&ssfil))
		{	fprintf(stderr,"%s:errno %d can not access %s\n",argv[0],errno,argv[i]);
			continue;
		}
		if ((ssfil.st_mode & S_IFMT) == S_IFDIR)
		{	fprintf(stderr,"%s: %s is a directory file.\n",argv[0],argv[i]);
			continue;
		}
		inn = ssfil.st_ino;	/* establish search inode number */
		found = 0;		/* set count of # found */
		need = ssfil.st_nlink;	/* set count of # of links */
		curdev = ssfil.st_dev;	/* # of device holding file */
#ifdef DEBUG
		printf("lln: debug: dev = %d, inode = %d, links = %d\n",
		curdev, inn, need);
#endif

/*
	Scratch through mount table looking for fs hosting file
	seeking a match on device number...
*/

		for (x = 0; x < NMOUNT; ++x)
		{	if (read(mth,&mte,MTE_SIZE) != MTE_SIZE)
			{	fprintf(stderr,"%s: errno %d reading mnttab\n",argv[0],errno);
				exit(32);
			}
			STAT(mte.MT_FILSYS,&ssfs);	/* name of directory mounted as */
#ifdef DEBUG
			printf("lln: debug: Looking at mounted dir %s\n", mte.MT_FILSYS);
			printf(" st_dev=%d, st_rdev=%d, want dev # = %d\n", ssfs.st_dev, ssfs.st_rdev, curdev);
#endif	/* debug */
			if (ssfs.st_dev == ssfil.st_dev) break;
#ifdef DEBUG
			else printf("debug: wrong dir (%d != %d)\n", 
			ssfs.st_dev, ssfil.st_dev);
#endif
		}

#ifdef DEBUG
		printf("lln: debug: found mounted dir starting point\n");
#endif

/*
	The path name is constructed in 'pathbuf'.
	We start with mounted directory from /etc/mnttab entry...
*/
#ifdef BSD_FILESYS
		strncpy(pathbuf,mte.MT_FILSYS,strlen(mte.MT_FILSYS)+1);
#else
		strncpy(pathbuf,mte.MT_FILSYS,14);
#endif
#ifdef DEBUG
		printf("lln: debug: search starting at mounted dir %s\n", pathbuf);
#endif
		if (pathbuf[1] == '\0') pathbuf[0] = '\0';	/* special case for root fs (avoids // at front of path) */
		if (chdir(mte.MT_FILSYS) == -1)
			{
			fprintf(stderr, "lln: chdir(%s) failed\n", mte.MT_FILSYS);
			exit(1);
			}
#ifdef BSD_FILESYS
		if ((dirfd = OPENDIR(mte.MT_FILSYS)) == (DIR *)NULL)
#else
		if ((dirfd = open(mte.MT_FILSYS,O_RDONLY)) == -1)
#endif
		{	fprintf(stderr,"%s: errno %d opening %s\n",argv[0],errno,mte.MT_FILSYS);
			exit(-1);
		}
		if (lnsrch(dirfd))
			fprintf(stderr,"%s incomplete list: only %d of %d links were found.\n",argv[0],found,need);
#ifdef BSD_FILESYS
		CLOSEDIR(dirfd);
#else
		close(dirfd);
#endif
		lseek(mth,0L,0);	/* reset to beginning of mnttab */
	}
	close(mth);
	exit(0);
}

lnsrch(dirfd)
#ifdef BSD_FILESYS
	DIR	*dirfd;
#else
int dirfd;
#endif
{	struct stat curf;
#ifdef BSD_FILESYS
	struct direct *cd;
	DIR	*newdfd;
#else
	struct direct cd;
	int newdfd;
#endif
	char *pathend;
	BOOL	isdir;
#ifdef BSD_SYMLINK
	BOOL	issymlink;
#endif

#ifdef BSD_FILESYS
	/* skip over . and .. in read loop of directory */
#else
	lseek(dirfd,(long) (DIRESIZ << 1),0);	/* go past . and .. */
	/* BUG FIX (long) added for mchs with diff size int & long. rlgvax!dennis */
#endif
#ifdef BSD_FILESYS
	while (cd = readdir(dirfd) )
	{
		if (STR_SAME(cd->d_name, ".") || STR_SAME(cd->d_name, "..") )
			continue;	/* skip . and .. entries */
		if (STAT(cd->d_name,&curf))
		{	fprintf(stderr,"lln: stat error %s/%s\n",pathbuf,cd->d_name);
#else
	while (read(dirfd,&cd,DIRESIZ) == DIRESIZ)	/* bug fix - don't let -1 be okay, rlgvax!dennis */
	{
	if (!cd.d_ino) continue;	/* skip empty entries (i = 0) */
		if (STAT(cd.d_name,&curf))
		{	fprintf(stderr,"lln: stat error %s/%s\n",pathbuf,cd.d_name);
#endif
			continue;
		}
		isdir =  ((curf.st_mode & S_IFMT) == S_IFDIR);
#ifdef BSD_SYMLINK
		issymlink = ((curf.st_mode & S_IFMT) == S_IFLNK);
		if (isdir && issymlink)
			continue;	/* avoid infinite recursion */
		/* recursion when /a/b/c symlinked to /a */
#endif
		if ( isdir )		/* sub-dir?? */
		{
/*
	ignore MT directories and keep search to objective fs (dev)
*/
#ifdef BSD_FILSYS
			if (curf.st_dev != curdev) continue; /* diff mount point */
#else
			if (curf.st_size == (2*DIRESIZ) || /* . && .. ie dir empty */
			    curf.st_dev != curdev) continue; /* wrong mount pt */
#endif

#ifdef BSD_FILESYS
#ifdef DEBUG
			/* track down cannot visit problem */
			printf("Will OPENDIR(%s) Current directory is ", cd->d_name);
			fflush(stdout);
			system("pwd");
#endif
			if ((newdfd = OPENDIR(cd->d_name)) == (DIR *)NULL)
			{	fprintf(stderr,"lln: Can't open %s/%s: %s\n",pathbuf,cd->d_name, u_errmesg());
#else
			if ((newdfd = open(cd.d_name,O_RDONLY)) == -1)
			{	fprintf(stderr,"lln: Can't open %s/%s\n",pathbuf,cd.d_name);
#endif
				continue;
			}
/*
	Maintain current path in pathbuf global area
*/
			for (pathend = pathbuf; *pathend; ++pathend) ;
			*pathend = '/';
#ifdef BSD_FILESYS
			strncpy(pathend + 1,cd->d_name,cd->d_namlen + 1);
			chdir(cd->d_name);
#else /* !bsd_filsys */
			strncpy(pathend + 1,cd.d_name,14);
			chdir(cd.d_name);
#endif
#ifdef DEBUG
			printf("lln: debug: visiting %s\n", pathbuf);
#endif /* debug */
			if (!lnsrch(newdfd))
				{
#ifdef BSD_FILESYS
#ifdef DEBUG
				printf("WILL CLOSEDIR\n");
#endif
				CLOSEDIR(newdfd);
#else
				close(newdfd);
#endif
				return 0;	/* recursively return 0 if all were found */
				}
#ifdef BSD_FILESYS
#ifdef DEBUG
			printf("WILL CLOSEDIR()!\n");
#endif
			CLOSEDIR(newdfd);
#else
			close(newdfd);
#endif
			chdir("..");
			*pathend = '\0';
			continue;
		}

#ifdef DEBUG
		else
			printf("debug: reg file: %s/%s\n", pathbuf,
#ifdef BSD_FILESYS
			cd->d_name);
#else
			cd.d_name);
#endif /* bsd */
#endif /* debug */

#ifdef BSD_FILESYS
		if (cd->d_ino == inn)	/* Print out path on match!! */
		{	printf("%s/%.14s\n",pathbuf,cd->d_name);
#else
		if (cd.d_ino == inn)	/* Print out path on match!! */
		{	printf("%s/%.14s\n",pathbuf,cd.d_name);
#endif
			if (++found == need) return 0;
		}
	}
	return -1;
}


#if defined(BSD_FILESYS) && defined(DEBUG)
/*
 * OPENDIR and CLOSEDIR are debug routines instead of the
 * regular OPENDIR(), CLOSEDIR() routines.  It is used
 * to help track down a problem that I think is "too many
 * files open".
 */

static	open_count = 0;		/* number of open files */
#define MAXFILES _NFILE+1	/* one extra for precaution */
static	char	*fname[MAXFILES];	/* file names, fname[0] is first file name */
static	DIR	*fptr[MAXFILES];	/* store DIR * ptrs in parellel w fname[] */

/*
 * the array fname[] is needed since we cannot derive the directory
 * name when we are closing it.
 */

DIR *
OPENDIR(name)
	char	*name;
{
	DIR	*dirp;
	char	*str_malloc();	/* fw ref */

	fname[open_count] = str_malloc(name);
	dirp = opendir(name);
	fptr[ open_count ] = dirp;
	++open_count;
	printf("OPENDIR(%s) %d\n", name, open_count);	/* number open *after* open */
	return (dirp);
}

void
CLOSEDIR( dirp )
	DIR	*dirp;
{
	int	i;
	if (open_count < 1 )	/* nothing open now */
		error("CLOSEDIR without OPENDIR");

	/* must check all slots, not just first open_count,
	 * because of "holes" in middle!!! */
	for (i = 0; i < MAXFILES; ++i)
		if (dirp == fptr[i])
			break;	/* found ptr */
	if (i >= MAXFILES)
		error("not found in table");
	--open_count;
	printf("CLOSEDIR(%s) %d\n", fname[i], open_count);
	free( fname[i]);
	return;
}

/*
 * malloc space for a string and copy it in
 * return ptr to malloc'd string
 */
char	*
str_malloc( s )
	char	*s;
{
	extern	char	*malloc();
	char	*cp;
	cp = malloc( strlen(s) + 1);
	if (!cp )
		error("out of space");
	strcpy( cp, s );
	return cp;
}

error(msg)
	char	*msg;
{
	fprintf(stderr, "%s\n", msg);
	exit (1);
}
#endif /* bsd && debug */

/*
 * return UNIX error message associated with errno
 * more flexible than perror(3)
 */

char *
u_errmesg()
{
	extern	int	errno;
	extern	int	sys_nerr;
	extern	char	*sys_errlist[];
static	char	buffer[50];	/* don't loose it !!! */

	if (errno < 0 || errno >= sys_nerr)
		{
		sprintf( buffer, "errno %d undefined (%d=max)", errno, sys_nerr);
		return(buffer);
		}

	return( sys_errlist[errno] );
}
-- 
-Dennis Bednar
{decvax,ihnp4,harpo,allegra}!seismo!rlgvax!dennis	UUCP



More information about the Comp.sources.bugs mailing list