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;
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";
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
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
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
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;
runObject (CancellationSignal signal)
throws Throwable
{
- return openDocument1 (uri, documentId, write, truncate,
- signal);
+ return openDocument1 (uri, documentId, read,
+ write, truncate, signal);
}
});
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
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. */
storageThread.start ();
}
- return storageThread.openDocument (uri, documentId, write,
+ return storageThread.openDocument (uri, documentId, read, write,
truncate);
}
"(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;"
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;
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;
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))
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'.