1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <ctype.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/mount.h>
22 
23 #include "fs_mgr_priv.h"
24 
25 struct fs_mgr_flag_values {
26     char *key_loc;
27     long long part_length;
28     char *label;
29     int partnum;
30     int swap_prio;
31     unsigned int zram_size;
32 };
33 
34 struct flag_list {
35     const char *name;
36     unsigned flag;
37 };
38 
39 static struct flag_list mount_flags[] = {
40     { "noatime",    MS_NOATIME },
41     { "noexec",     MS_NOEXEC },
42     { "nosuid",     MS_NOSUID },
43     { "nodev",      MS_NODEV },
44     { "nodiratime", MS_NODIRATIME },
45     { "ro",         MS_RDONLY },
46     { "rw",         0 },
47     { "remount",    MS_REMOUNT },
48     { "bind",       MS_BIND },
49     { "rec",        MS_REC },
50     { "unbindable", MS_UNBINDABLE },
51     { "private",    MS_PRIVATE },
52     { "slave",      MS_SLAVE },
53     { "shared",     MS_SHARED },
54     { "defaults",   0 },
55     { 0,            0 },
56 };
57 
58 static struct flag_list fs_mgr_flags[] = {
59     { "wait",        MF_WAIT },
60     { "check",       MF_CHECK },
61     { "encryptable=",MF_CRYPT },
62     { "forceencrypt=",MF_FORCECRYPT },
63     { "nonremovable",MF_NONREMOVABLE },
64     { "voldmanaged=",MF_VOLDMANAGED},
65     { "length=",     MF_LENGTH },
66     { "recoveryonly",MF_RECOVERYONLY },
67     { "swapprio=",   MF_SWAPPRIO },
68     { "zramsize=",   MF_ZRAMSIZE },
69     { "verify",      MF_VERIFY },
70     { "noemulatedsd", MF_NOEMULATEDSD },
71     { "defaults",    0 },
72     { 0,             0 },
73 };
74 
parse_flags(char * flags,struct flag_list * fl,struct fs_mgr_flag_values * flag_vals,char * fs_options,int fs_options_len)75 static int parse_flags(char *flags, struct flag_list *fl,
76                        struct fs_mgr_flag_values *flag_vals,
77                        char *fs_options, int fs_options_len)
78 {
79     int f = 0;
80     int i;
81     char *p;
82     char *savep;
83 
84     /* initialize flag values.  If we find a relevant flag, we'll
85      * update the value */
86     if (flag_vals) {
87         memset(flag_vals, 0, sizeof(*flag_vals));
88         flag_vals->partnum = -1;
89         flag_vals->swap_prio = -1; /* negative means it wasn't specified. */
90     }
91 
92     /* initialize fs_options to the null string */
93     if (fs_options && (fs_options_len > 0)) {
94         fs_options[0] = '\0';
95     }
96 
97     p = strtok_r(flags, ",", &savep);
98     while (p) {
99         /* Look for the flag "p" in the flag list "fl"
100          * If not found, the loop exits with fl[i].name being null.
101          */
102         for (i = 0; fl[i].name; i++) {
103             if (!strncmp(p, fl[i].name, strlen(fl[i].name))) {
104                 f |= fl[i].flag;
105                 if ((fl[i].flag == MF_CRYPT) && flag_vals) {
106                     /* The encryptable flag is followed by an = and the
107                      * location of the keys.  Get it and return it.
108                      */
109                     flag_vals->key_loc = strdup(strchr(p, '=') + 1);
110                 } else if ((fl[i].flag == MF_FORCECRYPT) && flag_vals) {
111                     /* The forceencrypt flag is followed by an = and the
112                      * location of the keys.  Get it and return it.
113                      */
114                     flag_vals->key_loc = strdup(strchr(p, '=') + 1);
115                 } else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
116                     /* The length flag is followed by an = and the
117                      * size of the partition.  Get it and return it.
118                      */
119                     flag_vals->part_length = strtoll(strchr(p, '=') + 1, NULL, 0);
120                 } else if ((fl[i].flag == MF_VOLDMANAGED) && flag_vals) {
121                     /* The voldmanaged flag is followed by an = and the
122                      * label, a colon and the partition number or the
123                      * word "auto", e.g.
124                      *   voldmanaged=sdcard:3
125                      * Get and return them.
126                      */
127                     char *label_start;
128                     char *label_end;
129                     char *part_start;
130 
131                     label_start = strchr(p, '=') + 1;
132                     label_end = strchr(p, ':');
133                     if (label_end) {
134                         flag_vals->label = strndup(label_start,
135                                                    (int) (label_end - label_start));
136                         part_start = strchr(p, ':') + 1;
137                         if (!strcmp(part_start, "auto")) {
138                             flag_vals->partnum = -1;
139                         } else {
140                             flag_vals->partnum = strtol(part_start, NULL, 0);
141                         }
142                     } else {
143                         ERROR("Warning: voldmanaged= flag malformed\n");
144                     }
145                 } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) {
146                     flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0);
147                 } else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) {
148                     flag_vals->zram_size = strtoll(strchr(p, '=') + 1, NULL, 0);
149                 }
150                 break;
151             }
152         }
153 
154         if (!fl[i].name) {
155             if (fs_options) {
156                 /* It's not a known flag, so it must be a filesystem specific
157                  * option.  Add it to fs_options if it was passed in.
158                  */
159                 strlcat(fs_options, p, fs_options_len);
160                 strlcat(fs_options, ",", fs_options_len);
161             } else {
162                 /* fs_options was not passed in, so if the flag is unknown
163                  * it's an error.
164                  */
165                 ERROR("Warning: unknown flag %s\n", p);
166             }
167         }
168         p = strtok_r(NULL, ",", &savep);
169     }
170 
171     if (fs_options && fs_options[0]) {
172         /* remove the last trailing comma from the list of options */
173         fs_options[strlen(fs_options) - 1] = '\0';
174     }
175 
176     return f;
177 }
178 
fs_mgr_read_fstab(const char * fstab_path)179 struct fstab *fs_mgr_read_fstab(const char *fstab_path)
180 {
181     FILE *fstab_file;
182     int cnt, entries;
183     ssize_t len;
184     size_t alloc_len = 0;
185     char *line = NULL;
186     const char *delim = " \t";
187     char *save_ptr, *p;
188     struct fstab *fstab = NULL;
189     struct fs_mgr_flag_values flag_vals;
190 #define FS_OPTIONS_LEN 1024
191     char tmp_fs_options[FS_OPTIONS_LEN];
192 
193     fstab_file = fopen(fstab_path, "r");
194     if (!fstab_file) {
195         ERROR("Cannot open file %s\n", fstab_path);
196         return 0;
197     }
198 
199     entries = 0;
200     while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
201         /* if the last character is a newline, shorten the string by 1 byte */
202         if (line[len - 1] == '\n') {
203             line[len - 1] = '\0';
204         }
205         /* Skip any leading whitespace */
206         p = line;
207         while (isspace(*p)) {
208             p++;
209         }
210         /* ignore comments or empty lines */
211         if (*p == '#' || *p == '\0')
212             continue;
213         entries++;
214     }
215 
216     if (!entries) {
217         ERROR("No entries found in fstab\n");
218         goto err;
219     }
220 
221     /* Allocate and init the fstab structure */
222     fstab = calloc(1, sizeof(struct fstab));
223     fstab->num_entries = entries;
224     fstab->fstab_filename = strdup(fstab_path);
225     fstab->recs = calloc(fstab->num_entries, sizeof(struct fstab_rec));
226 
227     fseek(fstab_file, 0, SEEK_SET);
228 
229     cnt = 0;
230     while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
231         /* if the last character is a newline, shorten the string by 1 byte */
232         if (line[len - 1] == '\n') {
233             line[len - 1] = '\0';
234         }
235 
236         /* Skip any leading whitespace */
237         p = line;
238         while (isspace(*p)) {
239             p++;
240         }
241         /* ignore comments or empty lines */
242         if (*p == '#' || *p == '\0')
243             continue;
244 
245         /* If a non-comment entry is greater than the size we allocated, give an
246          * error and quit.  This can happen in the unlikely case the file changes
247          * between the two reads.
248          */
249         if (cnt >= entries) {
250             ERROR("Tried to process more entries than counted\n");
251             break;
252         }
253 
254         if (!(p = strtok_r(line, delim, &save_ptr))) {
255             ERROR("Error parsing mount source\n");
256             goto err;
257         }
258         fstab->recs[cnt].blk_device = strdup(p);
259 
260         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
261             ERROR("Error parsing mount_point\n");
262             goto err;
263         }
264         fstab->recs[cnt].mount_point = strdup(p);
265 
266         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
267             ERROR("Error parsing fs_type\n");
268             goto err;
269         }
270         fstab->recs[cnt].fs_type = strdup(p);
271 
272         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
273             ERROR("Error parsing mount_flags\n");
274             goto err;
275         }
276         tmp_fs_options[0] = '\0';
277         fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,
278                                        tmp_fs_options, FS_OPTIONS_LEN);
279 
280         /* fs_options are optional */
281         if (tmp_fs_options[0]) {
282             fstab->recs[cnt].fs_options = strdup(tmp_fs_options);
283         } else {
284             fstab->recs[cnt].fs_options = NULL;
285         }
286 
287         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
288             ERROR("Error parsing fs_mgr_options\n");
289             goto err;
290         }
291         fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
292                                                     &flag_vals, NULL, 0);
293         fstab->recs[cnt].key_loc = flag_vals.key_loc;
294         fstab->recs[cnt].length = flag_vals.part_length;
295         fstab->recs[cnt].label = flag_vals.label;
296         fstab->recs[cnt].partnum = flag_vals.partnum;
297         fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
298         fstab->recs[cnt].zram_size = flag_vals.zram_size;
299         cnt++;
300     }
301     fclose(fstab_file);
302     free(line);
303     return fstab;
304 
305 err:
306     fclose(fstab_file);
307     free(line);
308     if (fstab)
309         fs_mgr_free_fstab(fstab);
310     return NULL;
311 }
312 
fs_mgr_free_fstab(struct fstab * fstab)313 void fs_mgr_free_fstab(struct fstab *fstab)
314 {
315     int i;
316 
317     if (!fstab) {
318         return;
319     }
320 
321     for (i = 0; i < fstab->num_entries; i++) {
322         /* Free the pointers return by strdup(3) */
323         free(fstab->recs[i].blk_device);
324         free(fstab->recs[i].mount_point);
325         free(fstab->recs[i].fs_type);
326         free(fstab->recs[i].fs_options);
327         free(fstab->recs[i].key_loc);
328         free(fstab->recs[i].label);
329     }
330 
331     /* Free the fstab_recs array created by calloc(3) */
332     free(fstab->recs);
333 
334     /* Free the fstab filename */
335     free(fstab->fstab_filename);
336 
337     /* Free fstab */
338     free(fstab);
339 }
340 
341 /* Add an entry to the fstab, and return 0 on success or -1 on error */
fs_mgr_add_entry(struct fstab * fstab,const char * mount_point,const char * fs_type,const char * blk_device)342 int fs_mgr_add_entry(struct fstab *fstab,
343                      const char *mount_point, const char *fs_type,
344                      const char *blk_device)
345 {
346     struct fstab_rec *new_fstab_recs;
347     int n = fstab->num_entries;
348 
349     new_fstab_recs = (struct fstab_rec *)
350                      realloc(fstab->recs, sizeof(struct fstab_rec) * (n + 1));
351 
352     if (!new_fstab_recs) {
353         return -1;
354     }
355 
356     /* A new entry was added, so initialize it */
357      memset(&new_fstab_recs[n], 0, sizeof(struct fstab_rec));
358      new_fstab_recs[n].mount_point = strdup(mount_point);
359      new_fstab_recs[n].fs_type = strdup(fs_type);
360      new_fstab_recs[n].blk_device = strdup(blk_device);
361      new_fstab_recs[n].length = 0;
362 
363      /* Update the fstab struct */
364      fstab->recs = new_fstab_recs;
365      fstab->num_entries++;
366 
367      return 0;
368 }
369 
370 /*
371  * Returns the 1st matching fstab_rec that follows the start_rec.
372  * start_rec is the result of a previous search or NULL.
373  */
fs_mgr_get_entry_for_mount_point_after(struct fstab_rec * start_rec,struct fstab * fstab,const char * path)374 struct fstab_rec *fs_mgr_get_entry_for_mount_point_after(struct fstab_rec *start_rec, struct fstab *fstab, const char *path)
375 {
376     int i;
377     if (!fstab) {
378         return NULL;
379     }
380 
381     if (start_rec) {
382         for (i = 0; i < fstab->num_entries; i++) {
383             if (&fstab->recs[i] == start_rec) {
384                 i++;
385                 break;
386             }
387         }
388     } else {
389         i = 0;
390     }
391     for (; i < fstab->num_entries; i++) {
392         int len = strlen(fstab->recs[i].mount_point);
393         if (strncmp(path, fstab->recs[i].mount_point, len) == 0 &&
394             (path[len] == '\0' || path[len] == '/')) {
395             return &fstab->recs[i];
396         }
397     }
398     return NULL;
399 }
400 
401 /*
402  * Returns the 1st matching mount point.
403  * There might be more. To look for others, use fs_mgr_get_entry_for_mount_point_after()
404  * and give the fstab_rec from the previous search.
405  */
fs_mgr_get_entry_for_mount_point(struct fstab * fstab,const char * path)406 struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path)
407 {
408     return fs_mgr_get_entry_for_mount_point_after(NULL, fstab, path);
409 }
410 
fs_mgr_is_voldmanaged(struct fstab_rec * fstab)411 int fs_mgr_is_voldmanaged(struct fstab_rec *fstab)
412 {
413     return fstab->fs_mgr_flags & MF_VOLDMANAGED;
414 }
415 
fs_mgr_is_nonremovable(struct fstab_rec * fstab)416 int fs_mgr_is_nonremovable(struct fstab_rec *fstab)
417 {
418     return fstab->fs_mgr_flags & MF_NONREMOVABLE;
419 }
420 
fs_mgr_is_verified(struct fstab_rec * fstab)421 int fs_mgr_is_verified(struct fstab_rec *fstab)
422 {
423     return fstab->fs_mgr_flags & MF_VERIFY;
424 }
425 
fs_mgr_is_encryptable(struct fstab_rec * fstab)426 int fs_mgr_is_encryptable(struct fstab_rec *fstab)
427 {
428     return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT);
429 }
430 
fs_mgr_is_noemulatedsd(struct fstab_rec * fstab)431 int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab)
432 {
433     return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
434 }
435