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 && optlist) memmove(s, optlist, strlen(optlist)+1);
101    }
102  }
103
104  return got;
105}
106
107// return true if all scanlist options enabled in optlist
108int comma_scanall(char *optlist, char *scanlist)
109{
110  int i = 1;
111
112  while (scanlist && *scanlist) {
113    char *opt = comma_iterate(&scanlist, &i), *s = xstrndup(opt, i);
114
115    i = comma_scan(optlist, s, 0);
116    free(s);
117    if (!i) break;
118  }
119
120  return i;
121}
122
123// Check if this type matches list.
124// Odd syntax: typelist all yes = if any, typelist all no = if none.
125
126int mountlist_istype(struct mtab_list *ml, char *typelist)
127{
128  int len, skip;
129  char *t;
130
131  if (!typelist) return 1;
132
133  skip = strncmp(typelist, "no", 2);
134
135  for (;;) {
136    if (!(t = comma_iterate(&typelist, &len))) break;
137    if (!skip) {
138      // If one -t starts with "no", the rest must too
139      if (strncmp(t, "no", 2)) error_exit("bad typelist");
140      if (!strncmp(t+2, ml->type, len-2)) {
141        skip = 1;
142        break;
143      }
144    } else if (!strncmp(t, ml->type, len) && !ml->type[len]) {
145      skip = 0;
146      break;
147    }
148  }
149
150  return !skip;
151}
152
153// Get list of mounted filesystems, including stat and statvfs info.
154// Returns a reversed list, which is good for finding overmounts and such.
155
156struct mtab_list *xgetmountlist(char *path)
157{
158  struct mtab_list *mtlist = 0, *mt;
159  struct mntent *me;
160  FILE *fp;
161  char *p = path ? path : "/proc/mounts";
162
163  if (!(fp = setmntent(p, "r"))) perror_exit("bad %s", p);
164
165  // The "test" part of the loop is done before the first time through and
166  // again after each "increment", so putting the actual load there avoids
167  // duplicating it. If the load was NULL, the loop stops.
168
169  while ((me = getmntent(fp))) {
170    mt = xzalloc(sizeof(struct mtab_list) + strlen(me->mnt_fsname) +
171      strlen(me->mnt_dir) + strlen(me->mnt_type) + strlen(me->mnt_opts) + 4);
172    dlist_add_nomalloc((void *)&mtlist, (void *)mt);
173
174    // Collect details about mounted filesystem
175    // Don't report errors, just leave data zeroed
176    if (!path) {
177      stat(me->mnt_dir, &(mt->stat));
178      statvfs(me->mnt_dir, &(mt->statvfs));
179    }
180
181    // Remember information from /proc/mounts
182    mt->dir = stpcpy(mt->type, me->mnt_type)+1;
183    mt->device = stpcpy(mt->dir, me->mnt_dir)+1;
184    mt->opts = stpcpy(mt->device, me->mnt_fsname)+1;
185    strcpy(mt->opts, me->mnt_opts);
186
187    octal_deslash(mt->dir);
188    octal_deslash(mt->device);
189  }
190  endmntent(fp);
191
192  return mtlist;
193}
194