android4.4 的时候vold,也是利用fuse文件系统达到,将sd卡的目录(storage目录)获取sd实际挂载目录(mnt/media_rw)的权限。但是android4.4的时候vold只是写属性而已,然后init监测这个属性,属性改变时,才会去启动sdcard进程。
然后android6.0直接在vold中,fork一个进程直接开启sdcard进程挂载fuse文件系统。并且在卸载sd的时候,在vold中卸载fuse文件系统。
一、挂载sd卡
下面是解析android6.0vold,挂载sd卡是的一段代码,我们来看下。显示挂载sd卡,然后进行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";
}
}
我们再来看看fuse的代码,也就是在sdcard中。先在main函数中获取数据,
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卡实际挂载的地址,然后调用run函数,run函数中进行了一些初始化,然后挂载了default,read,write 3个fuse文件系统,后面又开启3个线程处理这3个文件系统的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文件系统
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个fuse文件系统。
但好像只有default有用,因为在init.rc中将default目录直接挂载到了storage,而应用也就只能拿到storage目录,所以只有default的fuse实际有用。
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卡卸载过程
然后我们再来看看android卸载sd卡的过程,卸载sd卡的时候,是先卸载了fuse文件系统,然后在卸载了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地址的时候,会去检查哪些进程在使用sd卡中的文件。
如何检查呢?是通过proc/pid下面各个文件的软链接,然后通过readlink找到真正的文件地址,来判定是否正在占用sd卡中的文件。
但是在卸载fuse文件系统的时候,比如你有进程在操作sd卡中的文件,这个时候操作sd卡的storage目录会fuse到sd卡真正的挂载地址上,实际上fuse文件系统是在工作的,导致不能卸载。
但是这个时候去查找谁占用fuse文件又是查不出来的,因为是进程在操作sd卡文件,会导致fuse文件系统的操作,才会卸载不掉fuse文件系统。但是能找到占用的文件只能是sd卡的。
而且实际中也碰到这样的问题,所以个人认为应该先kill正在使用sd卡的进程,然后再卸载fuse文件系统。这样就不会有进程操作sd卡中的文件的时候,导致fuse文件系统也在忙而卸载不掉了。我碰到的问题是:一个如下进程占用的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卡的进程呢:
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文件系统之前,调用KillProcessesUsingPath,来kill那些正在使用sd卡目录的进程。而这个mPath路径,如果是sd卡的话,它是storage下的目录,而不是sd卡的mount地址。如果是otg插的sd卡的话,是sd卡的mount地址,因为otg在storage目录下没有目录,只有一个mount地址访问,也有没有fuse。这样问题就解决了。
以上这篇详谈android 6.0 fuse文件系统的挂载和卸载问题就是小编分享给大家的全部内容了,希望能给大家一个参考。