這篇文章主要介紹android6.0中fuse文件系統(tǒng)如何掛載和卸載,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
成都創(chuàng)新互聯(lián)服務(wù)項(xiàng)目包括紅花崗網(wǎng)站建設(shè)、紅花崗網(wǎng)站制作、紅花崗網(wǎng)頁制作以及紅花崗網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,紅花崗網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到紅花崗省份的部分城市,未來相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
android4.4 的時(shí)候vold,也是利用fuse文件系統(tǒng)達(dá)到,將sd卡的目錄(storage目錄)獲取sd實(shí)際掛載目錄(mnt/media_rw)的權(quán)限。但是android4.4的時(shí)候vold只是寫屬性而已,然后init監(jiān)測(cè)這個(gè)屬性,屬性改變時(shí),才會(huì)去啟動(dòng)sdcard進(jìn)程。
然后android6.0直接在vold中,fork一個(gè)進(jìn)程直接開啟sdcard進(jìn)程掛載fuse文件系統(tǒng)。并且在卸載sd的時(shí)候,在vold中卸載fuse文件系統(tǒng)。
一、掛載sd卡
下面是解析android6.0vold,掛載sd卡是的一段代碼,我們來看下。顯示掛載sd卡,然后進(jìn)行fuse操作。
if (vfat::Mount(mDevPath, mRawPath, false, false, false,// 掛載sd卡 AID_MEDIA_RW, AID_MEDIA_RW, 0007, true)) { 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; } //creat dir by fuse before fuse action. 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)) { PLOG(ERROR) << getId() << " failed to create fuse points"; return -errno; } dev_t before = GetDevice(mFuseWrite); if (!(mFusePid = fork())) { if (getMountFlags() & MountFlags::kPrimary) { if (execl(kFusePath, kFusePath,//fuse操作 "-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)) { PLOG(ERROR) << "Failed to exec"; } } else { 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)) { PLOG(ERROR) << "Failed to exec"; } }
我們?cè)賮砜纯磃use的代碼,也就是在sdcard中。先在main函數(shù)中獲取數(shù)據(jù),
int main(int argc, char **argv) { const char *source_path = NULL; const char *label = NULL; uid_t uid = 0; gid_t gid = 0; userid_t userid = 0; bool multi_user = false; bool full_write = false; int i; struct rlimit rlim; int fs_version; int opt; while ((opt = getopt(argc, argv, "u:g:U:mw")) != -1) { switch (opt) { case 'u': uid = strtoul(optarg, NULL, 10); break; case 'g': gid = strtoul(optarg, NULL, 10); break; case 'U': userid = strtoul(optarg, NULL, 10); break; case 'm': multi_user = true; break; case 'w': full_write = true; break; case '?': default: return usage(); } } for (i = optind; i < argc; i++) { char* arg = argv[i]; if (!source_path) { source_path = arg; } else if (!label) { label = arg; } else { ERROR("too many arguments\n"); return usage(); } } if (!source_path) { ERROR("no source path specified\n"); return usage(); } if (!label) { ERROR("no label specified\n"); return usage(); } if (!uid || !gid) { ERROR("uid and gid must be nonzero\n"); return usage(); } rlim.rlim_cur = 8192; rlim.rlim_max = 8192; if (setrlimit(RLIMIT_NOFILE, &rlim)) { ERROR("Error setting RLIMIT_NOFILE, errno = %d\n", errno); } while ((fs_read_atomic_int("/data/.layout_version", &fs_version) == -1) || (fs_version < 3)) { ERROR("installd fs upgrade not yet complete. Waiting...\n"); sleep(1); } ERROR("kangchen sdcard path:%s label:%s\n", source_path, label); run(source_path, label, uid, gid, userid, multi_user, full_write); return 1; }
其中source_path就是sd卡實(shí)際掛載的地址,然后調(diào)用run函數(shù),run函數(shù)中進(jìn)行了一些初始化,然后掛載了default,read,write 3個(gè)fuse文件系統(tǒng),后面又開啟3個(gè)線程處理這3個(gè)文件系統(tǒng)的read,write,open等處理。
static void run(const char* source_path, const char* label, uid_t uid, gid_t gid, userid_t userid, bool multi_user, bool full_write) { struct fuse_global global; struct fuse fuse_default; struct fuse fuse_read; struct fuse fuse_write; struct fuse_handler handler_default; struct fuse_handler handler_read; struct fuse_handler handler_write; pthread_t thread_default; pthread_t thread_read; pthread_t thread_write; memset(&global, 0, sizeof(global)); memset(&fuse_default, 0, sizeof(fuse_default)); memset(&fuse_read, 0, sizeof(fuse_read)); memset(&fuse_write, 0, sizeof(fuse_write)); memset(&handler_default, 0, sizeof(handler_default)); memset(&handler_read, 0, sizeof(handler_read)); memset(&handler_write, 0, sizeof(handler_write)); pthread_mutex_init(&global.lock, NULL); global.package_to_appid = hashmapCreate(256, str_hash, str_icase_equals); global.uid = uid; global.gid = gid; global.multi_user = multi_user; global.next_generation = 0; global.inode_ctr = 1; memset(&global.root, 0, sizeof(global.root)); global.root.nid = FUSE_ROOT_ID; /* 1 */ global.root.refcount = 2; global.root.namelen = strlen(source_path); global.root.name = strdup(source_path); global.root.userid = userid; global.root.uid = AID_ROOT; global.root.under_android = false; strcpy(global.source_path, source_path); if (multi_user) { global.root.perm = PERM_PRE_ROOT; snprintf(global.obb_path, sizeof(global.obb_path), "%s/obb", source_path); } else { global.root.perm = PERM_ROOT; snprintf(global.obb_path, sizeof(global.obb_path), "%s/Android/obb", source_path); } fuse_default.global = &global; fuse_read.global = &global; fuse_write.global = &global; global.fuse_default = &fuse_default; global.fuse_read = &fuse_read; global.fuse_write = &fuse_write; snprintf(fuse_default.dest_path, PATH_MAX, "/mnt/runtime/default/%s", label); snprintf(fuse_read.dest_path, PATH_MAX, "/mnt/runtime/read/%s", label); snprintf(fuse_write.dest_path, PATH_MAX, "/mnt/runtime/write/%s", label); handler_default.fuse = &fuse_default; handler_read.fuse = &fuse_read; handler_write.fuse = &fuse_write; handler_default.token = 0; handler_read.token = 1; handler_write.token = 2; umask(0); if (multi_user) { /* Multi-user storage is fully isolated per user, so "other" * permissions are completely masked off. */ if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006) || fuse_setup(&fuse_read, AID_EVERYBODY, 0027) || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0027)) { ERROR("failed to fuse_setup\n"); exit(1); } } else { /* Physical storage is readable by all users on device, but * the Android directories are masked off to a single user * deep inside attr_from_stat(). */ if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006) || fuse_setup(&fuse_read, AID_EVERYBODY, full_write ? 0027 : 0022) || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0022)) { ERROR("failed to fuse_setup\n"); exit(1); } } /* Drop privs */ if (setgroups(sizeof(kGroups) / sizeof(kGroups[0]), kGroups) < 0) { ERROR("cannot setgroups: %s\n", strerror(errno)); exit(1); } if (setgid(gid) < 0) { ERROR("cannot setgid: %s\n", strerror(errno)); exit(1); } if (setuid(uid) < 0) { ERROR("cannot setuid: %s\n", strerror(errno)); exit(1); } if (multi_user) { fs_prepare_dir(global.obb_path, 0775, uid, gid); } if (pthread_create(&thread_default, NULL, start_handler, &handler_default) || pthread_create(&thread_read, NULL, start_handler, &handler_read) || pthread_create(&thread_write, NULL, start_handler, &handler_write)) { ERROR("failed to pthread_create\n"); exit(1); } watch_package_list(&global); ERROR("terminated prematurely\n"); exit(1); }
其中在fuse_setup中掛載了fuse文件系統(tǒng)
static int fuse_setup(struct fuse* fuse, gid_t gid, mode_t mask) { char opts[256]; fuse->fd = open("/dev/fuse", O_RDWR); if (fuse->fd == -1) { ERROR("failed to open fuse device: %s\n", strerror(errno)); return -1; } umount2(fuse->dest_path, MNT_DETACH); snprintf(opts, sizeof(opts), "fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d", fuse->fd, fuse->global->uid, fuse->global->gid); if (mount("/dev/fuse", fuse->dest_path, "fuse", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts) != 0) { ERROR("failed to mount fuse filesystem: %s\n", strerror(errno)); return -1; } fuse->gid = gid; fuse->mask = mask; return 0; }
但是雖然掛載了default,read,write 3個(gè)fuse文件系統(tǒng)。
但好像只有default有用,因?yàn)樵趇nit.rc中將default目錄直接掛載到了storage,而應(yīng)用也就只能拿到storage目錄,所以只有default的fuse實(shí)際有用。
on post-fs start logd #add for amt chmod 0755 /amt # once everything is setup, no need to modify / mount rootfs rootfs / ro remount # Mount shared so changes propagate into child namespaces mount rootfs rootfs / shared rec # Mount default storage into root namespace mount none /mnt/runtime/default /storage slave bind rec
二、sd卡卸載過程
然后我們?cè)賮砜纯碼ndroid卸載sd卡的過程,卸載sd卡的時(shí)候,是先卸載了fuse文件系統(tǒng),然后在卸載了sd卡的mount地址。
status_t PublicVolume::doUnmount() { if (mFusePid > 0) { kill(mFusePid, SIGTERM); TEMP_FAILURE_RETRY(waitpid(mFusePid, nullptr, 0)); mFusePid = 0; } ForceUnmount(kAsecPath); ForceUnmount(mFuseDefault); ForceUnmount(mFuseRead); ForceUnmount(mFuseWrite); ForceUnmount(mRawPath); rmdir(mFuseDefault.c_str()); rmdir(mFuseRead.c_str()); rmdir(mFuseWrite.c_str()); rmdir(mRawPath.c_str()); mFuseDefault.clear(); mFuseRead.clear(); mFuseWrite.clear(); mRawPath.clear(); return OK; }
總所周知,卸載sd卡mount地址的時(shí)候,會(huì)去檢查哪些進(jìn)程在使用sd卡中的文件。
如何檢查呢?是通過proc/pid下面各個(gè)文件的軟鏈接,然后通過readlink找到真正的文件地址,來判定是否正在占用sd卡中的文件。
但是在卸載fuse文件系統(tǒng)的時(shí)候,比如你有進(jìn)程在操作sd卡中的文件,這個(gè)時(shí)候操作sd卡的storage目錄會(huì)fuse到sd卡真正的掛載地址上,實(shí)際上fuse文件系統(tǒng)是在工作的,導(dǎo)致不能卸載。
但是這個(gè)時(shí)候去查找誰占用fuse文件又是查不出來的,因?yàn)槭沁M(jìn)程在操作sd卡文件,會(huì)導(dǎo)致fuse文件系統(tǒng)的操作,才會(huì)卸載不掉fuse文件系統(tǒng)。但是能找到占用的文件只能是sd卡的。
而且實(shí)際中也碰到這樣的問題,所以個(gè)人認(rèn)為應(yīng)該先kill正在使用sd卡的進(jìn)程,然后再卸載fuse文件系統(tǒng)。這樣就不會(huì)有進(jìn)程操作sd卡中的文件的時(shí)候,導(dǎo)致fuse文件系統(tǒng)也在忙而卸載不掉了。我碰到的問題是:一個(gè)如下進(jìn)程占用的sd卡文件
root@lte26007:/proc/2365/fd # ls -l lrwx------ root radio 2016-05-25 13:42 0 -> /dev/null lrwx------ root radio 2016-05-25 13:42 1 -> /dev/null lrwx------ root radio 2016-05-25 13:42 10 -> /storage/2C10-0CCC/elog/elog_20160525_134206/PHY0/test_20160525_134208_00.bin_last_0 lrwx------ root radio 2016-05-25 13:42 11 -> /storage/2C10-0CCC/elog/elog_20160525_134206/PHY0/log_up_data.dat lrwx------ root radio 2016-05-25 13:42 12 -> /storage/2C10-0CCC/elog/elog_20160525_134206/PHY1/test_20160525_134209_head.bin lrwx------ root radio 2016-05-25 13:42 13 -> /storage/2C10-0CCC/elog/elog_20160525_134206/PHY1/test_20160525_134209_00.bin_last_0 lrwx------ root radio 2016-05-25 13:42 14 -> /storage/2C10-0CCC/elog/elog_20160525_134206/PHY1/log_up_data.dat lrwx------ root radio 2016-05-25 13:42 15 -> /storage/2C10-0CCC/elog/elog_20160525_134206/PFM/test_20160525_134209_head.bin lrwx------ root radio 2016-05-25 13:42 16 -> /storage/2C10-0CCC/elog/elog_20160525_134206/PFM/test_20160525_134209_00.bin_last_0 lrwx------ root radio 2016-05-25 13:42 17 -> /storage/2C10-0CCC/elog/elog_20160525_134206/PFM/log_up_data.dat lrwx------ root radio 2016-05-25 13:42 18 -> /dev/lmi10 lrwx------ root radio 2016-05-25 13:42 19 -> /dev/TPC0 lrwx------ root radio 2016-05-25 13:42 2 -> /dev/null lrwx------ root radio 2016-05-25 13:42 20 -> /dev/modem lrwx------ root radio 2016-05-25 13:42 21 -> /dev/TPC1 lrwx------ root radio 2016-05-25 13:42 22 -> /dev/modem lrwx------ root radio 2016-05-25 13:42 23 -> /dev/lmi9 lrwx------ root radio 2016-05-25 13:42 24 -> socket:[14761] lrwx------ root radio 2016-05-25 13:42 26 -> socket:[14764] lrwx------ root radio 2016-05-25 13:42 3 -> socket:[15482] lrwx------ root radio 2016-05-25 13:42 4 -> /tmp/lc-elog.pid lrwx------ root radio 2016-05-25 13:42 5 -> /storage/2C10-0CCC/elog/elog_20160525_134206/HLS/test_20160525_134208_head.bin lrwx------ root radio 2016-05-25 13:42 6 -> /storage/2C10-0CCC/elog/elog_20160525_134206/HLS/test_20160525_134208_00.bin_last_0 lrwx------ root radio 2016-05-25 13:42 7 -> /storage/2C10-0CCC/elog/elog_20160525_134206/HLS/log_up_data.dat lrwx------ root radio 2016-05-25 13:42 8 -> /storage/2C10-0CCC/elog/elog_20160525_134206/PHY0/test_20160525_134208_head.bin lr-x------ root radio 2016-05-25 13:42 9 -> /dev/__properties__ root@lte26007:/proc/2365/fd
至于如何kill正在使用sd卡的進(jìn)程呢:
status_t PublicVolume::doUnmount() { if (mFusePid > 0) { kill(mFusePid, SIGTERM); TEMP_FAILURE_RETRY(waitpid(mFusePid, nullptr, 0)); mFusePid = 0; } ForceUnmount(kAsecPath); LOG(VERBOSE) << "start"; KillProcessesUsingPath(getPath()); LOG(VERBOSE) << "end"; ForceUnmount(mFuseDefault); ForceUnmount(mFuseRead); ForceUnmount(mFuseWrite); ForceUnmount(mRawPath); rmdir(mFuseDefault.c_str()); rmdir(mFuseRead.c_str()); rmdir(mFuseWrite.c_str()); rmdir(mRawPath.c_str()); mFuseDefault.clear(); mFuseRead.clear(); mFuseWrite.clear(); mRawPath.clear(); return OK; }
可以在卸載fuse文件系統(tǒng)之前,調(diào)用KillProcessesUsingPath,來kill那些正在使用sd卡目錄的進(jìn)程。而這個(gè)mPath路徑,如果是sd卡的話,它是storage下的目錄,而不是sd卡的mount地址。如果是otg插的sd卡的話,是sd卡的mount地址,因?yàn)閛tg在storage目錄下沒有目錄,只有一個(gè)mount地址訪問,也有沒有fuse。這樣問題就解決了。
以上是“android6.0中fuse文件系統(tǒng)如何掛載和卸載”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!