/* * This file is part of MultiROM. * * MultiROM is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * MultiROM is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MultiROM. If not, see . */ #include #include #include #include #include #include #include #include "fstab.h" #include "util.h" #include "log.h" #include "containers.h" // flags from system/core/fs_mgr/fs_mgr.c struct flag_list { const char *name; unsigned flag; }; static struct flag_list mount_flags[] = { { "noatime", MS_NOATIME }, { "noexec", MS_NOEXEC }, { "nosuid", MS_NOSUID }, { "nodev", MS_NODEV }, { "nodiratime", MS_NODIRATIME }, { "ro", MS_RDONLY }, { "rw", 0 }, { "remount", MS_REMOUNT }, { "bind", MS_BIND }, { "rec", MS_REC }, { "unbindable", MS_UNBINDABLE }, { "private", MS_PRIVATE }, { "slave", MS_SLAVE }, { "shared", MS_SHARED }, { "sync", MS_SYNCHRONOUS }, { "defaults", 0 }, { 0, 0 }, }; struct fstab *fstab_create_empty(int version) { struct fstab *t = mzalloc(sizeof(struct fstab)); t->version = version; return t; } struct fstab *fstab_load(const char *path, int resolve_symlinks) { FILE *f = fopen(path, "re"); if(!f) { ERROR("Failed to open fstab %s\n", path); return NULL; } struct fstab *t = fstab_create_empty(-1); const char *delim = " \t"; char *saveptr = NULL; char *p; char line[1024]; int len, is_dev_on_line = 0; struct fstab_part *part = NULL; t->path = strdup(path); while((p = fgets(line, sizeof(line), f))) { len = strlen(line); if(line[len-1] == '\n') line[len-1] = 0; while(isspace(*p)) ++p; if(*p == '#' || *p == 0) continue; if(t->version == -1) is_dev_on_line = (strstr(line, "/dev/") != NULL); part = mzalloc(sizeof(struct fstab_part)); if(!(p = strtok_r(line, delim, &saveptr))) { ERROR("Error first token\n"); goto fail; } if(t->version == -1) { if(is_dev_on_line && strstr(p, "/dev/") != p) t->version = 1; else t->version = 2; } if(t->version == 2) part->device = resolve_symlinks ? readlink_recursive(p) : strdup(p); else part->path = strdup (p); if(!(p = strtok_r(NULL, delim, &saveptr))) { ERROR("Error second token\n"); goto fail; } if(t->version == 2) part->path = strdup(p); else part->type = strdup(p); if(!(p = strtok_r(NULL, delim, &saveptr))) { ERROR("Error third token\n"); goto fail; } if(t->version == 2) part->type = strdup(p); else part->device = resolve_symlinks ? readlink_recursive(p) : strdup(p); if((p = strtok_r(NULL, delim, &saveptr))) { part->options_raw = strdup(p); fstab_parse_options(p, part); } if((p = strtok_r(NULL, delim, &saveptr))) part->options2 = strdup(p); // Check device if(!part->device) { if (strcmp(part->path, "/data") == 0 || strcmp(part->path, "/system") == 0 || strcmp(part->path, "/boot") == 0 || strcmp(part->path, "/cache") == 0) { ERROR("fstab: device for part %s does not exist!\n", part->path); } fstab_destroy_part(part); part = NULL; continue; } list_add(&t->parts, part); ++t->count; part = NULL; } fclose(f); return t; fail: fclose(f); free(part); fstab_destroy(t); return NULL; } int fstab_save(struct fstab *f, const char *path) { int i; FILE *out; struct fstab_part *p; out = fopen(path, "we"); if(!f) { ERROR("fstab_save: failed to open %s!", path); return -1; } for(i = 0; i < f->count; ++i) { p = f->parts[i]; if(p->disabled) fputc('#', out); if(f->version == 1) fprintf(out, "%s\t%s\t%s\t", p->path, p->type, p->device); else fprintf(out, "%s\t%s\t%s\t", p->device, p->path, p->type); fprintf(out, "%s\t%s\n", p->options_raw, p->options2); } fclose(out); return 0; } void fstab_destroy(struct fstab *f) { list_clear(&f->parts, fstab_destroy_part); free(f->path); free(f); } void fstab_destroy_part(struct fstab_part *p) { free(p->path); free(p->type); free(p->device); free(p->options); free(p->options_raw); free(p->options2); free(p); } void fstab_dump(struct fstab *f) { INFO("Dumping fstab:\n"); INFO("version: %d\n", f->version); INFO("count: %d\n", f->count); int i; for(i = 0; i < f->count; ++i) { INFO("Partition %d:\n", i); INFO(" path: %s\n", f->parts[i]->path); INFO(" device: %s\n", f->parts[i]->device); INFO(" type: %s\n", f->parts[i]->type); INFO(" mountflags: 0x%lX\n", f->parts[i]->mountflags); INFO(" options: %s\n", f->parts[i]->options); INFO(" options2: %s\n", f->parts[i]->options2); } } struct fstab_part *fstab_find_first_by_path(struct fstab *f, const char *path) { int i; for(i = 0; i < f->count; ++i) if(strcmp(f->parts[i]->path, path) == 0) return f->parts[i]; return NULL; } struct fstab_part *fstab_find_next_by_path(struct fstab *f, const char *path, struct fstab_part *prev) { int i, found_prev = 0; for(i = 0; i < f->count; ++i) { if(!found_prev) { if(f->parts[i] == prev) found_prev = 1; } else if(strcmp(f->parts[i]->path, path) == 0) { return f->parts[i]; } } return NULL; } int fstab_disable_parts(struct fstab *f, const char *path) { int i, cnt = 0; for(i = 0; i < f->count; ++i) { if(strcmp(f->parts[i]->path, path) == 0) { f->parts[i]->disabled = 1; ++cnt; } } if(cnt == 0) { ERROR("Failed to disable partition %s, couldn't find it in fstab!\n", path); return -1; } return cnt; } void fstab_parse_options(char *opt, struct fstab_part *part) { int i; char *p; char *saveptr = NULL; part->options = malloc(strlen(opt) + 2); // NULL and possible trailing comma part->options[0] = 0; p = strtok_r(opt, ",", &saveptr); while(p) { for(i = 0; mount_flags[i].name; ++i) { if(strcmp(mount_flags[i].name, p) == 0) { part->mountflags |= mount_flags[i].flag; break; } } if(!mount_flags[i].name) { strcat(part->options, p); strcat(part->options, ","); } p = strtok_r(NULL, ",", &saveptr); } int len = strlen(part->options); if(len != 0) { part->options[len-1] = 0; // remove trailing comma part->options = realloc(part->options, len); } else { free(part->options); part->options = NULL; } } struct fstab *fstab_auto_load(void) { char path[64]; path[0] = 0; if(access("/mrom.fstab", F_OK) >= 0) strcpy(path, "/mrom.fstab"); else { DIR *d = opendir("/"); if(!d) { ERROR("Failed to open /\n"); return NULL; } struct dirent *dt; while((dt = readdir(d))) { if(dt->d_type != DT_REG) continue; // For some reason, CM includes goldfish's fstab, ignore it // (goldfish is the virtual device for emulator) if(strcmp(dt->d_name, "fstab.goldfish") == 0) continue; if(strncmp(dt->d_name, "fstab.", sizeof("fstab.")-1) == 0) { strcpy(path, "/"); strcat(path, dt->d_name); // try to find specifically fstab.device #ifdef TARGET_DEVICE if(strcmp(dt->d_name, "fstab."TARGET_DEVICE) == 0) break; #endif } } closedir(d); } if(path[0] == 0) { ERROR("Failed to find fstab!\n"); return NULL; } ERROR("Loading fstab \"%s\"...\n", path); return fstab_load(path, 1); } void fstab_add_part(struct fstab *f, const char *dev, const char *path, const char *type, const char *options, const char *options2) { struct fstab_part *p = mzalloc(sizeof(struct fstab_part)); p->path = strdup(path); p->device = strdup(dev); p->type = strdup(type); p->options_raw = strdup(options); fstab_parse_options(p->options_raw, p); p->options2 = strdup(options2); list_add(&f->parts, p); ++f->count; } struct fstab_part *fstab_clone_part(struct fstab_part *p) { struct fstab_part *new_p = mzalloc(sizeof(struct fstab_part)); memcpy(new_p, p, sizeof(struct fstab_part)); new_p->path = strdup(p->path); new_p->device = strdup(p->device); new_p->type = strdup(p->type); new_p->options_raw = strdup(p->options_raw); new_p->options = strdup(p->options); new_p->options2 = strdup(p->options2); return new_p; } void fstab_add_part_struct(struct fstab *f, struct fstab_part *p) { list_add(&f->parts, p); ++f->count; } void fstab_update_device(struct fstab *f, const char *oldDev, const char *newDev) { int i; char *tmp = strdup(oldDev); for(i = 0; i < f->count; ++i) { if(strcmp(f->parts[i]->device, tmp) == 0) { f->parts[i]->device = realloc(f->parts[i]->device, strlen(newDev)+1); strcpy(f->parts[i]->device, newDev); } } free(tmp); }