[alt.sources] Another "Rapid Location of Mount Points"

jms@close.cs.columbia.edu (Jonathan M. Smith) (01/18/90)

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by Jonathan M. Smith <jms@close> on Wed Jan 17 14:07:08 1990
#
# This archive contains:
#	README		Script		defs.h		dev_tab.c	
#	direct.c	getmnt.c	makefile	namelist.c	
#	ngetmnt.c	print.c		tree.c		util.c		
#	xgetmnt.c	
#

LANG=""; export LANG
PATH=/bin:/usr/bin:$PATH; export PATH

echo x - README
cat >README <<'@EOF'

These are the various versions of "getmnt" described in the article
"Rapid Location of Mount Points", by me, in the September 1989 issue
of Software - Practice and Experience.

getmnt.c is the source for getmnt1 of the article
ngetmnt.c is the source for getmnt2 of the article
and
the remaining source is for getmnt3 of the article.

Script was used for running the tests,
and makefile makes the various executables.

Since the programs were developed for high performance in a production
environment (System V UNIX on VAXen), there are various non-portabilities.

My current address is (for questions, comments, et cetera):
	Jonathan M. Smith
	Asst. Prof., Dept. CIS
	Univ. of Penn., 200 South 33rd St.,
	Phila., PA 19104-6389
	(215) 898-9509
	jms@dsl.cis.upenn.edu

@EOF

chmod 666 README

echo x - Script
cat >Script <<'@EOF'
#
# location of executables
#
DIR=/pkg/bcrcc/tst_spe.d
#
# initial setup work
#
cd ${DIR}
/bin/make -f makefile clean
/bin/make -f makefile getmnt ngetmnt xgetmnt
#
# begin by initializing namelist data for [nx]getmnt
#
${DIR}/ngetmnt >/dev/null
${DIR}/xgetmnt >/dev/null
#
# create the output file, which will contain the timings.
#
/bin/uname -a >/tmp/output
#
# now flush buffers, so that buffer cache does not
# interfere with timing.
#
ulimit 300000
/bin/dd if=/dev/mem of=/tmp/file bs=128k count=20
/bin/sync
/bin/rm -f /tmp/file
/usr/bin/timex ${DIR}/getmnt >/dev/null 2>>/tmp/output
/bin/dd if=/dev/mem of=/tmp/file bs=128k count=20
/bin/sync
/bin/rm -f /tmp/file
/usr/bin/timex ${DIR}/ngetmnt >/dev/null 2>>/tmp/output
/bin/dd if=/dev/mem of=/tmp/file bs=128k count=20
/bin/sync
/bin/rm -f /tmp/file
/usr/bin/timex ${DIR}/xgetmnt >/dev/null 2>>/tmp/output
/bin/sync
/bin/make -f makefile clean
/bin/mail pyuxe!smith </tmp/output
@EOF

chmod 666 Script

echo x - defs.h
cat >defs.h <<'@EOF'

# include 	<stdio.h>
# include 	<fcntl.h>
# include	<sys/var.h>
# include	<sys/types.h>
# include	<sys/stat.h>
# include 	<sys/dir.h>
# include 	<sys/inode.h>
# include 	<sys/buf.h>
# include 	<sys/mount.h>
# include	<sys/sysmacros.h>

#define EOS '\0'	/* End of String */
#define SLASH '/'	/* separates directory components */
#define ROOT_DIR "/"
#define DEV_DIR	"/dev/dsk"  /* /dev/dsk saves search of /dev, less general */
#define DEV "/dev"
#define DEV_LEN (4)	/* strlen("/dev") */
#define UNIX "/unix"
#define KMEM "/dev/kmem"
#define NL_FILE "/letc/xgetmnt_nlist"
				
#define VMASK 0x3fffffff	/* VAX address mask	*/
				
#define HASHSIZE 128 		/* size, table for directory structures	*/

#define MAX_DIRS (128)		/* maximum *printable* number of directories */
				/* in a path */

#ifndef brdev
# ifdef u3b2
#  define brdev(x) ((int)(x)&0xFF)
# else
#  define brdev(x) ((int)(x)&0x7F)
# endif
#endif

struct node {
	dev_t n_dev;
	char *n_name;
	struct node *n_next;
	struct node *n_prev;
	struct node *n_parent;
};

struct device {
	dev_t d_number;		/* device number */
	short d_flags;		/* mainly to indicate DELETED */
#define NO_FLAGS (0)
#define IN_TREE  (1)
#define DELETED  (2)
	char *d_name;		/* device pathname */
	struct device *d_parent;/* parent device in tree */
	struct device *d_next;  /* used for hashlist maintenance */
	struct device *d_children; /* children of this device in tree */
	struct device *d_next_child; /* siblings of this device in tree */
};

struct device *lookup();

extern char
  	*strsave(), 
	*emalloc(), 
	*malloc();

#include <a.out.h>

/* deal with stupidity in sym.h */
#ifdef n_name
# undef n_name
#endif

/* indices for elements of Namelist structure in namelist.c */
#define MOUNT 0
#define V 1

/* stuff for directory management routines */
typedef struct _dirdesc {
  	int dd_fd;
  	long dd_loc;
	long dd_size;
	long dd_bbase;
	long dd_entno;
	long dd_bsize;
	char *dd_buf;
} DIR;

extern DIR *opendir();
extern struct direct *readdir();
extern long telldir();
extern void seekdir();
#define rewinddir(dirp) seekdir( (dirp), (long) 0)
extern void closedir();

#define TRUE 1
#define FALSE 0



@EOF

chmod 666 defs.h

echo x - dev_tab.c
cat >dev_tab.c <<'@EOF'
#include "defs.h"

/* Devnames should contain regular expressions
 * which characterize block
 * special files containing file systems.
 * The last entry should be a null string.
 */

char *Devnames[] = {
	"/dev/dsk/*",
	""
};

struct device 	
	*new_device(),
	*Hashtab[HASHSIZE];

struct device *
lookup( device )
dev_t device;
{
	register struct device *ptr;
	register int index;

	index = hash( (int)device );

	for( ptr = Hashtab[ index ];
		ptr != (struct device *)NULL; 
		ptr = ptr->d_next )
	{
		if( ptr->d_number == device )
			break;
	}
	return( ptr );
}

int
hash( j )
int j;
{

	return( ( 7*j ) % HASHSIZE );

}

insert( dev_name )
char *dev_name;
{
	struct stat stat_buf;
	int index, hash();
	struct device *ptr, *lookup();

	if( !valid_name( dev_name ) )
		return;

	stat( dev_name, &stat_buf );

	if( lookup( brdev(stat_buf.st_rdev) ) == (struct device *) NULL )
	{
		index = hash( (int)brdev(stat_buf.st_rdev) );
		ptr = new_device( (int)brdev(stat_buf.st_rdev), dev_name );
		ptr->d_next = Hashtab[ index ];
		Hashtab[ index ] = ptr;
	}

	return;
}

struct device *
new_device( dev, name )
int dev;
char *name;
{
	struct device *ptr;

	ptr = (struct device *) emalloc( sizeof(struct device) );
	ptr->d_number = dev;
	ptr->d_flags = NO_FLAGS;
	ptr->d_name = strsave( name );
	ptr->d_next = 
	  ptr->d_children = 
	  ptr->d_next_child = 
	  ptr->d_parent = (struct device *) NULL;
	return( ptr );
}

#ifdef HDEBUG
hash_stats()
{
	int i, count;
	struct device *np;

	fprintf( stderr, "%s\n",
		"getmnt device name hash table performance report" );
	fprintf( stderr, "Hash table size: %d\n", HASHSIZE );

	for( i = 0; i < HASHSIZE; i += 1 )
	{
		for( count = 0, np = Hashtab[i];
			np != (struct device *) NULL;
			np = np->d_next )
			count = count + 1;

		fprintf( stderr, "Bucket Number: %d, List Length: %d\n",
			i, count );
	}

}
#endif


