源码:http://aospxref.com/?
系统启动过程可以参看?Android Init 进程启动流程研究
第一阶段启动??First stage init
init 的main函数:
//system/core/init/main.cpp
using namespace android::init;
int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
__asan_set_error_report_callback(AsanReportCallback);
#endif
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
if (argc > 1) {
if (!strcmp(argv[1], "subcontext")) {
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
return SubcontextMain(argc, argv, &function_map);
}
if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv);
}
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
return FirstStageMain(argc, argv);
}
跟踪?FirstStageMain
///system/core/init/first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
boot_clock::time_point start_time = boot_clock::now();
std::vector<std::pair<std::string, int>> errors;
#define CHECKCALL(x) \
if ((x) != 0) errors.emplace_back(#x " failed", errno);
// Clear the umask.
umask(0);
CHECKCALL(clearenv());
CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
// Get the basic filesystem setup we need put together in the initramdisk
// on / and then we'll let the rc file figure out the rest.
CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
CHECKCALL(mkdir("/dev/pts", 0755));
CHECKCALL(mkdir("/dev/socket", 0755));
CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)
CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
#undef MAKE_STR
// Don't expose the raw commandline to unprivileged processes.
CHECKCALL(chmod("/proc/cmdline", 0440));
std::string cmdline;
android::base::ReadFileToString("/proc/cmdline", &cmdline);
gid_t groups[] = {AID_READPROC};
CHECKCALL(setgroups(arraysize(groups), groups));
CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));
if constexpr (WORLD_WRITABLE_KMSG) {
CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
}
CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
// This is needed for log wrapper, which gets called before ueventd runs.
CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
// These below mounts are done in first stage init so that first stage mount can mount
// subdirectories of /mnt/{vendor,product}/. Other mounts, not required by first stage mount,
// should be done in rc files.
// Mount staging areas for devices managed by vold
// See storage config details at http://source.android.com/devices/storage/
CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=1000"));
// /mnt/vendor is used to mount vendor-specific partitions that can not be
// part of the vendor partition, e.g. because they are mounted read-write.
CHECKCALL(mkdir("/mnt/vendor", 0755));
// /mnt/product is used to mount product-specific partitions that can not be
// part of the product partition, e.g. because they are mounted read-write.
CHECKCALL(mkdir("/mnt/product", 0755));
// /debug_ramdisk is used to preserve additional files from the debug ramdisk
CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"));
#undef CHECKCALL
SetStdioToDevNull(argv);
// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
// talk to the outside world...
InitKernelLogging(argv);
if (!errors.empty()) {
for (const auto& [error_string, error_errno] : errors) {
LOG(ERROR) << error_string << " " << strerror(error_errno);
}
LOG(FATAL) << "Init encountered errors starting first stage, aborting";
}
LOG(INFO) << "init first stage started!";
auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};
if (!old_root_dir) {
PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";
}
struct stat old_root_info;
if (stat("/", &old_root_info) != 0) {
PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
old_root_dir.reset();
}
auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline) : 0;
if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline), want_console)) {
if (want_console != FirstStageConsoleParam::DISABLED) {
LOG(ERROR) << "Failed to load kernel modules, starting console";
} else {
LOG(FATAL) << "Failed to load kernel modules";
}
}
if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {
StartConsole();
}
if (ForceNormalBoot(cmdline)) {
mkdir("/first_stage_ramdisk", 0755);
// SwitchRoot() must be called with a mount point as the target, so we bind mount the
// target directory to itself here.
if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
}
SwitchRoot("/first_stage_ramdisk");
}
// If this file is present, the second-stage init will use a userdebug sepolicy
// and load adb_debug.prop to allow adb root, if the device is unlocked.
if (access("/force_debuggable", F_OK) == 0) {
std::error_code ec; // to invoke the overloaded copy_file() that won't throw.
if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) ||
!fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) {
LOG(ERROR) << "Failed to setup debug ramdisk";
} else {
// setenv for second-stage init to read above kDebugRamdisk* files.
setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
}
}
if (!DoFirstStageMount()) {
LOG(FATAL) << "Failed to mount required partitions early ...";
}
struct stat new_root_info;
if (stat("/", &new_root_info) != 0) {
PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
old_root_dir.reset();
}
if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) {
FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);
}
SetInitAvbVersionInRecovery();
setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),
1);
const char* path = "/system/bin/init";
const char* args[] = {path, "selinux_setup", nullptr};
auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
execv(path, const_cast<char**>(args));
// execv() only returns if an error happened, in which case we
// panic and never fall through this conditional.
PLOG(FATAL) << "execv(\"" << path << "\") failed";
return 1;
}
DoFirstStageMount
//system/core/init/first_stage_mount.cpp
// Public functions
// ----------------
// Mounts partitions specified by fstab in device tree.
bool DoFirstStageMount() {
// Skips first stage mount if we're in recovery mode.
if (IsRecoveryMode()) {
LOG(INFO) << "First stage mount skipped (recovery mode)";
return true;
}
std::unique_ptr<FirstStageMount> handle = FirstStageMount::Create();
if (!handle) {
LOG(ERROR) << "Failed to create FirstStageMount";
return false;
}
return handle->DoFirstStageMount();
}
std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
auto fstab = ReadFirstStageFstab();
if (IsDtVbmetaCompatible(fstab)) {
return std::make_unique<FirstStageMountVBootV2>(std::move(fstab));
} else {
return std::make_unique<FirstStageMountVBootV1>(std::move(fstab));
}
}
static Fstab ReadFirstStageFstab() {
Fstab fstab;
//bool ReadFstabFromDt(Fstab* fstab, bool log = true);
if (!ReadFstabFromDt(&fstab)) { //???
if (ReadDefaultFstab(&fstab)) { //???
fstab.erase(std::remove_if(fstab.begin(), fstab.end(),
[](const auto& entry) {
return !entry.fs_mgr_flags.first_stage_mount;
}),
fstab.end());
} else {
LOG(INFO) << "Failed to fstab for first stage mount";
}
}
return fstab;
}
bool FirstStageMount::DoFirstStageMount() {
if (!IsDmLinearEnabled() && fstab_.empty()) {
// Nothing to mount.
LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
return true;
}
if (!InitDevices()) return false;
if (!MountPartitions()) return false;
return true;
}
bool FirstStageMount::InitDevices() {
std::set<std::string> devices;
GetSuperDeviceName(&devices);
if (!GetDmVerityDevices(&devices)) {
return false;
}
if (!InitRequiredDevices(std::move(devices))) {
return false;
}
if (IsDmLinearEnabled()) {
auto super_symlink = "/dev/block/by-name/"s + super_partition_name_;
if (!android::base::Realpath(super_symlink, &super_path_)) {
PLOG(ERROR) << "realpath failed: " << super_symlink;
return false;
}
}
return true;
}
void FirstStageMount::GetSuperDeviceName(std::set<std::string>* devices) {
// Add any additional devices required for dm-linear mappings.
if (!IsDmLinearEnabled()) {
return;
}
devices->emplace(super_partition_name_);
}
FirstStageMount::FirstStageMount(Fstab fstab) : need_dm_verity_(false), fstab_(std::move(fstab)) {
super_partition_name_ = fs_mgr_get_super_partition_name(); //???
}
// Creates devices with uevent->partition_name matching ones in the given set.
// Found partitions will then be removed from it for the subsequent member
// function to check which devices are NOT created.
bool FirstStageMount::InitRequiredDevices(std::set<std::string> devices) {
//BlockDevInitializer block_dev_init_;
//往/sys目录下的所有设备发送uevent事件,创建device-mapper设备节点
if (!block_dev_init_.InitDeviceMapper()) { //???
return false;
}
if (devices.empty()) {
return true;
}
//创建dts中的vbmeta,vbmeta_system,vbmeta_vendor,boot,super分区设备
return block_dev_init_.InitDevices(std::move(devices)); //???
}
由于对GetSuperDeviceName不太明白,搜到如下文章 android R启动找不到super分区问题
//system/core/fs_mgr/fs_mgr_fstab.cpp
// Returns fstab entries parsed from the device tree if they exist
bool ReadFstabFromDt(Fstab* fstab, bool log) {
std::string fstab_buf = ReadFstabFromDt();
if (fstab_buf.empty()) {
if (log) LINFO << __FUNCTION__ << "(): failed to read fstab from dt";
return false;
}
std::unique_ptr<FILE, decltype(&fclose)> fstab_file(
fmemopen(static_cast<void*>(const_cast<char*>(fstab_buf.c_str())),
fstab_buf.length(), "r"), fclose);
if (!fstab_file) {
if (log) PERROR << __FUNCTION__ << "(): failed to create a file stream for fstab dt";
return false;
}
if (!ReadFstabFile(fstab_file.get(), false, fstab)) {
if (log) {
LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:" << std::endl
<< fstab_buf;
}
return false;
}
#ifndef NO_SKIP_MOUNT
SkipMountingPartitions(fstab);
#endif
return true;
}
std::string ReadFstabFromDt() {
if (!is_dt_compatible() || !IsDtFstabCompatible()) {
return {};
}
//const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");
//get_android_dt_dir 获取kernel命令行 "androidboot.android_dt_dir" 的键值,没有的话用kDefaultAndroidDtDir
//kernel cmdline ----> cat /proc/cmdline
std::string fstabdir_name = get_android_dt_dir() + "/fstab";
std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);
if (!fstabdir) return {}; //553没有dt fstab 直接返回
dirent* dp;
// Each element in fstab_dt_entries is <mount point, the line format in fstab file>.
std::vector<std::pair<std::string, std::string>> fstab_dt_entries;
while ((dp = readdir(fstabdir.get())) != NULL) {
// skip over name, compatible and .
if (dp->d_type != DT_DIR || dp->d_name[0] == '.') continue;
// create <dev> <mnt_point> <type> <mnt_flags> <fsmgr_flags>\n
std::vector<std::string> fstab_entry;
std::string file_name;
std::string value;
// skip a partition entry if the status property is present and not set to ok
file_name = android::base::StringPrintf("%s/%s/status", fstabdir_name.c_str(), dp->d_name);
if (ReadDtFile(file_name, &value)) {
if (value != "okay" && value != "ok") {
LINFO << "dt_fstab: Skip disabled entry for partition " << dp->d_name;
continue;
}
}
file_name = android::base::StringPrintf("%s/%s/dev", fstabdir_name.c_str(), dp->d_name);
if (!ReadDtFile(file_name, &value)) {
LERROR << "dt_fstab: Failed to find device for partition " << dp->d_name;
return {};
}
fstab_entry.push_back(value);
std::string mount_point;
file_name =
android::base::StringPrintf("%s/%s/mnt_point", fstabdir_name.c_str(), dp->d_name);
if (ReadDtFile(file_name, &value)) {
LINFO << "dt_fstab: Using a specified mount point " << value << " for " << dp->d_name;
mount_point = value;
} else {
mount_point = android::base::StringPrintf("/%s", dp->d_name);
}
fstab_entry.push_back(mount_point);
file_name = android::base::StringPrintf("%s/%s/type", fstabdir_name.c_str(), dp->d_name);
if (!ReadDtFile(file_name, &value)) {
LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
return {};
}
fstab_entry.push_back(value);
file_name = android::base::StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name);
if (!ReadDtFile(file_name, &value)) {
LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
return {};
}
fstab_entry.push_back(value);
file_name = android::base::StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name);
if (!ReadDtFile(file_name, &value)) {
LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
return {};
}
fstab_entry.push_back(value);
// Adds a fstab_entry to fstab_dt_entries, to be sorted by mount_point later.
fstab_dt_entries.emplace_back(mount_point, android::base::Join(fstab_entry, " "));
}
// Sort fstab_dt entries, to ensure /vendor is mounted before /vendor/abc is attempted.
std::sort(fstab_dt_entries.begin(), fstab_dt_entries.end(),
[](const auto& a, const auto& b) { return a.first < b.first; });
std::string fstab_result;
for (const auto& [_, dt_entry] : fstab_dt_entries) {
fstab_result += dt_entry + "\n";
}
return fstab_result;
}
// Loads the fstab file and combines with fstab entries passed in from device tree.
bool ReadDefaultFstab(Fstab* fstab) {
Fstab dt_fstab;
ReadFstabFromDt(&dt_fstab, false);
*fstab = std::move(dt_fstab);
std::string default_fstab_path;
// Use different fstab paths for normal boot and recovery boot, respectively
if (access("/system/bin/recovery", F_OK) == 0) { //recovery 模式
default_fstab_path = "/etc/recovery.fstab";
} else { // normal boot
default_fstab_path = GetFstabPath();
}
Fstab default_fstab;
if (!default_fstab_path.empty()) {
ReadFstabFromFile(default_fstab_path, &default_fstab);
} else {
LINFO << __FUNCTION__ << "(): failed to find device default fstab";
}
for (auto&& entry : default_fstab) {
fstab->emplace_back(std::move(entry));
}
return !fstab->empty();
}
// Identify path to fstab file. Lookup is based on pattern
// fstab.<fstab_suffix>, fstab.<hardware>, fstab.<hardware.platform> in
// folders /odm/etc, vendor/etc, or /.
std::string GetFstabPath() {
for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
std::string suffix;
//[ro.boot.hardware]: [HNR553T] 只有hardware满足条件
if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
for (const char* prefix : {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab."}) {
std::string fstab_path = prefix + suffix;
if (access(fstab_path.c_str(), F_OK) == 0) {
return fstab_path;
}
}
}
return "";
}
HNR553T:/proc/device-tree/firmware/android # ls compatible ?name ?vbmeta
553 的dt没有fstab
HNR553T:/proc/device-tree # find -name "fstab*" HNR553T:/proc/device-tree # find -name "fstab" HNR553T:/proc/device-tree # find -name "*fstab"
HNR553T:/ # cat /proc/cmdline earlycon=sprd_serial,0x20210000,115200n8 console=ttyS1,115200n8 loglevel=1 init=/init root=/dev/ram0 rw printk.devkmsg=on pcie_ports=compat swiotlb=2560 androidboot.hardware=HNR553T androidboot.dtbo_idx=0 lcd_id=ID8756 lcd_name=lcd_ft8756_txd_mipi_hdplus lcd_base=b0000000 lcd_size=2400x1080 logo_bpix=32 androidboot.ddrsize=6144M androidboot.ddrsize.range=[6144,) androidboot.lwfq.type=-1 androidboot.auto.efuse=-1 androidboot.auto.chipid=UMS9620-AA ?sysdump_magic=80001000 sysdump_re_flag=1 ?androidboot.wdten=0 ?androidboot.dswdten=disable modem=shutdown ?androidboot.meid=1 ?recoverytype=0 ?androidboot.fuse=0 rfboard.id=1 rfhw.id=18432 crystal=5 32k.less=0 pcb.version=0 androidboot.pmic.chipid=7520 cpcmdline=end powerup_reason=Reboot ?androidboot.verifiedbootstate=orange androidboot.flash.locked=0 ?androidboot.serialno=40322211589811 buildvariant=userdebug androidboot.vbmeta.device=PARTUUID=1.0 androidboot.vbmeta.avb_version=1.1 androidboot.vbmeta.device_state=unlocked androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=42560 androidboot.vbmeta.digest=c6f2d4e0d5d12cf691b2968b844c245f73146227c028bcc91d375531b464f3cb androidboot.vbmeta.invalidate_on_error=yes androidboot.veritymode=enforcing androidboot.slot_suffix=_a androidboot.force_normal_boot=1 androidboot.boot_devices=soc/soc:ap-ahb/22000000.ufs bootcause="Reboot into normal" pwroffcause="device power down" charge.shutdown_rtc_time=-1 ?charge.charge_cycle=-1 ?charge.basp=-1 ?charge.total_mah=-1 ?androidboot.vendor.skip.init=0
[ro.boot.flash.locked]: [0] [ro.boot.force_normal_boot]: [1] [ro.boot.fuse]: [0] [ro.boot.hardware]: [HNR553T] [ro.boot.lwfq.type]: [-1]
HNR553T:/ # ls /odm/etc/fstab.* ls: /odm/etc/fstab.*: No such file or directory
1|HNR553T:/ # ls /vendor/etc/fstab.* /vendor/etc/fstab.HNR553T ? ? ?/vendor/etc/fstab.factorytest /vendor/etc/fstab.ums9620_2c11?/vendor/etc/fstab.P50 ? ? ? ? ?/vendor/etc/fstab.ums9520_haps ? ? ? ?/vendor/etc/fstab.ums9620_2h10?/vendor/etc/fstab.cali ? ? ? ? /vendor/etc/fstab.ums9520_haps_flash ?/vendor/etc/fstab.ums9620_2h10_car?/vendor/etc/fstab.charge_mode ?/vendor/etc/fstab.ums9620_1h10 ? ? ? ?/vendor/etc/fstab.ums9620_3c10?/vendor/etc/fstab.enableswap ? /vendor/etc/fstab.ums9620_2c10
HNR553T:/ # ls /fstab.* ls: /fstab.*: No such file or directory
这样就找到了唯一符合条件的:/vendor/etc/fstab.HNR553T
HNR553T:/ # cat /vendor/etc/fstab.HNR553T # Android fstab file. # The filesystem that contains the filesystem checker binary (typically /system) cannot # specify MF_CHECK, and must come before any filesystems that do specify MF_CHECK # <src> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? <mnt_point> ?<type> ?<mnt_flags and options> ? ? ? ? ? ? <fs_mgr_flags>
system /system ext4 ro,barrier=1 wait,logical,first_stage_mount,avb_keys=/avb/q-gsi.avbpubkey:/avb/r-gsi.avbpubkey:/avb/s-gsi.avbpubkey,slotselect system_ext /system_ext ext4 ro,barrier=1 wait,logical,first_stage_mount,slotselect vendor /vendor ext4 ro,barrier=1 wait,logical,first_stage_mount,slotselect product /product ext4 ro,barrier=1 wait,logical,first_stage_mount,slotselect /dev/block/by-name/userdata /data ? ? ? ?f2fs noatime,nosuid,nodev,discard,inline_xattr,inline_data,inlinecrypt,fsync_mode=nobarrier latemount,wait,fileencryption=aes-256-xts:aes-256-cts:v2+inlinecrypt_optimized,keydirectory=/metadata/vold/metadata_encryption,check,reservedsize=128M,checkpoint=fs,formattable /dev/block/by-name/metadata /metadata ? ?ext4 nodev,noatime,nosuid,errors=panic wait,formattable,first_stage_mount,check /devices/platform/soc/soc:ipa-apb/25100000.usb3/25100000.dwc3/xhci-hcd.*.auto/usb* ? ? ?auto ? ? ? ? vfat defaults voldmanaged=usbdisk:auto /devices/platform/soc/soc:ap-ahb/22210000.sdio/mmc_host/mmc1/mmc1:*/block/mmcblk1 ? ? ?auto ? ? ? ? vfat defaults voldmanaged=sdcard0:auto,noemulatedsd /dev/block/by-name/prodnv ? ?/mnt/vendor ext4 noatime,nosuid,nodev,nomblk_io_submit,noauto_da_alloc wait,check /dev/block/by-name/cache ? ?/cache ? ? ? ext4 noatime,nosuid,nodev,nomblk_io_submit,noauto_da_alloc wait,check # Should after mount prodnv for prodnv wholly occupying /mnt/vendor /dev/block/by-name/socko /mnt/vendor/socko ext4 ro,noatime,nosuid,nodev,nomblk_io_submit,noauto_da_alloc wait,avb=socko,slotselect /dev/block/by-name/odmko /mnt/vendor/odmko ext4 ro,noatime,nosuid,nodev,nomblk_io_submit,noauto_da_alloc wait,avb=odmko,slotselect /dev/block/by-name/misc ? ? /misc ? ? ? ?emmc ? ?defaults ? ?defaults #/dev/block/memdisk.0 ? ?/system ? ? ?ext4 rw,barrier=1 ? ? ? ? ? ? ? ? ? ? ? ? ?wait #/dev/block/memdisk.1 ? ?/data ? ? ? ?ext4 noatime,nosuid,nodev,noauto_da_alloc,journal_async_commit,errors=panic wait
|