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