do_devs( dir )
char *dir;
{
	struct stat stat_buf;
	register struct direct *d_ptr;
	DIR *dir_fd;
#define NAMELEN 128	/* longest possible device name */
	char name_buf[ NAMELEN ];

	dir_fd = opendir( dir );
	if( dir_fd == (DIR *) NULL )
	  	panic( "opendir() failed in do_devs()" );

	if( chdir( dir ) < 0 )
		panic( "chdir( dir ) failed in do_devs()" );

	while( (d_ptr = readdir( dir_fd )) != (struct direct *) NULL )
	{

		/* crude but fast code to boogie thru ino==0, "." and ".." */
	  	if( (d_ptr->d_ino == 0) ||
			(d_ptr->d_name[0] == '.' && ((d_ptr->d_name[1] == EOS) ||
			(d_ptr->d_name[1] == '.' && d_ptr->d_name[2] == EOS))))
			continue;

		strcpy( name_buf, dir );
		strcat( name_buf, "/" );
		strncat( name_buf, d_ptr->d_name, DIRSIZ );

		stat( name_buf, &stat_buf );
	
		if( (stat_buf.st_mode & S_IFMT) == S_IFBLK )
		{
			insert( name_buf );
		}
		else if( (stat_buf.st_mode & S_IFMT) == S_IFDIR )
		{
			do_devs( name_buf );
		}
	}

	if( chdir( ".." ) < 0 )
		panic( "chdir(..) failed in do_devs()" );

	closedir( dir_fd );
	return;
}

int
valid_name( dev_name )
register char *dev_name;
{
	register int index;
	int match();

	for( index = 0; *Devnames[ index ] != EOS ; index += 1 )
		if( match( Devnames[ index ], dev_name ) )
			return( 1 );

	return( 0 );
}

#ifdef EBUG
dump_devs()
{
  	struct device *device;
	int index;

	for( index = 0; index < HASHSIZE; index += 1 )
		for( device = Hashtab[index];
			device != (struct device *) NULL;
			device = device->d_next )
			print_dev( device );

	return;
}

print_dev( device )
struct device *device;
{
	printf( "device: 0x%x\n", device );
	printf( "device->d_number: 0x%x\n", device->d_number );
	printf( "device->d_name: %s\n", device->d_name );
	printf( "device->d_parent: 0x%x\n", device->d_parent );
	printf( "device->d_children: 0x%x\n", device->d_children );
	printf( "device->d_next_child: 0x%x\n", device->d_next_child );
	printf( "device->d_next: 0x%x\n", device->d_next );
	return;
}

#endif


@EOF

chmod 666 dev_tab.c

echo x - direct.c
cat >direct.c <<'@EOF'
/* 
 * this file contains routines which deal with directories 
 * and pathnames.
 *
 */

#include "defs.h"

int
matched_dirs( path1, path2 )
register char *path1, *path2;
{
	int count;

	for( count = 0; *path1 == *path2; ++path1, ++path2 )
	{
		if( *path1 == EOS )
			return( count+1 );
		if( *path1 == SLASH )
			count += 1;
	}
	return( count );
}

int
count_dirs( path )
register char *path;
{
	int count;

	for( count = 1; *path != EOS; ++path )
		if( *path == SLASH )
			count += 1;

	return( count );
}

char *
next_dir( path )
register char *path;
{
	while( *path != EOS )
	{
		if( *path == SLASH )
		{
			++path;
			break;
		}
		++path;
	}
	return( path );
}

int
cheap_cd( current, destination )
char *current, *destination;
{
	int cl, dl, ml, strip_dirs, prefix_dd;
	char buf[BUFSIZ];
	register char *ptr;

	cl = count_dirs( current );
	dl = count_dirs( destination );
	ml = matched_dirs( current, destination );

#ifdef EBUG
fprintf( stderr, "current: %s, destination: %s, cl: %d, dl: %d, ml: %d\n",
current,destination, cl, dl, ml );
#endif

	if( cl == dl && cl == ml )
		return( 0 );	/* already there, no work */

	strip_dirs = ml;
	while( strip_dirs-- > 0 )
		destination = next_dir( destination );

	prefix_dd = cl-ml;
	ptr = buf;
	while( prefix_dd-- > 0 )
	{
		*ptr++ = '.';	/* effect of strcat( foo, "../" ) */
		*ptr++ = '.';	/* but a lot	*/
		*ptr++ = SLASH;	/* faster	*/
	}
	while( *ptr++ = *destination++ )
		;
#ifdef EBUG
fprintf( stderr, "chdir( %s )\n", buf );
#endif

	return( chdir( buf ) );
}

#ifndef BSD

/*
 * C routines to implement Berkeley style directory
 * manipulation stuff.
 */

DIR *
opendir( filename )
char *filename;
{
  	char *malloc();
	DIR *dirp;

	dirp = (DIR *) malloc( (unsigned) sizeof( DIR ) );
	if( dirp != (DIR *) NULL )
	{
		struct stat sb;
		int nbytes;

		dirp->dd_fd = open( filename, 0 );
		if( dirp->dd_fd < 0 )
		{
			free( dirp );
			return( (DIR *) NULL );
		}

		fstat( dirp->dd_fd, &sb );
		dirp->dd_size = dirp->dd_bsize = sb.st_size;
		dirp->dd_buf = malloc( (unsigned) dirp->dd_bsize );
		if( dirp->dd_buf == (char *) NULL )
		{
			close( dirp->dd_fd );
			free( dirp );
			return( (DIR *) NULL );
		}

		/* 
		 * Read in the entire directory.
		 * ?should this be in readdir()? 
		 */
		nbytes = read( dirp->dd_fd, dirp->dd_buf, dirp->dd_bsize );

		if( nbytes < dirp->dd_bsize )
		{
			close( dirp->dd_fd );
			free( dirp->dd_buf );
			free( dirp );
			return( (DIR *) NULL );
		}

		/* should this be in closedir()?
		 * It's not needed after that big read...
		 */
		close( dirp->dd_fd );

		dirp->dd_loc = 0L;
		dirp->dd_entno = 0L;
	}

	return( dirp );	
}

struct direct *
readdir( dirp )
DIR *dirp;
{
	struct direct *dp;

	if( dirp->dd_loc >= dirp->dd_size )
		return( (struct direct *) NULL );
	dp = (struct direct *) 
		&(dirp->dd_buf[ dirp->dd_loc ]);
	dirp->dd_entno += 1;
	dirp->dd_loc = dirp->dd_entno * sizeof( struct direct ); /* Sys V */

	return( dp );

}

long
telldir( dirp )
DIR *dirp;
{
	return( dirp->dd_loc );
}

void
seekdir( dirp, loc )
DIR *dirp;
long loc;
{
	dirp->dd_loc = loc;
	dirp->dd_entno = loc / (sizeof (struct direct) ); /* Sys V */

	return;
}

/*
 * rewinddir() is implemented as a macro.
 */

void
closedir( dirp )
DIR *dirp;
{
	/* deallocate buffer */
	free( dirp->dd_buf );

	/* deallocate "dirp" */
	free( dirp );

	return;
}

#endif

@EOF

chmod 666 direct.c

echo x - getmnt.c
cat >getmnt.c <<'@EOF'

/*
 * Algorithm: 
 * 1.)Build a hash table containing all block devices,
 *    (satisfying naming constraints: see Devnames table)
 *    indexed by hash( dev_id ), where dev_id is the
 *    device id alluded to in the "stat(2)" documentation.
 *    This table also contains the path name of the special file.
 * 2.)Peruse the directory structure, limited by MAXDEPTH, to
 *    look for mount points. Mount points are found by comparing the
 *    device id of the parent to the device id of the child.
 *    When they differ, we have a mount point. Then we can
 *    hash to the appropriate device name and dump the data.
 *
 * Possible Problems:
 * a.)It's slow.
 * b.)Non-unique names ( "/dev/ra0" and "/dev/ra00" for example )
 * c.)The program is not guaranteed to perform correctly
 *    if either the user is not root **or** mount points are
 *    found more than MAXDEPTH-1 deep.
 *
 * Notes:
 * a.)Assumes devices found under /dev
 * b.)get_mnt_pts() uses both a full path name and a relative
 *    path name to save time on "stat()" system calls, which
 *    use namei().
 *
 */

