diff options
Diffstat (limited to 'dialects/linux/dmnt.c')
-rw-r--r-- | dialects/linux/dmnt.c | 698 |
1 files changed, 698 insertions, 0 deletions
diff --git a/dialects/linux/dmnt.c b/dialects/linux/dmnt.c new file mode 100644 index 0000000..ab57bbb --- /dev/null +++ b/dialects/linux/dmnt.c @@ -0,0 +1,698 @@ +/* + * dmnt.c -- Linux mount support functions for /proc-based lsof + */ + + +/* + * 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n"; +static char *rcsid = "$Id: dmnt.c,v 1.19 2012/04/10 16:39:50 abe Exp $"; +#endif + + +#include "lsof.h" + + +/* + * Local definitions + */ + +#if defined(HASMNTSUP) +#define HASHMNT 128 /* mount supplement hash bucket count + * !!!MUST BE A POWER OF 2!!! */ +#endif /* defined(HASMNTSUP) */ + + +/* + * Local function prototypes + */ + +_PROTOTYPE(static char *cvtoe,(char *os)); + +#if defined(HASMNTSUP) +_PROTOTYPE(static int getmntdev,(char *dn, size_t dnl, struct stat *s, int *ss)); +_PROTOTYPE(static int hash_mnt,(char *dn)); +#endif /* defined(HASMNTSUP) */ + + +/* + * Local structure definitions. + */ + +#if defined(HASMNTSUP) +typedef struct mntsup { + char *dn; /* mounted directory name */ + size_t dnl; /* strlen(dn) */ + dev_t dev; /* device number */ + int ln; /* line on which defined */ + struct mntsup *next; /* next entry */ +} mntsup_t; +#endif /* defined(HASMNTSUP) */ + + +/* + * Local static definitions + */ + +static struct mounts *Lmi = (struct mounts *)NULL; /* local mount info */ +static int Lmist = 0; /* Lmi status */ +static mntsup_t **MSHash = (mntsup_t **)NULL; /* mount supplement + * hash buckets */ + + +/* + * cvtoe() -- convert octal-escaped characters in string + */ + +static char * +cvtoe(os) + char *os; /* original string */ +{ + int c, cl, cx, ol, ox, tx; + char *cs; + int tc; +/* + * Allocate space for a copy of the string in which octal-escaped characters + * can be replaced by the octal value -- e.g., \040 with ' '. Leave room for + * a '\0' terminator. + */ + if (!(ol = (int)strlen(os))) + return((char *)NULL); + if (!(cs = (char *)malloc(ol + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for octal-escaping.\n", + Pn, ol + 1); + Exit(1); + } +/* + * Copy the string, replacing octal-escaped characters as they are found. + */ + for (cx = ox = 0, cl = ol; ox < ol; ox++) { + if (((c = (int)os[ox]) == (int)'\\') && ((ox + 3) < ol)) { + + /* + * The beginning of an octal-escaped character has been found. + * + * Convert the octal value to a character value. + */ + for (tc = 0, tx = 1; os[ox + tx] && (tx < 4); tx++) { + if (((int)os[ox + tx] < (int)'0') + || ((int)os[ox + tx] > (int)'7')) + { + + /* + * The escape isn't followed by octets, so ignore the + * escape and just copy it. + */ + break; + } + tc <<= 3; + tc += (int)(os[ox + tx] - '0'); + } + if (tx == 4) { + + /* + * If three octets (plus the escape) were assembled, use their + * character-forming result. + * + * Otherwise copy the escape and what follows it until another + * escape is found. + */ + ox += 3; + c = (tc & 0xff); + } + } + if (cx >= cl) { + + /* + * Expand the copy string, as required. Leave room for a '\0' + * terminator. + */ + cl += 64; /* (Make an arbitrary increase.) */ + if (!(cs = (char *)realloc(cs, cl + 1))) { + (void) fprintf(stderr, + "%s: can't realloc %d bytes for octal-escaping.\n", + Pn, cl + 1); + Exit(1); + } + } + /* + * Copy the character. + */ + cs[cx++] = (char)c; + } +/* + * Terminate the copy and return its pointer. + */ + cs[cx] = '\0'; + return(cs); +} + + +#if defined(HASMNTSUP) +/* + * getmntdev() - get mount device from mount supplement + */ + +static int +getmntdev(dn, dnl, s, ss) + char *dn; /* mounted directory name */ + size_t dnl; /* strlen(dn) */ + struct stat *s; /* stat(2) buffer receptor */ + int *ss; /* stat(2) status result -- i.e., SB_* + * values */ +{ + static int err = 0; + int h; + mntsup_t *mp, *mpn; + static char *vbuf = (char *)NULL; + static size_t vsz = (size_t)0; + + if (err) + return(0); + if (!MSHash) { + + /* + * No mount supplement hash buckets have been allocated, so read the + * mount supplement file and create hash buckets for its entries. + */ + char buf[(MAXPATHLEN*2) + 1], *dp, path[(MAXPATHLEN*2) + 1]; + dev_t dev; + FILE *fs; + int ln = 0; + size_t sz; + + if ((MntSup != 2) || !MntSupP) + return(0); + if (!is_readable(MntSupP, 1)) { + + /* + * The mount supplement file isn't readable. + */ + err = 1; + return(0); + } + if (!(fs = open_proc_stream(MntSupP, "r", &vbuf, &vsz, 0))) { + + /* + * The mount supplement file can't be opened for reading. + */ + if (!Fwarn) + (void) fprintf(stderr, "%s: can't open(%s): %s\n", + Pn, MntSupP, strerror(errno)); + err = 1; + return(0); + } + buf[sizeof(buf) - 1] = '\0'; + /* + * Read the mount supplement file. + */ + while (fgets(buf, sizeof(buf) - 1, fs)) { + ln++; + if ((dp = strchr(buf, '\n'))) + *dp = '\0'; + if (buf[0] != '/') { + + /* + * The mount supplement line doesn't begin with the absolute + * path character '/'. + */ + if (!Fwarn) + (void) fprintf(stderr, + "%s: %s line %d: no path: \"%s\"\n", + Pn, MntSupP, ln, buf); + err = 1; + continue; + } + if (!(dp = strchr(buf, ' ')) || strncmp(dp + 1, "0x", 2)) { + + /* + * The path on the mount supplement line isn't followed by + * " 0x". + */ + if (!Fwarn) + (void) fprintf(stderr, + "%s: %s line %d: no device: \"%s\"\n", + Pn, MntSupP, ln, buf); + err = 1; + continue; + } + sz = (size_t)(dp - buf); + (void) strncpy(path, buf, sz); + path[sz] = '\0'; + /* + * Assemble the hexadecimal device number of the mount supplement + * line. + */ + for (dev = 0, dp += 3; *dp; dp++) { + if (!isxdigit((int)*dp)) + break; + if (isdigit((int)*dp)) + dev = (dev << 4) + (int)*dp - (int)'0'; + else + dev = (dev << 4) + (int)tolower(*dp) - (int)'a' + 10; + } + if (*dp) { + + /* + * The device number couldn't be assembled. + */ + if (!Fwarn) + (void) fprintf(stderr, + "%s: %s line %d: illegal device: \"%s\"\n", + Pn, MntSupP, ln, buf); + err = 1; + continue; + } + /* + * Search the mount supplement hash buckets. (Allocate them as + * required.) + */ + if (!MSHash) { + if (!(MSHash = (mntsup_t **)calloc(HASHMNT, + sizeof(mntsup_t *))) + ) { + (void) fprintf(stderr, + "%s: no space for mount supplement hash buckets\n", + Pn); + Exit(1); + } + } + h = hash_mnt(path); + for (mp = MSHash[h]; mp; mp = mp->next) { + if ((mp->dnl == dnl) && !strcmp(mp->dn, path)) + break; + } + if (mp) { + + /* + * A path match was located. If the device number is the + * same, skip this mount supplement line. Otherwise, issue + * a warning. + */ + if (mp->dev != dev) { + (void) fprintf(stderr, + "%s: %s line %d path duplicate of %d: \"%s\"\n", + Pn, MntSupP, ln, mp->ln, buf); + err = 1; + } + continue; + } + /* + * Allocate and fill a new mount supplement hash entry. + */ + if (!(mpn = (mntsup_t *)malloc(sizeof(mntsup_t)))) { + (void) fprintf(stderr, + "%s: no space for mount supplement entry: %d \"%s\"\n", + Pn, ln, buf); + Exit(1); + } + if (!(mpn->dn = (char *)malloc(sz + 1))) { + (void) fprintf(stderr, + "%s: no space for mount supplement path: %d \"%s\"\n", + Pn, ln, buf); + Exit(1); + } + (void) strcpy(mpn->dn, path); + mpn->dnl = sz; + mpn->dev = dev; + mpn->ln = ln; + mpn->next = MSHash[h]; + MSHash[h] = mpn; + } + if (ferror(fs)) { + if (!Fwarn) + (void) fprintf(stderr, "%s: error reading %s\n", + Pn, MntSupP); + err = 1; + } + (void) fclose(fs); + if (err) { + if (MSHash) { + for (h = 0; h < HASHMNT; h++) { + for (mp = MSHash[h]; mp; mp = mpn) { + mpn = mp->next; + if (mp->dn) + (void) free((MALLOC_P *)mp->dn); + (void) free((MALLOC_P *)mp); + } + } + (void) free((MALLOC_P *)MSHash); + MSHash = (mntsup_t **)NULL; + } + return(0); + } + } +/* + * If no errors have been detected reading the mount supplement file, search + * its hash buckets for the supplied directory path. + */ + if (err) + return(0); + h = hash_mnt(dn); + for (mp = MSHash[h]; mp; mp = mp->next) { + if ((dnl == mp->dnl) && !strcmp(dn, mp->dn)) { + memset((void *)s, 0, sizeof(struct stat)); + s->st_dev = mp->dev; + *ss |= SB_DEV; + return(1); + } + } + return(0); +} + + +/* + * hash_mnt() - hash mount point + */ + +static int +hash_mnt(dn) + char *dn; /* mount point directory name */ +{ + register int i, h; + size_t l; + + if (!(l = strlen(dn))) + return(0); + if (l == 1) + return((int)*dn & (HASHMNT - 1)); + for (i = h = 0; i < (int)(l - 1); i++) { + h ^= ((int)dn[i] * (int)dn[i+1]) << ((i*3)%13); + } + return(h & (HASHMNT - 1)); +} +#endif /* defined(HASMNTSUP) */ + + +/* + * readmnt() - read mount table + */ + +struct mounts * +readmnt() +{ + char buf[MAXPATHLEN], *cp, **fp; + char *dn = (char *)NULL; + size_t dnl; + int ds, ne; + char *fp0 = (char *)NULL; + char *fp1 = (char *)NULL; + int fr, ignrdl, ignstat; + char *ln; + struct mounts *mp; + FILE *ms; + int nfs; + struct stat sb; + static char *vbuf = (char *)NULL; + static size_t vsz = (size_t)0; + + if (Lmi || Lmist) + return(Lmi); +/* + * Open access to /proc/mounts, assigning a page size buffer to its stream. + */ + (void) snpf(buf, sizeof(buf), "%s/mounts", PROCFS); + ms = open_proc_stream(buf, "r", &vbuf, &vsz, 1); +/* + * Read mount table entries. + */ + while (fgets(buf, sizeof(buf), ms)) { + if (get_fields(buf, (char *)NULL, &fp, (int *)NULL, 0) < 3 + || !fp[0] || !fp[1] || !fp[2]) + continue; + /* + * Convert octal-escaped characters in the device name and mounted-on + * path name. + */ + if (fp0) { + (void) free((FREE_P *)fp0); + fp0 = (char *)NULL; + } + if (fp1) { + (void) free((FREE_P *)fp1); + fp1 = (char *)NULL; + } + if (!(fp0 = cvtoe(fp[0])) || !(fp1 = cvtoe(fp[1]))) + continue; + /* + * Locate any colon (':') in the device name. + * + * If the colon is followed by * "(pid*" -- it's probably an + * automounter entry. + * + * Ignore autofs, pipefs, and sockfs entries. + */ + cp = strchr(fp0, ':'); + if (cp && !strncasecmp(++cp, "(pid", 4)) + continue; + if (!strcasecmp(fp[2], "autofs") || !strcasecmp(fp[2], "pipefs") + || !strcasecmp(fp[2], "sockfs")) + continue; + /* + * Interpolate a possible symbolic mounted directory link. + */ + if (dn) + (void) free((FREE_P *)dn); + dn = fp1; + fp1 = (char *)NULL; + +#if defined(HASEOPT) + if (Efsysl) { + + /* + * If there is an -e file system list, check it to decide if a stat() + * and Readlink() on this one should be performed. + */ + efsys_list_t *ep; + + for (ignrdl = ignstat = 0, ep = Efsysl; ep; ep = ep->next) { + if (!strcmp(dn, ep->path)) { + ignrdl = ep->rdlnk; + ignstat = 1; + break; + } + } + } else + +#endif /* defined(HASEOPT */ + + ignrdl = ignstat = 0; + + /* + * Avoid Readlink() when requested. + */ + if (!ignrdl) { + if (!(ln = Readlink(dn))) { + if (!Fwarn) { + (void) fprintf(stderr, + " Output information may be incomplete.\n"); + } + continue; + } + if (ln != dn) { + (void) free((FREE_P *)dn); + dn = ln; + } + } + if (*dn != '/') + continue; + dnl = strlen(dn); + /* + * Test for duplicate and NFS directories. + */ + for (mp = Lmi; mp; mp = mp->next) { + if ((dnl == mp->dirl) && !strcmp(dn, mp->dir)) + break; + } + if ((nfs = strcasecmp(fp[2], "nfs"))) { + if ((nfs = strcasecmp(fp[2], "nfs3"))) + nfs = strcasecmp(fp[2], "nfs4"); + } + if (mp) { + + /* + * If this duplicate directory is not root, ignore it. If the + * already remembered entry is NFS-mounted, ignore this one. If + * this one is NFS-mounted, ignore the already remembered entry. + */ + if (strcmp(dn, "/")) + continue; + if (mp->ty == N_NFS) + continue; + if (nfs) + continue; + } + /* + * Stat() the directory. + */ + if (ignstat) + fr = 1; + else { + if ((fr = statsafely(dn, &sb))) { + if (!Fwarn) { + (void) fprintf(stderr, "%s: WARNING: can't stat() ", + Pn); + safestrprt(fp[2], stderr, 0); + (void) fprintf(stderr, " file system "); + safestrprt(dn, stderr, 1); + (void) fprintf(stderr, + " Output information may be incomplete.\n"); + } + } else + ds = SB_ALL; + } + +#if defined(HASMNTSUP) + if (fr) { + + /* + * If the stat() failed or wasn't called, check the mount + * supplement table, if possible. + */ + if ((MntSup == 2) && MntSupP) { + ds = 0; + if (getmntdev(dn, dnl, &sb, &ds) || !(ds & SB_DEV)) { + (void) fprintf(stderr, + "%s: assuming dev=%#lx for %s from %s\n", + Pn, (long)sb.st_dev, dn, MntSupP); + } + } else { + if (!ignstat) + continue; + ds = 0; /* No stat() was allowed. */ + } + } +#else /* !defined(HASMNTSUP) */ + if (fr) { + if (!ignstat) + continue; + ds = 0; /* No stat() was allowed. */ + } +#endif /* defined(HASMNTSUP) */ + + /* + * Fill a local mount structure or reuse a previous entry when + * indicated. + */ + if (mp) { + ne = 0; + if (mp->dir) { + (void) free((FREE_P *)mp->dir); + mp->dir = (char *)NULL; + } + if (mp->fsname) { + (void) free((FREE_P *)mp->fsname); + mp->fsname = (char *)NULL; + } + } else { + ne = 1; + if (!(mp = (struct mounts *)malloc(sizeof(struct mounts)))) { + (void) fprintf(stderr, + "%s: can't allocate mounts struct for: ", Pn); + safestrprt(dn, stderr, 1); + Exit(1); + } + } + mp->dir = dn; + dn = (char *)NULL; + mp->dirl = dnl; + if (ne) + mp->next = Lmi; + mp->dev = ((mp->ds = ds) & SB_DEV) ? sb.st_dev : 0; + mp->rdev = (ds & SB_RDEV) ? sb.st_rdev : 0; + mp->inode = (INODETYPE)((ds & SB_INO) ? sb.st_ino : 0); + mp->mode = (ds & SB_MODE) ? sb.st_mode : 0; + if (!nfs) { + mp->ty = N_NFS; + if (HasNFS < 2) + HasNFS = 2; + } else + mp->ty = N_REGLR; + +#if defined(HASMNTSUP) + /* + * If support for the mount supplement file is defined and if the + * +m option was supplied, print mount supplement information. + */ + if (MntSup == 1) { + if (mp->dev) + (void) printf("%s %#lx\n", mp->dir, (long)mp->dev); + else + (void) printf("%s 0x0\n", mp->dir); + } +#endif /* defined(HASMNTSUP) */ + + /* + * Save mounted-on device or directory name. + */ + dn = fp0; + fp0 = (char *)NULL; + mp->fsname = dn; + /* + * Interpolate a possible file system (mounted-on) device name or + * directory name link. + * + * Avoid Readlink() when requested. + */ + if (ignrdl || (*dn != '/')) { + if (!(ln = mkstrcpy(dn, (MALLOC_S *)NULL))) { + (void) fprintf(stderr, + "%s: can't allocate space for: ", Pn); + safestrprt(dn, stderr, 1); + Exit(1); + } + ignstat = 1; + } else + ln = Readlink(dn); + dn = (char *)NULL; + /* + * Stat() the file system (mounted-on) name and add file system + * information to the local mount table entry. + */ + if (ignstat || !ln || statsafely(ln, &sb)) + sb.st_mode = 0; + mp->fsnmres = ln; + mp->fs_mode = sb.st_mode; + if (ne) + Lmi = mp; + } +/* + * Clean up and return the local mount info table address. + */ + (void) fclose(ms); + if (dn) + (void) free((FREE_P *)dn); + if (fp0) + (void) free((FREE_P *)fp0); + if (fp1) + (void) free((FREE_P *)fp1); + Lmist = 1; + return(Lmi); +} |