2012-01-07 03:09:42 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2012 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <sys/mount.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <libgen.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
#include <private/android_filesystem_config.h>
|
|
|
|
#include <cutils/partition_utils.h>
|
|
|
|
#include <cutils/properties.h>
|
fs_mgr: Capture the output of e2fsck and add to the kernel log
Currently, the output of e2fsck is not saved, and we have no insight
into how many errors e2fsck is finding and fixing. Using the new
abbreviated logging feature in liblogwrap, up to the first 100 lines,
and last 4K bytes of the output of e2fsck is captured by fs_mgr, and
added to the kernel log.
Usually, the filesystem will be clean, and this will only add a few
lines to the kernel log on boot, but when things go wrong, it should
save enough to indicate what the problem is, without potentially
filling the kernel log with only e2fsck output if the filesystem is
really corrupted.
Change-Id: I9c264798e6fe721c8f818b5ce15d0975027ddbdd
2013-03-20 02:38:44 +00:00
|
|
|
#include <logwrap/logwrap.h>
|
2012-01-07 03:09:42 +00:00
|
|
|
|
|
|
|
#include "fs_mgr_priv.h"
|
|
|
|
|
|
|
|
#define KEY_LOC_PROP "ro.crypto.keyfile.userdata"
|
|
|
|
#define KEY_IN_FOOTER "footer"
|
|
|
|
|
|
|
|
#define E2FSCK_BIN "/system/bin/e2fsck"
|
|
|
|
|
fs_mgr: Capture the output of e2fsck and add to the kernel log
Currently, the output of e2fsck is not saved, and we have no insight
into how many errors e2fsck is finding and fixing. Using the new
abbreviated logging feature in liblogwrap, up to the first 100 lines,
and last 4K bytes of the output of e2fsck is captured by fs_mgr, and
added to the kernel log.
Usually, the filesystem will be clean, and this will only add a few
lines to the kernel log on boot, but when things go wrong, it should
save enough to indicate what the problem is, without potentially
filling the kernel log with only e2fsck output if the filesystem is
really corrupted.
Change-Id: I9c264798e6fe721c8f818b5ce15d0975027ddbdd
2013-03-20 02:38:44 +00:00
|
|
|
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
|
|
|
|
|
2012-01-07 03:09:42 +00:00
|
|
|
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 },
|
2012-08-14 18:34:34 +00:00
|
|
|
{ "bind", MS_BIND },
|
|
|
|
{ "rec", MS_REC },
|
|
|
|
{ "unbindable", MS_UNBINDABLE },
|
|
|
|
{ "private", MS_PRIVATE },
|
|
|
|
{ "slave", MS_SLAVE },
|
|
|
|
{ "shared", MS_SHARED },
|
2012-01-07 03:09:42 +00:00
|
|
|
{ "defaults", 0 },
|
|
|
|
{ 0, 0 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct flag_list fs_mgr_flags[] = {
|
|
|
|
{ "wait", MF_WAIT },
|
|
|
|
{ "check", MF_CHECK },
|
|
|
|
{ "encryptable=",MF_CRYPT },
|
2013-02-13 20:58:40 +00:00
|
|
|
{ "nonremovable",MF_NONREMOVABLE },
|
|
|
|
{ "voldmanaged=",MF_VOLDMANAGED},
|
|
|
|
{ "length=", MF_LENGTH },
|
2013-02-23 01:36:21 +00:00
|
|
|
{ "recoveryonly",MF_RECOVERYONLY },
|
2012-01-07 03:09:42 +00:00
|
|
|
{ "defaults", 0 },
|
|
|
|
{ 0, 0 },
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* gettime() - returns the time in seconds of the system's monotonic clock or
|
|
|
|
* zero on error.
|
|
|
|
*/
|
|
|
|
static time_t gettime(void)
|
|
|
|
{
|
|
|
|
struct timespec ts;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
|
|
if (ret < 0) {
|
|
|
|
ERROR("clock_gettime(CLOCK_MONOTONIC) failed: %s\n", strerror(errno));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ts.tv_sec;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int wait_for_file(const char *filename, int timeout)
|
|
|
|
{
|
|
|
|
struct stat info;
|
|
|
|
time_t timeout_time = gettime() + timeout;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0))
|
|
|
|
usleep(10000);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
static int parse_flags(char *flags, struct flag_list *fl,
|
|
|
|
char **key_loc, long long *part_length, char **label, int *partnum,
|
2012-01-07 03:09:42 +00:00
|
|
|
char *fs_options, int fs_options_len)
|
|
|
|
{
|
|
|
|
int f = 0;
|
|
|
|
int i;
|
|
|
|
char *p;
|
|
|
|
char *savep;
|
|
|
|
|
|
|
|
/* initialize key_loc to null, if we find an MF_CRYPT flag,
|
|
|
|
* then we'll set key_loc to the proper value */
|
|
|
|
if (key_loc) {
|
|
|
|
*key_loc = NULL;
|
|
|
|
}
|
2013-02-13 20:58:40 +00:00
|
|
|
/* initialize part_length to 0, if we find an MF_LENGTH flag,
|
|
|
|
* then we'll set part_length to the proper value */
|
|
|
|
if (part_length) {
|
|
|
|
*part_length = 0;
|
|
|
|
}
|
|
|
|
if (partnum) {
|
|
|
|
*partnum = -1;
|
|
|
|
}
|
|
|
|
if (label) {
|
|
|
|
*label = NULL;
|
|
|
|
}
|
|
|
|
|
2012-01-07 03:09:42 +00:00
|
|
|
/* initialize fs_options to the null string */
|
|
|
|
if (fs_options && (fs_options_len > 0)) {
|
|
|
|
fs_options[0] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
p = strtok_r(flags, ",", &savep);
|
|
|
|
while (p) {
|
|
|
|
/* Look for the flag "p" in the flag list "fl"
|
|
|
|
* If not found, the loop exits with fl[i].name being null.
|
|
|
|
*/
|
|
|
|
for (i = 0; fl[i].name; i++) {
|
|
|
|
if (!strncmp(p, fl[i].name, strlen(fl[i].name))) {
|
|
|
|
f |= fl[i].flag;
|
|
|
|
if ((fl[i].flag == MF_CRYPT) && key_loc) {
|
|
|
|
/* The encryptable flag is followed by an = and the
|
|
|
|
* location of the keys. Get it and return it.
|
|
|
|
*/
|
|
|
|
*key_loc = strdup(strchr(p, '=') + 1);
|
2013-02-13 20:58:40 +00:00
|
|
|
} else if ((fl[i].flag == MF_LENGTH) && part_length) {
|
|
|
|
/* The length flag is followed by an = and the
|
|
|
|
* size of the partition. Get it and return it.
|
|
|
|
*/
|
|
|
|
*part_length = strtoll(strchr(p, '=') + 1, NULL, 0);
|
|
|
|
} else if ((fl[i].flag == MF_VOLDMANAGED) && label && partnum) {
|
|
|
|
/* The voldmanaged flag is followed by an = and the
|
|
|
|
* label, a colon and the partition number or the
|
|
|
|
* word "auto", e.g.
|
|
|
|
* voldmanaged=sdcard:3
|
|
|
|
* Get and return them.
|
|
|
|
*/
|
|
|
|
char *label_start;
|
|
|
|
char *label_end;
|
|
|
|
char *part_start;
|
|
|
|
|
|
|
|
label_start = strchr(p, '=') + 1;
|
|
|
|
label_end = strchr(p, ':');
|
|
|
|
if (label_end) {
|
|
|
|
*label = strndup(label_start,
|
|
|
|
(int) (label_end - label_start));
|
|
|
|
part_start = strchr(p, ':') + 1;
|
|
|
|
if (!strcmp(part_start, "auto")) {
|
|
|
|
*partnum = -1;
|
|
|
|
} else {
|
|
|
|
*partnum = strtol(part_start, NULL, 0);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ERROR("Warning: voldmanaged= flag malformed\n");
|
|
|
|
}
|
2012-01-07 03:09:42 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!fl[i].name) {
|
|
|
|
if (fs_options) {
|
|
|
|
/* It's not a known flag, so it must be a filesystem specific
|
|
|
|
* option. Add it to fs_options if it was passed in.
|
|
|
|
*/
|
|
|
|
strlcat(fs_options, p, fs_options_len);
|
|
|
|
strlcat(fs_options, ",", fs_options_len);
|
|
|
|
} else {
|
|
|
|
/* fs_options was not passed in, so if the flag is unknown
|
|
|
|
* it's an error.
|
|
|
|
*/
|
|
|
|
ERROR("Warning: unknown flag %s\n", p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p = strtok_r(NULL, ",", &savep);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (fs_options && fs_options[0]) {
|
|
|
|
/* remove the last trailing comma from the list of options */
|
|
|
|
fs_options[strlen(fs_options) - 1] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read a line of text till the next newline character.
|
|
|
|
* If no newline is found before the buffer is full, continue reading till a new line is seen,
|
|
|
|
* then return an empty buffer. This effectively ignores lines that are too long.
|
|
|
|
* On EOF, return null.
|
|
|
|
*/
|
2012-08-27 16:10:57 +00:00
|
|
|
static char *fs_getline(char *buf, int size, FILE *file)
|
2012-01-07 03:09:42 +00:00
|
|
|
{
|
|
|
|
int cnt = 0;
|
|
|
|
int eof = 0;
|
|
|
|
int eol = 0;
|
|
|
|
int c;
|
|
|
|
|
|
|
|
if (size < 1) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (cnt < (size - 1)) {
|
|
|
|
c = getc(file);
|
|
|
|
if (c == EOF) {
|
|
|
|
eof = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
*(buf + cnt) = c;
|
|
|
|
cnt++;
|
|
|
|
|
|
|
|
if (c == '\n') {
|
|
|
|
eol = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Null terminate what we've read */
|
|
|
|
*(buf + cnt) = '\0';
|
|
|
|
|
|
|
|
if (eof) {
|
|
|
|
if (cnt) {
|
|
|
|
return buf;
|
|
|
|
} else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
} else if (eol) {
|
|
|
|
return buf;
|
|
|
|
} else {
|
|
|
|
/* The line is too long. Read till a newline or EOF.
|
|
|
|
* If EOF, return null, if newline, return an empty buffer.
|
|
|
|
*/
|
|
|
|
while(1) {
|
|
|
|
c = getc(file);
|
|
|
|
if (c == EOF) {
|
|
|
|
return NULL;
|
|
|
|
} else if (c == '\n') {
|
|
|
|
*buf = '\0';
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
struct fstab *fs_mgr_read_fstab(const char *fstab_path)
|
2012-01-07 03:09:42 +00:00
|
|
|
{
|
|
|
|
FILE *fstab_file;
|
|
|
|
int cnt, entries;
|
|
|
|
int len;
|
|
|
|
char line[256];
|
|
|
|
const char *delim = " \t";
|
|
|
|
char *save_ptr, *p;
|
2013-02-13 20:58:40 +00:00
|
|
|
struct fstab *fstab;
|
|
|
|
struct fstab_rec *recs;
|
2012-01-07 03:09:42 +00:00
|
|
|
char *key_loc;
|
2013-02-13 20:58:40 +00:00
|
|
|
long long part_length;
|
|
|
|
char *label;
|
|
|
|
int partnum;
|
2012-01-07 03:09:42 +00:00
|
|
|
#define FS_OPTIONS_LEN 1024
|
|
|
|
char tmp_fs_options[FS_OPTIONS_LEN];
|
|
|
|
|
|
|
|
fstab_file = fopen(fstab_path, "r");
|
|
|
|
if (!fstab_file) {
|
|
|
|
ERROR("Cannot open file %s\n", fstab_path);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
entries = 0;
|
2012-08-27 16:10:57 +00:00
|
|
|
while (fs_getline(line, sizeof(line), fstab_file)) {
|
2012-01-07 03:09:42 +00:00
|
|
|
/* if the last character is a newline, shorten the string by 1 byte */
|
|
|
|
len = strlen(line);
|
|
|
|
if (line[len - 1] == '\n') {
|
|
|
|
line[len - 1] = '\0';
|
|
|
|
}
|
|
|
|
/* Skip any leading whitespace */
|
|
|
|
p = line;
|
|
|
|
while (isspace(*p)) {
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
/* ignore comments or empty lines */
|
|
|
|
if (*p == '#' || *p == '\0')
|
|
|
|
continue;
|
|
|
|
entries++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!entries) {
|
|
|
|
ERROR("No entries found in fstab\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
/* Allocate and init the fstab structure */
|
|
|
|
fstab = calloc(1, sizeof(struct fstab));
|
|
|
|
fstab->num_entries = entries;
|
|
|
|
fstab->fstab_filename = strdup(fstab_path);
|
|
|
|
fstab->recs = calloc(fstab->num_entries, sizeof(struct fstab_rec));
|
2012-01-07 03:09:42 +00:00
|
|
|
|
|
|
|
fseek(fstab_file, 0, SEEK_SET);
|
|
|
|
|
|
|
|
cnt = 0;
|
2012-08-27 16:10:57 +00:00
|
|
|
while (fs_getline(line, sizeof(line), fstab_file)) {
|
2012-01-07 03:09:42 +00:00
|
|
|
/* if the last character is a newline, shorten the string by 1 byte */
|
|
|
|
len = strlen(line);
|
|
|
|
if (line[len - 1] == '\n') {
|
|
|
|
line[len - 1] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip any leading whitespace */
|
|
|
|
p = line;
|
|
|
|
while (isspace(*p)) {
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
/* ignore comments or empty lines */
|
|
|
|
if (*p == '#' || *p == '\0')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* If a non-comment entry is greater than the size we allocated, give an
|
|
|
|
* error and quit. This can happen in the unlikely case the file changes
|
|
|
|
* between the two reads.
|
|
|
|
*/
|
|
|
|
if (cnt >= entries) {
|
|
|
|
ERROR("Tried to process more entries than counted\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(p = strtok_r(line, delim, &save_ptr))) {
|
|
|
|
ERROR("Error parsing mount source\n");
|
|
|
|
return 0;
|
|
|
|
}
|
2013-02-13 20:58:40 +00:00
|
|
|
fstab->recs[cnt].blk_device = strdup(p);
|
2012-01-07 03:09:42 +00:00
|
|
|
|
|
|
|
if (!(p = strtok_r(NULL, delim, &save_ptr))) {
|
2013-02-13 20:58:40 +00:00
|
|
|
ERROR("Error parsing mount_point\n");
|
2012-01-07 03:09:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2013-02-13 20:58:40 +00:00
|
|
|
fstab->recs[cnt].mount_point = strdup(p);
|
2012-01-07 03:09:42 +00:00
|
|
|
|
|
|
|
if (!(p = strtok_r(NULL, delim, &save_ptr))) {
|
|
|
|
ERROR("Error parsing fs_type\n");
|
|
|
|
return 0;
|
|
|
|
}
|
2013-02-13 20:58:40 +00:00
|
|
|
fstab->recs[cnt].fs_type = strdup(p);
|
2012-01-07 03:09:42 +00:00
|
|
|
|
|
|
|
if (!(p = strtok_r(NULL, delim, &save_ptr))) {
|
|
|
|
ERROR("Error parsing mount_flags\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
tmp_fs_options[0] = '\0';
|
2013-02-13 20:58:40 +00:00
|
|
|
fstab->recs[cnt].flags = parse_flags(p, mount_flags,
|
|
|
|
NULL, NULL, NULL, NULL,
|
|
|
|
tmp_fs_options, FS_OPTIONS_LEN);
|
2012-01-07 03:09:42 +00:00
|
|
|
|
|
|
|
/* fs_options are optional */
|
|
|
|
if (tmp_fs_options[0]) {
|
2013-02-13 20:58:40 +00:00
|
|
|
fstab->recs[cnt].fs_options = strdup(tmp_fs_options);
|
2012-01-07 03:09:42 +00:00
|
|
|
} else {
|
2013-02-13 20:58:40 +00:00
|
|
|
fstab->recs[cnt].fs_options = NULL;
|
2012-01-07 03:09:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!(p = strtok_r(NULL, delim, &save_ptr))) {
|
|
|
|
ERROR("Error parsing fs_mgr_options\n");
|
|
|
|
return 0;
|
|
|
|
}
|
2013-02-13 20:58:40 +00:00
|
|
|
fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
|
|
|
|
&key_loc, &part_length,
|
|
|
|
&label, &partnum,
|
|
|
|
NULL, 0);
|
|
|
|
fstab->recs[cnt].key_loc = key_loc;
|
|
|
|
fstab->recs[cnt].length = part_length;
|
|
|
|
fstab->recs[cnt].label = label;
|
|
|
|
fstab->recs[cnt].partnum = partnum;
|
2012-01-07 03:09:42 +00:00
|
|
|
cnt++;
|
|
|
|
}
|
|
|
|
fclose(fstab_file);
|
|
|
|
|
|
|
|
return fstab;
|
|
|
|
}
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
void fs_mgr_free_fstab(struct fstab *fstab)
|
2012-01-07 03:09:42 +00:00
|
|
|
{
|
2013-02-13 20:58:40 +00:00
|
|
|
int i;
|
2012-01-07 03:09:42 +00:00
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
for (i = 0; i < fstab->num_entries; i++) {
|
2012-01-07 03:09:42 +00:00
|
|
|
/* Free the pointers return by strdup(3) */
|
2013-02-13 20:58:40 +00:00
|
|
|
free(fstab->recs[i].blk_device);
|
|
|
|
free(fstab->recs[i].mount_point);
|
|
|
|
free(fstab->recs[i].fs_type);
|
|
|
|
free(fstab->recs[i].fs_options);
|
|
|
|
free(fstab->recs[i].key_loc);
|
|
|
|
free(fstab->recs[i].label);
|
2012-01-07 03:09:42 +00:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
/* Free the fstab_recs array created by calloc(3) */
|
|
|
|
free(fstab->recs);
|
|
|
|
|
|
|
|
/* Free the fstab filename */
|
|
|
|
free(fstab->fstab_filename);
|
|
|
|
|
|
|
|
/* Free fstab */
|
2012-01-07 03:09:42 +00:00
|
|
|
free(fstab);
|
|
|
|
}
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
static void check_fs(char *blk_device, char *fs_type, char *target)
|
2012-01-07 03:09:42 +00:00
|
|
|
{
|
|
|
|
int status;
|
2012-07-24 02:34:00 +00:00
|
|
|
int ret;
|
|
|
|
long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;
|
|
|
|
char *tmpmnt_opts = "nomblk_io_submit,errors=remount-ro";
|
fs_mgr: Capture the output of e2fsck and add to the kernel log
Currently, the output of e2fsck is not saved, and we have no insight
into how many errors e2fsck is finding and fixing. Using the new
abbreviated logging feature in liblogwrap, up to the first 100 lines,
and last 4K bytes of the output of e2fsck is captured by fs_mgr, and
added to the kernel log.
Usually, the filesystem will be clean, and this will only add a few
lines to the kernel log on boot, but when things go wrong, it should
save enough to indicate what the problem is, without potentially
filling the kernel log with only e2fsck output if the filesystem is
really corrupted.
Change-Id: I9c264798e6fe721c8f818b5ce15d0975027ddbdd
2013-03-20 02:38:44 +00:00
|
|
|
char *e2fsck_argv[] = {
|
|
|
|
E2FSCK_BIN,
|
|
|
|
"-y",
|
|
|
|
blk_device
|
|
|
|
};
|
2012-01-07 03:09:42 +00:00
|
|
|
|
|
|
|
/* Check for the types of filesystems we know how to check */
|
2013-02-13 20:58:40 +00:00
|
|
|
if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
|
2012-07-24 02:34:00 +00:00
|
|
|
/*
|
|
|
|
* First try to mount and unmount the filesystem. We do this because
|
|
|
|
* the kernel is more efficient than e2fsck in running the journal and
|
|
|
|
* processing orphaned inodes, and on at least one device with a
|
|
|
|
* performance issue in the emmc firmware, it can take e2fsck 2.5 minutes
|
|
|
|
* to do what the kernel does in about a second.
|
|
|
|
*
|
|
|
|
* After mounting and unmounting the filesystem, run e2fsck, and if an
|
|
|
|
* error is recorded in the filesystem superblock, e2fsck will do a full
|
|
|
|
* check. Otherwise, it does nothing. If the kernel cannot mount the
|
|
|
|
* filesytsem due to an error, e2fsck is still run to do a full check
|
|
|
|
* fix the filesystem.
|
|
|
|
*/
|
2013-02-13 20:58:40 +00:00
|
|
|
ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
|
|
|
|
if (!ret) {
|
2012-07-24 02:34:00 +00:00
|
|
|
umount(target);
|
|
|
|
}
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
INFO("Running %s on %s\n", E2FSCK_BIN, blk_device);
|
fs_mgr: Capture the output of e2fsck and add to the kernel log
Currently, the output of e2fsck is not saved, and we have no insight
into how many errors e2fsck is finding and fixing. Using the new
abbreviated logging feature in liblogwrap, up to the first 100 lines,
and last 4K bytes of the output of e2fsck is captured by fs_mgr, and
added to the kernel log.
Usually, the filesystem will be clean, and this will only add a few
lines to the kernel log on boot, but when things go wrong, it should
save enough to indicate what the problem is, without potentially
filling the kernel log with only e2fsck output if the filesystem is
really corrupted.
Change-Id: I9c264798e6fe721c8f818b5ce15d0975027ddbdd
2013-03-20 02:38:44 +00:00
|
|
|
|
|
|
|
ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv), e2fsck_argv,
|
|
|
|
&status, true, LOG_KLOG, true);
|
|
|
|
|
|
|
|
if (ret < 0) {
|
2012-01-07 03:09:42 +00:00
|
|
|
/* No need to check for error in fork, we can't really handle it now */
|
fs_mgr: Capture the output of e2fsck and add to the kernel log
Currently, the output of e2fsck is not saved, and we have no insight
into how many errors e2fsck is finding and fixing. Using the new
abbreviated logging feature in liblogwrap, up to the first 100 lines,
and last 4K bytes of the output of e2fsck is captured by fs_mgr, and
added to the kernel log.
Usually, the filesystem will be clean, and this will only add a few
lines to the kernel log on boot, but when things go wrong, it should
save enough to indicate what the problem is, without potentially
filling the kernel log with only e2fsck output if the filesystem is
really corrupted.
Change-Id: I9c264798e6fe721c8f818b5ce15d0975027ddbdd
2013-03-20 02:38:44 +00:00
|
|
|
ERROR("Failed trying to run %s\n", E2FSCK_BIN);
|
2012-01-07 03:09:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void remove_trailing_slashes(char *n)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = strlen(n) - 1;
|
|
|
|
while ((*(n + len) == '/') && len) {
|
|
|
|
*(n + len) = '\0';
|
|
|
|
len--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-16 23:41:32 +00:00
|
|
|
/*
|
|
|
|
* Mark the given block device as read-only, using the BLKROSET ioctl.
|
|
|
|
* Return 0 on success, and -1 on error.
|
|
|
|
*/
|
|
|
|
static void fs_set_blk_ro(const char *blockdev)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
int ON = 1;
|
|
|
|
|
|
|
|
fd = open(blockdev, O_RDONLY);
|
|
|
|
if (fd < 0) {
|
|
|
|
// should never happen
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ioctl(fd, BLKROSET, &ON);
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __mount(): wrapper around the mount() system call which also
|
|
|
|
* sets the underlying block device to read-only if the mount is read-only.
|
|
|
|
* See "man 2 mount" for return values.
|
|
|
|
*/
|
|
|
|
static int __mount(const char *source, const char *target,
|
|
|
|
const char *filesystemtype, unsigned long mountflags,
|
|
|
|
const void *data)
|
|
|
|
{
|
|
|
|
int ret = mount(source, target, filesystemtype, mountflags, data);
|
|
|
|
|
|
|
|
if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
|
|
|
|
fs_set_blk_ro(source);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-01-07 03:09:42 +00:00
|
|
|
static int fs_match(char *in1, char *in2)
|
|
|
|
{
|
|
|
|
char *n1;
|
|
|
|
char *n2;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
n1 = strdup(in1);
|
|
|
|
n2 = strdup(in2);
|
|
|
|
|
|
|
|
remove_trailing_slashes(n1);
|
|
|
|
remove_trailing_slashes(n2);
|
|
|
|
|
|
|
|
ret = !strcmp(n1, n2);
|
|
|
|
|
|
|
|
free(n1);
|
|
|
|
free(n2);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
int fs_mgr_mount_all(struct fstab *fstab)
|
2012-01-07 03:09:42 +00:00
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
int encrypted = 0;
|
|
|
|
int ret = -1;
|
|
|
|
int mret;
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
if (!fstab) {
|
2012-01-07 03:09:42 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
for (i = 0; i < fstab->num_entries; i++) {
|
|
|
|
/* Don't mount entries that are managed by vold */
|
2013-02-23 01:36:21 +00:00
|
|
|
if (fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) {
|
2013-02-13 20:58:40 +00:00
|
|
|
continue;
|
2012-01-07 03:09:42 +00:00
|
|
|
}
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
/* Skip raw partition entries such as boot, recovery, etc */
|
|
|
|
if (!strcmp(fstab->recs[i].fs_type, "emmc") ||
|
|
|
|
!strcmp(fstab->recs[i].fs_type, "mtd")) {
|
|
|
|
continue;
|
2012-01-07 03:09:42 +00:00
|
|
|
}
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
|
|
|
|
wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
|
|
|
|
check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
|
|
|
|
fstab->recs[i].mount_point);
|
|
|
|
}
|
|
|
|
|
2013-04-16 23:41:32 +00:00
|
|
|
mret = __mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point,
|
|
|
|
fstab->recs[i].fs_type, fstab->recs[i].flags,
|
|
|
|
fstab->recs[i].fs_options);
|
2012-01-07 03:09:42 +00:00
|
|
|
if (!mret) {
|
|
|
|
/* Success! Go get the next one */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* mount(2) returned an error, check if it's encrypted and deal with it */
|
2013-02-13 20:58:40 +00:00
|
|
|
if ((fstab->recs[i].fs_mgr_flags & MF_CRYPT) &&
|
|
|
|
!partition_wiped(fstab->recs[i].blk_device)) {
|
2012-01-07 03:09:42 +00:00
|
|
|
/* Need to mount a tmpfs at this mountpoint for now, and set
|
|
|
|
* properties that vold will query later for decrypting
|
|
|
|
*/
|
2013-02-13 20:58:40 +00:00
|
|
|
if (mount("tmpfs", fstab->recs[i].mount_point, "tmpfs",
|
2012-01-07 03:09:42 +00:00
|
|
|
MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS) < 0) {
|
|
|
|
ERROR("Cannot mount tmpfs filesystem for encrypted fs at %s\n",
|
2013-02-13 20:58:40 +00:00
|
|
|
fstab->recs[i].mount_point);
|
2012-01-07 03:09:42 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
encrypted = 1;
|
|
|
|
} else {
|
|
|
|
ERROR("Cannot mount filesystem on %s at %s\n",
|
2013-02-13 20:58:40 +00:00
|
|
|
fstab->recs[i].blk_device, fstab->recs[i].mount_point);
|
2012-01-07 03:09:42 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (encrypted) {
|
|
|
|
ret = 1;
|
|
|
|
} else {
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
/* If tmp_mount_point is non-null, mount the filesystem there. This is for the
|
2012-01-07 03:09:42 +00:00
|
|
|
* tmp mount we do to check the user password
|
|
|
|
*/
|
2013-02-13 20:58:40 +00:00
|
|
|
int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device,
|
|
|
|
char *tmp_mount_point)
|
2012-01-07 03:09:42 +00:00
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
int ret = -1;
|
|
|
|
char *m;
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
if (!fstab) {
|
2012-01-07 03:09:42 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
for (i = 0; i < fstab->num_entries; i++) {
|
|
|
|
if (!fs_match(fstab->recs[i].mount_point, n_name)) {
|
2012-01-07 03:09:42 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We found our match */
|
2013-02-13 20:58:40 +00:00
|
|
|
/* If this is a raw partition, report an error */
|
|
|
|
if (!strcmp(fstab->recs[i].fs_type, "emmc") ||
|
|
|
|
!strcmp(fstab->recs[i].fs_type, "mtd")) {
|
|
|
|
ERROR("Cannot mount filesystem of type %s on %s\n",
|
|
|
|
fstab->recs[i].fs_type, n_blk_device);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2012-01-07 03:09:42 +00:00
|
|
|
/* First check the filesystem if requested */
|
2013-02-13 20:58:40 +00:00
|
|
|
if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
|
|
|
|
wait_for_file(n_blk_device, WAIT_TIMEOUT);
|
2012-01-07 03:09:42 +00:00
|
|
|
}
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
|
|
|
|
check_fs(n_blk_device, fstab->recs[i].fs_type,
|
|
|
|
fstab->recs[i].mount_point);
|
2012-01-07 03:09:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Now mount it where requested */
|
2013-02-13 20:58:40 +00:00
|
|
|
if (tmp_mount_point) {
|
|
|
|
m = tmp_mount_point;
|
2012-01-07 03:09:42 +00:00
|
|
|
} else {
|
2013-02-13 20:58:40 +00:00
|
|
|
m = fstab->recs[i].mount_point;
|
2012-01-07 03:09:42 +00:00
|
|
|
}
|
2013-04-16 23:41:32 +00:00
|
|
|
if (__mount(n_blk_device, m, fstab->recs[i].fs_type,
|
|
|
|
fstab->recs[i].flags, fstab->recs[i].fs_options)) {
|
2012-01-07 03:09:42 +00:00
|
|
|
ERROR("Cannot mount filesystem on %s at %s\n",
|
2013-02-13 20:58:40 +00:00
|
|
|
n_blk_device, m);
|
2012-01-07 03:09:42 +00:00
|
|
|
goto out;
|
|
|
|
} else {
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We didn't find a match, say so and return an error */
|
2013-02-13 20:58:40 +00:00
|
|
|
ERROR("Cannot find mount point %s in fstab\n", fstab->recs[i].mount_point);
|
2012-01-07 03:09:42 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* mount a tmpfs filesystem at the given point.
|
|
|
|
* return 0 on success, non-zero on failure.
|
|
|
|
*/
|
|
|
|
int fs_mgr_do_tmpfs_mount(char *n_name)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = mount("tmpfs", n_name, "tmpfs",
|
|
|
|
MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS);
|
|
|
|
if (ret < 0) {
|
|
|
|
ERROR("Cannot mount tmpfs filesystem at %s\n", n_name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Success */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
int fs_mgr_unmount_all(struct fstab *fstab)
|
2012-01-07 03:09:42 +00:00
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
int ret = 0;
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
if (!fstab) {
|
2012-01-07 03:09:42 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
while (fstab->recs[i].blk_device) {
|
|
|
|
if (umount(fstab->recs[i].mount_point)) {
|
|
|
|
ERROR("Cannot unmount filesystem at %s\n", fstab->recs[i].mount_point);
|
2012-01-07 03:09:42 +00:00
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* key_loc must be at least PROPERTY_VALUE_MAX bytes long
|
|
|
|
*
|
2013-02-13 20:58:40 +00:00
|
|
|
* real_blk_device must be at least PROPERTY_VALUE_MAX bytes long
|
2012-01-07 03:09:42 +00:00
|
|
|
*/
|
2013-02-13 20:58:40 +00:00
|
|
|
int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc, char *real_blk_device, int size)
|
2012-01-07 03:09:42 +00:00
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
if (!fstab) {
|
2012-01-07 03:09:42 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* Initialize return values to null strings */
|
|
|
|
if (key_loc) {
|
|
|
|
*key_loc = '\0';
|
|
|
|
}
|
2013-02-13 20:58:40 +00:00
|
|
|
if (real_blk_device) {
|
|
|
|
*real_blk_device = '\0';
|
2012-01-07 03:09:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Look for the encryptable partition to find the data */
|
2013-02-13 20:58:40 +00:00
|
|
|
for (i = 0; i < fstab->num_entries; i++) {
|
|
|
|
/* Don't deal with vold managed enryptable partitions here */
|
|
|
|
if (fstab->recs[i].fs_mgr_flags & MF_VOLDMANAGED) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!(fstab->recs[i].fs_mgr_flags & MF_CRYPT)) {
|
2012-01-07 03:09:42 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We found a match */
|
|
|
|
if (key_loc) {
|
2013-02-13 20:58:40 +00:00
|
|
|
strlcpy(key_loc, fstab->recs[i].key_loc, size);
|
2012-01-07 03:09:42 +00:00
|
|
|
}
|
2013-02-13 20:58:40 +00:00
|
|
|
if (real_blk_device) {
|
|
|
|
strlcpy(real_blk_device, fstab->recs[i].blk_device, size);
|
2012-01-07 03:09:42 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-02-13 20:58:40 +00:00
|
|
|
/* Add an entry to the fstab, and return 0 on success or -1 on error */
|
|
|
|
int fs_mgr_add_entry(struct fstab *fstab,
|
|
|
|
const char *mount_point, const char *fs_type,
|
|
|
|
const char *blk_device, long long length)
|
|
|
|
{
|
|
|
|
struct fstab_rec *new_fstab_recs;
|
|
|
|
int n = fstab->num_entries;
|
|
|
|
|
|
|
|
new_fstab_recs = (struct fstab_rec *)
|
|
|
|
realloc(fstab->recs, sizeof(struct fstab_rec) * (n + 1));
|
|
|
|
|
|
|
|
if (!new_fstab_recs) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A new entry was added, so initialize it */
|
|
|
|
memset(&new_fstab_recs[n], 0, sizeof(struct fstab_rec));
|
|
|
|
new_fstab_recs[n].mount_point = strdup(mount_point);
|
|
|
|
new_fstab_recs[n].fs_type = strdup(fs_type);
|
|
|
|
new_fstab_recs[n].blk_device = strdup(blk_device);
|
|
|
|
new_fstab_recs[n].length = 0;
|
|
|
|
|
|
|
|
/* Update the fstab struct */
|
|
|
|
fstab->recs = new_fstab_recs;
|
|
|
|
fstab->num_entries++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!fstab) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < fstab->num_entries; i++) {
|
|
|
|
int len = strlen(fstab->recs[i].mount_point);
|
|
|
|
if (strncmp(path, fstab->recs[i].mount_point, len) == 0 &&
|
|
|
|
(path[len] == '\0' || path[len] == '/')) {
|
|
|
|
return &fstab->recs[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int fs_mgr_is_voldmanaged(struct fstab_rec *fstab)
|
|
|
|
{
|
|
|
|
return fstab->fs_mgr_flags & MF_VOLDMANAGED;
|
|
|
|
}
|
|
|
|
|
|
|
|
int fs_mgr_is_nonremovable(struct fstab_rec *fstab)
|
|
|
|
{
|
|
|
|
return fstab->fs_mgr_flags & MF_NONREMOVABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int fs_mgr_is_encryptable(struct fstab_rec *fstab)
|
|
|
|
{
|
|
|
|
return fstab->fs_mgr_flags & MF_CRYPT;
|
|
|
|
}
|
|
|
|
|