static char *SCCS_ID_STR = "@(#) /STD/pkg/bcrcc/getmnt.d/s.getmnt.c REL LEVEL=1.2 LAST DELTA ON 5/2/85 ";

# include 	<stdio.h>
# include 	<fcntl.h>
# include	<sys/types.h>
# include	<sys/stat.h>
# include 	<sys/dir.h>

#define ROOT_DIR "/"
#define DEV_DIR	"/dev"
#define SPECIAL 070000		/* mode val., all special files		*/
#define BLOCK_SPECIAL 060000 /* mode val. for block special file	*/
#define DIR_MODE   040000    /* mode val. for dir. returned by stat() 	*/
#define TABSIZE	32	/* size, table for directory structures	*/
#define HASHSIZE 128
#define MAXDEPTH 4	/* how deep to go		*/
#define NAMELEN ( (MAXDEPTH+1) * (DIRSIZ+1) + 1 )

struct stat S_buf;
int Cur_Depth = 1;

struct node {
	dev_t dev_id;
	char *name;
	struct node *next;
};

/* Devnames should contain regular expressions
 * which characterize block
 * special files containing file systems.
 * The last entry should be a null string.
 */

char *Devnames[] = {
	"*/dsk*",
	"*/rp*",
	"*/ra*",
	""
};

/*
 * Flag to indicate short output format.
 */

int Short_Flag;


struct node *Hashtab[HASHSIZE];

main( argc, argv )
int argc;
char *argv[];
{
	struct stat stat_buf;

	if( argc > 2 )
	{
		usage();
	}
	else
	if( argc == 2 )
	{
		if( strcmp( argv[1], "-s" ) == 0 )
			Short_Flag = 1;
		else
			usage();
	}
	else
	{
		Short_Flag = 0;
	}

	
	close( 0 );	/* not used, nothing read. 		*/
	
	setup( DEV_DIR );

	print_data( ROOT_DIR );

	if( stat( ROOT_DIR, &stat_buf ) < 0 )
	{
		panic("can\'t stat /");
	}

	get_mnt_pts( ROOT_DIR, ROOT_DIR, stat_buf.st_dev );

	exit( 0 );
}


get_mnt_pts( full_name, rel_name, P_dev )
register char *full_name, *rel_name;
dev_t P_dev;
{
	int file;
	struct direct dir_tab[TABSIZE];
	register struct direct *d_ptr;
	int entries;
	char name_buf[ NAMELEN ];
	char dir_name_buf[ DIRSIZ+1 ];
	dev_t device;



	if( Cur_Depth > MAXDEPTH )
		return;

	if( stat( rel_name, &S_buf ) < 0 )
	{

		fprintf( stderr, "can\'t stat " );
		panic( full_name );
	}

	if( !(S_buf.st_mode & SPECIAL ))  		/* e.g. regular */
	{
		return;
	}
	else if( (S_buf.st_mode & SPECIAL) == DIR_MODE )
	{
		if( S_buf.st_dev != P_dev )
		{

			print_data( full_name );
		
		}

		device = S_buf.st_dev;
		
		file = open( rel_name , O_RDONLY );
		if( file < 0 )
		{
			fprintf(stderr, "can\'t open " );
			panic( full_name );
		}
			

		/* seek past "." and ".."	*/
		lseek( file, 2L * sizeof( struct direct ) , 0 );

		/* deal with rest of directory	*/
		
		if( chdir( rel_name ) < 0 )	/* can't get there	*/
		{
			fprintf(stderr, "can\'t cd to ");
			panic( full_name );
		}
			
		++Cur_Depth;
		
		while( (entries = read(file, dir_tab, sizeof(dir_tab))) 
				>= sizeof( struct direct) )
		{
			d_ptr = dir_tab;
			entries = entries / sizeof( struct direct );
		
			for( d_ptr = dir_tab; entries>0; --entries, ++d_ptr )
			{
				if( d_ptr->d_ino != 0 )
				{
					strcpy( name_buf, full_name );
					if( strcmp( full_name, "/" ) !=0 )
					{
						strcat( name_buf, "/" );
					}

					/* following code protects against
					 * names of length DIRSIZ.
				 	 */

					strncpy( dir_name_buf, d_ptr->d_name,
						DIRSIZ );
					dir_name_buf[ DIRSIZ ] = '\0';

					strcat( name_buf, dir_name_buf );

					get_mnt_pts( name_buf,
						dir_name_buf, device );

				}
			}
		}

		close( file );

		if( chdir( ".." ) < 0 )
		{
			panic("can\'t cd to ..");
		}

		--Cur_Depth;
		return;   
	}
	else
	{		
		return;
	}
}

print_data( path_name )
register char *path_name;
{
	struct stat stat_buf;
	struct node *device;
	struct node *lookup();

	/* no need to check ret. value; already stat'd	*/

	stat( path_name, &stat_buf );

	device = lookup( stat_buf.st_dev );

	if( device == NULL )
	{

		panic("error in dumping data.");
	}

	if( Short_Flag )
		printf("%s %s\n", &device->name[ strlen(DEV_DIR)+1 ], path_name );
	else
		printf("%s %s\n", device->name, path_name );

	return;
}

panic( str )
char *str;
{
	
	write( 2, str, strlen( str ) );
	write( 2, "\n", strlen( "\n" ) );
	
	exit( 1 );
}

usage()
{
	panic( "usage: getmnt [-s]" );
}

struct node *
lookup( device )
dev_t device;
{
	register struct node *ptr;
	register int index;

	index = hash( (int)device );

	
	for( ptr = Hashtab[ index ];
		ptr != NULL; ptr = ptr->next )
	{
		if( ptr->dev_id == device )
			break;
	}

	return( ptr );
}

int
hash( j )
int j;
{

	return( ( 7*j ) % HASHSIZE );

}

insert( dev_name )
char *dev_name;
{
	struct stat stat_buf;
	int index;
	struct node *ptr;
	char *malloc(), *strsave();
	int hash();
	struct node *lookup();

	if( !valid_name( dev_name ) )
		return;

	stat( dev_name, &stat_buf );

	if( lookup( stat_buf.st_rdev ) == NULL )
	{
		index = hash( (int)stat_buf.st_rdev );
		ptr = (struct node *) malloc( sizeof(struct node) );
		if( ptr == NULL )
			panic("no space");

		ptr->dev_id = stat_buf.st_rdev;
		ptr->name = strsave( dev_name );
		if( ptr->name == NULL )
			panic("no space");
		ptr->next = Hashtab[ index ];
		Hashtab[ index ] = ptr;
	}

	return;
}

setup( dir )
char *dir;
{
	struct stat stat_buf;
	struct direct dirent;
	char name_buf[ NAMELEN ];
	int file;


	if( chdir( dir ) < 0 )
	{
		panic("Can\'t get to device directory.");
	}

	file = open( ".", O_RDONLY );
	
	if( file < 0 )
	{
		panic("Can\'t read device directory.");
	}

	lseek( file, 2L * sizeof (struct direct), 0 );	/* skip . and .. */

	while( read( file, &dirent, sizeof dirent ) == sizeof dirent )
	{
		if( dirent.d_ino == 0 )
			continue;

		stat( dirent.d_name, &stat_buf );

		if( (stat_buf.st_mode & BLOCK_SPECIAL) == BLOCK_SPECIAL )
		{
			strcpy( name_buf, dir );
			strcat( name_buf, "/" );
			strcat( name_buf, dirent.d_name );
			insert( name_buf );
		}
		else if( stat_buf.st_mode & DIR_MODE )
		{
			strcpy( name_buf, dir );
			strcat( name_buf, "/" );
			strcat( name_buf, dirent.d_name );
			setup( name_buf );
		}
	}

	close( file );
	chdir( ".." );
	return;


}


char *
strsave( str )
char *str;
{
        char *ptr, *malloc();
        int strlen();

        if( (ptr = malloc( (unsigned) strlen(str)+1 )) != NULL )
                strcpy(ptr, str);
        return( ptr );  /* NULL if malloc() NULL      */
}




