summaryrefslogtreecommitdiffstats
path: root/fs_mgr/fs_mgr.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs_mgr/fs_mgr.c')
-rw-r--r--fs_mgr/fs_mgr.c221
1 files changed, 196 insertions, 25 deletions
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index 8533ff6..5f639b7 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -28,6 +28,10 @@
#include <libgen.h>
#include <time.h>
#include <sys/swap.h>
+#include <dirent.h>
+#include <ext4.h>
+#include <ext4_sb.h>
+#include <ext4_crypt.h>
#include <linux/loop.h>
#include <private/android_filesystem_config.h>
@@ -116,10 +120,23 @@ static void check_fs(char *blk_device, char *fs_type, char *target)
* filesytsem due to an error, e2fsck is still run to do a full check
* fix the filesystem.
*/
+ errno = 0;
ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
- INFO("%s(): mount(%s,%s,%s)=%d\n", __func__, blk_device, target, fs_type, ret);
+ INFO("%s(): mount(%s,%s,%s)=%d: %s\n",
+ __func__, blk_device, target, fs_type, ret, strerror(errno));
if (!ret) {
- umount(target);
+ int i;
+ for (i = 0; i < 5; i++) {
+ // Try to umount 5 times before continuing on.
+ // Should we try rebooting if all attempts fail?
+ int result = umount(target);
+ if (result == 0) {
+ INFO("%s(): unmount(%s) succeeded\n", __func__, target);
+ break;
+ }
+ ERROR("%s(): umount(%s)=%d: %s\n", __func__, target, result, strerror(errno));
+ sleep(1);
+ }
}
/*
@@ -176,19 +193,22 @@ static void remove_trailing_slashes(char *n)
* 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 fs_mgr_set_blk_ro(const char *blockdev)
{
int fd;
+ int rc = -1;
int ON = 1;
- fd = open(blockdev, O_RDONLY);
+ fd = TEMP_FAILURE_RETRY(open(blockdev, O_RDONLY | O_CLOEXEC));
if (fd < 0) {
// should never happen
- return;
+ return rc;
}
- ioctl(fd, BLKROSET, &ON);
- close(fd);
+ rc = ioctl(fd, BLKROSET, &ON);
+ TEMP_FAILURE_RETRY(close(fd));
+
+ return rc;
}
/*
@@ -214,7 +234,7 @@ static int __mount(const char *source, const char *target, const struct fstab_re
save_errno = errno;
INFO("%s(source=%s,target=%s,type=%s)=%d\n", __func__, source, target, rec->fs_type, ret);
if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
- fs_set_blk_ro(source);
+ fs_mgr_set_blk_ro(source);
}
errno = save_errno;
return ret;
@@ -338,6 +358,150 @@ static int mount_with_alternatives(struct fstab *fstab, int start_idx, int *end_
return 0;
}
+static int translate_ext_labels(struct fstab_rec *rec)
+{
+ DIR *blockdir = NULL;
+ struct dirent *ent;
+ char *label;
+ size_t label_len;
+ int ret = -1;
+
+ if (strncmp(rec->blk_device, "LABEL=", 6))
+ return 0;
+
+ label = rec->blk_device + 6;
+ label_len = strlen(label);
+
+ if (label_len > 16) {
+ ERROR("FS label is longer than allowed by filesystem\n");
+ goto out;
+ }
+
+
+ blockdir = opendir("/dev/block");
+ if (!blockdir) {
+ ERROR("couldn't open /dev/block\n");
+ goto out;
+ }
+
+ while ((ent = readdir(blockdir))) {
+ int fd;
+ char super_buf[1024];
+ struct ext4_super_block *sb;
+
+ if (ent->d_type != DT_BLK)
+ continue;
+
+ fd = openat(dirfd(blockdir), ent->d_name, O_RDONLY);
+ if (fd < 0) {
+ ERROR("Cannot open block device /dev/block/%s\n", ent->d_name);
+ goto out;
+ }
+
+ if (TEMP_FAILURE_RETRY(lseek(fd, 1024, SEEK_SET)) < 0 ||
+ TEMP_FAILURE_RETRY(read(fd, super_buf, 1024)) != 1024) {
+ /* Probably a loopback device or something else without a readable
+ * superblock.
+ */
+ close(fd);
+ continue;
+ }
+
+ sb = (struct ext4_super_block *)super_buf;
+ if (sb->s_magic != EXT4_SUPER_MAGIC) {
+ INFO("/dev/block/%s not ext{234}\n", ent->d_name);
+ continue;
+ }
+
+ if (!strncmp(label, sb->s_volume_name, label_len)) {
+ char *new_blk_device;
+
+ if (asprintf(&new_blk_device, "/dev/block/%s", ent->d_name) < 0) {
+ ERROR("Could not allocate block device string\n");
+ goto out;
+ }
+
+ INFO("resolved label %s to %s\n", rec->blk_device, new_blk_device);
+
+ free(rec->blk_device);
+ rec->blk_device = new_blk_device;
+ ret = 0;
+ break;
+ }
+ }
+
+out:
+ closedir(blockdir);
+ return ret;
+}
+
+// Check to see if a mountable volume has encryption requirements
+static int handle_encryptable(struct fstab *fstab, const struct fstab_rec* rec)
+{
+ /* If this is block encryptable, need to trigger encryption */
+ if ( (rec->fs_mgr_flags & MF_FORCECRYPT)
+ || (device_is_force_encrypted() && fs_mgr_is_encryptable(rec))) {
+ if (umount(rec->mount_point) == 0) {
+ return FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
+ } else {
+ WARNING("Could not umount %s (%s) - allow continue unencrypted\n",
+ rec->mount_point, strerror(errno));
+ return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
+ }
+ }
+
+ // Deal with file level encryption
+ if (rec->fs_mgr_flags & MF_FILEENCRYPTION) {
+ // Default or not yet initialized encryption requires no more work here
+ if (!e4crypt_non_default_key(rec->mount_point)) {
+ INFO("%s is default file encrypted\n", rec->mount_point);
+ return FS_MGR_MNTALL_DEV_DEFAULT_FILE_ENCRYPTED;
+ }
+
+ INFO("%s is non-default file encrypted\n", rec->mount_point);
+
+ // Uses non-default key, so must unmount and set up temp file system
+ if (umount(rec->mount_point)) {
+ ERROR("Failed to umount %s - rebooting\n", rec->mount_point);
+ return FS_MGR_MNTALL_FAIL;
+ }
+
+ if (fs_mgr_do_tmpfs_mount(rec->mount_point) != 0) {
+ ERROR("Failed to mount a tmpfs at %s\n", rec->mount_point);
+ return FS_MGR_MNTALL_FAIL;
+ }
+
+ // Mount data temporarily so we can access unencrypted dir
+ char tmp_mnt[PATH_MAX];
+ strlcpy(tmp_mnt, rec->mount_point, sizeof(tmp_mnt));
+ strlcat(tmp_mnt, "/tmp_mnt", sizeof(tmp_mnt));
+ if (mkdir(tmp_mnt, 0700)) {
+ ERROR("Failed to create temp mount point\n");
+ return FS_MGR_MNTALL_FAIL;
+ }
+
+ if (fs_mgr_do_mount(fstab, rec->mount_point,
+ rec->blk_device, tmp_mnt)) {
+ ERROR("Error temp mounting encrypted file system\n");
+ return FS_MGR_MNTALL_FAIL;
+ }
+
+ // Link it to the normal place so ext4_crypt functions work normally
+ strlcat(tmp_mnt, "/unencrypted", sizeof(tmp_mnt));
+ char link_path[PATH_MAX];
+ strlcpy(link_path, rec->mount_point, sizeof(link_path));
+ strlcat(link_path, "/unencrypted", sizeof(link_path));
+ if (symlink(tmp_mnt, link_path)) {
+ ERROR("Error creating symlink to unencrypted directory\n");
+ return FS_MGR_MNTALL_FAIL;
+ }
+
+ return FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED;
+ }
+
+ return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
+}
+
/* When multiple fstab records share the same mount_point, it will
* try to mount each one in turn, and ignore any duplicates after a
* first successful mount.
@@ -369,6 +533,17 @@ int fs_mgr_mount_all(struct fstab *fstab)
continue;
}
+ /* Translate LABEL= file system labels into block devices */
+ if (!strcmp(fstab->recs[i].fs_type, "ext2") ||
+ !strcmp(fstab->recs[i].fs_type, "ext3") ||
+ !strcmp(fstab->recs[i].fs_type, "ext4")) {
+ int tret = translate_ext_labels(&fstab->recs[i]);
+ if (tret < 0) {
+ ERROR("Could not translate label to block device\n");
+ continue;
+ }
+ }
+
if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
}
@@ -391,25 +566,21 @@ int fs_mgr_mount_all(struct fstab *fstab)
/* Deal with encryptability. */
if (!mret) {
- /* If this is encryptable, need to trigger encryption */
- if ( (fstab->recs[attempted_idx].fs_mgr_flags & MF_FORCECRYPT)
- || (device_is_force_encrypted()
- && fs_mgr_is_encryptable(&fstab->recs[attempted_idx]))) {
- if (umount(fstab->recs[attempted_idx].mount_point) == 0) {
- if (encryptable == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
- ERROR("Will try to encrypt %s %s\n", fstab->recs[attempted_idx].mount_point,
- fstab->recs[attempted_idx].fs_type);
- encryptable = FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
- } else {
- ERROR("Only one encryptable/encrypted partition supported\n");
- encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
- }
- } else {
- INFO("Could not umount %s - allow continue unencrypted\n",
- fstab->recs[attempted_idx].mount_point);
- continue;
+ int status = handle_encryptable(fstab, &fstab->recs[attempted_idx]);
+
+ if (status == FS_MGR_MNTALL_FAIL) {
+ /* Fatal error - no point continuing */
+ return status;
+ }
+
+ if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
+ if (encryptable != FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
+ // Log and continue
+ ERROR("Only one encryptable/encrypted partition supported\n");
}
+ encryptable = status;
}
+
/* Success! Go get the next one */
continue;
}