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