[net.sources] lln - a routine to list all links to a file

heff@bsdpkh.UUCP (Paul K Heffner) (12/30/86)

This little routine will list out all paths to a file. (A list of all
links). I suspect it is terribly System V dependant and I welcome
anybody's converting it for other systems. It seems to run really
well on most System V boxes...

Paul Heffner
AT&T-IS, Altamonte Springs, FL.
(305)-869-2245
{ihnp4,akgua,attmail}!bsdpkh!heff

======================= Cut here and send rest thru sh ===============
#!bin/sh
# shar: Shell Archiver
#	Run the following text with /bin/sh to create:
#		Readme
#		lln.1
#		lln.c
#
echo shar: extracting Readme '(1420 characters)'
sed 's/^XX//' << \SHAR_EOF > Readme
XX		=========== Readme file for lln ==========
XX
XX
XXThis little terror searches through a file system like a juggernaut,
XXlooking for all links to the files in his arg list. I wrote it because
XXncheck is a pain to use and I really felt like doing it. I have been
XXusing it for some time now with no problems so I feel it's pretty stable.
XX
XX
XXBuilding lln:
XX
XXMake's internal rules should be sufficient to build lln. Just type
XX"make lln". If that fails use "cc -O -olln lln.c".
XX
XXDependencies:
XX
XXLln has been compiled and runs fine on the following AT&T System V
XXmachines: 6300+ (small model under simultask), 3b1/7300, 3b2(all), 
XX3b5 and 3b15. With no BSD system at my disposal, I didn't include
XXany IFDEFs for BSD and I suspect it would fail to compile under same.
XXI would be delighted to see what would need to be changed for lln
XXto run under BSD systems or Xenix, etc. (Probable trouble areas:
XXuse of /etc/mnttab, stat calls)
XX
XXUsage rights:
XX
XXI wrote this routine to fill a technical need, not a financial one. Use
XXit however you want and give it to anyone who needs it. (I'd appreciate
XXsome credit, but I don't really care!!)
XX
XXContact info:
XX
XXlln was scratched together during spare time by me:
XX
XXPaul Heffner
XXATT Info Systems 78Q-611
XX1024 Wymore Rd.
XXAltamonte Springs, FL.  32714
XX
XX(305)-869-2245			(I.S. Comm RNX 755)
XX{ihnp4,akgua,attmail}!bsdpkh!heff
XX
XXComments, coding suggestions, and technical errata are solicited and welcome!
XX
SHAR_EOF
if test 1420 -ne `wc -c Readme`
then
	echo shar: error transmitting Readme 'expected 1420 characters'
fi
echo shar: extracting lln.1 '(1015 characters)'
sed 's/^XX//' << \SHAR_EOF > lln.1
XX.deTH
XX.PD
XX.nrIN \\n()Mu
XX.ift .ds ]H \\$1\^(\^\\$2\^)
XX.ifn .ds ]H \\$1(\\$2)
XX.if\\n()s .ds ]D
XX.if\\n()t .ds ]D UNIX System V
XX.ifn .ds ]D UNIX System V
XX.ds]L
XX.if!\\$3 .ds ]L (\^\\$3\^)
XX.if!\\$4 .ds ]D \\$4
XX.wh0 }H
XX.wh-\\n(:mu }F
XX.em}M
XX.if\\n(nl .bp
XX.nr)I \\n()Mu
XX.nr)R 0
XX.}E
XX.DT
XX.ifn \{.na
XX.nh\}
XX.ift \{.bd S 3 3
XX.hy14 \}
XX..
XX.TH LLN 1
XX.SH NAME
XXlln \- list all paths to a file
XX.SH SYNOPSIS
XX.B lln
XXfile names..
XX.SH DESCRIPTION
XX.I Lln\^
XXattempts to list the complete paths for all links to the given file
XXnames. The paths are printed on
XX.I stdout,
XXeach path separated by a newline. The order of the list is simply the
XXorder in which each link was found during a recursive file system search.
XX.SH BUGS/LIMITATIONS
XXThe file system search opens a new file descriptor for each sub-directory
XXdescent. The search will halt if directory nesting level exceeds the
XXmaximum open files per process. (NOFILE tunable parameter on later System
XXV releases, 20 on older) 
XX.SH FILES
XX/etc/mnttab
XX.SH SEE ALSO
XXncheck(1m),
XXmnttab(4).
SHAR_EOF
if test 1015 -ne `wc -c lln.1`
then
	echo shar: error transmitting lln.1 'expected 1015 characters'
