Android Vold简介(三)

 
之前两篇主要介绍了Vold的架构以及运行机制,本篇主要来介绍下Vold是内置存储和外置存储的mount流程。前面已经介绍过,无论什么存储,最终都会调用doMount()这个虚函数,对于不同类型的则会有不同的mount流程(即挂在到不同的文件系统下);对于内置存储,使用EmulatedVolume类来处理,而外接sd卡或者OTG设备则基本上都使用PublicVolume类来处理,因此uevent传递上来的消息会通知到vold到底这个存储介质属于什么类型。
 
首先来看下EmulatedVolume处理流程,首先会判断Primary标志,表明是内置主要的存储,一般都会跑进这个逻辑,因为只有data分区下的存储才会走这边;接着创建/mnt/runtime/default/emulated等四个目录,这个是Google针对sdcardfs添加的用来处理app之间相互的访问权限。拥有不同权限的app会bind mount到对应的目录,例如只有读权限的app则会bind到/mnt/runtime/read/emulated。

status_t EmulatedVolume::doMount() {
    // We could have migrated storage to an adopted private volume, so always
    // call primary storage "emulated" to avoid media rescans.
    std::string label = mLabel;  //存储介质的卷名
    if (getMountFlags() & MountFlags::kPrimary) {
        label = "emulated";
    }

    mFuseDefault = StringPrintf("/mnt/runtime/default/%s", label.c_str()); //默认权限,一般是只读权限;
    mFuseRead = StringPrintf("/mnt/runtime/read/%s", label.c_str());    //读权限
    mFuseWrite = StringPrintf("/mnt/runtime/write/%s", label.c_str()); //写权限
    mFuseFull = StringPrintf("/mnt/runtime/full/%s", label.c_str());  //所有权限

    setInternalPath(mRawPath);
    setPath(StringPrintf("/storage/%s", label.c_str()));

    //创建对应的四个目录
    if (fs_prepare_dir(mFuseDefault.c_str(), 0700, AID_ROOT, AID_ROOT) ||
        fs_prepare_dir(mFuseRead.c_str(), 0700, AID_ROOT, AID_ROOT) ||
        fs_prepare_dir(mFuseWrite.c_str(), 0700, AID_ROOT, AID_ROOT) ||
        fs_prepare_dir(mFuseFull.c_str(), 0700, AID_ROOT, AID_ROOT)) {
        PLOG(ERROR) << getId() << " failed to create mount points";
        return -errno;
    }

    dev_t before = GetDevice(mFuseFull);

    //创建子进程,启动sdcard进程来处理具体的挂在流程;
    if (!(mFusePid = fork())) {
        // clang-format off
        if (execl(kFusePath, kFusePath,      //执行sdcard进程
                "-u", "1023", // AID_MEDIA_RW  挂载使用的uid
                "-g", "1023", // AID_MEDIA_RW  挂载使用的gid
                "-m",
                "-w",
                "-G",
                "-i",
                "-o",
                mRawPath.c_str(),
                label.c_str(),
                NULL)) {
            // clang-format on
            PLOG(ERROR) << "Failed to exec";
        }

        LOG(ERROR) << "FUSE exiting";
        _exit(1);
    }

    if (mFusePid == -1) {
        PLOG(ERROR) << getId() << " failed to fork";
        return -errno;
    }

    nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
    while (before == GetDevice(mFuseFull)) {
        LOG(DEBUG) << "Waiting for FUSE to spin up...";
        usleep(50000);  // 50ms

        nsecs_t now = systemTime(SYSTEM_TIME_BOOTTIME);
        if (nanoseconds_to_milliseconds(now - start) > 5000) {
            LOG(WARNING) << "Timed out while waiting for FUSE to spin up";
            return -ETIMEDOUT;
        }
    }
    /* sdcardfs will have exited already. FUSE will still be running */
    TEMP_FAILURE_RETRY(waitpid(mFusePid, nullptr, 0));
    mFusePid = 0;

    return OK;
}

真正挂载的命令是在sdcard.cpp文件中执行的,从代码中又看到了熟悉的四个目录,不错,就是针对不同权限给的四个目录;四个目录都会挂载到/storage/emulated/目录下,区别就在于挂载的时候给的参数不同,包括传递的属性掩码以及用户组,只有default是传递的1023,其他都是9997(AID_EVERYBODY)。这都是挂载到了sdcardfs下,如果有esdfs则会挂载到对应的文件系统,一般没有这个标志,也就都使用了sdcardfs。

