From 9fb00904f959a7e94cf992acb3a96e78a05e719c Mon Sep 17 00:00:00 2001 From: Po Lu Date: Mon, 14 Aug 2023 13:15:08 +0800 Subject: [PATCH] Improve efficiency of checking for access to authority documents * java/org/gnu/emacs/EmacsService.java (checkContentUri): Take a string instead of a byte array. Then, use checkCallingUriPermission, in lieu of opening the file. * src/android.c (android_check_content_access): Delete unused function. (android_init_emacs_service): Adjust for changes to checkContentUri's signature. * src/androidvfs.c (android_get_content_name): Return the file name in a new buffer. (android_check_content_access): Adjust correspondingly. (android_authority_name): Verify NAME is a valid JNI string. --- java/org/gnu/emacs/EmacsService.java | 46 ++++++++-------------- src/android.c | 37 +----------------- src/androidvfs.c | 57 +++++++++++++++++----------- 3 files changed, 51 insertions(+), 89 deletions(-) diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index cee823abc83..19ce67c59b7 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -960,44 +960,30 @@ public final class EmacsService extends Service } } + /* Return whether Emacs is directly permitted to access the + content:// URI NAME. This is not a suitable test for files which + Emacs can access by virtue of their containing document + trees. */ + public boolean - checkContentUri (byte[] string, boolean readable, boolean writable) + checkContentUri (String name, boolean readable, boolean writable) { - String mode, name; + String mode; ParcelFileDescriptor fd; + Uri uri; + int rc, flags; - /* Decode this into a URI. */ - - try - { - /* The usual file name encoding question rears its ugly head - again. */ - name = new String (string, "UTF-8"); - } - catch (UnsupportedEncodingException exception) - { - name = null; - throw new RuntimeException (exception); - } + uri = Uri.parse (name); + flags = 0; - mode = "r"; + if (readable) + flags |= Intent.FLAG_GRANT_READ_URI_PERMISSION; if (writable) - mode += "w"; - - try - { - fd = resolver.openFileDescriptor (Uri.parse (name), mode); - fd.close (); + flags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION; - return true; - } - catch (Exception exception) - { - /* Fall through. */ - } - - return false; + rc = checkCallingUriPermission (uri, flags); + return rc == PackageManager.PERMISSION_GRANTED; } /* Build a content file name for URI. diff --git a/src/android.c b/src/android.c index 70779f8ccae..ed304baf0e6 100644 --- a/src/android.c +++ b/src/android.c @@ -1107,41 +1107,6 @@ android_get_content_name (const char *filename) return NULL; } -/* Return whether or not the specified FILENAME is an accessible - content URI. MODE specifies what to check. */ - -static bool -android_check_content_access (const char *filename, int mode) -{ - const char *name; - jobject string; - size_t length; - jboolean rc; - - name = android_get_content_name (filename); - length = strlen (name); - - string = (*android_java_env)->NewByteArray (android_java_env, - length); - android_exception_check (); - - (*android_java_env)->SetByteArrayRegion (android_java_env, - string, 0, length, - (jbyte *) name); - rc = (*android_java_env)->CallBooleanMethod (android_java_env, - emacs_service, - service_class.check_content_uri, - string, - (jboolean) ((mode & R_OK) - != 0), - (jboolean) ((mode & W_OK) - != 0)); - android_exception_check_1 (string); - ANDROID_DELETE_LOCAL_REF (string); - - return rc; -} - #endif /* 0 */ /* Return the current user's ``home'' directory, which is actually the @@ -1549,7 +1514,7 @@ android_init_emacs_service (void) FIND_METHOD (open_content_uri, "openContentUri", "([BZZZ)I"); FIND_METHOD (check_content_uri, "checkContentUri", - "([BZZ)Z"); + "(Ljava/lang/String;ZZ)Z"); FIND_METHOD (query_battery, "queryBattery", "()[J"); FIND_METHOD (update_extracted_text, "updateExtractedText", "(Lorg/gnu/emacs/EmacsWindow;" diff --git a/src/androidvfs.c b/src/androidvfs.c index 0385e7348c6..1b82753be3d 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -2765,14 +2765,13 @@ android_content_initial (char *name, size_t length) /* Return the content URI corresponding to a `/content/by-authority' file name, or NULL if it is invalid for some reason. FILENAME should be relative to /content/by-authority, with no leading - directory separator character. + directory separator character. */ - This function is not reentrant. */ - -static const char * +static char * android_get_content_name (const char *filename) { - static char buffer[PATH_MAX + 1], *fill; + char *fill, *buffer; + size_t length; /* Make sure FILENAME isn't obviously invalid: it must contain an authority name and a file name component. */ @@ -2784,48 +2783,53 @@ android_get_content_name (const char *filename) return NULL; } - /* FILENAME must also not be a directory. */ + /* FILENAME must also not be a directory. Accessing content + provider directories is not supported by this interface. */ - if (filename[strlen (filename)] == '/') + length = strlen (filename); + if (filename[length] == '/') { errno = ENOTDIR; return NULL; } - snprintf (buffer, PATH_MAX + 1, "content://%s", filename); + /* Prefix FILENAME with content:// and return the buffer containing + that URI. */ + + buffer = xmalloc (sizeof "content://" + length); + sprintf (buffer, "content://%s", filename); return buffer; } /* Return whether or not the specified URI is an accessible content - URI. MODE specifies what to check. */ + URI. MODE specifies what to check. + + URI must be a string in the JVM's extended UTF-8 format. */ static bool android_check_content_access (const char *uri, int mode) { jobject string; size_t length; - jboolean rc; + jboolean rc, read, write; length = strlen (uri); - string = (*android_java_env)->NewByteArray (android_java_env, - length); + string = (*android_java_env)->NewStringUTF (android_java_env, uri); android_exception_check (); - (*android_java_env)->SetByteArrayRegion (android_java_env, - string, 0, length, - (jbyte *) uri); + /* Establish what is being checked. Checking for read access is + identical to checking if the file exists. */ + + read = (bool) (mode & R_OK || (mode == F_OK)); + write = (bool) (mode & W_OK); + rc = (*android_java_env)->CallBooleanMethod (android_java_env, emacs_service, service_class.check_content_uri, - string, - (jboolean) ((mode & R_OK) - != 0), - (jboolean) ((mode & W_OK) - != 0)); + string, read, write); android_exception_check_1 (string); ANDROID_DELETE_LOCAL_REF (string); - return rc; } @@ -2889,7 +2893,7 @@ android_authority_name (struct android_vnode *vnode, char *name, size_t length) { struct android_authority_vnode *vp; - const char *uri_name; + char *uri_name; if (!android_init_gui) { @@ -2922,6 +2926,12 @@ android_authority_name (struct android_vnode *vnode, char *name, if (*name == '/') name++, length -= 1; + /* NAME must be a valid JNI string, so that it can be encoded + properly. */ + + if (android_verify_jni_string (name)) + goto no_entry; + uri_name = android_get_content_name (name); if (!uri_name) goto error; @@ -2931,11 +2941,12 @@ android_authority_name (struct android_vnode *vnode, char *name, vp->vnode.ops = &authority_vfs_ops; vp->vnode.type = ANDROID_VNODE_CONTENT_AUTHORITY; vp->vnode.flags = 0; - vp->uri = xstrdup (uri_name); + vp->uri = uri_name; return &vp->vnode; } /* Content files can't have children. */ + no_entry: errno = ENOENT; error: return NULL; -- 2.39.2