/*
 *	Pattern matching function:
 *	returns 1 on a match, 0 otherwise.
 *	The meta-characters '*' and '?'
 *	behave as with the shell when
 *	found in string "a".
 */

int
match(a,b)
char *a, *b;
{
	if( *b == '\0' )
	{
		if( *a == '\0' )
			return(1);
		else if( *a == '*' && *(a+1) == '\0' )
			return(1);
		else
			return(0);
	}
	else if ( *a == *b || *a == '?' )
		return( match(a+1,b+1) );
	else if( *a == '*' )
		return( match(a,b+1) || match(a+1,b+1) || match(a+1,b) );
	else
		return(0);
}

int
valid_name( dev_name )
register char *dev_name;
{
	register int index;
	int match();

	for( index = 0; *Devnames[ index ] != '\0' ; index += 1 )
		if( match( Devnames[ index ], dev_name ) )
			return( 1 );

	return( 0 );
}


@EOF

chmod 666 getmnt.c

echo x - makefile
cat >makefile <<'@EOF'
CFLAGS = -O
CC = /bin/cc
OBJS = xgetmnt.o direct.o namelist.o util.o print.o dev_tab.o tree.o

getmnt:
	$(CC) $(CFLAGS) getmnt.c -o getmnt

ngetmnt:
	$(CC) $(CFLAGS) ngetmnt.c -o ngetmnt

xgetmnt: $(OBJS)
	$(CC) $(CFLAGS) -o xgetmnt $(OBJS)

direct.o: defs.h direct.c
	$(CC) -c $(CFLAGS) direct.c

xgetmnt.o: defs.h xgetmnt.c
	$(CC) -c $(CFLAGS) xgetmnt.c

namelist.o: defs.h namelist.c
	$(CC) -c $(CFLAGS) namelist.c

util.o: defs.h util.c
	$(CC) -c $(CFLAGS) util.c

print.o: defs.h print.c
	$(CC) -c $(CFLAGS) print.c

dev_tab.o: defs.h dev_tab.c
	$(CC) -c $(CFLAGS) dev_tab.c

tree.o: defs.h tree.c
	$(CC) -c $(CFLAGS) tree.c

clean: 
	rm -f *~ *.o xgetmnt ngetmnt getmnt


@EOF

chmod 666 makefile

echo x - namelist.c
cat >namelist.c <<'@EOF'
#include "defs.h"

/*
 * Entries to look for in system name list.
 */

struct nlist NameList[] = {
#ifdef u3b2
	{ "mount" },
	{ "v" },
#else
	{ "_mount" },
	{ "_v" },
#endif
	{ NULL },
};

/* routine to get nlist data
 * it's isolated here because the call to nlist is
 * possible to circumvent, as we do here,
 * but this should not be
 * known to the calling routine.
 *
 * Implementation Notes:
 * The algorithm is pretty straightforward:
 * we look for a file containing the results of
 * a previous nlist call. Its name is passed in
 * fyle, and the nlist values come in via "list".
 * If the file is there, and is valid ( see below )
 * we use data from the file. If any of this fails ,
 * we do an nlist, and write the data values to fyle
 * for next time.
 *
 * File validity checking:
 * 1.)Check existence ( "stat()" call )
 * 2.)Check if up to date ( against time of /unix )
 * 3.)Check if readable by us.
 * 4.)For each nlist entry in list:
 *    a.)Read in an nlist structure from the file.
 *    b.)Verify the name and position in the list.
 *
 */

get_nl(fyle, list )
char *fyle;
struct nlist *list;
{
	int fd, ret;
	struct stat s_buf;
	time_t sys_mtime;
	struct nlist *n_ptr;
	char save_name[256];		/* for flexinames	*/
	FILE *filep;


	ret = stat( UNIX, &s_buf );
	sys_mtime = s_buf.st_mtime;	/* modification time of system */

	ret = stat(fyle, &s_buf );

	if( ret != -1 
		&& s_buf.st_mtime > sys_mtime 
		&& ( s_buf.st_mode & 0400 ))
	{
		filep = fopen( fyle, "r" );
		if( filep == NULL )	
		{	
			/* this shouldn't happen if we got here */
			goto do_nl;
		}

		for( n_ptr=list; n_ptr->n_name != (char *) NULL; ++n_ptr )
		{
			strcpy( save_name, n_ptr->n_name );
			if( !n_read(filep, n_ptr) )
			{
				fclose( filep );
				goto do_nl;
			}

			if( strcmp( n_ptr->n_name, save_name ) != 0 )
			{
				strcpy( n_ptr->n_name, save_name );
				fclose( filep );
				goto do_nl;
			}
		}

		fclose( filep );
		return;
	}

do_nl:	/* do an actual nlist call; this can be time-consuming!!!! */
	
	ret = nlist("/unix", list);
	if( ret == -1 )
		panic("nlist system call failed");

#ifndef u3b2	/* implying VAX .... */
	for ( n_ptr=list; n_ptr->n_name != (char *)NULL; ++n_ptr )
		n_ptr->n_value &= VMASK;
#endif

	fd = creat(fyle, 0400);

	if( fd < 0 ) 
	{
		return;
	}

	filep = fdopen( fd, "a" );
	if( filep == (FILE *) NULL )
	{
		return;		/* can't write to it */
	}

	for( n_ptr=list; n_ptr->n_name != (char *)NULL; ++n_ptr )
		n_write(filep, n_ptr );

	fclose( filep );
	close( fd );
	
	return;
}

int
n_write( filep, n_ptr )
FILE *filep;
struct nlist *n_ptr;
{
	
	fprintf( filep, "%d:%ld:%s\n", n_ptr->n_type,
		n_ptr->n_value, n_ptr->n_name );
	
	return;
}

int
n_read( filep, n_ptr )
FILE *filep;
struct nlist *n_ptr;
{
	char buf[BUFSIZ];
	char *strsave();
	int type;
	long value;
	char *name;

	fscanf( filep, "%d:%ld:%s\n", &type, &value, buf );

	n_ptr->n_value = value;
	n_ptr->n_type = type;

	name = strsave( buf );

	if( name == (char *) NULL )
	{
		return( 0 );
	}
	else
	{
		n_ptr->n_name = name;
		return( 1 );
	}
}
@EOF

chmod 666 namelist.c

echo x - ngetmnt.c
cat >ngetmnt.c <<'@EOF'

/*
 * Algorithm: 
 * 1.)Build a hash table containing all block devices,
 *    (satisfying naming constraints: see Devnames table)
 *    indexed by hash( dev_id ), where dev_id is the
 *    device id alluded to in the "stat(2)" documentation.
 *    This table also contains the path name of the special file.
 * 2.)Construct a "skeleton" file system tree by reading
 *    /dev/kmem and obtaining information about the relations
 *    between mount points. This information can then be used
 *    to decide if we are on a "leaf" file system (where no other
 *    file systems are mounted) or on a file system which contains
 *    other mount points ("internal").
 * 3.)Peruse the directory structure, limited by MAXDEPTH, to
 *    look for mount points. Mount points are found by comparing the
 *    device id of the parent to the device id of the child.
 *    When they differ, we have a mount point. Then we can
 *    hash to the appropriate device name and dump the data.
 *
 * Possible Problems:
 * a.)It's potentially quite slow.
 * b.)Non-unique names ( "/dev/ra0" and "/dev/ra00" for example )
 *    can have the same device number and confuse the program;
 *    the specification of naming conventions with Devnames[]
 *    alleviates this to some degree.
 * c.)The program is not guaranteed to perform correctly
 *    if the user is not root. It should probably do a getuid()
 *    and print an informational message.
 *
 * Notes:
 * a.)Assumes devices found under DEV_DIR.
 * b.)get_mnt_pts() uses both a full path name (for
 *    robustness) and a relative path name (to save time 
 *    on "stat()" and "chdir()" system calls, which use namei().)
 *
 */

static char *SCCS_ID_STR = "@(#) /STD/pkg/bcrcc/getmnt.d/s.getmnt.c REL LEVEL=1.2 LAST DELTA ON 5/2/85 ";

