Android 9.0中sdcard 的权限和挂载问题

您所在的位置:网站首页 sdcard0和sdcard1 Android 9.0中sdcard 的权限和挂载问题

Android 9.0中sdcard 的权限和挂载问题

2023-08-23 01:16| 来源: 网络整理| 查看: 265

文章来源:https://blog.csdn.net/shift_wwx/article/details/85633801 

请转载的朋友表明出处,请支持原创~~

0. 前言

Android 从6.0 开始引入了Runtime permission,应用对于storage 进行读取、存储的时候,需要注册、申请对应的权限。Android 8.0中对于sdcard 读写只需要申请权限即可使用,可以在Android 9.0 中同样的应用执行同样的步骤,却提示了Permission denied。

本文将借此对sdcard 进行简单地剖析。代码基于版本Android 9.0

1. 问题描述 1.1 应用中的代码 private boolean doCreate(File file) { try { File parentFile = file.getParentFile(); if (!isFileExists(parentFile)) { parentFile.mkdirs(); } file.createNewFile(); } catch (IOException e) { e.printStackTrace(); return false; } return true; } private boolean createFile(File file) { if (isFileExists(file)) return true; return doCreate(file); } /** * 通过此函数进行文件创建操作 */ private boolean createFile(String filePath) { Log.d(TAG, "==== createFile, filePath = " + filePath); File file = new File(filePath); return createFile(file); } private boolean isFileExists(File file) { return file != null && file.exists(); }

如代码,通过createFile() 来进行文件创建操作,因为是测试,其中的filePath 直接写死为:/storage/6344-0FEF/test.txt

在应用的AndroidManifest.xml 中权限也已经给出:

1.2 问题log 01-02 05:47:32.711 24318 24318 W System.err: java.io.IOException: Permission denied 01-02 05:47:32.711 24318 24318 W System.err: at java.io.UnixFileSystem.createFileExclusively0(Native Method) 01-02 05:47:32.711 24318 24318 W System.err: at java.io.UnixFileSystem.createFileExclusively(UnixFileSystem.java:281) 01-02 05:47:32.712 24318 24318 W System.err: at java.io.File.createNewFile(File.java:1008) 01-02 05:47:32.712 24318 24318 W System.err: at com.shift.test.testfile.TestFileActivity.doCreate(TestFileActivity.java:126) 01-02 05:47:32.712 24318 24318 W System.err: at com.shift.test.testfile.TestFileActivity.createFile(TestFileActivity.java:138) 01-02 05:47:32.712 24318 24318 W System.err: at com.shift.test.testfile.TestFileActivity.createFile(TestFileActivity.java:145) 01-02 05:47:32.713 24318 24318 W System.err: at com.shift.test.testfile.TestFileActivity.createInSdcard(TestFileActivity.java:110) 01-02 05:47:32.713 24318 24318 W System.err: at com.shift.test.testfile.TestFileActivity.onClick(TestFileActivity.java:183) 01-02 05:47:32.714 24318 24318 W System.err: at android.view.View.performClick(View.java:6597) 01-02 05:47:32.714 24318 24318 W System.err: at android.view.View.performClickInternal(View.java:6574) 01-02 05:47:32.714 24318 24318 W System.err: at android.view.View.access$3100(View.java:778) 01-02 05:47:32.714 24318 24318 W System.err: at android.view.View$PerformClick.run(View.java:25889) 01-02 05:47:32.714 24318 24318 W System.err: at android.os.Handler.handleCallback(Handler.java:873) 01-02 05:47:32.714 24318 24318 W System.err: at android.os.Handler.dispatchMessage(Handler.java:99) 01-02 05:47:32.714 24318 24318 W System.err: at android.os.Looper.loop(Looper.java:193) 01-02 05:47:32.714 24318 24318 W System.err: at android.app.ActivityThread.main(ActivityThread.java:6692) 01-02 05:47:32.714 24318 24318 W System.err: at java.lang.reflect.Method.invoke(Native Method) 01-02 05:47:32.715 24318 24318 W System.err: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 01-02 05:47:32.715 24318 24318 W System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

2. 问题解析

通过log 可以看到最终对于File 的操作调用到UnixFileSystem.createFileExclusively0(),来看下source code:

/* -- File operations -- */ // Android-changed: Added thread policy check public boolean createFileExclusively(String path) throws IOException { BlockGuard.getThreadPolicy().onWriteToDisk(); return createFileExclusively0(path); } private native boolean createFileExclusively0(String path) throws IOException;

最终调用的是native 的方法createFileExclusively0()

详见 libcore/ojluni/src/main/native/UnixFileSystem_md.c

