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
10void 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
30void 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
49char *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
63static 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.
89int 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
111int 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
129int 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
159struct 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