# include 	<stdio.h>
# include 	<fcntl.h>
# include	<sys/var.h>
# include	<sys/types.h>
# include	<sys/stat.h>
# include 	<sys/dir.h>
# include 	<sys/inode.h>
# include 	<sys/buf.h>
# include 	<sys/mount.h>
# include	<sys/sysmacros.h>
# include	<a.out.h>

/* deal with stupidity in sym.h */
#ifdef n_name
# undef n_name
#endif

#define EOS '\0'	/* End of String */
#define ROOT_DIR "/"
#define DEV_DIR	"/dev"
#define UNIX "/unix"
#define KMEM "/dev/kmem"
#define NL_FILE "/letc/getmnt_nlist"
     				/* mode val., special files	*/
#define SPECIAL (S_IFBLK | S_IFCHR)	
				/* mode val. for block special file	*/
#define BLOCK_SPECIAL S_IFBLK
				/* mode val. for dir. returned by stat() */
#define DIR_MODE S_IFDIR
				/* VAX address mask	*/
#define VMASK 0x3fffffff
				/* size, table for directory structures	*/
#define TABSIZE	(BUFSIZ/sizeof(struct direct))
#define HASHSIZE 128
#define MAXDEPTH 4	/* how deep to go		*/
#define NAMELEN ( (MAXDEPTH+1) * (DIRSIZ+1) + 1 )

struct stat S_buf;
int Cur_Depth = 1;

struct node {
	dev_t dev_id;
	char *name;
	struct node *next;
	int status;
#define UNREFERENCED 0
#define INTERNAL 1
#define LEAF 2
};

struct node *lookup();
char *strsave(), *emalloc(), *malloc();

/* Devnames should contain regular expressions
 * which characterize block
 * special files containing file systems.
 * The last entry should be a null string.
 */

char *Devnames[] = {
	"*/dsk*",
	""
};

/*
 * Entries to look for in system name list.
 */

struct nlist NameList[] = {
#define MOUNT 0
	{ "_mount" },
#define V 1
	{ "_v" },
	{ NULL },
};

/*
 * Flag to indicate short output format.
 */

int Short_Flag;


struct node *Hashtab[HASHSIZE];

main( argc, argv )
int argc;
char *argv[];
{
	struct stat stat_buf;

	if( argc > 2 )
	{
		usage();
	}
	else
	if( argc == 2 )
	{
		if( strcmp( argv[1], "-s" ) == 0 )
			Short_Flag = 1;
		else
			usage();
	}
	else
	{
		Short_Flag = 0;
	}

	
	setup();

	print_data( ROOT_DIR );

	if( stat( ROOT_DIR, &stat_buf ) < 0 )
	{
		panic("can't stat /");
	}

	get_mnt_pts( ROOT_DIR, ROOT_DIR, stat_buf.st_dev );

#ifdef HDEBUG
	hash_stats();
#endif

	exit( 0 );
}


get_mnt_pts( full_name, rel_name, P_dev )
register char *full_name, *rel_name;
dev_t P_dev;
{
	struct direct dir_tab[TABSIZE];
	register struct direct *d_ptr;
	char name_buf[ NAMELEN ], dir_name_buf[ DIRSIZ+1 ];
	int entries, file, new_dir = 1;
	dev_t device;
	struct node *node_p;



	if( stat( rel_name, &S_buf ) < 0 )
	{

		fprintf( stderr, "can't stat " );
		warn( full_name );
		return;	/* check this */
	}

	if( !(S_buf.st_mode & SPECIAL ))  		/* e.g. regular */
	{
		return;
	}
	else if( (S_buf.st_mode & SPECIAL) == DIR_MODE )
	{
		if( S_buf.st_dev != P_dev )
		{

			print_data( full_name );
			node_p = lookup( S_buf.st_dev );	/* safe; chked alrdy */
			if( node_p->status == LEAF )
				return;
		
		}
		
		if( Cur_Depth >= MAXDEPTH )
			return;

		device = S_buf.st_dev;
		
		file = open( rel_name , O_RDONLY );
		if( file < 0 )
		{
			fprintf(stderr, "can't open " );
			warn( full_name );
			return;	/* check this */
		}
			

		/* deal with rest of directory	*/
		
		if( chdir( rel_name ) < 0 )	/* can't get there	*/
		{
			fprintf(stderr, "can't cd to ");
			warn( full_name );
			close( file );
			return;	/* check this	*/
		}
			
		++Cur_Depth;
		
		while( (entries = read(file, dir_tab, sizeof(dir_tab))) 
				>= sizeof( struct direct) )
		{
			d_ptr = dir_tab;
			entries = entries / sizeof( struct direct );

			if( new_dir == 1 )
			{
				++d_ptr; ++d_ptr;
				entries = entries - 2;
				new_dir = 0;
			}
		
			for( ; entries > 0; --entries, ++d_ptr )
			{
				if( d_ptr->d_ino != 0 )
				{
					strcpy( name_buf, full_name );
					if( strcmp( full_name, "/" ) !=0 )
					{
						strcat( name_buf, "/" );
					}

					/* following code protects against
					 * names of length DIRSIZ.
				 	 */

					strncpy( dir_name_buf, d_ptr->d_name,
						DIRSIZ );
					dir_name_buf[ DIRSIZ ] = EOS;

					strcat( name_buf, dir_name_buf );

					get_mnt_pts( name_buf,
						dir_name_buf, device );

				}
			}
		}

		close( file );

		if( chdir( ".." ) < 0 )
		{
			char *p1, *p2, *strrchr();

			warn("can't cd to ..");
			p1 = strsave( full_name );
			p2 = strrchr( p1 , '/' );
			if( p1 != p2 && p2 != (char *)NULL )
				*p2 = EOS;
			if( chdir( p2 ) < 0 )
			{
				fprintf( stderr, "%s", "Can't escape " );
				panic( p2 );
			}
		}

		--Cur_Depth;
		return;   
	}
	else
	{		
		return;
	}
}

print_data( path_name )
register char *path_name;
{
	struct stat stat_buf;
	struct node *device;
	struct node *lookup();

	/* no need to check ret. value; already stat'd	*/

	stat( path_name, &stat_buf );

	device = lookup( stat_buf.st_dev );

	if( device == NULL )
	{

		panic("error in dumping data.");
	}

	if( Short_Flag )
		printf("%s %s\n", &device->name[ strlen(DEV_DIR)+1 ], path_name );
	else
		printf("%s %s\n", device->name, path_name );

	return;
}

panic( str )
char *str;
{
	fprintf( stderr, "%s\n", str );
	exit( 1 );
}

warn( str )
char *str;
{
	fprintf( stderr, "%s\n", str );
	return;
}

usage()
{
	panic( "usage: getmnt [-s]" );
}

struct node *
lookup( device )
dev_t device;
{
	register struct node *ptr;
	register int index;

	index = hash( (int)device );

	
	for( ptr = Hashtab[ index ];
		ptr != NULL; ptr = ptr->next )
	{
		if( ptr->dev_id == device )
			break;
	}

	return( ptr );
}

int
hash( j )
int j;
{

	return( ( 7*j ) % HASHSIZE );

}

insert( dev_name )
char *dev_name;
{
	struct stat stat_buf;
	int index, hash();
	struct node *ptr, *lookup();

	if( !valid_name( dev_name ) )
		return;

	stat( dev_name, &stat_buf );

	if( lookup( stat_buf.st_rdev ) == NULL )
	{
		index = hash( (int)stat_buf.st_rdev );
		ptr = (struct node *) emalloc( sizeof(struct node) );

		ptr->dev_id = stat_buf.st_rdev;
		ptr->name = strsave( dev_name );

		ptr->next = Hashtab[ index ];
		ptr->status = UNREFERENCED;
		Hashtab[ index ] = ptr;
	}

	return;
}

setup()
{
	do_devs( DEV_DIR );

	analyze_tree();

	return;
}

