Imported Upstream version 4.87
[platform/upstream/lsof.git] / dialects / linux / dmnt.c
1 /*
2  * dmnt.c -- Linux mount support functions for /proc-based lsof
3  */
4
5
6 /*
7  * Copyright 1997 Purdue Research Foundation, West Lafayette, Indiana
8  * 47907.  All rights reserved.
9  *
10  * Written by Victor A. Abell
11  *
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.
14  *
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:
18  *
19  * 1. Neither the authors nor Purdue University are responsible for any
20  *    consequences of the use of this software.
21  *
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.
25  *
26  * 3. Altered versions must be plainly marked as such, and must not be
27  *    misrepresented as being the original software.
28  *
29  * 4. This notice may not be removed or altered.
30  */
31
32 #ifndef lint
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 $";
36 #endif
37
38
39 #include "lsof.h"
40
41
42 /*
43  * Local definitions
44  */
45
46 #if     defined(HASMNTSUP)
47 #define HASHMNT 128                     /* mount supplement hash bucket count
48                                          * !!!MUST BE A POWER OF 2!!! */
49 #endif  /* defined(HASMNTSUP) */
50
51
52 /*
53  * Local function prototypes
54  */
55
56 _PROTOTYPE(static char *cvtoe,(char *os));
57
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) */
62
63
64 /*
65  * Local structure definitions.
66  */
67
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 */
75 } mntsup_t;
76 #endif  /* defined(HASMNTSUP) */
77
78
79 /*
80  * Local static definitions
81  */
82
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
86                                                          * hash buckets */
87
88
89 /*
90  * cvtoe() -- convert octal-escaped characters in string
91  */
92
93 static char *
94 cvtoe(os)
95         char *os;                       /* original string */
96 {
97         int c, cl, cx, ol, ox, tx;
98         char *cs;
99         int tc;
100 /*
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
103  * a '\0' terminator.
104  */
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",
110                 Pn, ol + 1);
111             Exit(1);
112         }
113 /*
114  * Copy the string, replacing octal-escaped characters as they are found.
115  */
116         for (cx = ox = 0, cl = ol; ox < ol; ox++) {
117             if (((c = (int)os[ox]) == (int)'\\') && ((ox + 3) < ol)) {
118
119             /*
120              * The beginning of an octal-escaped character has been found.
121              *
122              * Convert the octal value to a character value.
123              */
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'))
127                     {
128
129                     /*
130                      * The escape isn't followed by octets, so ignore the
131                      * escape and just copy it.
132                      */
133                         break;
134                     }
135                     tc <<= 3;
136                     tc += (int)(os[ox + tx] - '0');
137                 }
138                 if (tx == 4) {
139
140                 /*
141                  * If three octets (plus the escape) were assembled, use their
142                  * character-forming result.
143                  *
144                  * Otherwise copy the escape and what follows it until another
145                  * escape is found.
146                  */
147                     ox += 3;
148                     c = (tc & 0xff);
149                 }
150             }
151             if (cx >= cl) {
152
153             /*
154              * Expand the copy string, as required.  Leave room for a '\0'
155              * terminator.
156              */
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",
161                         Pn, cl + 1);
162                     Exit(1);
163                 }
164             }
165         /*
166          * Copy the character.
167          */
168             cs[cx++] = (char)c;
169         }
170 /*
171  * Terminate the copy and return its pointer.
172  */
173         cs[cx] = '\0';
174         return(cs);
175 }
176
177
178 #if     defined(HASMNTSUP)
179 /*
180  * getmntdev() - get mount device from mount supplement
181  */
182
183 static int
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_*
189                                          * values */
190 {
191         static int err = 0;
192         int h;
193         mntsup_t *mp, *mpn;
194         static char *vbuf = (char *)NULL;
195         static size_t vsz = (size_t)0;
196
197         if (err)
198             return(0);
199         if (!MSHash) {
200
201         /*
202          * No mount supplement hash buckets have been allocated, so read the
203          * mount supplement file and create hash buckets for its entries.
204          */
205             char buf[(MAXPATHLEN*2) + 1], *dp, path[(MAXPATHLEN*2) + 1];
206             dev_t dev;
207             FILE *fs;
208             int ln = 0;
209             size_t sz;
210
211             if ((MntSup != 2) || !MntSupP)
212                 return(0);
213             if (!is_readable(MntSupP, 1)) {
214
215             /*
216              * The mount supplement file isn't readable.
217              */
218                 err = 1;
219                 return(0);
220             }
221             if (!(fs = open_proc_stream(MntSupP, "r", &vbuf, &vsz, 0))) {
222
223             /*
224              * The mount supplement file can't be opened for reading.
225              */
226                 if (!Fwarn)
227                     (void) fprintf(stderr, "%s: can't open(%s): %s\n",
228                         Pn, MntSupP, strerror(errno));
229                 err = 1;
230                 return(0);
231             }
232             buf[sizeof(buf) - 1] = '\0';
233         /*
234          * Read the mount supplement file.
235          */
236             while (fgets(buf, sizeof(buf) - 1, fs)) {
237                 ln++;
238                 if ((dp = strchr(buf, '\n')))
239                     *dp = '\0';
240                 if (buf[0] != '/') {
241
242                 /*
243                  * The mount supplement line doesn't begin with the absolute
244                  * path character '/'.
245                  */
246                     if (!Fwarn)
247                         (void) fprintf(stderr,
248                             "%s: %s line %d: no path: \"%s\"\n",
249                             Pn, MntSupP, ln, buf);
250                     err = 1;
251                     continue;
252                 }
253                 if (!(dp = strchr(buf, ' ')) || strncmp(dp + 1, "0x", 2)) {
254
255                 /*
256                  * The path on the mount supplement line isn't followed by
257                  * " 0x".
258                  */
259                     if (!Fwarn)
260                         (void) fprintf(stderr,
261                             "%s: %s line %d: no device: \"%s\"\n",
262                             Pn, MntSupP, ln, buf);
263                     err = 1;
264                     continue;
265                 }
266                 sz = (size_t)(dp - buf);
267                 (void) strncpy(path, buf, sz);
268                 path[sz] = '\0';
269             /*
270              * Assemble the hexadecimal device number of the mount supplement
271              * line.
272              */
273                 for (dev = 0, dp += 3; *dp; dp++) {
274                     if (!isxdigit((int)*dp))
275                         break;
276                     if (isdigit((int)*dp))
277                         dev = (dev << 4) + (int)*dp - (int)'0';
278                     else
279                         dev = (dev << 4) + (int)tolower(*dp) - (int)'a' + 10;
280                 }
281                 if (*dp) {
282
283                 /*
284                  * The device number couldn't be assembled.
285                  */
286                     if (!Fwarn)
287                         (void) fprintf(stderr,
288                             "%s: %s line %d: illegal device: \"%s\"\n",
289                             Pn, MntSupP, ln, buf);
290                     err = 1;
291                     continue;
292                 }
293             /*
294              * Search the mount supplement hash buckets.  (Allocate them as
295              * required.)
296              */
297                 if (!MSHash) {
298                     if (!(MSHash = (mntsup_t **)calloc(HASHMNT,
299                                                        sizeof(mntsup_t *)))
300                     ) {
301                         (void) fprintf(stderr,
302                             "%s: no space for mount supplement hash buckets\n",
303                             Pn);
304                         Exit(1);
305                     }
306                 }
307                 h = hash_mnt(path);
308                 for (mp = MSHash[h]; mp; mp = mp->next) {
309                     if ((mp->dnl == dnl) && !strcmp(mp->dn, path))
310                         break;
311                 }
312                 if (mp) {
313
314                 /*
315                  * A path match was located.  If the device number is the
316                  * same, skip this mount supplement line.  Otherwise, issue
317                  * a warning.
318                  */
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);
323                         err = 1;
324                     }
325                     continue;
326                 }
327             /*
328              * Allocate and fill a new mount supplement hash entry.
329              */
330                 if (!(mpn = (mntsup_t *)malloc(sizeof(mntsup_t)))) {
331                     (void) fprintf(stderr,
332                         "%s: no space for mount supplement entry: %d \"%s\"\n",
333                         Pn, ln, buf);
334                     Exit(1);
335                 }
336                 if (!(mpn->dn = (char *)malloc(sz + 1))) {
337                     (void) fprintf(stderr,
338                         "%s: no space for mount supplement path: %d \"%s\"\n",
339                         Pn, ln, buf);
340                     Exit(1);
341                 }
342                 (void) strcpy(mpn->dn, path);
343                 mpn->dnl = sz;
344                 mpn->dev = dev;
345                 mpn->ln = ln;
346                 mpn->next = MSHash[h];
347                 MSHash[h] = mpn;
348             }
349             if (ferror(fs)) {
350                 if (!Fwarn)
351                     (void) fprintf(stderr, "%s: error reading %s\n",
352                         Pn, MntSupP);
353                 err = 1;
354             }
355             (void) fclose(fs);
356             if (err) {
357                 if (MSHash) {
358                     for (h = 0; h < HASHMNT; h++) {
359                         for (mp = MSHash[h]; mp; mp = mpn) {
360                             mpn = mp->next;
361                             if (mp->dn)
362                                 (void) free((MALLOC_P *)mp->dn);
363                             (void) free((MALLOC_P *)mp);
364                         }
365                     }
366                     (void) free((MALLOC_P *)MSHash);
367                     MSHash = (mntsup_t **)NULL;
368                 }
369                 return(0);
370             }
371         }
372 /*
373  * If no errors have been detected reading the mount supplement file, search
374  * its hash buckets for the supplied directory path.
375  */
376         if (err)
377             return(0);
378         h = hash_mnt(dn);
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));
382                 s->st_dev = mp->dev;
383                 *ss |= SB_DEV;
384                 return(1);
385             }
386         }
387         return(0);
388 }
389
390
391 /*
392  * hash_mnt() - hash mount point
393  */
394
395 static int
396 hash_mnt(dn)
397         char *dn;                       /* mount point directory name */
398 {
399         register int i, h;
400         size_t l;
401
402         if (!(l = strlen(dn)))
403             return(0);
404         if (l == 1)
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);
408         }
409         return(h & (HASHMNT - 1));
410 }
411 #endif  /* defined(HASMNTSUP) */
412
413
414 /*
415  * readmnt() - read mount table
416  */
417
418 struct mounts *
419 readmnt()
420 {
421         char buf[MAXPATHLEN], *cp, **fp;
422         char *dn = (char *)NULL;
423         size_t dnl;
424         int ds, ne;
425         char *fp0 = (char *)NULL;
426         char *fp1 = (char *)NULL;
427         int fr, ignrdl, ignstat;
428         char *ln;
429         struct mounts *mp;
430         FILE *ms;
431         int nfs;
432         struct stat sb;
433         static char *vbuf = (char *)NULL;
434         static size_t vsz = (size_t)0;
435
436         if (Lmi || Lmist)
437             return(Lmi);
438 /*
439  * Open access to /proc/mounts, assigning a page size buffer to its stream.
440  */
441         (void) snpf(buf, sizeof(buf), "%s/mounts", PROCFS);
442         ms = open_proc_stream(buf, "r", &vbuf, &vsz, 1);
443 /*
444  * Read mount table entries.
445  */
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])
449                 continue;
450         /*
451          * Convert octal-escaped characters in the device name and mounted-on
452          * path name.
453          */
454             if (fp0) {
455                 (void) free((FREE_P *)fp0);
456                 fp0 = (char *)NULL;
457             }
458             if (fp1) {
459                 (void) free((FREE_P *)fp1);
460                 fp1 = (char *)NULL;
461             }
462             if (!(fp0 = cvtoe(fp[0])) || !(fp1 = cvtoe(fp[1])))
463                 continue;
464         /*
465          * Locate any colon (':') in the device name.
466          *
467          * If the colon is followed by * "(pid*" -- it's probably an
468          * automounter entry.
469          *
470          * Ignore autofs, pipefs, and sockfs entries.
471          */
472             cp = strchr(fp0, ':');
473             if (cp && !strncasecmp(++cp, "(pid", 4))
474                 continue;
475             if (!strcasecmp(fp[2], "autofs") || !strcasecmp(fp[2], "pipefs")
476             ||  !strcasecmp(fp[2], "sockfs"))
477                 continue;
478         /*
479          * Interpolate a possible symbolic mounted directory link.
480          */
481             if (dn)
482                 (void) free((FREE_P *)dn);
483             dn = fp1;
484             fp1 = (char *)NULL;
485
486 #if     defined(HASEOPT)
487         if (Efsysl) {
488
489         /*
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.
492          */
493             efsys_list_t *ep;
494
495             for (ignrdl = ignstat = 0, ep = Efsysl; ep; ep = ep->next) {
496                 if (!strcmp(dn, ep->path)) {
497                     ignrdl = ep->rdlnk;
498                     ignstat = 1;
499                     break;
500                 }
501             }
502         } else
503
504 #endif  /* defined(HASEOPT */
505
506             ignrdl = ignstat = 0;
507
508         /*
509          * Avoid Readlink() when requested.
510          */
511             if (!ignrdl) {
512                 if (!(ln = Readlink(dn))) {
513                     if (!Fwarn) {
514                         (void) fprintf(stderr,
515                         "      Output information may be incomplete.\n");
516                     }
517                         continue;
518                 }
519                 if (ln != dn) {
520                     (void) free((FREE_P *)dn);
521                     dn = ln;
522                 }
523             }
524             if (*dn != '/')
525                 continue;
526             dnl = strlen(dn);
527         /*
528          * Test for duplicate and NFS directories.
529          */
530             for (mp = Lmi; mp; mp = mp->next) {
531                 if ((dnl == mp->dirl) && !strcmp(dn, mp->dir))
532                     break;
533             }
534             if ((nfs = strcasecmp(fp[2], "nfs"))) {
535                 if ((nfs = strcasecmp(fp[2], "nfs3")))
536                     nfs = strcasecmp(fp[2], "nfs4");
537             }
538             if (mp) {
539
540             /*
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.
544              */
545                 if (strcmp(dn, "/"))
546                     continue;
547                 if (mp->ty == N_NFS)
548                     continue;
549                 if (nfs)
550                     continue;
551             }
552         /*
553          * Stat() the directory.
554          */
555             if (ignstat)
556                 fr = 1;
557             else {
558                 if ((fr = statsafely(dn, &sb))) {
559                     if (!Fwarn) {
560                         (void) fprintf(stderr, "%s: WARNING: can't stat() ",
561                             Pn);
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");
567                     }
568                 } else
569                     ds = SB_ALL;
570             }
571
572 #if     defined(HASMNTSUP)
573             if (fr) {
574
575             /*
576              * If the stat() failed or wasn't called, check the mount
577              * supplement table, if possible.
578              */
579                 if ((MntSup == 2) && MntSupP) {
580                     ds = 0;
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);
585                         }
586                 } else {
587                     if (!ignstat)
588                         continue;
589                    ds = 0;              /* No stat() was allowed. */
590                 }
591             }
592 #else   /* !defined(HASMNTSUP) */
593             if (fr) {
594                 if (!ignstat)
595                     continue;
596                 ds = 0;                 /* No stat() was allowed. */
597             }
598 #endif  /* defined(HASMNTSUP) */
599
600         /*
601          * Fill a local mount structure or reuse a previous entry when
602          * indicated.
603          */
604             if (mp) {
605                 ne = 0;
606                 if (mp->dir) {
607                     (void) free((FREE_P *)mp->dir);
608                     mp->dir = (char *)NULL;
609                 }
610                 if (mp->fsname) {
611                     (void) free((FREE_P *)mp->fsname);
612                     mp->fsname = (char *)NULL;
613                 }
614             } else {
615                 ne = 1;
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);
620                     Exit(1);
621                 }
622             }
623             mp->dir = dn;
624             dn = (char *)NULL;
625             mp->dirl = dnl;
626             if (ne)
627                 mp->next = Lmi;
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;
632             if (!nfs) {
633                 mp->ty = N_NFS;
634                 if (HasNFS < 2)
635                     HasNFS = 2;
636             } else
637                 mp->ty = N_REGLR;
638
639 #if     defined(HASMNTSUP)
640         /*
641          * If support for the mount supplement file is defined and if the
642          * +m option was supplied, print mount supplement information.
643          */
644             if (MntSup == 1) {
645                 if (mp->dev)
646                     (void) printf("%s %#lx\n", mp->dir, (long)mp->dev);
647                 else
648                     (void) printf("%s 0x0\n", mp->dir);
649             }
650 #endif  /* defined(HASMNTSUP) */
651
652         /*
653          * Save mounted-on device or directory name.
654          */
655             dn = fp0;
656             fp0 = (char *)NULL;
657             mp->fsname = dn;
658         /*
659          * Interpolate a possible file system (mounted-on) device name or
660          * directory name link.
661          *
662          * Avoid Readlink() when requested.
663          */
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);
669                     Exit(1);
670                 }
671                 ignstat = 1;
672             } else
673                 ln = Readlink(dn);
674             dn = (char *)NULL;
675         /*
676          * Stat() the file system (mounted-on) name and add file system
677          * information to the local mount table entry.
678          */
679             if (ignstat || !ln || statsafely(ln, &sb))
680                 sb.st_mode = 0;
681             mp->fsnmres = ln;
682             mp->fs_mode = sb.st_mode;
683             if (ne)
684                 Lmi = mp;
685         }
686 /*
687  * Clean up and return the local mount info table address.
688  */
689         (void) fclose(ms);
690         if (dn)
691             (void) free((FREE_P *)dn);
692         if (fp0)
693             (void) free((FREE_P *)fp0);
694         if (fp1)
695             (void) free((FREE_P *)fp1);
696         Lmist = 1;
697         return(Lmi);
698 }