[alt.sources] Rapid Location of Mount Points

bba@cbnewsj.ATT.COM (b.banerjee) (12/12/89)

The following perl script is my implementation of the program
described in the article:

	Rapid Location of Mount Points
		Jonathan M. Smith
		Computer Science Dept.
		Columbia University, New York NY 10027
	Software Practice and Experience Vol. 19(9), Sep. 1989.

This script (and  the associated C program) will  read the kernel
and  print out  the contents  of the  mount table  given a  Sys V
kernel. But,  you say,  /etc/mount does  the same  thing! Wrong!!
/etc/mount  merely prints  out  the contents  of /etc/mnttab.  If
something munges this file -- you are SOL.

I make no claims on the code,  nor any apologies. This is a quick
hack. I needed something quickly, as the /etc/mnttab on my system
was getting zorched on a  semi-regular basis (still need to track
down the culprit).

It  is easy  enough  to  modify this  to  generate  input to  the
setmnt(1m)  command  or  to  use  it in  a  variety  of  ways  to
re-generate /etc/mnttab.

There are probably  still bugs in it -- but  it works well enough
for the time being. Some time  when I don't have a zillion things
on my to-do list, I'll clean it up and rewrite it in C. It should
run about 2-3 times faster.

INSTALLATION:
------------

Install the  dumpmnt shell script wherever  convenient. Make sure
that the  path to  dumpmnt is  specified in  findmount.pl. Invoke
with:

	perl findmount.pl

You'll need read access to /dev/kmem. You can either make dumpmnt
setgid to whichever group owns /dev/kmem (sys?) or run it as root.

I   hope  that   you  find   this   useful.  If   you  make   any
fixes/enhancements, I'd appreciate hearing about them.

	Binayak Banerjee
	bba@mtuxo.ATT.COM
	AT&T Bell Labs
	Middletown, NJ 07724.


#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# If this archive is complete, you will see the following message at the end:
#		"End of shell archive."
# Contents:  README dumpmnt.c findmount.pl
# Wrapped by bba@mtuxo on Mon Dec 11 20:03:00 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(1695 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XThe following perl script is my implementation of the program
Xdescribed in the article:
X
X	Rapid Location of Mount Points
X		Jonathan M. Smith
X		Computer Science Dept.
X		Columbia University, New York NY 10027
X	Software Practice and Experience Vol. 19(9), Sep. 1989.
X
XThis script (and  the associated C program) will  read the kernel
Xand  print out  the contents  of the  mount table  given a  Sys V
Xkernel. But,  you say,  /etc/mount does  the same  thing! Wrong!!
X/etc/mount  merely prints  out  the contents  of /etc/mnttab.  If
Xsomething munges this file -- you are SOL.
X
XI make no claims on the code,  nor any apologies. This is a quick
Xhack. I needed something quickly, as the /etc/mnttab on my system
Xwas getting zorched on a  semi-regular basis (still need to track
Xdown the culprit).
X
XIt  is easy  enough  to  modify this  to  generate  input to  the
Xsetmnt(1m)  command  or  to  use  it in  a  variety  of  ways  to
Xre-generate /etc/mnttab.
X
XThere are probably  still bugs in it -- but  it works well enough
Xfor the time being. Some time  when I don't have a zillion things
Xon my to-do list, I'll clean it up and rewrite it in C. It should
Xrun about 2-3 times faster.
X
XINSTALLATION:
X------------
X
XInstall the  dumpmnt shell script wherever  convenient. Make sure
Xthat the  path to  dumpmnt is  specified in  findmount.pl. Invoke
Xwith:
X
X	perl findmount.pl
X
XYou'll need read access to /dev/kmem. You can either make dumpmnt
Xsetgid to whichever group owns /dev/kmem (sys?) or run it as root.
X
XI   hope  that   you  find   this   useful.  If   you  make   any
Xfixes/enhancements, I'd appreciate hearing about them.
X
X	Binayak Banerjee
X	bba@mtuxo.ATT.COM
X	AT&T Bell Labs
X	Middletown, NJ 07724.