do_devs( dir )
char *dir;
{
	struct stat stat_buf;
	struct direct dir_tab[TABSIZE];
	register struct direct *d_ptr;
	char name_buf[ NAMELEN ];
	int file, entries, new_dir = 1;


	if( chdir( dir ) < 0 )
	{
		panic("Can't get to device directory.");
	}

	file = open( ".", O_RDONLY );
	
	if( file < 0 )
	{
		panic("Can't read device directory.");
	}


	while( (entries = read(file, dir_tab, sizeof(dir_tab)))
		>= sizeof(struct direct))
	{
		d_ptr = dir_tab;
		entries = entries / sizeof(struct direct);

		if( new_dir == 1 )	/* deal with "." and ".." */
		{
			++d_ptr; ++d_ptr; 
			entries = entries - 2;
			new_dir = 0;
		}

		for( ; entries > 0 ; --entries, ++d_ptr )
		{
			if( d_ptr->d_ino == 0 )
				continue;

			stat( d_ptr->d_name, &stat_buf );
	
			if( (stat_buf.st_mode & BLOCK_SPECIAL) == BLOCK_SPECIAL )
			{
				strcpy( name_buf, dir );
				strcat( name_buf, "/" );
				strncat( name_buf, d_ptr->d_name, DIRSIZ );
				insert( name_buf );
			}
			else if( stat_buf.st_mode & DIR_MODE )
			{
				strcpy( name_buf, dir );
				strcat( name_buf, "/" );
				strncat( name_buf, d_ptr->d_name, DIRSIZ );
				do_devs( name_buf );
			}
		}
	}

	close( file );
	chdir( ".." );
	return;


}


char *
strsave( str )
char *str;
{
        char *ptr;
        int strlen();

        ptr = emalloc( (unsigned) strlen(str)+1 );
        strcpy(ptr, str);
        return( ptr );
}

char *emalloc( size )
unsigned size;
{
	char *ptr;

	ptr = malloc( size );
	if( ptr == (char *) NULL )
		panic( "no space" );
	else
		return( ptr );
}



/*
 *	Pattern matching function:
 *	returns 1 on a match, 0 otherwise.
 *	The meta-characters '*' and '?'
 *	behave as with the shell when
 *	found in string "a".
 */

int
match(a,b)
char *a, *b;
{
	if( *b == EOS )
	{
		if( *a == EOS )
			return(1);
		else if( *a == '*' && *(a+1) == EOS )
			return(1);
		else
			return(0);
	}
	else if ( *a == *b || *a == '?' )
		return( match(a+1,b+1) );
	else if( *a == '*' )
		return( match(a,b+1) || match(a+1,b+1) || match(a+1,b) );
	else
		return(0);
}

int
valid_name( dev_name )
register char *dev_name;
{
	register int index;
	int match();

	for( index = 0; *Devnames[ index ] != EOS ; index += 1 )
		if( match( Devnames[ index ], dev_name ) )
			return( 1 );

	return( 0 );
}

analyze_tree()
{
	int index, kmem_fd;
	struct var v;
	struct mount mtab_entry;
	struct inode itab_entry;
	struct node *device, *lookup();

	get_nl( NL_FILE, NameList );

	for( index = 0;
		NameList[index].n_name != (char *) NULL;
		index += 1 )
	{
		if( NameList[index].n_value == 0 )
			panic( "bad nlist data" );
	}

	kmem_fd = open( KMEM, O_RDONLY );
	if( kmem_fd < 0 )
	{
		panic( "can't open KMEM" );
	}

	eseek( kmem_fd, (long) NameList[V].n_value, 0 );
	eread( kmem_fd, &v, sizeof(v) );

	/* we look at each mount table entry, in turn */

	for( index = 0;
		index < v.v_mount;
		index += 1 )
	{
		eseek( kmem_fd, 
			(long ) (NameList[MOUNT].n_value 
				+ index * sizeof( mtab_entry )),
			0 );
		eread( kmem_fd,	&mtab_entry, sizeof( mtab_entry ) );
		if( mtab_entry.m_flags != MINUSE )
			continue;

		device = lookup( brdev(mtab_entry.m_dev) );

		if( device == (struct node *) NULL )
			panic( "error in lookup() for device" );

		if( device->status == UNREFERENCED )
			device->status = LEAF;


		eseek( kmem_fd,
			(long ) ( (unsigned) mtab_entry.m_inodp & VMASK),
			0 );
		eread( kmem_fd,	&itab_entry, sizeof( itab_entry ) );

		device = lookup( brdev(itab_entry.i_dev) );

		if( device == (struct node *) NULL )
			panic( "error in lookup() for device" );

		device->status = INTERNAL;

	}

	return;
}

#ifdef HDEBUG
hash_stats()
{
	int i, count;
	struct node *np;

	fprintf( stderr, "%s\n",
		"getmnt device name hash table performance report" );
	fprintf( stderr, "Hash table size: %d\n", HASHSIZE );

	for( i = 0; i < HASHSIZE; i += 1 )
	{
		for( count = 0, np = Hashtab[i];
			np != (struct node *) NULL;
			np = np->next )
			count = count + 1;

		fprintf( stderr, "Bucket Number: %d, List Length: %d\n",
			i, count );
	}

}
#endif



/* routine to get nlist data
 * it's isolated here because the call to nlist is
 * possible to circumvent, as we do here,
 * but this should not be
 * known to the calling routine.
 *
 * Implementation Notes:
 * The algorithm is pretty straightforward:
 * we look for a file containing the results of
 * a previous nlist call. Its name is passed in
 * fyle, and the nlist values come in via "list".
 * If the file is there, and is valid ( see below )
 * we use data from the file. If any of this fails ,
 * we do an nlist, and write the data values to fyle
 * for next time.
 *
 * File validity checking:
 * 1.)Check existence ( "stat()" call )
 * 2.)Check if up to date ( against time of /unix )
 * 3.)Check if readable by us.
 * 4.)For each nlist entry in list:
 *    a.)Read in an nlist structure from the file.
 *    b.)Verify the name and position in the list.
 *
 */

get_nl(fyle, list )
char *fyle;
struct nlist *list;
{
	int fd, ret;
	struct stat s_buf;
	time_t sys_mtime;
	struct nlist *n_ptr;
	char save_name[256];		/* for flexinames	*/
	FILE *filep;


	ret = stat( UNIX, &s_buf );
	sys_mtime = s_buf.st_mtime;	/* modification time of system */

	ret = stat(fyle, &s_buf );

	if( ret != -1 
		&& s_buf.st_mtime > sys_mtime 
		&& ( s_buf.st_mode & 0400 ))
	{
		filep = fopen( fyle, "r" );
		if( filep == NULL )	
		{	
			/* this shouldn't happen if we got here */
			goto do_nl;
		}

		for( n_ptr=list; n_ptr->n_name != (char *) NULL; ++n_ptr )
		{
			strcpy( save_name, n_ptr->n_name );
			if( !n_read(filep, n_ptr) )
			{
				fclose( filep );
				goto do_nl;
			}

			if( strcmp( n_ptr->n_name, save_name ) != 0 )
			{
				strcpy( n_ptr->n_name, save_name );
				fclose( filep );
				goto do_nl;
			}
		}

		fclose( filep );
		return;
	}

do_nl:	/* do an actual nlist call; this can be time-consuming!!!! */
	
	ret = nlist("/unix", list);
	if( ret == -1 )
		panic("nlist system call failed");

	for ( n_ptr=list; n_ptr->n_name != (char *)NULL; ++n_ptr )
		n_ptr->n_value &= VMASK;


	fd = creat(fyle, 0400);

	if( fd < 0 ) 
	{
		return;
	}

	filep = fdopen( fd, "a" );
	if( filep == (FILE *) NULL )
	{
		return;		/* can't write to it */
	}

	for( n_ptr=list; n_ptr->n_name != (char *)NULL; ++n_ptr )
		n_write(filep, n_ptr );

	fclose( filep );
	close( fd );
	
	return;
}

int
n_write( filep, n_ptr )
FILE *filep;
struct nlist *n_ptr;
{
	
	fprintf( filep, "%d:%ld:%s\n", n_ptr->n_type,
		n_ptr->n_value, n_ptr->n_name );
	
