/* * 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) */