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