	return;
}

int
n_read( filep, n_ptr )
FILE *filep;
struct nlist *n_ptr;
{
	char buf[BUFSIZ];
	char *strsave();
	int type;
	long value;
	char *name;

	fscanf( filep, "%d:%ld:%s\n", &type, &value, buf );

	n_ptr->n_value = value;
	n_ptr->n_type = type;

	name = strsave( buf );

	if( name == (char *) NULL )
	{
		return( 0 );
	}
	else
	{
		n_ptr->n_name = name;
		return( 1 );
	}
}

int
eseek( fd, addr, whence )
int fd, whence;
long addr;
{
	long l;

	l = lseek( fd, addr, whence );

	if( l < 0L )
		panic( "can't seek" );

	return( l );
}

int
eread( fd, buf, count )
int fd, count;
char *buf;
{
	int n;

	n = read( fd, buf, count );

	if( n < 0 )
		panic( "can't read" );

	return( n );
}

@EOF

chmod 666 ngetmnt.c

echo x - print.c
cat >print.c <<'@EOF'
#include "defs.h"

extern int Short_Flag;

panic( str )
char *str;
{
	fprintf( stderr, "%s\n", str );
	exit( 1 );
}

warn( str )
char *str;
{
	fprintf( stderr, "%s\n", str );
	return;
}

usage()
{
	panic( "usage: getmnt [-s]" );
}

/*
 * given some information, we print the mount point
 * data in a format usable by setmnt or whatever,
 * modified by the flag ShortFlag.
 */

void
prt_mnt_pt( node, name, sb )
struct node *node;
char *name;
struct stat *sb;
{
	struct device *device, *lookup();
	char buf[BUFSIZ];
	int len;

	device = lookup( brdev(sb->st_dev) );

	if( device == NULL )
	{
		panic("error in dumping data.");
	}

	if( Short_Flag )
		printf("%s", &device->d_name[ DEV_LEN+1 ] );
	else
		printf("%s", device->d_name );	

	putchar( ' ' );

	sprt_path_from_node( buf, node );
	len = strlen( buf );

	if( len>0 && buf[len-1] != '/' )
		buf[len] = '/', buf[len+1] = EOS;

	printf( "%s%s\n", buf, name );

	return;
}

sprt_path_from_node( str, node )
char *str;
struct node *node;
{
  	char *nn[MAX_DIRS];
	register char *name;
	register int index;
	int stack;
	struct node *np;

	/* we use nn[] as a stack of character strings;
	 * this loop pushes them onto the stack.
 	 */

	for( np = node, stack = 0, nn[0] = node->n_name; 
		np != np->n_parent; /* i.e., root */
		np = np->n_parent )
	{
		if( stack >= MAX_DIRS )
			panic( "sprt_path_from_node: stack > MAX_DIRS" );
		nn[stack] = np->n_name;
		stack += 1;
	}

	/* this loop sucks stuff back off of the nn[] "stack".	*/

	for( stack -= 1, index = 0, str[0] = '/'; 
		stack >= 0; 
		stack -= 1 )
	{
		for( name = nn[stack]; *name != EOS; ++name )
			str[++index] = *name;
		if( stack > 0 )
			str[++index] = '/';
	}

	str[++index] = EOS;

	return;
}







@EOF

chmod 666 print.c

echo x - tree.c
cat >tree.c <<'@EOF'
#include "defs.h"

extern struct nlist NameList[];

analyze_tree()
{
	int index, kmem_fd;
	struct var v;
	struct mount mtab_entry;
	struct inode itab_entry;
	struct device *device, *parent, *lookup();

	get_nl( NL_FILE, NameList );

	for( index = 0;
		NameList[index].n_name != (char *) NULL;
		index += 1 )
	{
		if( NameList[index].n_value == 0 )
			panic( "bad nlist data" );
	}

	kmem_fd = open( KMEM, O_RDONLY );
	if( kmem_fd < 0 )
	{
		panic( "can't open KMEM" );
	}

	eseek( kmem_fd, (long) NameList[V].n_value, 0 );
	eread( kmem_fd, &v, sizeof(v) );

	/* we look at each mount table entry, in turn */

	for( index = 0;
		index < v.v_mount;
		index += 1 )
	{
		eseek( kmem_fd, 
			(long ) (NameList[MOUNT].n_value 
				+ index * sizeof( mtab_entry )),
			0 );
		eread( kmem_fd,	&mtab_entry, sizeof( mtab_entry ) );
		if( mtab_entry.m_flags != MINUSE )
			continue;

		device = lookup( brdev(mtab_entry.m_dev) );

		if( device == (struct device *) NULL )
			panic( "error in lookup() for device" );
#ifndef u3b2
		eseek( kmem_fd,
			(long ) ( (unsigned) mtab_entry.m_inodp & VMASK),
			0 );
		eread( kmem_fd,	&itab_entry, sizeof( itab_entry ) );
#else
		if( mtab_entry.m_inodp == 0 ) /* root */
		 	itab_entry.i_dev = 0;
		else
		{	
			eseek( kmem_fd, (long) mtab_entry.m_inodp, 0 );
			eread( kmem_fd,	&itab_entry, sizeof( itab_entry ) );
		}
#endif

		parent = lookup( brdev(itab_entry.i_dev) );
		
		if( parent == (struct device *) NULL )
			panic( "error in lookup() for device" );

		device->d_parent = parent;
		device->d_flags |= IN_TREE;
		parent->d_flags |= IN_TREE;
		add_child( parent, device );
	}

	return;
}


int
leaf( dev_num )
dev_t dev_num;
{
  	struct device *dev_ptr;

	dev_ptr = lookup( brdev(dev_num) );

	if( dev_ptr == (struct device *) NULL )
		panic( "leaf(): device not found." );

	return( 
		((dev_ptr->d_children == (struct device *) NULL ) 
		&& (dev_ptr->d_parent != (struct device *) NULL ))
		? TRUE : FALSE );
}

int
all_found()
{
  	struct device *root;
	extern struct node *ListHead;

	root = lookup( brdev(ListHead->n_dev) );	/* should be root */
	
	return( 
		(root->d_children == (struct device *) NULL ) ? 
		TRUE : FALSE );
}

add_child( parent, new_child )
struct device *parent, *new_child;
{
  	struct device *tmp;

	if( new_child == parent )
	  	return;	/* put in for idiot 3b2 */
#ifdef EBUG
fprintf( stderr, "Adding child 0x%x to parent 0x%x\n",
new_child->d_number, parent->d_number );
#endif

	tmp = parent->d_children;
	parent->d_children = new_child;
	new_child->d_next_child = tmp;

	return;
}

remove_child( parent, old_child )
struct device *parent, *old_child;
{
	struct device *ptr, *old;

	/* dispense with bad args. parent == NULL may be root */
	if( (parent == (struct device *) NULL) ||
		(old_child == (struct device *) NULL ) )
		return;

	for( old = ptr = parent->d_children; 
		ptr != (struct device *) NULL;
		old = ptr, ptr = ptr->d_next_child )
	{
		if( ptr == old_child )	/* i.e., found on chain */
		{
		  	if( ptr->d_children == (struct device *) NULL )
			{
#ifdef EBUG
fprintf(stderr, "deleting 0x%x from 0x%x\n", ptr->d_number, parent->d_number );
#endif
				if( ptr == parent->d_children ) /* was old == */
					parent->d_children = ptr->d_next_child;
				else
					old->d_next_child = ptr->d_next_child;
				/* check for pending removal */
				if( (parent->d_flags & DELETED ) == DELETED )
					remove_child( parent->d_parent, 
						parent );
			}
			else
			{
#ifdef EBUG
fprintf( stderr, "marking 0x%x as DELETED\n", ptr->d_number );
#endif
				ptr->d_flags |= DELETED;
			}
			break;
		}
	}

#ifdef EBUG
if( ptr == (struct device *) NULL )
	fprintf( stderr, "device 0x%x not found under 0x%x\n", 
	old_child->d_number, parent->d_number );
#endif