static void run_sdcardfs(const std::string& source_path, const std::string& label, uid_t uid,
                         gid_t gid, userid_t userid, bool multi_user, bool full_write,
                         bool derive_gid, bool default_normal, bool unshared_obb, bool use_esdfs) {
    std::string dest_path_default = "/mnt/runtime/default/" + label;
    std::string dest_path_read = "/mnt/runtime/read/" + label;
    std::string dest_path_write = "/mnt/runtime/write/" + label;
    std::string dest_path_full = "/mnt/runtime/full/" + label;

    umask(0);
    if (multi_user) {
        // Multi-user storage is fully isolated per user, so "other"
        // permissions are completely masked off.
        if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
                            AID_SDCARD_RW, 0006, derive_gid, default_normal, unshared_obb,
                            use_esdfs) ||
            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
                                      multi_user, userid, AID_EVERYBODY, 0027, derive_gid,
                                      default_normal, unshared_obb, use_esdfs) ||
            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
                                      multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0027,
                                      derive_gid, default_normal, unshared_obb, use_esdfs) ||
            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
                                      multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
                                      default_normal, unshared_obb, use_esdfs)) {
            LOG(FATAL) << "failed to sdcardfs_setup";
        }
    } else {
        ...
        }
    }

    // Will abort if priv-dropping fails.
    drop_privs(uid, gid);

    if (multi_user) {
        std::string obb_path = source_path + "/obb";
        fs_prepare_dir(obb_path.c_str(), 0775, uid, gid);
    }

    exit(0);
}

static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path,
                           uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid, gid_t gid,
                           mode_t mask, bool derive_gid, bool default_normal, bool unshared_obb,
                           bool use_esdfs) {
    // Add new options at the end of the vector.
    std::vector<std::string> new_opts_list;
    if (multi_user) new_opts_list.push_back("multiuser,");
    if (derive_gid) new_opts_list.push_back("derive_gid,");
    if (default_normal) new_opts_list.push_back("default_normal,");
    if (unshared_obb) new_opts_list.push_back("unshared_obb,");
    // Try several attempts, each time with one less option, to gracefully
    // handle older kernels that aren't updated yet.
    for (int i = 0; i <= new_opts_list.size(); ++i) {
        std::string new_opts;
        for (int j = 0; j < new_opts_list.size() - i; ++j) {
            new_opts += new_opts_list[j];
        }

        auto opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
                                                fsuid, fsgid, new_opts.c_str(), mask, userid, gid);
        if (mount(source_path.c_str(), dest_path.c_str(), use_esdfs ? "esdfs" : "sdcardfs",
                  MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) == -1) {
            PLOG(WARNING) << "Failed to mount sdcardfs with options " << opts;
        } else {
            return true;
        }
    }

    return false;
}

sdcardfs并不是真正传统的文件系统,他只是Google为了控制访问权限在原本文件系统上增加的一个保护壳,真正的文件系统还是得看data分区是使用哪一个文件系统,一般是ext4或者f2fs。这样的话内置存储就有两个文件系统支持,文件的操作还是在底层文件系统中完成,sdcardfs用来做进一步的检查和控制。
 
第二个来看一下PublicVolume这个类型的挂载流程,和EmulatedVolume挂载类似,但是挂载不再只使用sdcard,而是先将整个存储介质挂载到storage下,有vfat和exfat两种类型,当然如果有其他文件系统也可以增加,例如ntfs等。这个相当于优先挂载底层文件系统,如果有需要则再挂载到sdcardfs中,而这个条件则取决于是否对外可见,由变量kVisible来判断。

