Utilize more frequently supported file access modes

* java/org/gnu/emacs/EmacsSafThread.java (openDocument1): Use
plain r or w where possible, as the fileio stuff is now better
prepared for FIFOs.
(openDocument): New argument READ.
* java/org/gnu/emacs/EmacsService.java (openDocument): New
argument READ.
* src/android.c (android_init_emacs_service): Adjust
correspondingly.
* src/androidvfs.c (android_saf_file_open): Don't support
O_APPEND.  Pass read as well as trunc and write.
This commit is contained in:
Po Lu 2023-08-08 13:39:16 +08:00
parent 3fb2c174d3
commit d35ead5bd8
4 changed files with 85 additions and 41 deletions

View file

@ -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);
}
});

View file

@ -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);
}

View file

@ -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;"

View file

@ -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'.