END_OF_FILE
echo shar: NEWLINE appended to \"'README'\"
if test 1696 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'dumpmnt.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'dumpmnt.c'\"
else
echo shar: Extracting \"'dumpmnt.c'\" \(6896 characters\)
sed "s/^X//" >'dumpmnt.c' <<'END_OF_FILE'
X/*
X * dumpmnt -c core -u executable
X *
X * Default value for core is /dev/kmem.
X * Default value for executable is /unix.
X *
X * This program will print out the following kernel information:
X *
X * mount-device (maj/min) parent-mount-device (maj/min) readonly-flag
X *
X * It is meant to be called from the ``findmount'' perl script.
X * (Appended to this file)
X *
X *  Binayak Banerjee
X *  bba@mtuxo.ATT.COM
X *  AT&T Bell Labs
X *  Middletown, NJ 07724
X *  Mon Dec 11 19:53:39 EST 1989
X */
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/param.h>
X#include <sys/inode.h>
X#include <sys/sysmacros.h>
X#include <sys/buf.h>
X#include <sys/filsys.h>
X#include <sys/mount.h>
X#include <sys/var.h>
X#include <nlist.h>
X
X/* useful externals */
Xextern int errno;
Xextern char *sys_errlist[];
Xlong lseek();
X
X# define UNIX	"/unix"		/* kernel */
X# define KMEM	"/dev/kmem"	/* kernel virtual memory */
X# define MEM	"/dev/mem"	/* kernel real memory */
X
Xstatic int kmem = -1;	/* File Descriptor for /dev/kmem */
Xstatic int mem = -1;	/* File Descriptor for /dev/mem */
X
Xstruct var	Var;
Xstruct filsys	SBlock;
Xstruct inode	PInode;
Xstruct mount	*mnttab;
Xstruct buf	SbBuffer;
X
Xstatic struct nlist nlst[] = {
X# define MOUNT		0
X	{ "mount" },
X# define VAR		1
X	{ "v" },
X	{ NULL },
X};
X
Xinit_kernel(U_kmem,U_unix,U_nlst)
Xchar *U_kmem,*U_unix;
Xstruct nlist *U_nlst;
X{
X
X    /* open kernel memory */
X    if ((kmem = open(U_kmem, 0)) < 0)
X    {
X	perror(U_kmem);
X	exit(20);
X    }
X
X    if (nlist(U_unix,U_nlst) == -1)
X    {
X	fprintf(stderr, "%s: can't nlist image\n",U_unix);
X	exit(2);
X    }
X
X    return;
X}
X
X
Xgetkval(offset, ptr, size, refstr)
X
Xunsigned long offset;
Xint *ptr;
Xint size;
Xchar *refstr;
X
X{
X    if (lseek(kmem, (long)offset, 0) == -1)
X    {
X	if (*refstr == '!')
X	{
X	    refstr++;
X	}
X	fprintf(stderr, "%s: lseek to %s: %s\n",
X	    KMEM, refstr, sys_errlist[errno]);
X	exit(22);
X    }
X    if (read(kmem, (char *)ptr, size) == -1)
X    {
X	if (*refstr == '!')
X	{
X	    /* we lost the race with the kernel, process isn't in memory */
X	    return(0);
X	} 
X	else 
X	{
X	    fprintf(stderr, "%s: reading %s: %s\n",
X		KMEM, refstr, sys_errlist[errno]);
X	    exit(23);
X	}
X    }
X    return;
X}
X
Xmain(argc,argv)
Xint argc;
Xchar **argv;
X{
X	int c;
X	int MntSiz;
X	int i;
X	struct mount *mptr;
X	char *core_file = KMEM;
X	char *ex_file  = UNIX;
X
X	extern int optind, getopt();
X	extern char *optarg;
X
X	while ((c = getopt(argc,argv,"c:u:")) != EOF)
X		switch (c)
X		 {
X			case 'c': /* Specify core file */
X				core_file = optarg;
X				break;
X
X			case 'u': /* Specify executable image */
X				ex_file = optarg;
X				break;
X
X			default:
X				break;
X		 }
X	
X	init_kernel(core_file,ex_file,&nlst[0]);
X
X	/*
X	 * First Get var structure to find size of mount table.
X	 */
X	(void) getkval(nlst[VAR].n_value,
X		(int *)(&Var),
X		sizeof(struct var),
X		nlst[VAR].n_name);
X
X	MntSiz = Var.v_mount;
X
X	/*
X	 * Now, allocate space for the mount table and read it
X	 * all in -- in one shot.
X	 */
X	
X	mnttab = (struct mount *) malloc(sizeof(struct mount)*MntSiz);
X	if (mnttab == (struct mount *)0) {
X		fprintf(stderr,"%s: Couldn't allocate space\n",argv[0]);
X		exit(2);
X	}
X
X	(void) getkval(nlst[MOUNT].n_value,
X		(int *) mnttab,
X		sizeof(struct mount)*MntSiz,
X		nlst[MOUNT].n_name);
X	
X	/*
X	 * Now, we iterate through the entries in the mount table,
X	 * printing out the relevant information.
X	 */
X	
X	for (i = 0; i < MntSiz; i++) {
X		mptr = &mnttab[i];
X		if (! (mptr->m_flags & MINUSE)) continue;
X
X		/* Now grab the parent device inode */
X
X		(void) getkval(mptr->m_inodp,
X			(int *) &PInode,
X			sizeof(struct inode),
X			"inode");
X
X		/*
X		 * Also grab the boot block for readonly-ness 
X		 * But first must grab the buffer for the superblock.
X		 */
X
X		(void) getkval(mptr->m_bufp,
X			(int *) &SbBuffer,
X			sizeof(struct buf),
X			"super block buffer");
X
X
X		/* Now grab the superblock */
X		(void) getkval(SbBuffer.b_un.b_filsys,
X			(int *) &SBlock,
X			sizeof(struct filsys),
X			"superblock");
X
X
X		/* Now print it all out */
X
X		printf ("%d (%d/%d)  %d (%d/%d) %d\n",
X			mptr->m_dev,
X			bmajor(mptr->m_dev),
X			minor(mptr->m_dev),
X			PInode.i_dev,
X			bmajor(PInode.i_dev),
X			minor(PInode.i_dev),
X			SBlock.s_ronly);
X	}
X}
X/* Here is findmount.pl */
X# ifdef notdef
X#! /usr/bin/perl
X
X# This routine  will walk through  a directory (/dev) and  create a
X# map between device  numbers and their names. It  does a
X# depth first search through the directory.
X
X# Binayak Banerjee
X# bba@mtuxo.ATT.COM
X# AT&T Bell Labs
X# Middletown, NJ 07724
X# Mon Dec 11 19:53:39 EST 1989
X
Xsub devmap {
X	local($thisdir,$item,$dev,$ino,$mode);
X	local($nlink,$uid,$gid,$devno,@junk);
X
X	push(dlist,@_[0]);
X
X	while ($thisdir=pop(dlist)) {
X		opendir(DH,$thisdir);
X		while ($item = readdir(DH)) {
X			if ($item =~ /^\.$|^\.\.$/) { next;}
X			$item = join('/',$thisdir,$item);
X			if (-d $item) {
X				push(dlist,$item);
X				next;
X			}
X			if (-b _) {
X				($dev,$ino,$mode,$nlink,
X				$uid,$gid,$devno,@junk) = stat(_);
X				@devname[$devno] = $item;
X			}
X		}
X		closedir(DH);
X	}
X}
X
X# We  want to find out certain information about the mounted
X# file systems in order to cut down on the search.  We do this
X# by opening a pipe to an external program "dumpmnt" which prints
X# out 3 date items of interest.  These are:
X#
X#	1.  The mounted device.
X#	2.  The parent of the mounted device.
X#	3.  A boolean value specifying if the device is
X#	    mounted readonly.
X
X$INTERIOR = 100;
X$LEAF	  = 200;
X
Xsub GetDevInfo {
X	open(inp_pipe,'./dumpmnt |');
X
X	while (<inp_pipe>) {
X		($tdev,$pdev,$rflg) =
X			/^(\d+)\s+\(.*\)\s+(\d+)\s+\(.*\)\s+([01])$/;
X		# Now set up the leaf and interior node bit.
X		$devtyp[$pdev] = $INTERIOR;
X		if ($devtyp[$tdev] != $INTERIOR) {
X			$devtyp[$tdev] = $LEAF;
X		}
X		# Now readonly flags
X		$readonly[$tdev] = $rflg? "read only":"read/write";
X	}
X	close(inp_pipe);
X}
X
X# Read some information about the devices (other than names).
X
X# Make the map of device numbers to names.
Xdo devmap("/dev");
X
X# Do map of device number types.
X
Xdo GetDevInfo();
X
X# Now here is the meat of the whole thing.
X
X# prime the pump.
X
X$thisdir = '/';
X($dev,@junk) = stat($thisdir);
Xprint "/ on $devname[$dev] $readonly[$dev]\n";
Xpush(dirlist,$dev);
Xpush(dirlist,"/");
Xwhile ($thisdir=pop(dirlist)) {
X	$dirdev = pop(dirlist);
X	# if ($thisdir =~ m'^/dev$') { next; }
X	opendir(DH,$thisdir);
X	# Skip over '.' and '..'
X	$item = readdir(DH); $item = readdir(DH);
X	while ($item = readdir(DH)) {
X		$item = join('/',$thisdir,$item);
X		if (! -d $item) { next; }
X
X		($dev,$ino,$mode,$nlink,@junk) = stat(_);
X
X		# Mount point if dev is not the same as that
X		# of it's parent.
X		if ($dev != $dirdev) {
X			$item =~ s'^//'/';
X			print "$item on $devname[$dev] $readonly[$dev]\n";
X		}
X
X		# The directory has no subdirectories if it has
X		# only 2 links.
X		if ($nlink == 2 || $devtyp[$dev] == $LEAF){
X			# print STDERR "$item is skipped\n";
X			next;
X		}
X		# print "pushing $item\n";
X		push(dirlist,$dev);
X		push(dirlist,$item);
X	}
X	closedir(DH);
X}
X# endif
X

