fuse把/data/media映射为 /storage/emulated/0,供上层app用这个路径。/dev/fuse /mnt/runtime/default/emulated fuse rw,nosuid,nodev,noexec,noatime,user_id=1023,group_id=1023,default_permissions,allow_other 0 0/dev/fuse /storage/emulated fuse rw,nosuid,nodev,noexec,noatime,user_id=1023,group_id=1023,default_permissions,allow_other 0 0/dev/fuse /mnt/runtime/read/emulated fuse rw,nosuid,nodev,noexec,noatime,user_id=1023,group_id=1023,default_permissions,allow_other 0 0/dev/fuse /mnt/runtime/write/emulated fuse rw,nosuid,nodev,noexec,noatime,user_id=1023,group_id=1023,default_permissions,allow_other 0 0root@nikel:/ # ls -l /mnt/runtime/read/emulated/ drwxr-x--- root everybody 2015-01-01 00:08 0drwxr-x--- root everybody 2015-01-02 16:28 obbroot@nikel:/ # ls -l /mnt/runtime/read/emulated/0/ drwxr-x--- root everybody 2015-01-01 00:00 Alarmsdrwxr-x--- root everybody 2015-01-01 00:00 Androiddrwxr-x--- root everybody 2015-01-01 00:00 DCIMdrwxr-x--- root everybody 2015-01-01 00:00 Downloaddrwxr-x--- root everybody 2015-01-01 00:07 DuoKandrwxr-x--- root everybody 2015-01-01 00:27 MIUIdrwxr-x--- root everybody 2015-01-01 00:00 Moviesdrwxr-x--- root everybody 2015-01-01 00:00 Musicdrwxr-x--- root everybody 2015-01-01 00:00 Notificationsdrwxr-x--- root everybody 2015-01-01 00:00 Picturesdrwxr-x--- root everybody 2015-01-01 00:00 Podcastsdrwxr-x--- root everybody 2015-01-01 00:00 Ringtonesdrwxr-x--- root everybody 2015-01-01 00:06 Tencentdrwxr-x--- root everybody 2015-01-01 00:00 Xiaomi-rw-r----- root everybody 2 2015-01-01 00:27 dctp-rw-r----- root everybody 41 2015-01-01 00:27 diddrwxr-x--- root everybody 2015-01-01 00:00 mi_drivedrwxr-x--- root everybody 2015-01-01 00:00 miaddrwxr-x--- root everybody 2015-01-01 00:08 mtklogdrwxr-x--- root everybody 2015-01-01 00:00 sogouroot@nikel:/ # ls -l /mnt/runtime/write/emulated/0/ drwxrwx--- root everybody 2015-01-01 00:00 Alarmsdrwxrwx--- root everybody 2015-01-01 00:00 Androiddrwxrwx--- root everybody 2015-01-01 00:00 DCIMdrwxrwx--- root everybody 2015-01-01 00:00 Downloaddrwxrwx--- root everybody 2015-01-01 00:07 DuoKandrwxrwx--- root everybody 2015-01-01 00:27 MIUIdrwxrwx--- root everybody 2015-01-01 00:00 Moviesdrwxrwx--- root everybody 2015-01-01 00:00 Musicdrwxrwx--- root everybody 2015-01-01 00:00 Notificationsdrwxrwx--- root everybody 2015-01-01 00:00 Picturesdrwxrwx--- root everybody 2015-01-01 00:00 Podcastsdrwxrwx--- root everybody 2015-01-01 00:00 Ringtonesdrwxrwx--- root everybody 2015-01-01 00:06 Tencentdrwxrwx--- root everybody 2015-01-01 00:00 Xiaomi-rw-rw---- root everybody 2 2015-01-01 00:27 dctp-rw-rw---- root everybody 41 2015-01-01 00:27 diddrwxrwx--- root everybody 2015-01-01 00:00 mi_drivedrwxrwx--- root everybody 2015-01-01 00:00 miaddrwxrwx--- root everybody 2015-01-01 00:08 mtklogdrwxrwx--- root everybody 2015-01-01 00:00 sogouroot@nikel:/ # root@nikel:/ # ls -l /mnt/runtime/default/ /emulated/0/ emulated/ sdcard0/ self/ root@nikel:/ # ls -l /mnt/runtime/default/emulated/0/ drwxrwx--x root sdcard_rw 2015-01-01 00:00 Alarmsdrwxrwx--x root sdcard_rw 2015-01-01 00:00 Androiddrwxrwx--x root sdcard_rw 2015-01-01 00:00 DCIMdrwxrwx--x root sdcard_rw 2015-01-01 00:00 Downloaddrwxrwx--x root sdcard_rw 2015-01-01 00:07 DuoKandrwxrwx--x root sdcard_rw 2015-01-01 00:27 MIUIdrwxrwx--x root sdcard_rw 2015-01-01 00:00 Moviesdrwxrwx--x root sdcard_rw 2015-01-01 00:00 Musicdrwxrwx--x root sdcard_rw 2015-01-01 00:00 Notificationsdrwxrwx--x root sdcard_rw 2015-01-01 00:00 Picturesdrwxrwx--x root sdcard_rw 2015-01-01 00:00 Podcastsdrwxrwx--x root sdcard_rw 2015-01-01 00:00 Ringtonesdrwxrwx--x root sdcard_rw 2015-01-01 00:06 Tencentdrwxrwx--x root sdcard_rw 2015-01-01 00:00 Xiaomi-rw-rw---- root sdcard_rw 2 2015-01-01 00:27 dctp-rw-rw---- root sdcard_rw 41 2015-01-01 00:27 diddrwxrwx--x root sdcard_rw 2015-01-01 00:00 mi_drivedrwxrwx--x root sdcard_rw 2015-01-01 00:00 miaddrwxrwx--x root sdcard_rw 2015-01-01 00:08 mtklogdrwxrwx--x root sdcard_rw 2015-01-01 00:00 sogo127|root@nikel:/ # dumpsys mount Disks:Volumes: VolumeInfo{emulated}: type=EMULATED diskId=null partGuid=null mountFlags=PRIMARY|VISIBLE mountUserId=-1 state=MOUNTED fsType=null fsUuid=null fsLabel=null path=/storage/emulated internalPath=/data/media Disks: DiskInfo{disk:179,128}: flags=ADOPTABLE|SD size=1002962944 label= sysPath=/sys//devices/mtk-msdc.0/11240000.msdc1/mmc_host/mmc1/mmc1:0001/block/mmcblk1 Volumes://sd卡 VolumeInfo{public:179,129}: type=PUBLIC diskId=disk:179,128 partGuid=null mountFlags=VISIBLE mountUserId=0 state=MOUNTED fsType=vfat fsUuid=88FC-16F9 fsLabel= path=/storage/88FC-16F9 internalPath=/mnt/media_rw/88FC-16F9(path是fuse提供给上层的路径,internalPath是实际路径)//内部存储 VolumeInfo{emulated}: type=EMULATED diskId=null partGuid=null mountFlags=PRIMARY|VISIBLE mountUserId=-1 state=MOUNTED fsType=null fsUuid=null fsLabel=null path=/storage/emulated internalPath=/data/media 用户空间文件系统(Filesystem in Userspace,简称FUSE)指在用户态实现的文件系统
FUSE文件系统由两部分组成:
kernel fs/fuse用户态 fuse daemon(开发者需要实现的)以内置存储为例:
data 分区以ext4格式被挂载
fuse的root node为data/media,即利用data分区的media目录存储sdcard与data共用
内置存储的fuse daemon 为system/core/sdcard/sdcard.c
图1-1 黑色箭头表示 app通过fuse向sdcard daemon 发出文件系统操作请求(read write and so on)
绿色箭头表示 sdcard damon 实际完成操作
红色箭头表示 sdcard damon 通过fuse(/dev/fuse)向app反馈操作结果
以 ls -l storage/emulated/0 为例,看fuse文件系统操作过程
通过strace命令观测系统调用过程
root@scorpio:/ # ps | grep -i sdcardmedia_rw 1978 468 9336 1864 inotify_re 7fa1959e3c S /system/bin/sdcard
root@scorpio:/ # strace ls -l storage/emulated/0
root@scorpio:/ # strace -ftt -p 1978 //1978 是 sdcard 进程号
下面是strace片段
root@scorpio:/ # strace -ftt -p 1978 Process 1978 attached with 4 threads
[pid 2038] 22:42:28.162015 read(4, "P/0/0/0/34/0/0/0/n/20/0/0/0/0/0/0/0 0/241/177/0/0/0/0/0/0/0/0/0/0/0"..., 262224) = 80[pid 2038] 22:42:28.162310 getdents64(11, /* 0 entries */, 4200) = 0[pid 2038] 22:42:28.162466 write(4, "/20/0/0/0/0/0/0/0/n/20/0/0/0/0/0/0", 16) = 16[pid 2038] 22:42:28.163092 read(4, "8/0/0/0/3/0/0/0/v/20/0/0/0/0/0/0/0 0/241/177/0/0/0/0/0/0/0/0/0/0/0"..., 262224) = 56[pid 2038] 22:42:28.163294 newfstatat(AT_FDCWD, "/data/media/0", {st_mode=S_IFDIR|0770, st_size=4096, ...}, AT_SYMLINK_NOFOLLOW) = 0[pid 2038] 22:42:28.163507 writev(4, [{"x/0/0/0/0/0/0/0/v/20/0/0/0/0/0/0", 16}, {"/n/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/1/0/0/0/0/0/0/0/0/20/0/0/0/0/0/0"..., 104}], 2) = 120[pid 2038] 22:42:28.163700 read(4, "//0/0/0/1/0/0/0/f/20/0/0/0/0/0/0/0 0/241/177/0/0/0/0/0/0/0/0/0/0/0"..., 262224) = 47[pid 2038] 22:42:28.163866 faccessat(AT_FDCWD, "/data/media/0/Alarms", F_OK) = 0[pid 2038] 22:42:28.164053 newfstatat(AT_FDCWD, "/data/media/0/Alarms", {st_mode=S_IFDIR|0775, st_size=4096, ...}, AT_SYMLINK_NOFOLLOW) = 0[pid 2038] 22:42:28.164238 writev(4, [{"/220/0/0/0/0/0/0/0/f/20/0/0/0/0/0/0", 16}, {"/200/"0/241/177/0/0/0/5/0/0/0/0/0/0/0/n/0/0/0/0/0/0/0/n/0/0/0/0/0/0/0"..., 128}], 2) = 144[pid 2038] 22:42:28.164425 read(4, "-/0/0/0/1/0/0/0/r/20/0/0/0/0/0/0/0 0/241/177/0/0/0/0/0/0/0/0/0/0/0"..., 262224) = 45[pid 2038] 22:42:28.169564 faccessat(AT_FDCWD, "/data/media/0/DCIM", F_OK) = 0[pid 2038] 22:42:28.169775 newfstatat(AT_FDCWD, "/data/media/0/DCIM", {st_mode=S_IFDIR|0775, st_size=4096, ...}, AT_SYMLINK_NOFOLLOW) = 0[pid 2038] 22:42:28.169975 writev(4, [{"/220/0/0/0/0/0/0/0/r/20/0/0/0/0/0/0", 16}, {"/0%0/241/177/0/0/0/n/0/0/0/0/0/0/0/n/0/0/0/0/0/0/0/n/0/0/0/0/0/0/0"..., 128}], 2) = 144[pid 2038] 22:42:28.170184 read(4, "1/0/0/0/1/0/0/0/16/20/0/0/0/0/0/0/0 0/241/177/0/0/0/0/0/0/0/0/0/0/0"..., 262224) = 49[pid 2038] 22:42:28.171253 faccessat(AT_FDCWD, "/data/media/0/Download", F_OK) = 0[pid 2038] 22:42:28.171593 newfstatat(AT_FDCWD, "/data/media/0/Download", {st_mode=S_IFDIR|0775, st_size=4096, ...}, AT_SYMLINK_NOFOLLOW) = 0[pid 2038] 22:42:28.171855 writev(4, [{"/220/0/0/0/0/0/0/0/16/20/0/0/0/0/0/0", 16}, {"/200$0/241/177/0/0/0/t/0/0/0/0/0/0/0/n/0/0/0/0/0/0/0/n/0/0/0/0/0/0/0"..., 128}], 2) = 144
Android M sdcard daemon由vold启动
static const char* kFusePath = "/system/bin/sdcard";
if (!(mFusePid = fork())) {if (execl(kFusePath, kFusePath,"-u", "1023", // AID_MEDIA_RW"-g", "1023", // AID_MEDIA_RW"-m","-w",mRawPath.c_str(),label.c_str(),NULL)) {PLOG(ERROR) << "Failed to exec";}
从sdcard 进程启动的参数看sdcard 目录权限是入参-u -g -w带进去的.