status_t PublicVolume::doMount() {
    readMetadata();

    if (mFsType == "vfat" && vfat::IsSupported()) {
        if (vfat::Check(mDevPath)) {
            LOG(ERROR) << getId() << " failed filesystem check";
            return -EIO;
        }
    } else if (mFsType == "exfat" && exfat::IsSupported()) {
        if (exfat::Check(mDevPath)) {
            LOG(ERROR) << getId() << " failed filesystem check";
            return -EIO;
        }
    } else {
        LOG(ERROR) << getId() << " unsupported filesystem " << mFsType;
        return -EIO;
    }

    // Use UUID as stable name, if available
    std::string stableName = getId();
    if (!mFsUuid.empty()) {
        stableName = mFsUuid;
    }

    mRawPath = StringPrintf("/mnt/media_rw/%s", stableName.c_str());

    mFuseDefault = StringPrintf("/mnt/runtime/default/%s", stableName.c_str());
    mFuseRead = StringPrintf("/mnt/runtime/read/%s", stableName.c_str());
    mFuseWrite = StringPrintf("/mnt/runtime/write/%s", stableName.c_str());
    mFuseFull = StringPrintf("/mnt/runtime/full/%s", stableName.c_str());

    setInternalPath(mRawPath);
    if (getMountFlags() & MountFlags::kVisible) {
        setPath(StringPrintf("/storage/%s", stableName.c_str()));
    } else {
        setPath(mRawPath);
    }

    if (fs_prepare_dir(mRawPath.c_str(), 0700, AID_ROOT, AID_ROOT)) {
        PLOG(ERROR) << getId() << " failed to create mount points";
        return -errno;
    }

    if (mFsType == "vfat") {
        if (vfat::Mount(mDevPath, mRawPath, false, false, false, AID_MEDIA_RW, AID_MEDIA_RW, 0007,
                        true)) {
            //mount(mDevPath, mRawPath, "vfat", flags, mountData.c_str());
            PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
            return -EIO;
        }
    } else if (mFsType == "exfat") {
        if (exfat::Mount(mDevPath, mRawPath, AID_MEDIA_RW, AID_MEDIA_RW, 0007)) {
            //mount(mDevPath, mRawPath, "exfat", flags, mountData.c_str());
            PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
            return -EIO;
        }
    }

    if (getMountFlags() & MountFlags::kPrimary) {
        initAsecStage();
    }

    if (!(getMountFlags() & MountFlags::kVisible)) {
        // Not visible to apps, so no need to spin up FUSE
        return OK;
    }

    if (fs_prepare_dir(mFuseDefault.c_str(), 0700, AID_ROOT, AID_ROOT) ||
        fs_prepare_dir(mFuseRead.c_str(), 0700, AID_ROOT, AID_ROOT) ||
        fs_prepare_dir(mFuseWrite.c_str(), 0700, AID_ROOT, AID_ROOT) ||
        fs_prepare_dir(mFuseFull.c_str(), 0700, AID_ROOT, AID_ROOT)) {
        PLOG(ERROR) << getId() << " failed to create FUSE mount points";
        return -errno;
    }

    dev_t before = GetDevice(mFuseFull);

    if (!(mFusePid = fork())) {
        if (getMountFlags() & MountFlags::kPrimary) {
            // clang-format off
            if (execl(kFusePath, kFusePath,
                    "-u", "1023", // AID_MEDIA_RW
                    "-g", "1023", // AID_MEDIA_RW
                    "-U", std::to_string(getMountUserId()).c_str(),
                    "-w",
                    mRawPath.c_str(),
                    stableName.c_str(),
                    NULL)) {
                // clang-format on
                PLOG(ERROR) << "Failed to exec";
            }
        } else {
            // clang-format off
            if (execl(kFusePath, kFusePath,
                    "-u", "1023", // AID_MEDIA_RW
                    "-g", "1023", // AID_MEDIA_RW
                    "-U", std::to_string(getMountUserId()).c_str(),
                    mRawPath.c_str(),
                    stableName.c_str(),
                    NULL)) {
                // clang-format on
                PLOG(ERROR) << "Failed to exec";
            }
        }

        LOG(ERROR) << "FUSE exiting";
        _exit(1);
    }

    if (mFusePid == -1) {
        PLOG(ERROR) << getId() << " failed to fork";
        return -errno;
    }

    nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
    while (before == GetDevice(mFuseFull)) {
        LOG(DEBUG) << "Waiting for FUSE to spin up...";
        usleep(50000);  // 50ms

        nsecs_t now = systemTime(SYSTEM_TIME_BOOTTIME);
        if (nanoseconds_to_milliseconds(now - start) > 5000) {
            LOG(WARNING) << "Timed out while waiting for FUSE to spin up";
            return -ETIMEDOUT;
        }
    }
    /* sdcardfs will have exited already. FUSE will still be running */
    TEMP_FAILURE_RETRY(waitpid(mFusePid, nullptr, 0));
    mFusePid = 0;

    return OK;
}

以上就是vold处理存储介质挂载的流程,具体的mount流程处于kernel部分,不同的文件系统有不同的特性,mount流程也会不一样,通过系统调用由VFS来分配是由哪个文件系统来处理。
 
本片代码针对的是Android Q,在R版本Google已经强制开始使用Fuse,并且从kernel中取出sdcardfs,所以vold挂载时使用Fuse。


版权声明:本文为m0_46250244原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。