fi
echo shar: extracting lln.c '(3808 characters)'
sed 's/^XX//' << \SHAR_EOF > lln.c
XX#include <sys/types.h>
XX#include <sys/dir.h>
XX#include <sys/stat.h>
XX#include <stdio.h>
XX#include <fcntl.h>
XX#include <mnttab.h>
XX
XX
XX/*
XX	lln: list links for files
XX
XX	not (c) 1986 Paul Heffner @ AT&T-IS Altamonte Springs, FL.
XX		 use it however you wish.
XX
XX	Enquiries, advice, and calm criticism to:
XX
XX	ihnp4   \
XX	akgua   - bsdpkh!heff
XX	attmail /
XX*/
XX
XX#define MTE_SIZE	sizeof(struct mnttab)
XX#define DIRESIZ		sizeof(struct direct)
XX#define PATHMAX	512
XX
XXextern int errno;
XX
XX/*
XX	program globals
XX*/
XXstatic int inn;		/* inode number of file being searched for... */
XXstatic int found;	/* count of found directory entries.. */
XXstatic int need;	/* count of directory entries to find.. */
XXstatic int curdev;	/* device # of disk holding fs of objective file. */
XXstatic char pathbuf[PATHMAX];	/* holds current search path */
XX
XXmain(argc,argv)
XXint argc;
XXchar **argv;
XX{	static char *mtfil = "/etc/mnttab";
XX	struct stat ssfil, ssfs;
XX	struct mnttab mte;
XX	int i, mth, dirfd, x;
XX
XX	if (argc < 2) exit(0);
XX
XX	fclose(stdin);	/* free up an extra fd (just in case) */
XX
XX	if ((mth = open(mtfil,O_RDONLY)) == -1)
XX	{	fprintf(stderr,"%s: errno %d, cannot open %s\n",argv[0],errno,mtfil);
XX		exit(16);
XX	}
XX	for (i = 1; i < argc; ++i)
XX	{
XX		if (stat(argv[i],&ssfil))
XX		{	fprintf(stderr,"%s:errno %d can not access %s\n",argv[0],errno,argv[i]);
XX			continue;
XX		}
XX		if ((ssfil.st_mode & S_IFMT) == S_IFDIR)
XX		{	fprintf(stderr,"%s: %s is a directory file.\n",argv[0],argv[i]);
XX			continue;
XX		}
XX		inn = ssfil.st_ino;	/* establish search inode number */
XX		found = 0;		/* set count of # found */
XX		need = ssfil.st_nlink;	/* set count of # of links */
XX		curdev = ssfil.st_dev;	/* # of device holding file */
XX
XX/*
XX	Scratch through mount table looking for fs hosting file
XX	seeking a match on device number...
XX*/
XX
XX		for (x = 0; x < NMOUNT; ++x)
XX		{	if (read(mth,&mte,MTE_SIZE) != MTE_SIZE)
XX			{	fprintf(stderr,"%s: errno %d reading mnttab\n",argv[0],errno);
XX				exit(32);
XX			}
XX			stat(mte.mt_filsys,&ssfs);
XX			if (ssfs.st_dev == ssfil.st_dev) break;
XX		}
XX
XX/*
XX	The path name is constructed in 'pathbuf'.
XX	We start with mounted directory from /etc/mnttab entry...
XX*/
XX		strncpy(pathbuf,mte.mt_filsys,14);
XX		if (pathbuf[1] == '\0') pathbuf[0] = '\0';	/* special case for root fs (avoids // at front of path) */
XX		chdir(mte.mt_filsys);
XX		if ((dirfd = open(mte.mt_filsys,O_RDONLY)) == -1)
XX		{	fprintf(stderr,"%s: errno %d opening %s\n",argv[0],errno,mte.mt_filsys);
XX			exit(-1);
XX		}
XX		if (lnsrch(dirfd))
XX			fprintf(stderr,"%s incomplete list: only %d of %d links were found.\n",argv[0],found,need);
XX		close(dirfd);
XX
XX		lseek(mth,0L,0);	/* reset to beginning of mnttab */
XX	}
XX	close(mth);
XX	exit(0);
XX}
XX
XXlnsrch(dirfd)
XXint dirfd;
XX{	struct stat curf;
XX	struct direct cd;
XX	int newdfd;
XX	char *pathend;
XX
XX	lseek(dirfd,DIRESIZ << 1,0);	/* go past . and .. */
XX	while (read(dirfd,&cd,DIRESIZ))
XX	{	if (!cd.d_ino) continue;	/* skip empty entries (i = 0) */
XX		if (stat(cd.d_name,&curf))
XX		{	fprintf(stderr,"error - cannot stat %s/%s\n",pathbuf,cd.d_name);
XX			continue;
XX		}
XX		if ((curf.st_mode & S_IFMT) == S_IFDIR)	/* sub-dir?? */
XX		{
XX/*
XX	ignore MT directories and keep search to objective fs (dev)
XX*/			if (curf.st_size == 32 ||
XX			    curf.st_dev != curdev) continue;
XX
XX			if ((newdfd = open(cd.d_name,O_RDONLY)) == -1)
XX			{	fprintf(stderr,"error -cannot visit %s/%s\n",pathbuf,cd.d_name);
XX				continue;
XX			}
XX/*
XX	Maintain current path in pathbuf global area
XX*/
XX			for (pathend = pathbuf; *pathend; ++pathend) ;
XX			*pathend = '/';
XX			strncpy(pathend + 1,cd.d_name,14);
XX			chdir(cd.d_name);
XX			if (!lnsrch(newdfd)) return 0;	/* recursively return 0 if all were found */
XX			close(newdfd);
XX			chdir("..");
XX			*pathend = '\0';
XX			continue;
XX		}
XX		if (cd.d_ino == inn)	/* Print out path on match!! */
XX		{	printf("%s/%.14s\n",pathbuf,cd.d_name);
XX			if (++found == need) return 0;
XX		}
XX	}
XX	return -1;
XX}
SHAR_EOF
if test 3808 -ne `wc -c lln.c`
then
	echo shar: error transmitting lln.c 'expected 3808 characters'
