I have been using bind mounts without disabling mount namespaces on Android for a while - Marshmallow (Android 6) and Nougat (Android 7.1, LineageOS 14.1).
The bind mount is not visible to other apps because /storage/sdcard/
itself is also a layer of indirection. For example, when you run mount
, you can see that the emulated "internal" SD card is not located at /storage/emulated
, but at /data/media
:
/data/media on /mnt/runtime/default/emulated type sdcardfs (rw,nosuid,nodev,noexec,noatime,uid=1023,gid=1023,multiuser)
/data/media on /storage/emulated type sdcardfs (rw,nosuid,nodev,noexec,noatime,uid=1023,gid=1023,multiuser)
/data/media on /mnt/runtime/read/emulated type sdcardfs (rw,nosuid,nodev,noexec,noatime,uid=1023,gid=1023,multiuser)
/data/media on /mnt/runtime/write/emulated type sdcardfs (rw,nosuid,nodev,noexec,noatime,uid=1023,gid=1023,multiuser)
These three mountpoints in /mnt/runtime
are documented at Storage, Runtime permissions:
Android 6.0 introduces a new runtime permissions model where apps request capabilities when needed at runtime. Because the new model includes the READ/WRITE_EXTERNAL_STORAGE permissions, the platform needs to dynamically grant storage access without killing or restarting already-running apps. It does this by maintaining three distinct views of all mounted storage devices:
/mnt/runtime/default
is shown to apps with no special storage permissions, and to the root namespace where adbd and other system components live.
/mnt/runtime/read
is shown to apps with READ_EXTERNAL_STORAGE
/mnt/runtime/write
is shown to apps with WRITE_EXTERNAL_STORAGE
So to make a directory visible to all other apps, you need to create the mounts in these places:
mkdir /mnt/runtime/{default,read,write}/dir2
mount -o bind /mnt/runtime/default/sdcard/dir1 /mnt/runtime/default/sdcard/dir2
mount -o bind /mnt/runtime/read/sdcard/dir1 /mnt/runtime/read/sdcard/dir2
mount -o bind /mnt/runtime/write/sdcard/dir1 /mnt/runtime/write/sdcard/dir2
To unmount:
umount /mnt/runtime/default/dir2
umount /mnt/runtime/read/dir2
umount /mnt/runtime/write/dir2
rmdir /mnt/runtime/{default,read,write}/dir2
In your question, the source and target are at the same filesystem, so the above will probably work as-is and show dir2 to other apps.
You can verify that the bind mounts are "visible" as intended by looking up the process ID of the desired app, and then checking the list of mounts from that app's point of view:
# cat /proc/(process ID here)/mountinfo
Debugging tips
If you cannot access the contents of the bind mount (i.e. "Permission denied" or something similar), then you should check whether the original file has the right UNIX permissions/ownership AND a SELinux label that matches the context. The latter can be debugged by looking for "avc: denied" errors in logcat-color (or just adb logcat
). In some cases this can be fixed by using restorecon
or chcon
.
For example, I used to get the following error when I tried to access /sdcard/DCIM
(which is a bind mount to a directory on external SD card).
avc: denied { write } for comm=... name="Camera" dev="dm-1" ino=3547138 scontext=u:r:priv_app:s0:c512,c768 tcontext=u:object_r:mnt_media_rw_file:s0 tclass=dir permissive=0
And fixed that one by observing that the SELinux label is wrong (the tcontext
in the error message already told me so):
# ls -Z /mnt/media_rw/myextcard/my_DCIM_directory
drwxrwx--x 4 media_rw media_rw u:object_r:mnt_media_rw_file:s0 4096 2018-06-08 12:34 .
and changed that (recursively) to a label which is allowed by the default policy on LineageOS:
# chcon -R u:object_r:media_rw_data_file:s0:c512,c768 /mnt/media_rw/myextcard/my_DCIM_directory
This worked for me, because /sepolicy
has a rule that states that priv_app
can access media_rw_data_file
files. Those who have a different situation/ROM can use setools-android to parse their /sepolicy
file to see their options.