summaryrefslogtreecommitdiff
path: root/lib/dvch.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dvch.c')
-rw-r--r--lib/dvch.c1413
1 files changed, 1413 insertions, 0 deletions
diff --git a/lib/dvch.c b/lib/dvch.c
new file mode 100644
index 0000000..ffdc4b4
--- /dev/null
+++ b/lib/dvch.c
@@ -0,0 +1,1413 @@
+/*
+ * dvch.c -- device cache functions for lsof library
+ */
+
+
+/*
+ * Copyright 1997 Purdue Research Foundation, West Lafayette, Indiana
+ * 47907. All rights reserved.
+ *
+ * Written by Victor A. Abell
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. Neither the authors nor Purdue University are responsible for any
+ * consequences of the use of this software.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Credit to the authors and Purdue
+ * University must appear in documentation and sources.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+
+#include "../machine.h"
+
+#if defined(HASDCACHE)
+
+# if !defined(lint)
+static char copyright[] =
+"@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n";
+static char *rcsid = "$Id: dvch.c,v 1.16 2008/10/21 16:12:36 abe Exp $";
+# endif /* !defined(lint) */
+
+#include "../lsof.h"
+
+/*
+ * dvch.c - module that contains common device cache functions
+ *
+ * The caller may define the following:
+ *
+ * DCACHE_CLONE is the name of the function that reads and writes the
+ * clone section of the device cache file. The clone
+ * section follows the device section. If DCACHE_CLONE
+ * isn't defined, but HAS_STD_CLONE is defined to be 1,
+ * DCACHE_CLONE defaults to the local static function
+ * rw_clone_sect() that reads and writes a standard
+ * clone cache.
+ *
+ * DCACHE_CLR is the name of the function that clears the clone and
+ * pseudo caches when reading the device cache fails. If
+ * DCACHE_CLR isn't defined, but HAS_STD_CLONE is defined
+ * to be 1, DCACHE_CLR defaults to the local static
+ * function clr_sect() that clears a standard clone cache.
+ *
+ * DCACHE_PSEUDO is the name of the function that reads and writes
+ * the pseudo section of the device cache file. The
+ * pseudo section follows the device section and the
+ * clone section, if there is one.
+ *
+ * DVCH_CHOWN if the dialect has no fchown() function, so
+ * chown() must be used instead.
+ *
+ * DVCH_DEVPATH if the path to the device directory isn't "/dev".
+ *
+ * DVCH_EXPDEV if st_rdev must be expanded with the expdev()
+ * macro before use. (This is an EP/IX artifact.)
+ *
+ * HASBLKDEV if block device information is stored in BDevtp[].
+ */
+
+
+/*
+ * Local definitions
+ */
+
+# if !defined(DVCH_DEVPATH)
+#define DVCH_DEVPATH "/dev"
+# endif /* !defined(DVCH_DEVPATH) */
+
+/*
+ * Local storage
+ */
+
+static int crctbl[CRC_TBLL]; /* crc partial results table */
+
+
+/*
+ * Local function prototypes
+ */
+
+#undef DCACHE_CLR_LOCAL
+# if !defined(DCACHE_CLR)
+# if defined(HAS_STD_CLONE) && HAS_STD_CLONE==1
+#define DCACHE_CLR clr_sect
+#define DCACHE_CLR_LOCAL 1
+_PROTOTYPE(static void clr_sect,(void));
+# endif /* defined(HAS_STD_CLONE) && HAS_STD_CLONE==1 */
+# endif /* !defined(DCACHE_CLR) */
+
+#undef DCACHE_CLONE_LOCAL
+# if !defined(DCACHE_CLONE)
+# if defined(HAS_STD_CLONE) && HAS_STD_CLONE==1
+#define DCACHE_CLONE rw_clone_sect
+#define DCACHE_CLONE_LOCAL 1
+_PROTOTYPE(static int rw_clone_sect,(int m));
+# endif /* defined(HAS_STD_CLONE) && HAS_STD_CLONE==1 */
+# endif /*!defined(DCACHE_CLONE) */
+
+
+# if defined(HASBLKDEV)
+/*
+ * alloc_bdcache() - allocate block device cache
+ */
+
+void
+alloc_bdcache()
+{
+ if (!(BDevtp = (struct l_dev *)calloc((MALLOC_S)BNdev,
+ sizeof(struct l_dev))))
+ {
+ (void) fprintf(stderr, "%s: no space for block devices\n", Pn);
+ Exit(1);
+ }
+ if (!(BSdev = (struct l_dev **)malloc((MALLOC_S)(sizeof(struct l_dev *)
+ * BNdev))))
+ {
+ (void) fprintf(stderr, "%s: no space for block device pointers\n",
+ Pn);
+ Exit(1);
+ }
+}
+# endif /* defined(HASBLKDEV) */
+
+
+/*
+ * alloc_dcache() - allocate device cache
+ */
+
+void
+alloc_dcache()
+{
+ if (!(Devtp = (struct l_dev *)calloc((MALLOC_S)Ndev,
+ sizeof(struct l_dev))))
+ {
+ (void) fprintf(stderr, "%s: no space for devices\n", Pn);
+ Exit(1);
+ }
+ if (!(Sdev = (struct l_dev **)malloc((MALLOC_S)(sizeof(struct l_dev *)
+ * Ndev))))
+ {
+ (void) fprintf(stderr, "%s: no space for device pointers\n",
+ Pn);
+ Exit(1);
+ }
+}
+
+
+/*
+ * clr_devtab() - clear the device tables and free their space
+ */
+
+void
+clr_devtab()
+{
+ int i;
+
+ if (Devtp) {
+ for (i = 0; i < Ndev; i++) {
+ if (Devtp[i].name) {
+ (void) free((FREE_P *)Devtp[i].name);
+ Devtp[i].name = (char *)NULL;
+ }
+ }
+ (void) free((FREE_P *)Devtp);
+ Devtp = (struct l_dev *)NULL;
+ }
+ if (Sdev) {
+ (void) free((FREE_P *)Sdev);
+ Sdev = (struct l_dev **)NULL;
+ }
+ Ndev = 0;
+
+# if defined(HASBLKDEV)
+ if (BDevtp) {
+ for (i = 0; i < BNdev; i++) {
+ if (BDevtp[i].name) {
+ (void) free((FREE_P *)BDevtp[i].name);
+ BDevtp[i].name = (char *)NULL;
+ }
+ }
+ (void) free((FREE_P *)BDevtp);
+ BDevtp = (struct l_dev *)NULL;
+ }
+ if (BSdev) {
+ (void) free((FREE_P *)BSdev);
+ BSdev = (struct l_dev **)NULL;
+ }
+ BNdev = 0;
+# endif /* defined(HASBLKDEV) */
+
+}
+
+
+# if defined(DCACHE_CLR_LOCAL)
+/*
+ * clr_sect() - clear cached standard clone sections
+ */
+
+static void
+clr_sect()
+{
+ struct clone *c, *c1;
+
+ if (Clone) {
+ for (c = Clone; c; c = c1) {
+ c1 = c->next;
+ (void) free((FREE_P *)c);
+ }
+ Clone = (struct clone *)NULL;
+ }
+}
+# endif /* defined(DCACHE_CLR_LOCAL) */
+
+
+/*
+ * crc(b, l, s) - compute a crc for a block of bytes
+ */
+
+void
+crc(b, l, s)
+ char *b; /* block address */
+ int l; /* length */
+ unsigned *s; /* sum */
+{
+ char *cp; /* character pointer */
+ char *lm; /* character limit pointer */
+ unsigned sum; /* check sum */
+
+ cp = b;
+ lm = cp + l;
+ sum = *s;
+ do {
+ sum ^= ((int) *cp++) & 0xff;
+ sum = (sum >> 8) ^ crctbl[sum & 0xff];
+ } while (cp < lm);
+ *s = sum;
+}
+
+
+/*
+ * crcbld - build the CRC-16 partial results table
+ */
+
+void
+crcbld()
+{
+ int bit; /* temporary bit value */
+ unsigned entry; /* entry under construction */
+ int i; /* polynomial table index */
+ int j; /* bit shift count */
+
+ for(i = 0; i < CRC_TBLL; i++) {
+ entry = i;
+ for (j = 1; j <= CRC_BITS; j++) {
+ bit = entry & 1;
+ entry >>= 1;
+ if (bit)
+ entry ^= CRC_POLY;
+ }
+ crctbl[i] = entry;
+ }
+}
+
+
+/*
+ * dcpath() - define device cache file paths
+ */
+
+int
+dcpath(rw, npw)
+ int rw; /* read (1) or write (2) mode */
+ int npw; /* inhibit (0) or enable (1) no
+ * path warning message */
+{
+ char buf[MAXPATHLEN+1], *cp1, *cp2, hn[MAXPATHLEN+1];
+ int endf;
+ int i, j;
+ int l = 0;
+ int ierr = 0; /* intermediate error state */
+ int merr = 0; /* malloc error state */
+ struct passwd *p = (struct passwd *)NULL;
+ static short wenv = 1; /* HASENVDC warning state */
+ static short wpp = 1; /* HASPERSDCPATH warning state */
+/*
+ * Release any space reserved by previous path calls to dcpath().
+ */
+ if (DCpath[1]) {
+ (void) free((FREE_P *)DCpath[1]);
+ DCpath[1] = (char *)NULL;
+ }
+ if (DCpath[3]) {
+ (void) free((FREE_P *)DCpath[3]);
+ DCpath[3] = (char *)NULL;
+ }
+/*
+ * If a path was specified via -D, it's character address will have been
+ * stored in DCpathArg by ctrl_dcache(). Use that address if the real UID
+ * of this process is root, or the mode is read, or the process is neither
+ * setuid-root nor setgid.
+ */
+ if (Myuid == 0 || rw == 1 || (!Setuidroot && !Setgid))
+ DCpath[0] = DCpathArg;
+ else
+ DCpath[0] = (char *)NULL;
+
+# if defined(HASENVDC)
+/*
+ * If HASENVDC is defined, get its value from the environment, unless this
+ * is a setuid-root process, or the real UID of the process is 0, or the
+ * mode is write and the process is setgid.
+ */
+ if ((cp1 = getenv(HASENVDC)) && (l = strlen(cp1)) > 0
+ && !Setuidroot && Myuid && (rw == 1 || !Setgid)) {
+ if (!(cp2 = mkstrcpy(cp1, (MALLOC_S *)NULL))) {
+ (void) fprintf(stderr,
+ "%s: no space for device cache path: %s=", Pn, HASENVDC);
+ safestrprt(cp1, stderr, 1);
+ merr = 1;
+ } else
+ DCpath[1] = cp2;
+ } else if (cp1 && l > 0) {
+ if (!Fwarn && wenv) {
+ (void) fprintf(stderr,
+ "%s: WARNING: ignoring environment: %s=", Pn, HASENVDC);
+ safestrprt(cp1, stderr, 1);
+ }
+ wenv = 0;
+ }
+# endif /* defined(HASENVDC) */
+
+# if defined(HASSYSDC)
+/*
+ * If HASSYSDC is defined, record the path of the system-wide device
+ * cache file, unless the mode is write.
+ */
+ if (rw != 2)
+ DCpath[2] = HASSYSDC;
+ else
+ DCpath[2] = (char *)NULL;
+# endif /* defined(HASSYSDC) */
+
+# if defined(HASPERSDC)
+/*
+ * If HASPERSDC is defined, form a personal device cache path by
+ * interpreting the conversions specified in it.
+ *
+ * Get (HASPERSDCPATH) from the environment and add it to the home directory
+ * path, if possible.
+ */
+ for (cp1 = HASPERSDC, endf = i = 0; *cp1 && !endf; cp1++) {
+ if (*cp1 != '%') {
+
+ /*
+ * If the format character isn't a `%', copy it.
+ */
+ if (i < (int)sizeof(buf)) {
+ buf[i++] = *cp1;
+ continue;
+ } else {
+ ierr = 2;
+ break;
+ }
+ }
+ /*
+ * `%' starts a conversion; the next character specifies
+ * the conversion type.
+ */
+ cp1++;
+ switch (*cp1) {
+
+ /*
+ * Two consecutive `%' characters convert to one `%'
+ * character in the output.
+ */
+
+ case '%':
+ if (i < (int)sizeof(buf))
+ buf[i++] = '%';
+ else
+ ierr = 2;
+ break;
+
+ /*
+ * ``%0'' defines a root boundary. If the effective
+ * (setuid-root) or real UID of the process is root, any
+ * path formed to this point is discarded and path formation
+ * begins with the next character.
+ *
+ * If neither the effective nor the real UID is root, path
+ * formation ends.
+ *
+ * This allows HASPERSDC to specify one path for non-root
+ * UIDs and another for the root (effective or real) UID.
+ */
+
+ case '0':
+ if (Setuidroot || !Myuid)
+ i = 0;
+ else
+ endf = 1;
+ break;
+
+ /*
+ * ``%h'' converts to the home directory.
+ */
+
+ case 'h':
+ if (!p && !(p = getpwuid(Myuid))) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't get home dir for UID: %d\n",
+ Pn, (int)Myuid);
+ ierr = 1;
+ break;
+ }
+ if ((i + (l = strlen(p->pw_dir))) >= (int)sizeof(buf)) {
+ ierr = 2;
+ break;
+ }
+ (void) strcpy(&buf[i], p->pw_dir);
+ i += l;
+ if (i > 0 && buf[i - 1] == '/' && *(cp1 + 1)) {
+
+ /*
+ * If the home directory ends in a '/' and the next format
+ * character is a '/', delete the '/' at the end of the home
+ * directory.
+ */
+ i--;
+ buf[i] = '\0';
+ }
+ break;
+
+ /*
+ * ``%l'' converts to the full host name.
+ *
+ * ``%L'' converts to the first component (characters up
+ * to the first `.') of the host name.
+ */
+
+ case 'l':
+ case 'L':
+ if (gethostname(hn, sizeof(hn) - 1) < 0) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: no gethostname for %%l or %%L: %s\n",
+ Pn, strerror(errno));
+ ierr = 1;
+ break;
+ }
+ hn[sizeof(hn) - 1] = '\0';
+ if (*cp1 == 'L' && (cp2 = strchr(hn, '.')) && cp2 > hn)
+ *cp2 = '\0';
+ j = strlen(hn);
+ if ((j + i) < (int)sizeof(buf)) {
+ (void) strcpy(&buf[i], hn);
+ i += j;
+ } else
+ ierr = 2;
+ break;
+
+ /*
+ * ``%p'' converts to the contents of LSOFPERSDCPATH, followed
+ * by a '/'.
+ *
+ * It is ignored when:
+ *
+ * The lsof process is setuid-root;
+ * The real UID of the lsof process is 0;
+ * The mode is write and the process is setgid.
+ */
+
+ case 'p':
+
+# if defined(HASPERSDCPATH)
+ if ((cp2 = getenv(HASPERSDCPATH))
+ && (l = strlen(cp2)) > 0
+ && !Setuidroot
+ && Myuid
+ && (rw == 1 || !Setgid))
+ {
+ if (i && buf[i - 1] == '/' && *cp2 == '/') {
+ cp2++;
+ l--;
+ }
+ if ((i + l) < ((int)sizeof(buf) - 1)) {
+ (void) strcpy(&buf[i], cp2);
+ i += l;
+ if (buf[i - 1] != '/') {
+ if (i < ((int)sizeof(buf) - 2)) {
+ buf[i++] = '/';
+ buf[i] = '\0';
+ } else
+ ierr = 2;
+ }
+ } else
+ ierr = 2;
+ } else {
+ if (cp2 && l > 0) {
+ if (!Fwarn && wpp) {
+ (void) fprintf(stderr,
+ "%s: WARNING: ignoring environment: %s",
+ Pn, HASPERSDCPATH);
+ safestrprt(cp2, stderr, 1);
+ }
+ wpp = 0;
+ }
+ }
+# else /* !defined(HASPERSDCPATH) */
+ if (!Fwarn && wpp)
+ (void) fprintf(stderr,
+ "%s: WARNING: HASPERSDCPATH disabled: %s\n",
+ Pn, HASPERSDC);
+ ierr = 1;
+ wpp = 0;
+# endif /* defined(HASPERSDCPATH) */
+
+ break;
+
+ /*
+ * ``%u'' converts to the login name of the real UID of the
+ * lsof process.
+ */
+
+ case 'u':
+ if (!p && !(p = getpwuid(Myuid))) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't get login name for UID: %d\n",
+ Pn, (int)Myuid);
+ ierr = 1;
+ break;
+ }
+ if ((i + (l = strlen(p->pw_name))) >= (int)sizeof(buf)) {
+ ierr = 2;
+ break;
+ }
+ (void) strcpy(&buf[i], p->pw_name);
+ i += l;
+ break;
+
+ /*
+ * ``%U'' converts to the real UID of the lsof process.
+ */
+
+ case 'U':
+ (void) snpf(hn, sizeof(hn), "%d", (int)Myuid);
+ if ((i + (l = strlen(hn))) >= (int)sizeof(buf))
+ ierr = 2;
+ else {
+ (void) strcpy(&buf[i], hn);
+ i += l;
+ }
+ break;
+ default:
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: bad conversion (%%%c): %s\n",
+ Pn, *cp1, HASPERSDC);
+ ierr = 1;
+ }
+ if (endf || ierr > 1)
+ break;
+ }
+ if (ierr) {
+
+ /*
+ * If there was an intermediate error of some type, handle it.
+ * A type 1 intermediate error has already been noted with a
+ * warning message. A type 2 intermediate error requires the
+ * issuing of a buffer overlow warning message.
+ */
+ if (ierr == 2 && !Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: device cache path too large: %s\n",
+ Pn, HASPERSDC);
+ i = 0;
+ }
+ buf[i] = '\0';
+/*
+ * If there is one, allocate space for the personal device cache path,
+ * copy buf[] to it, and store its pointer in DCpath[3].
+ */
+ if (i) {
+ if (!(cp1 = mkstrcpy(buf, (MALLOC_S *)NULL))) {
+ (void) fprintf(stderr,
+ "%s: no space for device cache path: ", Pn);
+ safestrprt(buf, stderr, 1);
+ merr = 1;
+ } else
+ DCpath[3] = cp1;
+ }
+# endif /* defined(HASPERSDC) */
+
+/*
+ * Quit if there was a malloc() error. The appropriate error message
+ * will have been issued to stderr.
+ */
+ if (merr)
+ Exit(1);
+/*
+ * Return the index of the first defined path. Since DCpath[] is arranged
+ * in priority order, searching it beginning to end follows priority.
+ * Return an error indication if the search discloses no path name.
+ */
+ for (i = 0; i < MAXDCPATH; i++) {
+ if (DCpath[i])
+ return(i);
+ }
+ if (!Fwarn && npw)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't form any device cache path\n", Pn);
+ return(-1);
+}
+
+
+/*
+ * open_dcache() - open device cache file
+ */
+
+int
+open_dcache(m, r, s)
+ int m; /* mode: 1 = read; 2 = write */
+ int r; /* create DCpath[] if 0, reuse if 1 */
+ struct stat *s; /* stat() receiver */
+{
+ char buf[128];
+ char *w = (char *)NULL;
+/*
+ * Get the device cache file paths.
+ */
+ if (!r) {
+ if ((DCpathX = dcpath(m, 1)) < 0)
+ return(1);
+ }
+/*
+ * Switch to the requested open() action.
+ */
+ switch (m) {
+ case 1:
+
+ /*
+ * Check for access permission.
+ */
+ if (!is_readable(DCpath[DCpathX], 0)) {
+ if (DCpathX == 2 && errno == ENOENT)
+ return(2);
+ if (!Fwarn)
+ (void) fprintf(stderr, ACCESSERRFMT,
+ Pn, DCpath[DCpathX], strerror(errno));
+ return(1);
+ }
+ /*
+ * Open for reading.
+ */
+ if ((DCfd = open(DCpath[DCpathX], O_RDONLY, 0)) < 0) {
+ if (DCstate == 3 && errno == ENOENT)
+ return(1);
+
+cant_open:
+ (void) fprintf(stderr,
+ "%s: WARNING: can't open %s: %s\n",
+ Pn, DCpath[DCpathX], strerror(errno));
+ return(1);
+ }
+ if (stat(DCpath[DCpathX], s) != 0) {
+
+cant_stat:
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't stat(%s): %s\n",
+ Pn, DCpath[DCpathX], strerror(errno));
+close_exit:
+ (void) close(DCfd);
+ DCfd = -1;
+ return(1);
+ }
+ if ((int)(s->st_mode & 07777) != ((DCpathX == 2) ? 0644 : 0600)) {
+ (void) snpf(buf, sizeof(buf), "doesn't have %04o modes",
+ (DCpathX == 2) ? 0644 : 0600);
+ w = buf;
+ } else if ((s->st_mode & S_IFMT) != S_IFREG)
+ w = "isn't a regular file";
+ else if (!s->st_size)
+ w = "is empty";
+ if (w) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: %s %s.\n", Pn, DCpath[DCpathX], w);
+ goto close_exit;
+ }
+ return(0);
+ case 2:
+
+ /*
+ * Open for writing: first unlink any previous version; then
+ * open exclusively, specifying it's an error if the file exists.
+ */
+ if (unlink(DCpath[DCpathX]) < 0) {
+ if (errno != ENOENT) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't unlink %s: %s\n",
+ Pn, DCpath[DCpathX], strerror(errno));
+ return(1);
+ }
+ }
+ if ((DCfd = open(DCpath[DCpathX], O_RDWR|O_CREAT|O_EXCL, 0600)) < 0)
+ goto cant_open;
+ /*
+ * If the real user is not root, but the process is setuid-root,
+ * change the ownerships of the file to the real ones.
+ */
+ if (Myuid && Setuidroot) {
+
+# if defined(DVCH_CHOWN)
+ if (chown(DCpath[DCpathX], Myuid, getgid()) < 0)
+# else /* !defined(DVCH_CHOWN) */
+ if (fchown(DCfd, Myuid, getgid()) < 0)
+# endif /* defined(DVCH_CHOWN) */
+
+ {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't change ownerships of %s: %s\n",
+ Pn, DCpath[DCpathX], strerror(errno));
+ }
+ }
+ if (!Fwarn && DCstate != 1 && !DCunsafe)
+ (void) fprintf(stderr,
+ "%s: WARNING: created device cache file: %s\n",
+ Pn, DCpath[DCpathX]);
+ if (stat(DCpath[DCpathX], s) != 0) {
+ (void) unlink(DCpath[DCpathX]);
+ goto cant_stat;
+ }
+ return(0);
+ default:
+
+ /*
+ * Oops!
+ */
+ (void) fprintf(stderr, "%s: internal error: open_dcache=%d\n",
+ Pn, m);
+ Exit(1);
+ }
+ return(1);
+}
+
+
+/*
+ * read_dcache() - read device cache file
+ */
+
+int
+read_dcache()
+{
+ char buf[MAXPATHLEN*2], cbuf[64], *cp;
+ int i, len, ov;
+ struct stat sb, devsb;
+/*
+ * Open the device cache file.
+ *
+ * If the open at HASSYSDC fails because the file doesn't exist, and
+ * the real UID of this process is not zero, try to open a device cache
+ * file at HASPERSDC.
+ */
+ if ((ov = open_dcache(1, 0, &sb)) != 0) {
+ if (DCpathX == 2) {
+ if (ov == 2 && DCpath[3]) {
+ DCpathX = 3;
+ if (open_dcache(1, 1, &sb) != 0)
+ return(1);
+ } else
+ return(1);
+ } else
+ return(1);
+ }
+/*
+ * If the open device cache file's last mtime/ctime isn't greater than
+ * DVCH_DEVPATH's mtime/ctime, ignore it, unless -Dr was specified.
+ */
+ if (stat(DVCH_DEVPATH, &devsb) != 0) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't stat(%s): %s\n",
+ Pn, DVCH_DEVPATH, strerror(errno));
+ } else {
+ if (sb.st_mtime <= devsb.st_mtime || sb.st_ctime <= devsb.st_ctime)
+ DCunsafe = 1;
+ }
+ if (!(DCfs = fdopen(DCfd, "r"))) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't fdopen(%s)\n", Pn, DCpath[DCpathX]);
+ (void) close(DCfd);
+ DCfd = -1;
+ return(1);
+ }
+/*
+ * Read the section count line; initialize the CRC table;
+ * validate the section count line.
+ */
+ if (!fgets(buf, sizeof(buf), DCfs)) {
+
+cant_read:
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't fread %s: %s\n", Pn, DCpath[DCpathX],
+ strerror(errno));
+read_close:
+ (void) fclose(DCfs);
+ DCfd = -1;
+ DCfs = (FILE *)NULL;
+ (void) clr_devtab();
+
+# if defined(DCACHE_CLR)
+ (void) DCACHE_CLR();
+# endif /* defined(DCACHE_CLR) */
+
+ return(1);
+ }
+ (void) crcbld();
+ DCcksum = 0;
+ (void) crc(buf, strlen(buf), &DCcksum);
+ i = 1;
+ cp = "";
+
+# if defined(HASBLKDEV)
+ i++;
+ cp = "s";
+# endif /* defined(HASBLKDEV) */
+
+# if defined(DCACHE_CLONE)
+ i++;
+ cp = "s";
+# endif /* defined(DCACHE_CLONE) */
+
+# if defined(DCACHE_PSEUDO)
+ i++;
+ cp = "s";
+# endif /* defined(DCACHE_PSEUDO) */
+
+ (void) snpf(cbuf, sizeof(cbuf), "%d section%s", i, cp);
+ len = strlen(cbuf);
+ (void) snpf(&cbuf[len], sizeof(cbuf) - len, ", dev=%lx\n",
+ (long)DevDev);
+ if (!strncmp(buf, cbuf, len) && (buf[len] == '\n')) {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: no /dev device in %s: line ", Pn,
+ DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+ if (strcmp(buf, cbuf)) {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: bad section count line in %s: line ",
+ Pn, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+/*
+ * Read device section header and validate it.
+ */
+ if (!fgets(buf, sizeof(buf), DCfs))
+ goto cant_read;
+ (void) crc(buf, strlen(buf), &DCcksum);
+ len = strlen("device section: ");
+ if (strncmp(buf, "device section: ", len) != 0) {
+
+read_dhdr:
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: bad device section header in %s: line ",
+ Pn, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+/*
+ * Compute the device count; allocate Sdev[] and Devtp[] space.
+ */
+ if ((Ndev = atoi(&buf[len])) < 1)
+ goto read_dhdr;
+ alloc_dcache();
+/*
+ * Read the device lines and store their information in Devtp[].
+ * Construct the Sdev[] pointers to Devtp[].
+ */
+ for (i = 0; i < Ndev; i++) {
+ if (!fgets(buf, sizeof(buf), DCfs)) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't read device %d from %s\n",
+ Pn, i + 1, DCpath[DCpathX]);
+ goto read_close;
+ }
+ (void) crc(buf, strlen(buf), &DCcksum);
+ /*
+ * Convert hexadecimal device number.
+ */
+ if (!(cp = x2dev(buf, &Devtp[i].rdev)) || *cp != ' ') {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: device %d: bad device in %s: line ",
+ Pn, i + 1, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+ /*
+ * Convert inode number.
+ */
+ for (cp++, Devtp[i].inode = (INODETYPE)0; *cp != ' '; cp++) {
+ if (*cp < '0' || *cp > '9') {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: device %d: bad inode # in %s: line ",
+ Pn, i + 1, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+ Devtp[i].inode = (INODETYPE)((Devtp[i].inode * 10)
+ + (int)(*cp - '0'));
+ }
+ /*
+ * Get path name; allocate space for it; copy it; store the
+ * pointer in Devtp[]; clear verify status; construct the Sdev[]
+ * pointer to Devtp[].
+ */
+ if ((len = strlen(++cp)) < 2 || *(cp + len - 1) != '\n') {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: device %d: bad path in %s: line ",
+ Pn, i + 1, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+ *(cp + len - 1) = '\0';
+ if (!(Devtp[i].name = mkstrcpy(cp, (MALLOC_S *)NULL))) {
+ (void) fprintf(stderr,
+ "%s: device %d: no space for path: line ", Pn, i + 1);
+ safestrprt(buf, stderr, 1+4+8);
+ Exit(1);
+ }
+ Devtp[i].v = 0;
+ Sdev[i] = &Devtp[i];
+ }
+
+# if defined(HASBLKDEV)
+/*
+ * Read block device section header and validate it.
+ */
+ if (!fgets(buf, sizeof(buf), DCfs))
+ goto cant_read;
+ (void) crc(buf, strlen(buf), &DCcksum);
+ len = strlen("block device section: ");
+ if (strncmp(buf, "block device section: ", len) != 0) {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: bad block device section header in %s: line ",
+ Pn, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+/*
+ * Compute the block device count; allocate BSdev[] and BDevtp[] space.
+ */
+ if ((BNdev = atoi(&buf[len])) > 0) {
+ alloc_bdcache();
+ /*
+ * Read the block device lines and store their information in BDevtp[].
+ * Construct the BSdev[] pointers to BDevtp[].
+ */
+ for (i = 0; i < BNdev; i++) {
+ if (!fgets(buf, sizeof(buf), DCfs)) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't read block device %d from %s\n",
+ Pn, i + 1, DCpath[DCpathX]);
+ goto read_close;
+ }
+ (void) crc(buf, strlen(buf), &DCcksum);
+ /*
+ * Convert hexadecimal device number.
+ */
+ if (!(cp = x2dev(buf, &BDevtp[i].rdev)) || *cp != ' ') {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: block dev %d: bad device in %s: line ",
+ Pn, i + 1, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+ /*
+ * Convert inode number.
+ */
+ for (cp++, BDevtp[i].inode = (INODETYPE)0; *cp != ' '; cp++) {
+ if (*cp < '0' || *cp > '9') {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: block dev %d: bad inode # in %s: line ",
+ Pn, i + 1, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+ BDevtp[i].inode = (INODETYPE)((BDevtp[i].inode * 10)
+ + (int)(*cp - '0'));
+ }
+ /*
+ * Get path name; allocate space for it; copy it; store the
+ * pointer in BDevtp[]; clear verify status; construct the BSdev[]
+ * pointer to BDevtp[].
+ */
+ if ((len = strlen(++cp)) < 2 || *(cp + len - 1) != '\n') {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: block dev %d: bad path in %s: line",
+ Pn, i + 1, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+ *(cp + len - 1) = '\0';
+ if (!(BDevtp[i].name = mkstrcpy(cp, (MALLOC_S *)NULL))) {
+ (void) fprintf(stderr,
+ "%s: block dev %d: no space for path: line", Pn, i + 1);
+ safestrprt(buf, stderr, 1+4+8);
+ Exit(1);
+ }
+ BDevtp[i].v = 0;
+ BSdev[i] = &BDevtp[i];
+ }
+ }
+# endif /* defined(HASBLKDEV) */
+
+# if defined(DCACHE_CLONE)
+/*
+ * Read the clone section.
+ */
+ if (DCACHE_CLONE(1))
+ goto read_close;
+# endif /* defined(DCACHE_CLONE) */
+
+# if defined(DCACHE_PSEUDO)
+/*
+ * Read the pseudo section.
+ */
+ if (DCACHE_PSEUDO(1))
+ goto read_close;
+# endif /* defined(DCACHE_PSEUDO) */
+
+/*
+ * Read and check the CRC section; it must be the last thing in the file.
+ */
+ (void) snpf(cbuf, sizeof(cbuf), "CRC section: %x\n", DCcksum);
+ if (!fgets(buf, sizeof(buf), DCfs) || strcmp(buf, cbuf) != 0) {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: bad CRC section in %s: line ",
+ Pn, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+ if (fgets(buf, sizeof(buf), DCfs)) {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: WARNING: data follows CRC section in %s: line ",
+ Pn, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ goto read_close;
+ }
+/*
+ * Check one device entry at random -- the randomness based on our
+ * PID.
+ */
+ i = (int)(Mypid % Ndev);
+ if (stat(Devtp[i].name, &sb) != 0
+
+# if defined(DVCH_EXPDEV)
+ || expdev(sb.st_rdev) != Devtp[i].rdev
+# else /* !defined(DVCH_EXPDEV) */
+ || sb.st_rdev != Devtp[i].rdev
+# endif /* defined(DVCH_EXPDEV) */
+
+ || sb.st_ino != Devtp[i].inode) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: device cache mismatch: %s\n",
+ Pn, Devtp[i].name);
+ goto read_close;
+ }
+/*
+ * Close the device cache file and return OK.
+ */
+ (void) fclose(DCfs);
+ DCfd = -1;
+ DCfs = (FILE *)NULL;
+ return(0);
+}
+
+
+# if defined(DCACHE_CLONE_LOCAL)
+/*
+ * rw_clone_sect() - read/write the device cache file clone section
+ */
+
+static int
+rw_clone_sect(m)
+ int m; /* mode: 1 = read; 2 = write */
+{
+ char buf[MAXPATHLEN*2], *cp, *cp1;
+ struct clone *c;
+ struct l_dev *dp;
+ int i, j, len, n;
+
+ if (m == 1) {
+
+ /*
+ * Read the clone section header and validate it.
+ */
+ if (!fgets(buf, sizeof(buf), DCfs)) {
+
+bad_clone_sect:
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: bad clone section header in %s: line ",
+ Pn, DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ return(1);
+ }
+ (void) crc(buf, strlen(buf), &DCcksum);
+ len = strlen("clone section: ");
+ if (strncmp(buf, "clone section: ", len) != 0)
+ goto bad_clone_sect;
+ if ((n = atoi(&buf[len])) < 0)
+ goto bad_clone_sect;
+ /*
+ * Read the clone section lines and create the Clone list.
+ */
+ for (i = 0; i < n; i++) {
+ if (fgets(buf, sizeof(buf), DCfs) == NULL) {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: no %d clone line in %s: line ", Pn, i + 1,
+ DCpath[DCpathX]);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ return(1);
+ }
+ (void) crc(buf, strlen(buf), &DCcksum);
+ /*
+ * Assemble Devtp[] index and make sure it's correct.
+ */
+ for (cp = buf, j = 0; *cp != ' '; cp++) {
+ if (*cp < '0' || *cp > '9') {
+
+bad_clone_index:
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: clone %d: bad cached device index: line ",
+ Pn, i + 1);
+ safestrprt(buf, stderr, 1+4+8);
+ }
+ return(1);
+ }
+ j = (j * 10) + (int)(*cp - '0');
+ }
+ if (j < 0 || j >= Ndev || (cp1 = strchr(++cp, '\n')) == NULL)
+ goto bad_clone_index;
+ if (strncmp(cp, Devtp[j].name, (cp1 - cp)) != 0)
+ goto bad_clone_index;
+ /*
+ * Allocate and complete a clone structure.
+ */
+ if (!(c = (struct clone *)malloc(sizeof(struct clone)))) {
+ (void) fprintf(stderr,
+ "%s: clone %d: no space for cached clone: line ", Pn,
+ i + 1);
+ safestrprt(buf, stderr, 1+4+8);
+ Exit(1);
+ }
+ c->dx = j;
+ c->next = Clone;
+ Clone = c;
+ }
+ return(0);
+ } else if (m == 2) {
+
+ /*
+ * Write the clone section header.
+ */
+ for (c = Clone, n = 0; c; c = c->next, n++)
+ ;
+ (void) snpf(buf, sizeof(buf), "clone section: %d\n", n);
+ if (wr2DCfd(buf, &DCcksum))
+ return(1);
+ /*
+ * Write the clone section lines.
+ */
+ for (c = Clone; c; c = c->next) {
+ for (dp = &Devtp[c->dx], j = 0; j < Ndev; j++) {
+ if (dp == Sdev[j])
+ break;
+ }
+ if (j >= Ndev) {
+ if (!Fwarn) {
+ (void) fprintf(stderr,
+ "%s: can't make index for clone: ", Pn);
+ safestrprt(dp->name, stderr, 1);
+ }
+ (void) unlink(DCpath[DCpathX]);
+ (void) close(DCfd);
+ DCfd = -1;
+ return(1);
+ }
+ (void) snpf(buf, sizeof(buf), "%d %s\n", j, dp->name);
+ if (wr2DCfd(buf, &DCcksum))
+ return(1);
+ }
+ return(0);
+ }
+/*
+ * A shouldn't-happen case: mode neither 1 nor 2.
+ */
+ (void) fprintf(stderr, "%s: internal rw_clone_sect error: %d\n",
+ Pn, m);
+ Exit(1);
+ return(1); /* This useless return(1) keeps some
+ * compilers happy. */
+}
+# endif /* defined(DCACHE_CLONE_LOCAL) */
+
+
+/*
+ * write_dcache() - write device cache file
+ */
+
+void
+write_dcache()
+{
+ char buf[MAXPATHLEN*2], *cp;
+ struct l_dev *dp;
+ int i;
+ struct stat sb;
+/*
+ * Open the cache file; set up the CRC table; write the section count.
+ */
+ if (open_dcache(2, 0, &sb))
+ return;
+ i = 1;
+ cp = "";
+
+# if defined(HASBLKDEV)
+ i++;
+ cp = "s";
+# endif /* defined(HASBLKDEV) */
+
+# if defined(DCACHE_CLONE)
+ i++;
+ cp = "s";
+# endif /* defined(DCACHE_CLONE) */
+
+# if defined(DCACHE_PSEUDO)
+ i++;
+ cp = "s";
+# endif /* defined(DCACHE_PSEUDO) */
+
+ (void) snpf(buf, sizeof(buf), "%d section%s, dev=%lx\n", i, cp,
+ (long)DevDev);
+ (void) crcbld();
+ DCcksum = 0;
+ if (wr2DCfd(buf, &DCcksum))
+ return;
+/*
+ * Write the device section from the contents of Sdev[] and Devtp[].
+ */
+ (void) snpf(buf, sizeof(buf), "device section: %d\n", Ndev);
+ if (wr2DCfd(buf, &DCcksum))
+ return;
+ for (i = 0; i < Ndev; i++) {
+ dp = Sdev[i];
+ (void) snpf(buf, sizeof(buf), "%lx %ld %s\n", (long)dp->rdev,
+ (long)dp->inode, dp->name);
+ if (wr2DCfd(buf, &DCcksum))
+ return;
+ }
+
+# if defined(HASBLKDEV)
+/*
+ * Write the block device section from the contents of BSdev[] and BDevtp[].
+ */
+ (void) snpf(buf, sizeof(buf), "block device section: %d\n", BNdev);
+ if (wr2DCfd(buf, &DCcksum))
+ return;
+ if (BNdev) {
+ for (i = 0; i < BNdev; i++) {
+ dp = BSdev[i];
+ (void) snpf(buf, sizeof(buf), "%lx %ld %s\n", (long)dp->rdev,
+ (long)dp->inode, dp->name);
+ if (wr2DCfd(buf, &DCcksum))
+ return;
+ }
+ }
+# endif /* defined(HASBLKDEV) */
+
+# if defined(DCACHE_CLONE)
+/*
+ * Write the clone section.
+ */
+ if (DCACHE_CLONE(2))
+ return;
+# endif /* defined(DCACHE_CLONE) */
+
+# if defined(DCACHE_PSEUDO)
+/*
+ * Write the pseudo section.
+ */
+ if (DCACHE_PSEUDO(2))
+ return;
+# endif /* defined(DCACHE_PSEUDO) */
+
+/*
+ * Write the CRC section and close the file.
+ */
+ (void) snpf(buf, sizeof(buf), "CRC section: %x\n", DCcksum);
+ if (wr2DCfd(buf, (unsigned *)NULL))
+ return;
+ if (close(DCfd) != 0) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't close %s: %s\n",
+ Pn, DCpath[DCpathX], strerror(errno));
+ (void) unlink(DCpath[DCpathX]);
+ DCfd = -1;
+ }
+ DCfd = -1;
+/*
+ * If the previous reading of the previous device cache file marked it
+ * "unsafe," drop that marking and record that the device cache file was
+ * rebuilt.
+ */
+ if (DCunsafe) {
+ DCunsafe = 0;
+ DCrebuilt = 1;
+ }
+}
+
+
+/*
+ * wr2DCfd() - write to the DCfd file descriptor
+ */
+
+int
+wr2DCfd(b, c)
+ char *b; /* buffer */
+ unsigned *c; /* checksum receiver */
+{
+ int bl, bw;
+
+ bl = strlen(b);
+ if (c)
+ (void) crc(b, bl, c);
+ while (bl > 0) {
+ if ((bw = write(DCfd, b, bl)) < 0) {
+ if (!Fwarn)
+ (void) fprintf(stderr,
+ "%s: WARNING: can't write to %s: %s\n",
+ Pn, DCpath[DCpathX], strerror(errno));
+ (void) unlink(DCpath[DCpathX]);
+ (void) close(DCfd);
+ DCfd = -1;
+ return(1);
+ }
+ b += bw;
+ bl -= bw;
+ }
+ return(0);
+}
+#else /* !defined(HASDCACHE) */
+char dvch_d1[] = "d"; char *dvch_d2 = dvch_d1;
+#endif /* defined(HASDCACHE) */