// Android-changed: Name changed because of added thread policy check JNIEXPORT jboolean JNICALL Java_java_io_UnixFileSystem_createFileExclusively0(JNIEnv *env, jclass cls, jstring pathname) { jboolean rv = JNI_FALSE; WITH_PLATFORM_STRING(env, pathname, path) { FD fd; /* The root directory always exists */ if (strcmp (path, "/")) { fd = handleOpen(path, O_RDWR | O_CREAT | O_EXCL, 0666); ALOGD("path = %s, fd = %d, errno = %d", path, fd, errno); if (fd < 0) { if (errno != EEXIST) JNU_ThrowIOExceptionWithLastError(env, path); } else { if (close(fd) == -1) JNU_ThrowIOExceptionWithLastError(env, path); rv = JNI_TRUE; } } } END_PLATFORM_STRING(env, path); return rv; }

如果path 不为空的时候会调用handleOpen():

FD handleOpen(const char *path, int oflag, int mode) { FD fd; RESTARTABLE(open64(path, oflag, mode), fd); if (fd != -1) { struct stat64 buf64; int result; RESTARTABLE(fstat64(fd, &buf64), result); if (result != -1) { if (S_ISDIR(buf64.st_mode)) { close(fd); errno = EISDIR; fd = -1; } } else { close(fd); fd = -1; } } return fd; }

而open64就是库函数open,也就是说在open 的时候出现了error,errno 为13,也就是EACCES,即Permission denied。

那么导致这个问题的原因,大概就是文件节点的权限给的不够,带着这个想法来看下文件节点。

3. 问题剖析

首先来看下设备的mount 情况:

/dev/block/vold/public:179,65 on /mnt/media_rw/6344-0FEF type vfat (rw,dirsync,nosuid,nodev,noexec,noatime,uid=1023,gid=1023,fmask=0007,dmask=0007,allow_utime=0020,codepage=437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro) /mnt/media_rw/6344-0FEF on /mnt/runtime/default/6344-0FEF type sdcardfs (rw,nosuid,nodev,noexec,noatime,fsuid=1023,fsgid=1023,gid=1015,mask=6) /mnt/media_rw/6344-0FEF on /storage/6344-0FEF type sdcardfs (rw,nosuid,nodev,noexec,noatime,fsuid=1023,fsgid=1023,gid=1015,mask=6) /mnt/media_rw/6344-0FEF on /mnt/runtime/read/6344-0FEF type sdcardfs (rw,nosuid,nodev,noexec,noatime,fsuid=1023,fsgid=1023,gid=9997,mask=18) /mnt/media_rw/6344-0FEF on /mnt/runtime/write/6344-0FEF type sdcardfs (rw,nosuid,nodev,noexec,noatime,fsuid=1023,fsgid=1023,gid=9997,mask=18)

目前系统用的是sdcardfs 文件系统,较之前fuse 效率更高。

另外,得知:

/mnt/runtime/default 的gid 为1015,也就是sdcard_rw;mask 为6,也就是other 没有rw权限;/mnt/runtime/read 的gid 为9997,也就是everybody;mask 为18,也就是group、other都没有w 权限;/mnt/runtime/write 的gid 为9997,也就是everybody;mask 为18,也就是group、other都没有w 权限;

gid 与名称详细信息可以看source code,路径为system/core/include/cutils/android_filesystem_config.h

#define AID_SDCARD_RW 1015 /* external storage write access */ #define AID_MEDIA_RW 1023 /* internal media storage write access */ #define AID_EVERYBODY 9997 /* shared between all apps in the same profile */

来看下这几个节点:

msm8940_EVB:/mnt/runtime/write # ls -l total 36 drwxr-xr-x 9 root everybody 32768 2018-12-29 03:53 6344-0FEF msm8940_EVB:/mnt/runtime/read # ls -l total 36 drwxr-xr-x 9 root everybody 32768 2018-12-29 03:53 6344-0FEF msm8940_EVB:/mnt/runtime/default # ls -l total 36 drwxrwx--x 9 root sdcard_rw 32768 2018-12-29 03:53 6344-0FEF

如果default 节点,group id 是sdcard_rw,而其他两个的group 为everybody,这就跟上面mount 的结果一致了。通过这里其实就可以发现出现sdcard 无法写入是因为mount 的时候并没有给出 w 权限。

3.1 PublicVolume

sdcard 挂载的时候 vold 会调用到 PublicVolume 中的 doMount():

system/vold/model/PublicVolume.cpp status_t PublicVolume::doMount() { ... ... 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()); 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)


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3