Fix ContentResolver opening with O_DIRECT
To ensure upper and lower fs cache consistency, when we return fds to
the lower fs via ContentResolver, we hold a lock on the file so that
subsequent file path opens will use direct_io and the latest writes
will be visible across upper and lower fs, see:
I7726a75a51869c0e3ea3856103dd501b1aa19d14
Ideally, we only need to hold a read lock but we cannot grab a read
lock when the fd is opened 'w' only.
Previously, we would grab a read lock for 'r' or 'rw' and a write lock
for 'w' only.
Now, we convert 'w' to 'rw'. This allows us always grab a read lock
and fixes the following scenario:
1. App opens file for 'w' via ContentResolver: Gets fd to the lower fs
2. App opens file for 'w' again. Gets fd to FUSE but with
O_DIRECT (because we set the write) lock in (1). This second fd cannot
be used with mmap(PROT_SHARED) for instance.
Test: atest ScopedStorageTest#testVfsCacheConsistency
Bug: 158226779
Change-Id: Iaf8054bf56bdc3bb1f71a1ee543ebb261fd58fbd
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 42f7bfe..0836cc0 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -5659,8 +5659,13 @@
*/
private ParcelFileDescriptor openFileAndEnforcePathPermissionsHelper(Uri uri, int match,
String mode, CancellationSignal signal) throws FileNotFoundException {
- final int modeBits = ParcelFileDescriptor.parseMode(mode);
- final boolean forWrite = (modeBits & ParcelFileDescriptor.MODE_WRITE_ONLY) != 0;
+ int modeBits = ParcelFileDescriptor.parseMode(mode);
+ boolean forWrite = (modeBits & ParcelFileDescriptor.MODE_WRITE_ONLY) != 0;
+ if (forWrite) {
+ // Upgrade 'w' only to 'rw'. This allows us acquire a WR_LOCK when calling
+ // #shouldOpenWithFuse
+ modeBits |= ParcelFileDescriptor.MODE_READ_WRITE;
+ }
final boolean hasOwnerPackageName = hasOwnerPackageName(uri);
final String[] projection = new String[] {
@@ -5773,9 +5778,10 @@
} catch (FileNotFoundException ignored) {
}
ParcelFileDescriptor lowerFsFd = FileUtils.openSafely(file, modeBits);
- boolean forRead = (modeBits & ParcelFileDescriptor.MODE_READ_ONLY) != 0;
+ // Always acquire a readLock. This allows us make multiple opens via lower
+ // filesystem
boolean shouldOpenWithFuse = daemon != null
- && daemon.shouldOpenWithFuse(filePath, forRead, lowerFsFd.getFd());
+ && daemon.shouldOpenWithFuse(filePath, true /* forRead */, lowerFsFd.getFd());
if (SystemProperties.getBoolean(PROP_FUSE, false) && shouldOpenWithFuse) {
// If the file is already opened on the FUSE mount with VFS caching enabled