END_OF_FILE
echo shar: NEWLINE appended to \"'dumpmnt.c'\"
if test 6897 -ne `wc -c <'dumpmnt.c'`; then
    echo shar: \"'dumpmnt.c'\" unpacked with wrong size!
fi
# end of 'dumpmnt.c'
fi
if test -f 'findmount.pl' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'findmount.pl'\"
else
echo shar: Extracting \"'findmount.pl'\" \(2718 characters\)
sed "s/^X//" >'findmount.pl' <<'END_OF_FILE'
X#! /usr/bin/perl
X
X# This routine  will walk through  a directory (/dev) and  create a
X# map between device  numbers and their names. It  does a
X# depth first search through the directory.
X
X# Binayak Banerjee
X# bba@mtuxo.ATT.COM
X# AT&T Bell Labs
X# Middletown, NJ 07724
X# Mon Dec 11 19:53:39 EST 1989
X
Xsub devmap {
X	local($thisdir,$item,$dev,$ino,$mode);
X	local($nlink,$uid,$gid,$devno,@junk);
X
X	push(dlist,@_[0]);
X
X	while ($thisdir=pop(dlist)) {
X		opendir(DH,$thisdir);
X		while ($item = readdir(DH)) {
X			if ($item =~ /^\.$|^\.\.$/) { next;}
X			$item = join('/',$thisdir,$item);
X			if (-d $item) {
X				push(dlist,$item);
X				next;
X			}
X			if (-b _) {
X				($dev,$ino,$mode,$nlink,
X				$uid,$gid,$devno,@junk) = stat(_);
X				@devname[$devno] = $item;
X			}
X		}
X		closedir(DH);
X	}
X}
X
X# We  want to find out certain information about the mounted
X# file systems in order to cut down on the search.  We do this
X# by opening a pipe to an external program "dumpmnt" which prints
X# out 3 date items of interest.  These are:
X#
X#	1.  The mounted device.
X#	2.  The parent of the mounted device.
X#	3.  A boolean value specifying if the device is
X#	    mounted readonly.
X
X$INTERIOR = 100;
X$LEAF	  = 200;
X
Xsub GetDevInfo {
X	open(inp_pipe,'./dumpmnt |');
X
X	while (<inp_pipe>) {
X		($tdev,$pdev,$rflg) =
X			/^(\d+)\s+\(.*\)\s+(\d+)\s+\(.*\)\s+([01])$/;
X		# Now set up the leaf and interior node bit.
X		$devtyp[$pdev] = $INTERIOR;
X		if ($devtyp[$tdev] != $INTERIOR) {
X			$devtyp[$tdev] = $LEAF;
X		}
X		# Now readonly flags
X		$readonly[$tdev] = $rflg? "read only":"read/write";
X	}
X	close(inp_pipe);
X}
X
X# Read some information about the devices (other than names).
X
X# Make the map of device numbers to names.
Xdo devmap("/dev");
X
X# Do map of device number types.
X
Xdo GetDevInfo();
X
X# Now here is the meat of the whole thing.
X
X# prime the pump.
X
X$thisdir = '/';
X($dev,@junk) = stat($thisdir);
Xprint "/ on $devname[$dev] $readonly[$dev]\n";
Xpush(dirlist,$dev);
Xpush(dirlist,"/");
Xwhile ($thisdir=pop(dirlist)) {
X	$dirdev = pop(dirlist);
X	# if ($thisdir =~ m'^/dev$') { next; }
X	opendir(DH,$thisdir);
X	# Skip over '.' and '..'
X	$item = readdir(DH); $item = readdir(DH);
X	while ($item = readdir(DH)) {
X		$item = join('/',$thisdir,$item);
X		if (! -d $item) { next; }
X
X		($dev,$ino,$mode,$nlink,@junk) = stat(_);
X
X		# Mount point if dev is not the same as that
X		# of it's parent.
X		if ($dev != $dirdev) {
X			$item =~ s'^//'/';
X			print "$item on $devname[$dev] $readonly[$dev]\n";
X		}
X
X		# The directory has no subdirectories if it has
X		# only 2 links.
X		if ($nlink == 2 || $devtyp[$dev] == $LEAF){
X			# print STDERR "$item is skipped\n";
X			next;
X		}
X		# print "pushing $item\n";
X		push(dirlist,$dev);
X		push(dirlist,$item);
X	}
X	closedir(DH);
X}
X

END_OF_FILE
echo shar: NEWLINE appended to \"'findmount.pl'\"
if test 2719 -ne `wc -c <'findmount.pl'`; then
    echo shar: \"'findmount.pl'\" unpacked with wrong size!
fi
# end of 'findmount.pl'
fi
echo shar: End of shell archive.
exit 0