*BSD News Article 7808


Return to BSD News archive

Path: sserve!manuel.anu.edu.au!munnari.oz.au!spool.mu.edu!agate!doc.ic.ac.uk!uknet!mcsun!fuug!kiae!rdrel!guam.relcom.msk.su!vak
From: vak@guam.relcom.msk.su (Serge V.Vakulenko)
Newsgroups: comp.unix.bsd
Subject: [386BSD] fs - print info about opened files
Message-ID: <1992Nov16.210249.1609@rdrel.relcom.msk.su>
Date: 16 Nov 92 21:02:49 GMT
Sender: usenet@rdrel.relcom.msk.su (Usenet News Administrator)
Organization: Relcom R&D
Lines: 859
X-Newsreader: TIN [version 1.1 PL6]

Here is simple utility, called `fs', which reads from the kernel
and prints the information about files, opened by running
processes.  It also tries to convert dev/inode pairs to
absolute file names.

Share and enjoy!

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	Makefile
#	fs.1
#	fs.c
#	fs.h
#	fsdumpdb.c
#	fsmakedb.1
#	fsmakedb.c
#
echo x - Makefile
sed 's/^X//' >Makefile << 'END-of-Makefile'
X# Copyright (C) 1992 Sergey Vakulenko, <vak@kiae.su>.
X# Permission to use and distribute this software is permitted
X# without fee, provided that this copyright notice is not changed.
X
XSHELL   = /bin/sh
XCFLAGS  = -pipe -O
XLDFLAGS = -pipe -O
X
XSRCS    = fs.c fsdumpdb.c fsmakedb.c
X
Xall:    fs fsmakedb fs.0 fsmakedb.0
X
Xfs:     fs.o
X	$(CC) $(LDFLAGS) fs.o -o fs -lutil
X
Xfsmakedb: fsmakedb.o
X	$(CC) $(LDFLAGS) fsmakedb.o -o fsmakedb
X
Xfsdumpdb: fsdumpdb.o
X	$(CC) $(LDFLAGS) fsdumpdb.o -o fsdumpdb
X
Xinstall: fs fsmakedb fs.0 fsmakedb.0
X	install -c -s -o root -m 4755 fs /usr/bin/fs
X	install -c -s -o root -m 4755 fsmakedb /usr/sbin/fsmakedb
X	install -c fs.0 /usr/share/man/cat1/fs.0
X	install -c fsmakedb.0 /usr/share/man/cat8/fsmakedb.0
X
Xdepend:
X	mkdep $(SRCS) &&\
X	sed '/^###$$/,$$d' Makefile > Makefile~ &&\
X	echo '###' >> Makefile~ &&\
X	cat .depend >> Makefile~ &&\
X	rm .depend &&\
X	mv Makefile~ Makefile
X
Xclean:
X	rm -f core.* .,* *.b *.o fs fsmakedb fsdumpdb .depend *.0
X
X.SUFFIXES: .1 .0
X
X.1.0:
X	nroff -man $< > $@
X
X###
Xfs.o : fs.c /usr/include/stdio.h /usr/include/sys/cdefs.h \
X  /usr/include/machine/ansi.h /usr/include/nlist.h /usr/include/sys/types.h \
X  /usr/include/machine/types.h /usr/include/db.h /usr/include/dirent.h \
X  /usr/include/fcntl.h /usr/include/sys/param.h /usr/include/sys/syslimits.h \
X  /usr/include/sys/errno.h /usr/include/sys/time.h \
X  /usr/include/sys/resource.h /usr/include/sys/ucred.h /usr/include/sys/uio.h \
X  /usr/include/sys/signal.h /usr/include/machine/trap.h \
X  /usr/include/machine/param.h /usr/include/machine/endian.h \
X  /usr/include/machine/limits.h /usr/include/sys/stat.h \
X  /usr/include/ufs/quota.h /usr/include/sys/namei.h /usr/include/sys/file.h \
X  /usr/include/sys/fcntl.h /usr/include/sys/unistd.h /usr/include/sys/vnode.h \
X  /usr/include/ufs/inode.h /usr/include/ufs/../ufs/dinode.h \
X  /usr/include/sys/proc.h /usr/include/machine/proc.h fs.h 
Xfsdumpdb.o : fsdumpdb.c /usr/include/stdio.h /usr/include/sys/cdefs.h \
X  /usr/include/machine/ansi.h /usr/include/sys/types.h \
X  /usr/include/machine/types.h /usr/include/db.h /usr/include/fcntl.h fs.h 
Xfsmakedb.o : fsmakedb.c /usr/include/stdio.h /usr/include/sys/cdefs.h \
X  /usr/include/machine/ansi.h /usr/include/sys/types.h \
X  /usr/include/machine/types.h /usr/include/db.h /usr/include/dirent.h \
X  /usr/include/sys/stat.h /usr/include/fcntl.h fs.h 
END-of-Makefile
echo x - fs.1
sed 's/^X//' >fs.1 << 'END-of-fs.1'
X.\" Copyright (C) 1992 Sergey Vakulenko, <vak@kiae.su>.
X.\" Permission to use and distribute this software is permitted
X.\" without fee, provided that this copyright notice is not changed.
X.Dd November 16, 1992
X.Dt FS 1
X.Os BSD 3
X.Sh NAME
X.Nm fs
X.Nd print opened files
X.Sh SYNOPSIS
X.Nm fs
X.Op Fl s
X.Op Fl f Ar dbfile
X.Sh DESCRIPTION
XThe
X.Nm fs
Xutility prints information about files, which are currently opened by
Xrunning processes.
X.Pp
XGiven the device and inode numbers of file,
X.Nm fs
Xtries to compute the full file name, using the database of
Xall files in the filesystem.  This database should be create by
X.Xr fsmakedb 1 ,
Xand placed in
X.Ql /var/db/fs.db .
X.Pp
XThe options are as follows:
X.Bl -tag -width Ds
X.It Fl s
XShow opened sockets in addition to files.
X.It Fl f
XRead the file name info from the specified file.
X.El
X.Pp
XIn order to have read access to /dev/kmem and other system files, the
X.Nm fs
Xutility should run setuid root.
X.Sh SEE ALSO
X.Xr fsmakedb 1 .
END-of-fs.1
echo x - fs.c
sed 's/^X//' >fs.c << 'END-of-fs.c'
X/*
X * fs.c - print files, opened by running processes.
X *
X * Version 1.0, Tue Nov 17 00:03:11 MSK 1992
X *
X * Copyright (C) 1992 Sergey Vakulenko, <vak@kiae.su>.
X * Permission to use and distribute this software is permitted
X * without fee, provided that this copyright notice is not changed.
X */
X#include <stdio.h>
X#include <nlist.h>
X#include <sys/types.h>
X#include <db.h>
X#include <dirent.h>
X#define KERNEL
X#include <fcntl.h>
X#include <sys/param.h>
X#include <sys/time.h>
X#include <sys/stat.h>
X#include <ufs/quota.h>
X#include <sys/namei.h>
X#include <sys/uio.h>
X#include <sys/file.h>
X#include <sys/vnode.h>
X#include <ufs/inode.h>
X#include <sys/ucred.h>
X#include <sys/proc.h>
X#include "fs.h"
X
Xstruct devtab {
X	char *name;
X	int dev;
X};
X
Xstruct nlist symtab [] = {
X	{ "_filehead", },               /* struct file * */
X#define FILEHEAD symtab[0]
X	{ "_nfiles", },                 /* int */
X#define NFILES symtab[1]
X	{ 0, },
X};
X
Xint nfiles;
Xstruct file *filetab;
Xstruct vnode *vnodetab;
Xstruct devtab *cdev, *bdev;
Xint cdevlen, bdevlen;
Xint cdevcnt, bdevcnt;
Xint initdevtab = 0;
XDB *db;
Xint sflag = 0;
X
Xchar *progname;
X
Xextern char *strrchr (), *strdup (), *malloc (), *calloc ();
X
X/* VARARGS1 */
Xvoid fatal (msg, a, b, c, d)
Xchar *msg;
Xlong a, b, c, d;
X{
X	fprintf (stderr, "%s: ", progname);
X	fprintf (stderr, msg, a, b, c, d);
X	fprintf (stderr, "\n");
X	exit (-1);
X}
X
Xvoid kread (off, addr, sz)
Xlong off;
Xvoid *addr;
X{
X	if (kvm_read (off, addr, sz) != sz)
X		fatal ("cannot read kernel memory");
X}
X
Xvoid readproc ()
X{
X	struct file *f, *ptr;
X	struct vnode v;
X
X	if (kvm_openfiles (0, 0, 0) < 0)
X		fatal ("cannot open kernel files");
X	if (kvm_nlist (symtab) != 0)
X		fatal ("cannot read kernel symbols");
X	kread (NFILES.n_value, &nfiles, sizeof (nfiles));
X
X	filetab = (struct file *) calloc (nfiles, sizeof (struct file));
X	vnodetab = (struct vnode *) calloc (nfiles, sizeof (struct vnode));
X	if (!filetab || !vnodetab)
X		fatal ("out of memory");
X
X	kread (FILEHEAD.n_value, &ptr, sizeof (ptr));
X	for (f=filetab; ptr && f<filetab+nfiles; ++f) {
X		int i = f - filetab;
X		kread (ptr, f, sizeof (struct file));
X
X		if (f->f_type == DTYPE_VNODE)
X			kvm_read (f->f_data, vnodetab+i, sizeof (struct vnode));
X
X		ptr = f->f_filef;
X	}
X}
X
Xchar *fileflag (int fl)
X{
X	static char buf [8];
X	char *p;
X
X	if (fl & ~(FREAD | FWRITE | O_APPEND | O_NONBLOCK | FHASLOCK)) {
X		sprintf (buf, "%o", fl);
X		return (buf);
X	}
X	p = buf + sizeof(buf);
X	*--p = 0;
X	*--p = (fl & FREAD)      ? 'r' : '-';
X	*--p = (fl & FWRITE)     ? 'w' : '-';
X	*--p = (fl & O_APPEND)   ? 'a' : '-';
X	*--p = (fl & O_NONBLOCK) ? 'n' : '-';
X	*--p = (fl & FHASLOCK)   ? 'l' : '-';
X	return (p);
X}
X
Xchar *filecount (cnt)
X{
X	static char buf [16];
X
X	if (cnt == 1)
X		return ("");
X	sprintf (buf, "(%d)", cnt);
X	return (buf);
X}
X
Xchar *fileoffset (off)
Xlong off;
X{
X	static char buf [24];
X
X	if (off == 0)
X		return ("");
X	sprintf (buf, "%x", off);
X	return (buf);
X}
X
Xchar *vnodetype (typ)
X{
X	switch (typ) {
X	case VNON:      return ("non");
X	case VBAD:      return ("bad");
X	case VSOCK:     return ("sock");
X	case VFIFO:     return ("fifo");
X	case VLNK:      return ("lnk");
X	case VBLK:      return ("blk");
X	case VCHR:      return ("chr");
X	case VDIR:      return ("dir");
X	case VREG:      return ("file");
X	}
X	return ("?");
X}
X
Xvoid makedevtab ()
X{
X	DIR *dird;
X	struct dirent *d;
X	struct stat st;
X	char afname [128];
X
X	cdevcnt = bdevcnt = 0;
X	cdevlen = bdevlen = 64;
X	bdev = (struct devtab *) malloc (bdevlen * sizeof (struct devtab));
X	cdev = (struct devtab *) malloc (cdevlen * sizeof (struct devtab));
X	if (! bdev || ! cdev)
X		fatal ("out of memory");
X	strcpy (afname, "/dev/");
X	dird = opendir (afname);
X	if (! dird)
X		fatal ("cannot read /dev directory");
X	for (d=readdir(dird); d; d=readdir(dird)) {
X		strcpy (afname+5, d->d_name);
X		if (stat (afname, &st) < 0)
X			continue;
X		if ((st.st_mode & S_IFMT) == S_IFBLK) {
X			if (bdevcnt >= bdevlen) {
X				bdevlen += 64;
X				bdev = (struct devtab *) realloc ((char *) bdev,
X					bdevlen * sizeof (struct devtab));
X				if (! bdev)
X					fatal ("out of memory");
X			}
X			bdev[bdevcnt].dev = st.st_rdev;
X			bdev[bdevcnt].name = strdup (afname);
X			++bdevcnt;
X		} else if ((st.st_mode & S_IFMT) == S_IFCHR) {
X			if (cdevcnt >= cdevlen) {
X				cdevlen += 64;
X				cdev = (struct devtab *) realloc ((char *) cdev,
X					cdevlen * sizeof (struct devtab));
X				if (! cdev)
X					fatal ("out of memory");
X			}
X			cdev[cdevcnt].dev = st.st_rdev;
X			cdev[cdevcnt].name = strdup (afname);
X			++cdevcnt;
X		}
X	}
X	closedir (dird);
X}
X
Xchar *devname (typ, dev)
X{
X	struct devtab *p;
X	int cnt, i;
X	static char buf [16];
X
X	if (! initdevtab) {
X		makedevtab ();
X		initdevtab = 1;
X	}
X	p = typ=='b' ? bdev : cdev;
X	cnt = typ=='b' ? bdevcnt : cdevcnt;
X	for (; --cnt>=0; ++p)
X		if (p->dev == dev)
X			return (p->name);
X	sprintf (buf, "%c %d/%d", typ, major (dev), minor (dev));
X	return (buf);
X}
X
Xchar *fetch (dev, inode, pdev, pinode)
Xlong dev, inode, *pdev, *pinode;
X{
X	DBT key, val;
X	struct fskey keyrec;
X	struct fsrec *v;
X
X	keyrec.dev = dev;
X	keyrec.inode = inode;
X	key.data = (char *) &keyrec;
X	key.size = sizeof (keyrec);
X	if ((*db->get) (db, &key, &val, 0) != 0)
X		return (0);
X	v = (struct fsrec *) val.data;
X	*pdev = v->pdev;
X	*pinode = v->pinode;
X	return (v->filename);
X}
X
Xchar *strrcpy (to, from)
Xchar *to, *from;
X{
X	int len = strlen (from);
X
X	strncpy (to -= len, from, len);
X	return (to);
X}
X
Xchar *filename (dev, inode)
Xlong dev, inode;
X{
X	long pdev, pinode;
X	static char fullname [512];
X	char *p, *name;
X
X	if (! db)
X		return (0);
X	name = fetch (dev, inode, &pdev, &pinode);
X	if (! name)
X		return (0);
X	p = fullname + sizeof (fullname);
X	*--p = 0;
X	for (;;) {
X		p = strrcpy (p, name);
X		if (dev==pdev && inode==pinode)
X			break;
X		*--p = '/';
X		name = fetch (dev = pdev, inode = pinode, &pdev, &pinode);
X		if (! name)
X			return (0);
X	}
X	return (p);
X}
X
Xvoid printvnode (v)
Xstruct vnode *v;
X{
X	struct inode *i;
X	char *name;
X
X	switch (v->v_type) {
X	case VNON: case VBAD: case VSOCK: case VFIFO: case VLNK:
X	default:
X		printf (vnodetype (v->v_type));
X		return;
X	case VBLK: case VCHR: case VDIR: case VREG:
X		break;
X	}
X	switch (v->v_tag) {
X	default:
X		printf ("%s on %d fs", vnodetype (v->v_type), v->v_tag);
X		return;
X	case VT_NON:
X		printf ("%s nowhere", vnodetype (v->v_type));
X		return;
X	case VT_NFS:
X		printf ("%s on nfs", vnodetype (v->v_type));
X		return;
X	case VT_MFS:
X		printf ("%s on mfs", vnodetype (v->v_type));
X		return;
X	case VT_UFS:
X		break;
X	}
X	i = VTOI (v);
X	switch (v->v_type) {
X	case VBLK:
X		printf ("%s", devname ('b', i->i_rdev));
X		return;
X	case VCHR:
X		printf ("%s", devname ('c', i->i_rdev));
X		return;
X	}
X	name = filename (i->i_dev, i->i_number);
X	switch (v->v_type) {
X	case VDIR:
X		if (name)
X			printf ("%s/", name);
X		else
X			printf ("%s dir %d", devname ('b', i->i_dev), i->i_number);
X		break;
X	case VREG:
X		if (name)
X			printf ("%s", name);
X		else
X			printf ("%s file %d", devname ('b', i->i_dev), i->i_number);
X		break;
X	}
X}
X
Xvoid printproc (f)
Xstruct file *f;
X{
X	printf ("%6s %5s %8s ",
X		fileflag (f->f_flag),   /* read/write etc. */
X		filecount (f->f_count), /* reference count */
X		fileoffset (f->f_offset) /* seek offset in file */
X	);
X	if (f->f_type == DTYPE_VNODE)
X		printvnode (vnodetab + (f - filetab));
X	else if (f->f_type == DTYPE_SOCKET)
X		printf ("<socket>");
X	printf ("\n");
X}
X
Xusage ()
X{
X	fprintf (stderr, "Usage: %s [-s] [-f dbfile]\n", progname);
X	exit (-1);
X}
X
Xint fsrechash (a, sz)
Xstruct fskey *a;
X{
X	return (a->inode);
X}
X
Xint main (argc, argv)
Xchar **argv;
X{
X	struct file *f;
X	char *dbfile = FSDBFILE;
X	HASHINFO hi;
X
X	/* Store the program name for message printing. */
X	progname = strrchr (*argv, '/');
X	if (! progname)
X		progname = *argv;
X
X	/* Parse options. */
X	for (++argv, --argc; *argv && **argv=='-'; ++argv, --argc) {
X		char *p;
X
X		for (p=1+*argv; *p; ++p) {
X			switch (*p) {
X			case 's':
X				++sflag;
X				continue;
X			case 'f':
X				dbfile = *++argv;
X				--argc;
X				if (! dbfile)
X					usage ();
X				break;
X			default:
X				fprintf (stderr, "%s: no such option: %c\n",
X					progname, *p);
X				usage ();
X			}
X			break;
X		}
X	}
X	if (*argv)
X		usage ();
X
X	/* Read kernel info. */
X	readproc ();
X	kvm_close ();
X
X	/* Open dev/inode to file name conversion database */
X	hi.bsize = 512;                 /* page size */
X	hi.cachesize = 200*512;         /* cache size */
X	hi.hash = fsrechash;            /* hash function */
X	hi.lorder = 0;                  /* byte order */
X	hi.ffactor = 64;                /* fill factor */
X	db = hash_open (dbfile, O_RDONLY, 0, &hi);
X
X	/* Print processes information. */
X	printf (" FLAGS REFCNT  OFFSET FILENAME\n");
X
X	for (f=filetab; f->f_filef && f<filetab+nfiles; ++f)
X		if (sflag || f->f_type==DTYPE_VNODE)
X			printproc (f);
X
X	return (0);
X}
END-of-fs.c
echo x - fs.h
sed 's/^X//' >fs.h << 'END-of-fs.h'
X/*
X * Copyright (C) 1992 Sergey Vakulenko, <vak@kiae.su>.
X * Permission to use and distribute this software is permitted
X * without fee, provided that this copyright notice is not changed.
X */
X#define FSDBFILE "/var/db/fs.db"
X
Xstruct fskey {
X	long dev;
X	long inode;
X};
X
Xstruct fsrec {
X	long pdev;
X	long pinode;
X	char filename [256];
X};
END-of-fs.h
echo x - fsdumpdb.c
sed 's/^X//' >fsdumpdb.c << 'END-of-fsdumpdb.c'
X/*
X * Dump the file info database.  Used for debugging.
X *
X * Copyright (C) 1992 Sergey Vakulenko, <vak@kiae.su>.
X * Permission to use and distribute this software is permitted
X * without fee, provided that this copyright notice is not changed.
X */
X#include <stdio.h>
X#include <sys/types.h>
X#include <db.h>
X#include <fcntl.h>
X#include "fs.h"
X
XDB *db;
Xchar *progname;
Xint vflag = 0;
X
Xextern char *strrchr ();
X
Xvoid dump ()
X{
X	DBT key, val;
X	struct fskey *k;
X	struct fsrec *v;
X
X	for (;;) {
X		if ((*db->seq) (db, &key, &val, 0) != 0) {
X			perror ("seq");
X			return;
X		}
X		k = (struct fskey *) key.data;
X		v = (struct fsrec *) val.data;
X		printf ("%d/%d -> %s\n", k->dev, k->inode, v->filename);
X	}
X}
X
Xint fsrechash (a, sz)
Xstruct fskey *a;
X{
X	return (a->inode);
X}
X
Xusage ()
X{
X	fprintf (stderr, "Usage: %s [dbfile]\n", progname);
X	exit (-1);
X}
X
Xint main (argc, argv)
Xchar **argv;
X{
X	char *dbname = FSDBFILE;
X	HASHINFO hi;
X
X	/* Store the program name for message printing. */
X	progname = strrchr (*argv, '/');
X	if (! progname)
X		progname = *argv;
X
X	/* Parse options. */
X	for (++argv, --argc; *argv && **argv=='-'; ++argv, --argc) {
X		char *p;
X
X		for (p=1+*argv; *p; ++p) {
X			switch (*p) {
X			case 'v':
X				++vflag;
X				continue;
X			default:
X				fprintf (stderr, "%s: no such option: %c",
X					progname, *p);
X				usage ();
X			}
X		}
X	}
X	if (*argv)
X		dbname = *argv++;
X	if (*argv)
X		usage ();
X
X	/* Open dev/inode to file name conversion database */
X	hi.bsize = 512;                 /* page size */
X	hi.cachesize = 200*512;         /* cache size */
X	hi.hash = fsrechash;            /* hash function */
X	hi.lorder = 0;                  /* byte order */
X	hi.ffactor = 64;                /* fill factor */
X	db = hash_open (dbname, O_RDONLY, 0, &hi);
X	if (! db) {
X		fprintf (stderr, "%s: cannot read %s", progname, dbname);
X		exit (-1);
X	}
X	dump ();
X
X	return (0);
X}
END-of-fsdumpdb.c
echo x - fsmakedb.1
sed 's/^X//' >fsmakedb.1 << 'END-of-fsmakedb.1'
X.\" Copyright (C) 1992 Sergey Vakulenko, <vak@kiae.su>.
X.\" Permission to use and distribute this software is permitted
X.\" without fee, provided that this copyright notice is not changed.
X.Dd November 16, 1992
X.Dt FSMAKEDB 8
X.Os BSD 3
X.Sh NAME
X.Nm fsmakedb
X.Nd create file name database for fs(1) utility
X.Sh SYNOPSIS
X.Nm fsmakedb
X.Op Ar dbfile
X.Sh DESCRIPTION
XThe
X.Nm fsmakedb
Xutility creates the database of all files in the filesystem.
X.Pp
XThe
X.Ar dbfile
Xis the database name to create, default
X.Ql /var/db/fs.db .
X.Pp
XIn order to have read/chdir access to all directories, the
X.Nm fsmakedb
Xutility should run setuid root.
X.Sh SEE ALSO
X.Xr fs 1 .
END-of-fsmakedb.1
echo x - fsmakedb.c
sed 's/^X//' >fsmakedb.c << 'END-of-fsmakedb.c'
X/*
X * Create the file info database for the fs(1) utility.
X *
X * Copyright (C) 1992 Sergey Vakulenko, <vak@kiae.su>.
X * Permission to use and distribute this software is permitted
X * without fee, provided that this copyright notice is not changed.
X */
X#include <stdio.h>
X#include <sys/types.h>
X#include <db.h>
X#include <dirent.h>
X#include <sys/stat.h>
X#include <fcntl.h>
X#include "fs.h"
X
XDB *db;
Xchar *progname;
Xint vflag = 0;
X
Xextern char *strrchr ();
X
X/* VARARGS1 */
Xvoid fatal (msg, a, b, c, d)
Xchar *msg;
Xlong a, b, c, d;
X{
X	fprintf (stderr, "%s: ", progname);
X	fprintf (stderr, msg, a, b, c, d);
X	fprintf (stderr, "\n");
X	exit (-1);
X}
X
Xvoid store (dev, inode, name, pdev, pinode)
Xchar *name;
X{
X	DBT key, val;
X	struct fskey keyrec;
X	struct fsrec valrec;
X
X	keyrec.dev = dev;
X	keyrec.inode = inode;
X	key.data = (char *) &keyrec;
X	key.size = sizeof (keyrec);
X
X	valrec.pdev = pdev;
X	valrec.pinode = pinode;
X	strcpy (valrec.filename, name);
X	val.data = (char *) &valrec;
X	val.size = (char *) &valrec.filename - (char *) &valrec +
X		strlen (name) + 1;
X
X	if (vflag)
X		printf ("%d/%d -> %s\n", dev, inode, name);
X	if ((*db->put) (db, &key, &val, R_PUT) != 0)
X		fprintf (stderr, "error storing new record\n");
X}
X
Xvoid scan (dir, pst)
Xchar *dir;
Xstruct stat *pst;
X{
X	DIR *dird;
X	struct dirent *d;
X	struct stat st;
X
X	if (chdir (dir) < 0) {
X		perror (dir);
X		return;
X	}
X	dird = opendir (".");
X	if (! dird) {
X		perror (dir);
X		chdir ("..");
X		return;
X	}
X	for (d=readdir(dird); d; d=readdir(dird)) {
X		if (d->d_name[0] == '.' && (d->d_name[1] == 0 ||
X		    d->d_name[1] == '.' && d->d_name[2] == 0))
X			continue;
X		if (lstat (d->d_name, &st) < 0) {
X			perror (d->d_name);
X			continue;
X		}
X		store (st.st_dev, st.st_ino, d->d_name, pst->st_dev, pst->st_ino);
X		if ((st.st_mode & S_IFMT) == S_IFDIR)
X			scan (d->d_name, &st);
X	}
X	closedir (dird);
X	chdir ("..");
X}
X
Xint fsrechash (a, sz)
Xstruct fskey *a;
X{
X	return (a->inode);
X}
X
Xint main (argc, argv)
Xchar **argv;
X{
X	char *dbname = FSDBFILE;
X	struct stat st;
X	HASHINFO hi;
X
X	/* Store the program name for message printing. */
X	progname = strrchr (*argv, '/');
X	if (! progname)
X		progname = *argv;
X
X	/* Parse options. */
X	for (++argv, --argc; *argv && **argv=='-'; ++argv, --argc) {
X		char *p;
X
X		for (p=1+*argv; *p; ++p) {
X			switch (*p) {
X			case 'v':
X				++vflag;
X				break;
X			default:
X				fatal ("no such option: %c", *p);
X			}
X		}
X	}
X	if (*argv)
X		dbname = *argv++;
X	if (*argv)
X		fatal ("usage: %s [dbfile]", progname);
X
X	/* Open dev/inode to file name conversion database */
X	hi.bsize = 512;                 /* page size */
X	hi.cachesize = 200*512;         /* cache size */
X	hi.hash = fsrechash;            /* hash function */
X	hi.lorder = 0;                  /* byte order */
X	hi.ffactor = 64;                /* fill factor */
X	db = hash_open (dbname, O_RDWR|O_CREAT|O_TRUNC, 0644, &hi);
X	if (! db)
X		fatal ("cannot create %s", dbname);
X
X	if (lstat ("/", &st) < 0) {
X		perror ("/");
X		return (-1);
X	}
X
X	/* Scan all files recursively beginning from the root. */
X	store (st.st_dev, st.st_ino, "", st.st_dev, st.st_ino);
X	scan ("/", &st);
X
X	dbm_close (db);
X	return (0);
X}
END-of-fsmakedb.c
exit