1 /* getmountlist.c - Get a linked list of mount points, with stat information.
2  *
3  * Copyright 2006 Rob Landley <rob@landley.net>
4  */
5 
6 #include "toys.h"
7 #include <mntent.h>
8 
9 // Realloc *old with oldstring,newstring
10 
comma_collate(char ** old,char * new)11 void comma_collate(char **old, char *new)
12 {
13   char *temp, *atold = *old;
14 
15   // Only add a comma if old string didn't end with one
16   if (atold && *atold) {
17     char *comma = ",";
18 
19     if (atold[strlen(atold)-1] == ',') comma = "";
20     temp = xmprintf("%s%s%s", atold, comma, new);
21   } else temp = xstrdup(new);
22   free (atold);
23   *old = temp;
24 }
25 
26 // iterate through strings in a comma separated list.
27 // returns start of next entry or NULL if none
28 // sets *len to length of entry (not including comma)
29 // advances *list to start of next entry
comma_iterate(char ** list,int * len)30 char *comma_iterate(char **list, int *len)
31 {
32   char *start = *list, *end;
33 
34   if (!*list || !**list) return 0;
35 
36   if (!(end = strchr(*list, ','))) {
37     *len = strlen(*list);
38     *list = 0;
39   } else *list += (*len = end-start)+1;
40 
41   return start;
42 }
43 
octal_deslash(char * s)44 static void octal_deslash(char *s)
45 {
46   char *o = s;
47 
48   while (*s) {
49     if (*s == '\\') {
50       int i, oct = 0;
51 
52       for (i = 1; i < 4; i++) {
53         if (!isdigit(s[i])) break;
54         oct = (oct<<3)+s[i]-'0';
55       }
56       if (i == 4) {
57         *o++ = oct;
58         s += i;
59         continue;
60       }
61     }
62     *o++ = *s++;
63   }
64 
65   *o = 0;
66 }
67 
68 // check all instances of opt and "no"opt in optlist, return true if opt
69 // found and last instance wasn't no. If clean, remove each instance from list.
comma_scan(char * optlist,char * opt,int clean)70 int comma_scan(char *optlist, char *opt, int clean)
71 {
72   int optlen = strlen(opt), len, no, got = 0;
73 
74   if (optlist) for (;;) {
75     char *s = comma_iterate(&optlist, &len);
76 
77     if (!s) break;
78     no = 2*(*s == 'n' && s[1] == 'o');
79     if (optlen == len-no && !strncmp(opt, s+no, optlen)) {
80       got = !no;
81       if (clean) memmove(s, optlist, strlen(optlist)+1);
82     }
83   }
84 
85   return got;
86 }
87 
88 // return true if all scanlist options enabled in optlist
comma_scanall(char * optlist,char * scanlist)89 int comma_scanall(char *optlist, char *scanlist)
90 {
91   int i = 1;
92 
93   while (scanlist && *scanlist) {
94     char *opt = comma_iterate(&scanlist, &i), *s = xstrndup(opt, i);
95 
96     i = comma_scan(optlist, s, 0);
97     free(s);
98     if (!i) break;
99   }
100 
101   return i;
102 }
103 
104 // Check if this type matches list.
105 // Odd syntax: typelist all yes = if any, typelist all no = if none.
106 
mountlist_istype(struct mtab_list * ml,char * typelist)107 int mountlist_istype(struct mtab_list *ml, char *typelist)
108 {
109   int len, skip;
110   char *t;
111 
112   if (!typelist) return 1;
113 
114   skip = strncmp(typelist, "no", 2);
115 
116   for (;;) {
117     if (!(t = comma_iterate(&typelist, &len))) break;
118     if (!skip) {
119       // If one -t starts with "no", the rest must too
120       if (strncmp(t, "no", 2)) error_exit("bad typelist");
121       if (!strncmp(t+2, ml->type, len-2)) {
122         skip = 1;
123         break;
124       }
125     } else if (!strncmp(t, ml->type, len) && !ml->type[len]) {
126       skip = 0;
127       break;
128     }
129   }
130 
131   return !skip;
132 }
133 
134 // Get list of mounted filesystems, including stat and statvfs info.
135 // Returns a reversed list, which is good for finding overmounts and such.
136 
xgetmountlist(char * path)137 struct mtab_list *xgetmountlist(char *path)
138 {
139   struct mtab_list *mtlist = 0, *mt;
140   struct mntent *me;
141   FILE *fp;
142   char *p = path ? path : "/proc/mounts";
143 
144   if (!(fp = setmntent(p, "r"))) perror_exit("bad %s", p);
145 
146   // The "test" part of the loop is done before the first time through and
147   // again after each "increment", so putting the actual load there avoids
148   // duplicating it. If the load was NULL, the loop stops.
149 
150   while ((me = getmntent(fp))) {
151     mt = xzalloc(sizeof(struct mtab_list) + strlen(me->mnt_fsname) +
152       strlen(me->mnt_dir) + strlen(me->mnt_type) + strlen(me->mnt_opts) + 4);
153     dlist_add_nomalloc((void *)&mtlist, (void *)mt);
154 
155     // Collect details about mounted filesystem
156     // Don't report errors, just leave data zeroed
157     if (!path) {
158       stat(me->mnt_dir, &(mt->stat));
159       statvfs(me->mnt_dir, &(mt->statvfs));
160     }
161 
162     // Remember information from /proc/mounts
163     mt->dir = stpcpy(mt->type, me->mnt_type)+1;
164     mt->device = stpcpy(mt->dir, me->mnt_dir)+1;
165     mt->opts = stpcpy(mt->device, me->mnt_fsname)+1;
166     strcpy(mt->opts, me->mnt_opts);
167 
168     octal_deslash(mt->dir);
169     octal_deslash(mt->device);
170   }
171   endmntent(fp);
172 
173   return mtlist;
174 }
175