fi

amos@instable.UUCP (Amos Shapir) (01/01/87)

What's wrong with:

set `ls -id file`
find / -inum $1 -print

It's much more portable than the given program, can limit the search
to any dir instead of /, and is probably faster on most machines.
The moral is: before writing your own utilities (or hacking the kernel
as some budding gurus do), re-read the manual - you'll be surprised how
many new and useful flags are in utilities you have been using daily!
-- 
	Amos Shapir
National Semiconductor (Israel)
6 Maskit st. P.O.B. 3007, Herzlia 46104, Israel
(011-972) 52-522261  amos%nsta@nsc 34.48'E 32.10'N

kimcm@olamb.UUCP (Kim Chr. Madsen) (01/02/87)

In article <659@instable.UUCP>, amos@instable.UUCP (Amos Shapir) writes:
> What's wrong with:
> 
> set `ls -id file`
> find / -inum $1 -print
> 
Well nothing, if you don't have more than one filesystem or don't care
about references to files which have nothing in common with the file searched
for!

Don't forget that there is a separate inode table connected to each filesystem,
if / (root) and /usr is on separate filesystems they both have the inode 2,
but /usr is not a link to /.

What you'll have to do to make this work is to identify the filesystem where
the ``file'' is located and then make the find command start at that point.

					Best wishes for 1987
					Kim Chr. Madsen

kdw1@sphinx.UUCP (01/04/87)

(I'm directing followups to net.sources.bugs, which I gather is what 
corresponds to net.sources.d under the new naming scheme...)

In net.sources, in reference to the recently posted lln program (which
lists all files linked to a particular file), amos@instable.UUCP (Amos
Shapir) writes:

>What's wrong with:
>
>set `ls -id file`
>find / -inum $1 -print
>
>It's much more portable than the given program, can limit the search
>to any dir instead of /, and is probably faster on most machines.
>The moral is: before writing your own utilities [...]

I have a few comments in support of the author and poster of lln.
I am using System V on an AT&T 3B5.

1. Our man page for find does not list -inum as an option.  I tried it
   anyway, and was surprised to find that it works as I expect it's
   supposed to.  But it seems a bit harsh to blame the author of lln
   for not knowing about what is (for some SysV users, at least) an
   undocumented feature.

2. Lln may be non-portable, but assuming that the options of Unix
   utilities act the same way on different flavors of Unix is too.
   Witness the differences between SysV and BSD greps and awk, to name
   just a couple.

3. On my system, lln is 9 times as fast as the find -inum suggestion.

4. The find -inum suggestion doesn't work.  I-nodes are only unique
   within a file-system.  The find technique, if rooted at / as
   suggested, will find all files with the same i-node.  This is not
   the same as finding all links to a file, which is what lln does.
   When I tried the code above on a file which I knew to have two
   links (and which lln properly reports), find listed the two files,
   plus three totallly different files from two other file systems!

I've already found lln to be quite useful; thanks to the author!

		keith-- 
Keith Waclena               BITNET:       xrtkdw1@uchimvs1.bitnet
University of Chicago         UUCP: ...ihnp4!gargoyle!sphinx!kdw1
Graduate Library School