	return;
}
		
update_tree( dev )
dev_t dev;
{
  	struct device *device, *lookup();

	device = lookup( brdev(dev) );
	if( device == (struct device *) NULL )
		panic( "bad device passed to update_tree()" );
	remove_child( device->d_parent, device );
	return;
}

@EOF

chmod 666 tree.c

echo x - util.c
cat >util.c <<'@EOF'

#include "defs.h"

char *
strsave( str )
char *str;
{
        char *ptr;
        int strlen();

        ptr = emalloc( (unsigned) strlen(str)+1 );
        strcpy(ptr, str);
        return( ptr );
}

char *emalloc( size )
unsigned size;
{
	char *ptr;

	ptr = malloc( size );
	if( ptr == (char *) NULL )
		panic( "no space" );
	else
		return( ptr );
}



/*
 *	Pattern matching function:
 *	returns 1 on a match, 0 otherwise.
 *	The meta-characters '*' and '?'
 *	behave as with the shell when
 *	found in string "a".
 */

int
match(a,b)
char *a, *b;
{
	if( *b == EOS )
	{
		if( *a == EOS )
			return(1);
		else if( *a == '*' && *(a+1) == EOS )
			return(1);
		else
			return(0);
	}
	else if ( *a == *b || *a == '?' )
		return( match(a+1,b+1) );
	else if( *a == '*' )
		return( match(a,b+1) || match(a+1,b+1) || match(a+1,b) );
	else
		return(0);
}



int
eseek( fd, addr, whence )
int fd, whence;
long addr;
{
	long l;

	l = lseek( fd, addr, whence );

	if( l < 0L )
		panic( "can't seek" );

	return( l );
}

int
eread( fd, buf, count )
int fd, count;
char *buf;
{
	int n;

	n = read( fd, buf, count );

	if( n < 0 )
		panic( "can't read" );

	return( n );
}

@EOF

chmod 666 util.c

echo x - xgetmnt.c
cat >xgetmnt.c <<'@EOF'

/*
 * Algorithm: 
 * 1.)Build a hash table containing all block devices,
 *    (satisfying naming constraints: see Devnames table)
 *    indexed by hash( dev_id ), where dev_id is the
 *    device id alluded to in the "stat(2)" documentation.
 *    This table also contains the path name of the special file.
 * 2.)Construct a "skeleton" file system tree by reading
 *    /dev/kmem and obtaining information about the relations
 *    between mount points. This information can then be used
 *    to decide if we are on a "leaf" file system (where no other
 *    file systems are mounted) or on a file system which contains
 *    other mount points ("internal").
 * 3.)Peruse the directory structure breadth-first,
 *    looking for mount points. Mount points are found by comparing the
 *    device id of the parent to the device id of the child.
 *    When they differ, we have a mount point. Then we can
 *    hash to the appropriate device name and dump the data.
 *
 * Possible Problems:
 * a.)It's potentially quite slow.
 * b.)Memory utilization is potentially tremendous.
 * c.)The program is not guaranteed to perform correctly
 *    if the user is not root. It should probably do a getuid()
 *    and print an informational message.
 * d.)Non-unique device names; currently defaults to 
 *    those prefixed with DEV_DIR, e.g., for DEV_DIR "/dev/dsk",
 *    /dev/dsk/c0d0s6 is preferred to /dev/diskette.
 *
 * Notes:
 * a.)Assumes devices found under DEV_DIR.
 *
 */

#include "defs.h"

struct node 
  	*new_node(),
	*ListHead, 
	*ListTail;

char 
	*strsave(), 
	*emalloc(), 
	*malloc(),
  	*Current_Dir,	/* our idea of the current directory */
	*New_Dir;	/* destination when changing dirs    */


/*
 * Flag to indicate short output format.
 */

int Short_Flag;
char *StartBrk;	/* starting break value */


main( argc, argv )
int argc;
char *argv[];
{
	char *sbrk();

	if( argc > 2 )
	{
		usage();
	}
	else
	if( argc == 2 )
	{
		if( strcmp( argv[1], "-s" ) == 0 )
			Short_Flag = 1;
		else
			usage();
	}
	else
	{
		Short_Flag = 0;
	}
	
	setup();

	search();

#ifdef HDEBUG
	hash_stats();
#endif
#ifdef MEM
	printf( "Used %d characters of allocated memory.\n",
		sbrk(0) - StartBrk );
#endif
	exit( 0 );
}


setup()
{
  	char *sbrk();
	struct stat sb;
	struct node *nd;
	static char
		path_buf1[BUFSIZ],
		path_buf2[BUFSIZ];

	StartBrk = sbrk(0);

	nd = new_node();
	
	stat( ROOT_DIR, &sb );

	nd->n_name = strsave( ROOT_DIR );
	nd->n_parent = nd;
	nd->n_next = nd;
	nd->n_dev = sb.st_dev;
	ListHead = ListTail = nd;

	do_devs( DEV_DIR );

	analyze_tree();

	if( chdir( ROOT_DIR ) < 0 )
		panic( "can't cd to root directory" );

	prt_mnt_pt( nd, "", &sb );
	sprintf( path_buf1, "%s", ROOT_DIR );
	Current_Dir = path_buf1;
	New_Dir = path_buf2;

	return;
}

search()
{
  	int all_found();
	struct node *ptr;

	ptr = ListHead;

	while( !all_found() )
	{
		get_mnt_pts( ptr );
		ptr = ptr->n_next;
	}

	return;
}

get_mnt_pts( dir )
struct node *dir;
{
  	DIR *dir_fd;
	void append_List();
	struct direct *dir_ent;
	struct stat sb;
	char *tmp;

/*
  	test this code
	if( leaf( dir->n_dev ) )
		return;
 */
	sprt_path_from_node( New_Dir, dir );

	/* cheap hack; buys a lot on big systems;
	 * If there's a possibility of finding mount points
	 * under DEV, some code should be added to
	 * do_devs() so that stat()'s aren't done twice.
	 */
	if( strcmp( New_Dir, DEV ) == 0 )
		return;

	if( cheap_cd( Current_Dir, New_Dir ) < 0 )
		panic( "chdir() failed" );
	tmp = Current_Dir;
	Current_Dir = New_Dir;
	New_Dir = tmp;

	dir_fd = opendir( "." );
	if( dir_fd == (DIR *) NULL )
	{
	  	fprintf( stderr, "Current_Dir was: %s\n", Current_Dir );
		panic( "opendir(\".\") failed; exiting" );
	}

	while( (dir_ent = readdir( dir_fd )) != (struct direct *) NULL )
	{
		/* crude but fast code to boogie thru ino==0, "." and ".." */
	  	if( (dir_ent->d_ino == 0) ||
			(dir_ent->d_name[0] == '.' && ((dir_ent->d_name[1] == EOS) ||
			(dir_ent->d_name[1] == '.' && dir_ent->d_name[2] == EOS))))
			continue;

		stat( dir_ent->d_name, &sb );
		if( ((sb.st_mode & S_IFMT) == S_IFDIR ))
		{
			if( sb.st_dev != dir->n_dev )
			{
				prt_mnt_pt( dir, dir_ent->d_name, &sb );
				if( !leaf( sb.st_dev ) )
					append_List( dir, 
						dir_ent->d_name,
						sb.st_dev );
				update_tree( sb.st_dev );
			}
			else
			{
				append_List( dir, dir_ent->d_name, sb.st_dev );
			}
		}
	}

	closedir( dir_fd );

	return;
}


void
append_List( dir, name, dev )
struct node *dir;
char *name;
dev_t dev;
{
	struct node *nd;

	nd = new_node();

	nd->n_name = strsave( name );
	nd->n_parent = dir;
	nd->n_dev = dev;

	/* append to list, at tail */
	ListTail->n_next = nd;
	nd->n_prev = ListTail;
	nd->n_next = (struct node *) NULL;
	ListTail = nd;

	return;
}	

struct node *
new_node()
{
	return( (struct node *) emalloc( sizeof( struct node ) ) );
}

@EOF

chmod 666 xgetmnt.c

exit 0