439 lines
10 KiB
C
439 lines
10 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <sys/mount.h>
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
|
|
#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);
|
|
}
|