diff options
Diffstat (limited to 'lib/dvch.c')
-rw-r--r-- | lib/dvch.c | 1413 |
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) */ |