2 * dmnt.c -- Linux mount support functions for /proc-based lsof
7 * Copyright 1997 Purdue Research Foundation, West Lafayette, Indiana
8 * 47907. All rights reserved.
10 * Written by Victor A. Abell
12 * This software is not subject to any license of the American Telephone
13 * and Telegraph Company or the Regents of the University of California.
15 * Permission is granted to anyone to use this software for any purpose on
16 * any computer system, and to alter it and redistribute it freely, subject
17 * to the following restrictions:
19 * 1. Neither the authors nor Purdue University are responsible for any
20 * consequences of the use of this software.
22 * 2. The origin of this software must not be misrepresented, either by
23 * explicit claim or by omission. Credit to the authors and Purdue
24 * University must appear in documentation and sources.
26 * 3. Altered versions must be plainly marked as such, and must not be
27 * misrepresented as being the original software.
29 * 4. This notice may not be removed or altered.
33 static char copyright[] =
34 "@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n";
35 static char *rcsid = "$Id: dmnt.c,v 1.19 2012/04/10 16:39:50 abe Exp $";
46 #if defined(HASMNTSUP)
47 #define HASHMNT 128 /* mount supplement hash bucket count
48 * !!!MUST BE A POWER OF 2!!! */
49 #endif /* defined(HASMNTSUP) */
53 * Local function prototypes
56 _PROTOTYPE(static char *cvtoe,(char *os));
58 #if defined(HASMNTSUP)
59 _PROTOTYPE(static int getmntdev,(char *dn, size_t dnl, struct stat *s, int *ss));
60 _PROTOTYPE(static int hash_mnt,(char *dn));
61 #endif /* defined(HASMNTSUP) */
65 * Local structure definitions.
68 #if defined(HASMNTSUP)
69 typedef struct mntsup {
70 char *dn; /* mounted directory name */
71 size_t dnl; /* strlen(dn) */
72 dev_t dev; /* device number */
73 int ln; /* line on which defined */
74 struct mntsup *next; /* next entry */
76 #endif /* defined(HASMNTSUP) */
80 * Local static definitions
83 static struct mounts *Lmi = (struct mounts *)NULL; /* local mount info */
84 static int Lmist = 0; /* Lmi status */
85 static mntsup_t **MSHash = (mntsup_t **)NULL; /* mount supplement
90 * cvtoe() -- convert octal-escaped characters in string
95 char *os; /* original string */
97 int c, cl, cx, ol, ox, tx;
101 * Allocate space for a copy of the string in which octal-escaped characters
102 * can be replaced by the octal value -- e.g., \040 with ' '. Leave room for
105 if (!(ol = (int)strlen(os)))
106 return((char *)NULL);
107 if (!(cs = (char *)malloc(ol + 1))) {
108 (void) fprintf(stderr,
109 "%s: can't allocate %d bytes for octal-escaping.\n",
114 * Copy the string, replacing octal-escaped characters as they are found.
116 for (cx = ox = 0, cl = ol; ox < ol; ox++) {
117 if (((c = (int)os[ox]) == (int)'\\') && ((ox + 3) < ol)) {
120 * The beginning of an octal-escaped character has been found.
122 * Convert the octal value to a character value.
124 for (tc = 0, tx = 1; os[ox + tx] && (tx < 4); tx++) {
125 if (((int)os[ox + tx] < (int)'0')
126 || ((int)os[ox + tx] > (int)'7'))
130 * The escape isn't followed by octets, so ignore the
131 * escape and just copy it.
136 tc += (int)(os[ox + tx] - '0');
141 * If three octets (plus the escape) were assembled, use their
142 * character-forming result.
144 * Otherwise copy the escape and what follows it until another
154 * Expand the copy string, as required. Leave room for a '\0'
157 cl += 64; /* (Make an arbitrary increase.) */
158 if (!(cs = (char *)realloc(cs, cl + 1))) {
159 (void) fprintf(stderr,
160 "%s: can't realloc %d bytes for octal-escaping.\n",
166 * Copy the character.
171 * Terminate the copy and return its pointer.
178 #if defined(HASMNTSUP)
180 * getmntdev() - get mount device from mount supplement
184 getmntdev(dn, dnl, s, ss)
185 char *dn; /* mounted directory name */
186 size_t dnl; /* strlen(dn) */
187 struct stat *s; /* stat(2) buffer receptor */
188 int *ss; /* stat(2) status result -- i.e., SB_*
194 static char *vbuf = (char *)NULL;
195 static size_t vsz = (size_t)0;
202 * No mount supplement hash buckets have been allocated, so read the
203 * mount supplement file and create hash buckets for its entries.
205 char buf[(MAXPATHLEN*2) + 1], *dp, path[(MAXPATHLEN*2) + 1];
211 if ((MntSup != 2) || !MntSupP)
213 if (!is_readable(MntSupP, 1)) {
216 * The mount supplement file isn't readable.
221 if (!(fs = open_proc_stream(MntSupP, "r", &vbuf, &vsz, 0))) {
224 * The mount supplement file can't be opened for reading.
227 (void) fprintf(stderr, "%s: can't open(%s): %s\n",
228 Pn, MntSupP, strerror(errno));
232 buf[sizeof(buf) - 1] = '\0';
234 * Read the mount supplement file.
236 while (fgets(buf, sizeof(buf) - 1, fs)) {
238 if ((dp = strchr(buf, '\n')))
243 * The mount supplement line doesn't begin with the absolute
244 * path character '/'.
247 (void) fprintf(stderr,
248 "%s: %s line %d: no path: \"%s\"\n",
249 Pn, MntSupP, ln, buf);
253 if (!(dp = strchr(buf, ' ')) || strncmp(dp + 1, "0x", 2)) {
256 * The path on the mount supplement line isn't followed by
260 (void) fprintf(stderr,
261 "%s: %s line %d: no device: \"%s\"\n",
262 Pn, MntSupP, ln, buf);
266 sz = (size_t)(dp - buf);
267 (void) strncpy(path, buf, sz);
270 * Assemble the hexadecimal device number of the mount supplement
273 for (dev = 0, dp += 3; *dp; dp++) {
274 if (!isxdigit((int)*dp))
276 if (isdigit((int)*dp))
277 dev = (dev << 4) + (int)*dp - (int)'0';
279 dev = (dev << 4) + (int)tolower(*dp) - (int)'a' + 10;
284 * The device number couldn't be assembled.
287 (void) fprintf(stderr,
288 "%s: %s line %d: illegal device: \"%s\"\n",
289 Pn, MntSupP, ln, buf);
294 * Search the mount supplement hash buckets. (Allocate them as
298 if (!(MSHash = (mntsup_t **)calloc(HASHMNT,
301 (void) fprintf(stderr,
302 "%s: no space for mount supplement hash buckets\n",
308 for (mp = MSHash[h]; mp; mp = mp->next) {
309 if ((mp->dnl == dnl) && !strcmp(mp->dn, path))
315 * A path match was located. If the device number is the
316 * same, skip this mount supplement line. Otherwise, issue
319 if (mp->dev != dev) {
320 (void) fprintf(stderr,
321 "%s: %s line %d path duplicate of %d: \"%s\"\n",
322 Pn, MntSupP, ln, mp->ln, buf);
328 * Allocate and fill a new mount supplement hash entry.
330 if (!(mpn = (mntsup_t *)malloc(sizeof(mntsup_t)))) {
331 (void) fprintf(stderr,
332 "%s: no space for mount supplement entry: %d \"%s\"\n",
336 if (!(mpn->dn = (char *)malloc(sz + 1))) {
337 (void) fprintf(stderr,
338 "%s: no space for mount supplement path: %d \"%s\"\n",
342 (void) strcpy(mpn->dn, path);
346 mpn->next = MSHash[h];
351 (void) fprintf(stderr, "%s: error reading %s\n",
358 for (h = 0; h < HASHMNT; h++) {
359 for (mp = MSHash[h]; mp; mp = mpn) {
362 (void) free((MALLOC_P *)mp->dn);
363 (void) free((MALLOC_P *)mp);
366 (void) free((MALLOC_P *)MSHash);
367 MSHash = (mntsup_t **)NULL;
373 * If no errors have been detected reading the mount supplement file, search
374 * its hash buckets for the supplied directory path.
379 for (mp = MSHash[h]; mp; mp = mp->next) {
380 if ((dnl == mp->dnl) && !strcmp(dn, mp->dn)) {
381 memset((void *)s, 0, sizeof(struct stat));
392 * hash_mnt() - hash mount point
397 char *dn; /* mount point directory name */
402 if (!(l = strlen(dn)))
405 return((int)*dn & (HASHMNT - 1));
406 for (i = h = 0; i < (int)(l - 1); i++) {
407 h ^= ((int)dn[i] * (int)dn[i+1]) << ((i*3)%13);
409 return(h & (HASHMNT - 1));
411 #endif /* defined(HASMNTSUP) */
415 * readmnt() - read mount table
421 char buf[MAXPATHLEN], *cp, **fp;
422 char *dn = (char *)NULL;
425 char *fp0 = (char *)NULL;
426 char *fp1 = (char *)NULL;
427 int fr, ignrdl, ignstat;
433 static char *vbuf = (char *)NULL;
434 static size_t vsz = (size_t)0;
439 * Open access to /proc/mounts, assigning a page size buffer to its stream.
441 (void) snpf(buf, sizeof(buf), "%s/mounts", PROCFS);
442 ms = open_proc_stream(buf, "r", &vbuf, &vsz, 1);
444 * Read mount table entries.
446 while (fgets(buf, sizeof(buf), ms)) {
447 if (get_fields(buf, (char *)NULL, &fp, (int *)NULL, 0) < 3
448 || !fp[0] || !fp[1] || !fp[2])
451 * Convert octal-escaped characters in the device name and mounted-on
455 (void) free((FREE_P *)fp0);
459 (void) free((FREE_P *)fp1);
462 if (!(fp0 = cvtoe(fp[0])) || !(fp1 = cvtoe(fp[1])))
465 * Locate any colon (':') in the device name.
467 * If the colon is followed by * "(pid*" -- it's probably an
470 * Ignore autofs, pipefs, and sockfs entries.
472 cp = strchr(fp0, ':');
473 if (cp && !strncasecmp(++cp, "(pid", 4))
475 if (!strcasecmp(fp[2], "autofs") || !strcasecmp(fp[2], "pipefs")
476 || !strcasecmp(fp[2], "sockfs"))
479 * Interpolate a possible symbolic mounted directory link.
482 (void) free((FREE_P *)dn);
490 * If there is an -e file system list, check it to decide if a stat()
491 * and Readlink() on this one should be performed.
495 for (ignrdl = ignstat = 0, ep = Efsysl; ep; ep = ep->next) {
496 if (!strcmp(dn, ep->path)) {
504 #endif /* defined(HASEOPT */
506 ignrdl = ignstat = 0;
509 * Avoid Readlink() when requested.
512 if (!(ln = Readlink(dn))) {
514 (void) fprintf(stderr,
515 " Output information may be incomplete.\n");
520 (void) free((FREE_P *)dn);
528 * Test for duplicate and NFS directories.
530 for (mp = Lmi; mp; mp = mp->next) {
531 if ((dnl == mp->dirl) && !strcmp(dn, mp->dir))
534 if ((nfs = strcasecmp(fp[2], "nfs"))) {
535 if ((nfs = strcasecmp(fp[2], "nfs3")))
536 nfs = strcasecmp(fp[2], "nfs4");
541 * If this duplicate directory is not root, ignore it. If the
542 * already remembered entry is NFS-mounted, ignore this one. If
543 * this one is NFS-mounted, ignore the already remembered entry.
553 * Stat() the directory.
558 if ((fr = statsafely(dn, &sb))) {
560 (void) fprintf(stderr, "%s: WARNING: can't stat() ",
562 safestrprt(fp[2], stderr, 0);
563 (void) fprintf(stderr, " file system ");
564 safestrprt(dn, stderr, 1);
565 (void) fprintf(stderr,
566 " Output information may be incomplete.\n");
572 #if defined(HASMNTSUP)
576 * If the stat() failed or wasn't called, check the mount
577 * supplement table, if possible.
579 if ((MntSup == 2) && MntSupP) {
581 if (getmntdev(dn, dnl, &sb, &ds) || !(ds & SB_DEV)) {
582 (void) fprintf(stderr,
583 "%s: assuming dev=%#lx for %s from %s\n",
584 Pn, (long)sb.st_dev, dn, MntSupP);
589 ds = 0; /* No stat() was allowed. */
592 #else /* !defined(HASMNTSUP) */
596 ds = 0; /* No stat() was allowed. */
598 #endif /* defined(HASMNTSUP) */
601 * Fill a local mount structure or reuse a previous entry when
607 (void) free((FREE_P *)mp->dir);
608 mp->dir = (char *)NULL;
611 (void) free((FREE_P *)mp->fsname);
612 mp->fsname = (char *)NULL;
616 if (!(mp = (struct mounts *)malloc(sizeof(struct mounts)))) {
617 (void) fprintf(stderr,
618 "%s: can't allocate mounts struct for: ", Pn);
619 safestrprt(dn, stderr, 1);
628 mp->dev = ((mp->ds = ds) & SB_DEV) ? sb.st_dev : 0;
629 mp->rdev = (ds & SB_RDEV) ? sb.st_rdev : 0;
630 mp->inode = (INODETYPE)((ds & SB_INO) ? sb.st_ino : 0);
631 mp->mode = (ds & SB_MODE) ? sb.st_mode : 0;
639 #if defined(HASMNTSUP)
641 * If support for the mount supplement file is defined and if the
642 * +m option was supplied, print mount supplement information.
646 (void) printf("%s %#lx\n", mp->dir, (long)mp->dev);
648 (void) printf("%s 0x0\n", mp->dir);
650 #endif /* defined(HASMNTSUP) */
653 * Save mounted-on device or directory name.
659 * Interpolate a possible file system (mounted-on) device name or
660 * directory name link.
662 * Avoid Readlink() when requested.
664 if (ignrdl || (*dn != '/')) {
665 if (!(ln = mkstrcpy(dn, (MALLOC_S *)NULL))) {
666 (void) fprintf(stderr,
667 "%s: can't allocate space for: ", Pn);
668 safestrprt(dn, stderr, 1);
676 * Stat() the file system (mounted-on) name and add file system
677 * information to the local mount table entry.
679 if (ignstat || !ln || statsafely(ln, &sb))
682 mp->fs_mode = sb.st_mode;
687 * Clean up and return the local mount info table address.
691 (void) free((FREE_P *)dn);
693 (void) free((FREE_P *)fp0);
695 (void) free((FREE_P *)fp1);