blob: 210156b2766a2da925b30088a5cd694bcf3cf8fd [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <dirent.h>
#include <fcntl.h>
#include <stdalign.h>
#include <string.h>
#include <unistd.h>
#include <fs-management/mount.h>
#include <gpt/gpt.h>
#include <zircon/device/block.h>
#include <zircon/device/device.h>
#include <zircon/device/vfs.h>
typedef union {
vfs_query_info_t info;
struct {
alignas(vfs_query_info_t) char h[sizeof(vfs_query_info_t)];
char name[MAX_FS_NAME_LEN + 1];
};
} vfs_query_info_wrapper_t;
void check_and_remount(const char* device_path, const char* mount_path, disk_format_t disk_format, mount_options_t* mount_options) {
int mountfd = open(mount_path, O_RDONLY);
if (mountfd > 0) {
vfs_query_info_wrapper_t wrapper;
ssize_t r = ioctl_vfs_query_fs(mountfd, &wrapper.info, sizeof(wrapper) - 1);
close(mountfd);
if (r <= (ssize_t)sizeof(vfs_query_info_t)) {
return;
}
wrapper.name[r - sizeof(vfs_query_info_t)] = '\0';
if (strcmp(wrapper.name, "memfs")) {
return;
}
} else if (mountfd != ZX_ERR_INTERNAL) {
printf("fixfs: couldn't open: %s %d\n", mount_path, mountfd);
return;
}
printf("fixfs: Found device %s not mounted at %s - proceed with reformat? (y/n)\n", device_path, mount_path);
char response;
scanf("%c", &response);
if (response != 'y') {
return;
}
mkfs_options_t mkfs_options = default_mkfs_options;
ssize_t r;
if ((r = mkfs(device_path, disk_format, launch_stdio_sync, &mkfs_options)) < 0) {
fprintf(stderr, "fixfs: Failed to format device %s: %ld\n", device_path, r);
return;
}
int devfd;
if ((devfd = open(device_path, O_RDWR)) < 0) {
fprintf(stderr, "fixfs: Error opening block device %s\n", device_path);
return;
}
zx_status_t status = mount(devfd, mount_path, disk_format, mount_options, launch_logs_async);
if (status != ZX_OK) {
fprintf(stderr, "fixfs: Error while mounting %s at %s: %d\n", device_path, mount_path, status);
} else {
printf("fixfs: Successfully mounted device %s at %s\n", device_path, mount_path);
}
close(devfd);
}
void check_and_remount_fat(const char* device_path, const char* topo_path, mount_options_t* mount_options) {
char mount_path[PATH_MAX] = "\0";
int dirfd = open(PATH_VOLUME, O_RDONLY);
bool found = false;
if (dirfd < 0) {
printf("fixfs: Error opening %s\n", PATH_VOLUME);
return;
}
DIR* dir = fdopendir(dirfd);
if (!dir) {
printf("fixfs: Error opening %s\n", PATH_VOLUME);
return;
}
// This loop has two purposes: First, to find if the device is mounted at any of
// the existing paths under volume/. Second, to find the first "free" mount path
// under volume/ so that we can mount the partition there (if we don't find it at
// another path). Note that if we find an unmounted device but no "free" mount path,
// the device will not be mounted.
struct dirent* de;
while ((de = readdir(dir)) != NULL) {
int mountfd = openat(dirfd, de->d_name, O_RDONLY);
if (mountfd < 0) {
printf("fixfs: Error opening %s/%s\n", PATH_VOLUME, de->d_name);
continue;
}
if (!strcmp(de->d_name, ".")) {
close(mountfd);
continue;
}
char reported_path[PATH_MAX];
vfs_query_info_wrapper_t wrapper;
ssize_t r = ioctl_vfs_query_fs(mountfd, &wrapper.info, sizeof(wrapper) - 1);
ssize_t path_len = ioctl_vfs_get_device_path(mountfd, reported_path, sizeof(reported_path));
close(mountfd);
if (r <= (ssize_t)sizeof(vfs_query_info_t)) {
continue;
}
wrapper.name[r - sizeof(vfs_query_info_t)] = '\0';
// use the first memfs path we find as the mount path
if (!strcmp(wrapper.name, "memfs")) {
if (strlen(mount_path) == 0) {
snprintf(mount_path, sizeof(mount_path), "%s/%s", PATH_VOLUME, de->d_name);
}
}
// return if we found where this device is mounted
if (path_len > 0 && !strcmp(topo_path, reported_path)) {
found = true;
break;
}
}
closedir(dir);
close(dirfd);
if (found) {
return;
}
if (strlen(mount_path) == 0) {
printf("fixfs: Unable to mount device %s - no free paths found\n", device_path);
return;
}
printf("fixfs: Found device %s not mounted at %s - proceed with fsck? (y/n)\n", device_path, mount_path);
char response;
scanf("%c", &response);
if (response != 'y') {
return;
}
zx_status_t r;
fsck_options_t fsck_options = default_fsck_options;
fsck_options.always_modify = true;
if ((r = fsck(device_path, DISK_FORMAT_FAT, &fsck_options, launch_stdio_sync)) < 0) {
fprintf(stderr, "fs_fsck: Failed to check device: %d\n", r);
return;
}
int devfd;
if ((devfd = open(device_path, O_RDWR)) < 0) {
fprintf(stderr, "fixfs: Error opening block device %s\n", device_path);
return;
}
zx_status_t status = mount(devfd, mount_path, DISK_FORMAT_FAT, mount_options, launch_logs_async);
if (status != ZX_OK) {
fprintf(stderr, "fixfs: Error while mounting %s at %s: %d\n", device_path, mount_path, status);
} else {
printf("fixfs: Successfully mounted device %s at %s\n", device_path, mount_path);
}
close(devfd);
}
zx_status_t process_block_device(const char* device_name) {
char device_path[PATH_MAX];
disk_format_t disk_format;
mount_options_t mount_options;
snprintf(device_path, sizeof(device_path), "%s/%s", PATH_DEV_BLOCK, device_name);
int devfd = open(device_path, O_RDONLY);
if (devfd < 0) {
printf("fixfs: Error opening block device %s\n", device_path);
return ZX_ERR_ACCESS_DENIED;
}
disk_format = detect_disk_format(devfd);
mount_options = default_mount_options;
uint8_t guid[GPT_GUID_LEN];
ssize_t len = ioctl_block_get_type_guid(devfd, guid, sizeof(guid));
char topo_path[PATH_MAX];
ssize_t r = ioctl_device_get_topo_path(devfd, topo_path, sizeof(topo_path));
if (r <= 0) {
strcpy(topo_path, "UNKNOWN");
}
close(devfd);
switch (disk_format) {
case DISK_FORMAT_BLOBFS: {
mount_options.create_mountpoint = true;
check_and_remount(device_path, PATH_BLOBSTORE, disk_format, &mount_options);
break;
}
case DISK_FORMAT_MINFS: {
if (gpt_is_sys_guid(guid, len)) {
mount_options.readonly = true;
mount_options.wait_until_ready = true;
mount_options.create_mountpoint = true;
check_and_remount(device_path, PATH_SYSTEM, disk_format, &mount_options);
} else if (gpt_is_data_guid(guid, len)) {
mount_options.wait_until_ready = true;
check_and_remount(device_path, PATH_DATA, disk_format, &mount_options);
}
break;
}
case DISK_FORMAT_FAT: {
if (!gpt_is_efi_guid(guid, len)) {
mount_options.readonly = false;
mount_options.wait_until_ready = true;
check_and_remount_fat(device_path, topo_path, &mount_options);
}
break;
}
default: {
break;
}
}
return ZX_OK;
}
// This will only reformat the first matching device found for a particular mount path
int main(int argc, char** argv) {
struct dirent* de;
DIR* dir = opendir(PATH_DEV_BLOCK);
if (!dir) {
printf("fixfs: Error opening %s\n", PATH_DEV_BLOCK);
return -1;
}
while ((de = readdir(dir)) != NULL) {
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
continue;
}
if (process_block_device(de->d_name) != ZX_OK) {
return -1;
}
}
closedir(dir);
printf("fixfs: Done!\n");
return 0;
}