热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

详谈android6.0fuse文件系统的挂载和卸载问题

今天小编就为大家分享一篇详谈android6.0fuse文件系统的挂载和卸载问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

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) <

我们再来看看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 '&#63;':
   default:
    return usage();
  }
 }
 
 for (i = optind; i 

其中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 &#63; 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 &#63; 0027 : 0022)
    || fuse_setup(&fuse_write, AID_EVERYBODY, full_write &#63; 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文件系统的挂载和卸载问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。


推荐阅读
  • 本文探讨了如何通过最小生成树(MST)来计算严格次小生成树。在处理过程中,需特别注意所有边权重相等的情况,以避免错误。我们首先构建最小生成树,然后枚举每条非树边,检查其是否能形成更优的次小生成树。 ... [详细]
  • QUIC协议:快速UDP互联网连接
    QUIC(Quick UDP Internet Connections)是谷歌开发的一种旨在提高网络性能和安全性的传输层协议。它基于UDP,并结合了TLS级别的安全性,提供了更高效、更可靠的互联网通信方式。 ... [详细]
  • 本文详细介绍如何使用arm-eabi-gdb调试Android平台上的C/C++程序。通过具体步骤和实用技巧,帮助开发者更高效地进行调试工作。 ... [详细]
  • 深入理解 Oracle 存储函数:计算员工年收入
    本文介绍如何使用 Oracle 存储函数查询特定员工的年收入。我们将详细解释存储函数的创建过程,并提供完整的代码示例。 ... [详细]
  • 本文总结了2018年的关键成就,包括职业变动、购车、考取驾照等重要事件,并分享了读书、工作、家庭和朋友方面的感悟。同时,展望2019年,制定了健康、软实力提升和技术学习的具体目标。 ... [详细]
  • 在计算机技术的学习道路上,51CTO学院以其专业性和专注度给我留下了深刻印象。从2012年接触计算机到2014年开始系统学习网络技术和安全领域,51CTO学院始终是我信赖的学习平台。 ... [详细]
  • CSS 布局:液态三栏混合宽度布局
    本文介绍了如何使用 CSS 实现液态的三栏布局,其中各栏具有不同的宽度设置。通过调整容器和内容区域的属性,可以实现灵活且响应式的网页设计。 ... [详细]
  • Linux 系统启动故障排除指南:MBR 和 GRUB 问题
    本文详细介绍了 Linux 系统启动过程中常见的 MBR 扇区和 GRUB 引导程序故障及其解决方案,涵盖从备份、模拟故障到恢复的具体步骤。 ... [详细]
  • 本文介绍了如何使用jQuery根据元素的类型(如复选框)和标签名(如段落)来获取DOM对象。这有助于更高效地操作网页中的特定元素。 ... [详细]
  • 本文将详细介绍如何使用剪映应用中的镜像功能,帮助用户轻松实现视频的镜像效果。通过简单的步骤,您可以快速掌握这一实用技巧。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文介绍如何在 Xcode 中使用快捷键和菜单命令对多行代码进行缩进,包括右缩进和左缩进的具体操作方法。 ... [详细]
  • 本文介绍了一款用于自动化部署 Linux 服务的 Bash 脚本。该脚本不仅涵盖了基本的文件复制和目录创建,还处理了系统服务的配置和启动,确保在多种 Linux 发行版上都能顺利运行。 ... [详细]
  • 在Linux系统中配置并启动ActiveMQ
    本文详细介绍了如何在Linux环境中安装和配置ActiveMQ,包括端口开放及防火墙设置。通过本文,您可以掌握完整的ActiveMQ部署流程,确保其在网络环境中正常运行。 ... [详细]
  • Android 渐变圆环加载控件实现
    本文介绍了如何在 Android 中创建一个自定义的渐变圆环加载控件,该控件已在多个知名应用中使用。我们将详细探讨其工作原理和实现方法。 ... [详细]
author-avatar
jianyue1980_852
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有