diff --git a/java/org/gnu/emacs/EmacsSafThread.java b/java/org/gnu/emacs/EmacsSafThread.java index 3ae3c0839ce..9f5d7f3d0cf 100644 --- a/java/org/gnu/emacs/EmacsSafThread.java +++ b/java/org/gnu/emacs/EmacsSafThread.java @@ -1565,8 +1565,9 @@ In addition, arbitrary runtime exceptions (such as signal. */ public ParcelFileDescriptor - openDocument1 (String uri, String documentId, boolean write, - boolean truncate, CancellationSignal signal) + openDocument1 (String uri, String documentId, boolean read, + boolean write, boolean truncate, + CancellationSignal signal) throws Throwable { Uri treeUri, documentUri; @@ -1586,10 +1587,19 @@ In addition, arbitrary runtime exceptions (such as if (write) { - if (truncate) - mode = "rwt"; + if (read) + { + if (truncate) + mode = "rwt"; + else + mode = "rw"; + } else - mode = "rw"; + /* Set mode to w when WRITE && !READ, disregarding TRUNCATE. + In contradiction with the ContentResolver documentation, + document providers seem to truncate files whenever w is + specified, at least superficially. */ + mode = "w"; } else mode = "r"; @@ -1597,14 +1607,15 @@ In addition, arbitrary runtime exceptions (such as fileDescriptor = resolver.openFileDescriptor (documentUri, mode, signal); + Log.d (TAG, "openDocument1: " + mode + " " + fileDescriptor); - /* If a writable file descriptor is requested and TRUNCATE is set, - then probe the file descriptor to detect if it is actually - readable. If not, close this file descriptor and reopen it - with MODE set to rw; some document providers granting access to - Samba shares don't implement rwt, but these document providers - invariably truncate the file opened even when the mode is - merely rw. + /* If a writable on-disk file descriptor is requested and TRUNCATE + is set, then probe the file descriptor to detect if it is + actually readable. If not, close this file descriptor and + reopen it with MODE set to rw; some document providers granting + access to Samba shares don't implement rwt, but these document + providers invariably truncate the file opened even when the + mode is merely w. This may be ascribed to a mix-up in Android's documentation regardin DocumentsProvider: the `openDocument' function is only @@ -1612,7 +1623,7 @@ In addition, arbitrary runtime exceptions (such as implementation of the `openFile' function (which documents rwt) delegates to `openDocument'. */ - if (write && truncate && fileDescriptor != null + if (read && write && truncate && fileDescriptor != null && !EmacsNative.ftruncate (fileDescriptor.getFd ())) { try @@ -1647,15 +1658,13 @@ In addition, arbitrary runtime exceptions (such as TRUNCATE and the document already exists, truncate its contents before returning. - On Android 9.0 and earlier, always open the document in - ``read-write'' mode; this instructs the document provider to - return a seekable file that is stored on disk and returns correct - file status. + If READ && WRITE, open the file under either the `rw' or `rwt' + access mode, which implies that the value must be a seekable + on-disk file. If WRITE && !READ or TRUNC && WRITE, also truncate + the file after it is opened. - Under newer versions of Android, open the document in a - non-writable mode if WRITE is false. This is possible because - these versions allow Emacs to explicitly request a seekable - on-disk file. + If only READ or WRITE is set, value may be a non-seekable FIFO or + one end of a socket pair. Value is NULL upon failure or a parcel file descriptor upon success. Call `ParcelFileDescriptor.close' on this file @@ -1667,7 +1676,8 @@ In addition, arbitrary runtime exceptions (such as public ParcelFileDescriptor openDocument (final String uri, final String documentId, - final boolean write, final boolean truncate) + final boolean read, final boolean write, + final boolean truncate) { Object tem; @@ -1677,8 +1687,8 @@ In addition, arbitrary runtime exceptions (such as runObject (CancellationSignal signal) throws Throwable { - return openDocument1 (uri, documentId, write, truncate, - signal); + return openDocument1 (uri, documentId, read, + write, truncate, signal); } }); diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index d91d8f66009..14ff2cce98f 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -1537,15 +1537,13 @@ In addition, arbitrary runtime exceptions (such as TRUNCATE and the document already exists, truncate its contents before returning. - On Android 9.0 and earlier, always open the document in - ``read-write'' mode; this instructs the document provider to - return a seekable file that is stored on disk and returns correct - file status. + If READ && WRITE, open the file under either the `rw' or `rwt' + access mode, which implies that the value must be a seekable + on-disk file. If TRUNC && WRITE, also truncate the file after it + is opened. - Under newer versions of Android, open the document in a - non-writable mode if WRITE is false. This is possible because - these versions allow Emacs to explicitly request a seekable - on-disk file. + If only READ or WRITE is set, value may be a non-seekable FIFO or + one end of a socket pair. Value is NULL upon failure or a parcel file descriptor upon success. Call `ParcelFileDescriptor.close' on this file @@ -1555,8 +1553,8 @@ In addition, arbitrary runtime exceptions (such as UnsupportedOperationException may be thrown upon failure. */ public ParcelFileDescriptor - openDocument (String uri, String documentId, boolean write, - boolean truncate) + openDocument (String uri, String documentId, + boolean read, boolean write, boolean truncate) { /* Start the thread used to run SAF requests if it isn't already running. */ @@ -1567,7 +1565,7 @@ In addition, arbitrary runtime exceptions (such as storageThread.start (); } - return storageThread.openDocument (uri, documentId, write, + return storageThread.openDocument (uri, documentId, read, write, truncate); } diff --git a/src/android.c b/src/android.c index bd19107f53a..7f263bc83d1 100644 --- a/src/android.c +++ b/src/android.c @@ -1574,7 +1574,7 @@ android_init_emacs_service (void) "(Landroid/database/Cursor;)Lorg/gnu/emacs/" "EmacsDirectoryEntry;"); FIND_METHOD (open_document, "openDocument", - "(Ljava/lang/String;Ljava/lang/String;ZZ)" + "(Ljava/lang/String;Ljava/lang/String;ZZZ)" "Landroid/os/ParcelFileDescriptor;"); FIND_METHOD (create_document, "createDocument", "(Ljava/lang/String;Ljava/lang/String;" diff --git a/src/androidvfs.c b/src/androidvfs.c index 4234e337acb..0ee555c0ad4 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -5590,7 +5590,7 @@ android_saf_file_open (struct android_vnode *vnode, int flags, struct android_saf_file_vnode *vp; jobject uri, id, descriptor; jmethodID method; - jboolean trunc, write; + jboolean read, trunc, write; jint fd; struct android_parcel_fd *info; struct stat statb; @@ -5601,6 +5601,15 @@ android_saf_file_open (struct android_vnode *vnode, int flags, return -1; } + /* O_APPEND isn't supported as a consequence of Android content + providers defaulting to truncating the file. */ + + if (flags & O_APPEND) + { + errno = EOPNOTSUPP; + return -1; + } + /* Build strings for both the URI and ID. */ vp = (struct android_saf_file_vnode *) vnode; @@ -5611,18 +5620,43 @@ android_saf_file_open (struct android_vnode *vnode, int flags, vp->document_id); android_exception_check_1 (uri); - /* Open a parcel file descriptor according to flags. */ + /* Open a parcel file descriptor according to flags. Documentation + for the SAF openDocument operation is scant and seldom helpful. + It's clear that their file access modes are inconsistently + implemented, and that at least: + + r = either an FIFO or a real file, without truncation. + w = either an FIFO or a real file, with truncation. + wt = either an FIFO or a real file, with truncation. + rw = a real file, without truncation. + rwt = a real file, with truncation. + + This diverges from the self-contradicting documentation, where + openDocument says nothing about truncation, and openFile, where + w can elect not to truncate. + + Since Emacs is prepared to handle FIFOs within fileio.c, simply + use the straightforward relationships possible. */ method = service_class.open_document; - trunc = (flags & O_TRUNC); - write = (((flags & O_RDWR) == O_RDWR) || (flags & O_WRONLY)); + read = trunc = write = false; + + if ((flags & O_RDWR) == O_RDWR || (flags & O_WRONLY)) + write = true; + + if (flags & O_TRUNC) + trunc = true; + + if ((flags & O_RDWR) == O_RDWR || !write) + read = true; + inside_saf_critical_section = true; descriptor = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env, emacs_service, service_class.class, method, uri, id, - write, trunc); + read, write, trunc); inside_saf_critical_section = false; if (android_saf_exception_check (2, uri, id)) @@ -6448,6 +6482,8 @@ android_vfs_init (JNIEnv *env, jobject manager) vnodes may not be reentrant, but operating on them from within an async input handler will at worst cause an error to be returned. + The eight is that some vnode types do not support O_APPEND. + And the final drawback is that directories cannot be directly opened. Instead, `dirfd' must be called on a directory stream used by `openat'.