aboutsummaryrefslogtreecommitdiffstats
path: root/src/androidvfs.c
diff options
context:
space:
mode:
authorPo Lu2024-03-19 12:08:17 +0800
committerPo Lu2024-03-19 12:08:17 +0800
commitf2e239c6a7d54ec3849a3bb783685953b6683752 (patch)
tree0bb5d158eb1da4672d727057d2e045d8a9eb0bcc /src/androidvfs.c
parentce29ae32d0b05cedbc9ba65c1a347ab7c34420ad (diff)
downloademacs-f2e239c6a7d54ec3849a3bb783685953b6683752.tar.gz
emacs-f2e239c6a7d54ec3849a3bb783685953b6683752.zip
Respect display names of Android content URIs
* java/org/gnu/emacs/EmacsNative.java (displayNameHash): New function. * java/org/gnu/emacs/EmacsService.java (buildContentName): New argument RESOLVER. Generate names holding URI's display name if available. All callers changed. * lisp/international/mule-cmds.el (set-default-coding-systems): Fix file name coding system as utf-8-unix on Android as on Mac OS. * src/androidvfs.c (enum android_vnode_type): New enum ANDROID_VNODE_CONTENT_AUTHORITY_NAMED. (android_content_name): Register root directories for this new type. (displayNameHash): New function. (android_get_content_name): New argument WITH_CHECKSUM. If present, treat the final two components as a pair of checksum and display name, and verify and exclude the two. (android_authority_name): Provide new argument as appropriate. (android_authority_initial_name): New function.
Diffstat (limited to 'src/androidvfs.c')
-rw-r--r--src/androidvfs.c150
1 files changed, 136 insertions, 14 deletions
diff --git a/src/androidvfs.c b/src/androidvfs.c
index 4bb652f3eb7..9e3d5cab8cf 100644
--- a/src/androidvfs.c
+++ b/src/androidvfs.c
@@ -33,6 +33,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
33#include <sys/mman.h> 33#include <sys/mman.h>
34 34
35#include <stat-time.h> 35#include <stat-time.h>
36#include <md5.h>
36 37
37#include <linux/ashmem.h> 38#include <linux/ashmem.h>
38 39
@@ -255,6 +256,7 @@ enum android_vnode_type
255 ANDROID_VNODE_AFS, 256 ANDROID_VNODE_AFS,
256 ANDROID_VNODE_CONTENT, 257 ANDROID_VNODE_CONTENT,
257 ANDROID_VNODE_CONTENT_AUTHORITY, 258 ANDROID_VNODE_CONTENT_AUTHORITY,
259 ANDROID_VNODE_CONTENT_AUTHORITY_NAMED,
258 ANDROID_VNODE_SAF_ROOT, 260 ANDROID_VNODE_SAF_ROOT,
259 ANDROID_VNODE_SAF_TREE, 261 ANDROID_VNODE_SAF_TREE,
260 ANDROID_VNODE_SAF_FILE, 262 ANDROID_VNODE_SAF_FILE,
@@ -2435,6 +2437,7 @@ struct android_content_vdir
2435}; 2437};
2436 2438
2437static struct android_vnode *android_authority_initial (char *, size_t); 2439static struct android_vnode *android_authority_initial (char *, size_t);
2440static struct android_vnode *android_authority_initial_name (char *, size_t);
2438static struct android_vnode *android_saf_root_initial (char *, size_t); 2441static struct android_vnode *android_saf_root_initial (char *, size_t);
2439 2442
2440/* Content provider meta-interface. This implements a vnode at 2443/* Content provider meta-interface. This implements a vnode at
@@ -2445,9 +2448,9 @@ static struct android_vnode *android_saf_root_initial (char *, size_t);
2445 a list of each directory tree Emacs has been granted permanent 2448 a list of each directory tree Emacs has been granted permanent
2446 access to through the Storage Access Framework. 2449 access to through the Storage Access Framework.
2447 2450
2448 /content/by-authority exists on Android 4.4 and later; it contains 2451 /content/by-authority and /content/by-authority-named exists on
2449 no directories, but provides a `name' function that converts 2452 Android 4.4 and later; it contains no directories, but provides a
2450 children into content URIs. */ 2453 `name' function that converts children into content URIs. */
2451 2454
2452static struct android_vnode *android_content_name (struct android_vnode *, 2455static struct android_vnode *android_content_name (struct android_vnode *,
2453 char *, size_t); 2456 char *, size_t);
@@ -2490,7 +2493,7 @@ static struct android_vops content_vfs_ops =
2490 2493
2491static const char *content_directory_contents[] = 2494static const char *content_directory_contents[] =
2492 { 2495 {
2493 "storage", "by-authority", 2496 "storage", "by-authority", "by-authority-named",
2494 }; 2497 };
2495 2498
2496/* Chain consisting of all open content directory streams. */ 2499/* Chain consisting of all open content directory streams. */
@@ -2508,8 +2511,9 @@ android_content_name (struct android_vnode *vnode, char *name,
2508 int api; 2511 int api;
2509 2512
2510 static struct android_special_vnode content_vnodes[] = { 2513 static struct android_special_vnode content_vnodes[] = {
2511 { "storage", 7, android_saf_root_initial, }, 2514 { "storage", 7, android_saf_root_initial, },
2512 { "by-authority", 12, android_authority_initial, }, 2515 { "by-authority", 12, android_authority_initial, },
2516 { "by-authority-named", 18, android_authority_initial_name, },
2513 }; 2517 };
2514 2518
2515 /* Canonicalize NAME. */ 2519 /* Canonicalize NAME. */
@@ -2551,7 +2555,7 @@ android_content_name (struct android_vnode *vnode, char *name,
2551 call its root lookup function with the rest of NAME there. */ 2555 call its root lookup function with the rest of NAME there. */
2552 2556
2553 if (api < 19) 2557 if (api < 19)
2554 i = 2; 2558 i = 3;
2555 else if (api < 21) 2559 else if (api < 21)
2556 i = 1; 2560 i = 1;
2557 else 2561 else
@@ -2855,18 +2859,59 @@ android_content_initial (char *name, size_t length)
2855 2859
2856 2860
2857 2861
2862#ifdef __clang__
2863#pragma clang diagnostic push
2864#pragma clang diagnostic ignored "-Wmissing-prototypes"
2865#else /* GNUC */
2866#pragma GCC diagnostic push
2867#pragma GCC diagnostic ignored "-Wmissing-prototypes"
2868#endif /* __clang__ */
2869
2858/* Content URI management functions. */ 2870/* Content URI management functions. */
2859 2871
2872JNIEXPORT jstring JNICALL
2873NATIVE_NAME (displayNameHash) (JNIEnv *env, jobject object,
2874 jbyteArray display_name)
2875{
2876 char checksum[9], block[MD5_DIGEST_SIZE];
2877 jbyte *data;
2878
2879 data = (*env)->GetByteArrayElements (env, display_name, NULL);
2880 if (!data)
2881 return NULL;
2882
2883 /* Hash the buffer. */
2884 md5_buffer ((char *) data, (*env)->GetArrayLength (env, display_name),
2885 block);
2886 (*env)->ReleaseByteArrayElements (env, display_name, data, JNI_ABORT);
2887
2888 /* Generate the digest string. */
2889 hexbuf_digest (checksum, (char *) block, 4);
2890 checksum[8] = '\0';
2891 return (*env)->NewStringUTF (env, checksum);
2892}
2893
2894#ifdef __clang__
2895#pragma clang diagnostic pop
2896#else /* GNUC */
2897#pragma GCC diagnostic pop
2898#endif /* __clang__ */
2899
2860/* Return the content URI corresponding to a `/content/by-authority' 2900/* Return the content URI corresponding to a `/content/by-authority'
2861 file name, or NULL if it is invalid for some reason. FILENAME 2901 file name, or NULL if it is invalid for some reason. FILENAME
2862 should be relative to /content/by-authority, with no leading 2902 should be relative to /content/by-authority, with no leading
2863 directory separator character. */ 2903 directory separator character.
2904
2905 WITH_CHECKSUM should be true if FILENAME contains a display name and
2906 a checksum for that display name. */
2864 2907
2865static char * 2908static char *
2866android_get_content_name (const char *filename) 2909android_get_content_name (const char *filename, bool with_checksum)
2867{ 2910{
2868 char *fill, *buffer; 2911 char *fill, *buffer;
2869 size_t length; 2912 size_t length;
2913 char checksum[9], new_checksum[9], block[MD5_DIGEST_SIZE];
2914 const char *p2, *p1;
2870 2915
2871 /* Make sure FILENAME isn't obviously invalid: it must contain an 2916 /* Make sure FILENAME isn't obviously invalid: it must contain an
2872 authority name and a file name component. */ 2917 authority name and a file name component. */
@@ -2888,11 +2933,55 @@ android_get_content_name (const char *filename)
2888 return NULL; 2933 return NULL;
2889 } 2934 }
2890 2935
2936 if (!with_checksum)
2937 goto no_checksum;
2938
2939 /* Content file names hold two components providing a display name and
2940 a short checksum that protects against files being opened under
2941 display names besides those provided in the content file name at
2942 the time of generation. */
2943
2944 p1 = strrchr (filename, '/'); /* Display name. */
2945 p2 = memrchr (filename, '/', p1 - filename); /* Start of checksum. */
2946
2947 /* If the name be excessively short or the checksum of an invalid
2948 length, return. */
2949 if (!p2 || (p1 - p2) != 9)
2950 {
2951 errno = ENOENT;
2952 return NULL;
2953 }
2954
2955 /* Copy the checksum into CHECKSUM. */
2956 memcpy (checksum, p2 + 1, 8);
2957 new_checksum[8] = checksum[8] = '\0';
2958
2959 /* Hash this string and store 8 bytes of the resulting digest into
2960 new_checksum. */
2961 md5_buffer (p1 + 1, strlen (p1 + 1), block);
2962 hexbuf_digest (new_checksum, (char *) block, 4);
2963
2964 /* Compare both checksums. */
2965 if (strcmp (new_checksum, checksum))
2966 {
2967 errno = ENOENT;
2968 return NULL;
2969 }
2970
2971 /* Remove the checksum and file display name from the URI. */
2972 length = p2 - filename;
2973
2974 no_checksum:
2975 if (length > INT_MAX)
2976 {
2977 errno = ENOMEM;
2978 return NULL;
2979 }
2980
2891 /* Prefix FILENAME with content:// and return the buffer containing 2981 /* Prefix FILENAME with content:// and return the buffer containing
2892 that URI. */ 2982 that URI. */
2893 2983 buffer = xmalloc (sizeof "content://" + length + 1);
2894 buffer = xmalloc (sizeof "content://" + length); 2984 sprintf (buffer, "content://%.*s", (int) length, filename);
2895 sprintf (buffer, "content://%s", filename);
2896 return buffer; 2985 return buffer;
2897} 2986}
2898 2987
@@ -2932,7 +3021,7 @@ android_check_content_access (const char *uri, int mode)
2932 3021
2933/* Content authority-based vnode implementation. 3022/* Content authority-based vnode implementation.
2934 3023
2935 /contents/by-authority is a simple vnode implementation that converts 3024 /content/by-authority is a simple vnode implementation that converts
2936 components to content:// URIs. 3025 components to content:// URIs.
2937 3026
2938 It does not canonicalize file names by removing parent directory 3027 It does not canonicalize file names by removing parent directory
@@ -3039,7 +3128,14 @@ android_authority_name (struct android_vnode *vnode, char *name,
3039 if (android_verify_jni_string (name)) 3128 if (android_verify_jni_string (name))
3040 goto no_entry; 3129 goto no_entry;
3041 3130
3042 uri_name = android_get_content_name (name); 3131 if (vp->vnode.type == ANDROID_VNODE_CONTENT_AUTHORITY_NAMED)
3132 /* This indicates that the two trailing components of NAME
3133 provide a checksum and a file display name, to be verified,
3134 then excluded from the content URI. */
3135 uri_name = android_get_content_name (name, true);
3136 else
3137 uri_name = android_get_content_name (name, false);
3138
3043 if (!uri_name) 3139 if (!uri_name)
3044 goto error; 3140 goto error;
3045 3141
@@ -3333,6 +3429,32 @@ android_authority_initial (char *name, size_t length)
3333 return android_authority_name (&temp.vnode, name, length); 3429 return android_authority_name (&temp.vnode, name, length);
3334} 3430}
3335 3431
3432/* Find the vnode designated by NAME relative to the root of the
3433 by-authority-named directory.
3434
3435 If NAME is empty or a single leading separator character, return
3436 a vnode representing the by-authority directory itself.
3437
3438 Otherwise, represent the remainder of NAME as a URI (without
3439 normalizing it) and return a vnode corresponding to that.
3440
3441 Value may also be NULL with errno set if the designated vnode is
3442 not available, such as when Android windowing has not been
3443 initialized. */
3444
3445static struct android_vnode *
3446android_authority_initial_name (char *name, size_t length)
3447{
3448 struct android_authority_vnode temp;
3449
3450 temp.vnode.ops = &authority_vfs_ops;
3451 temp.vnode.type = ANDROID_VNODE_CONTENT_AUTHORITY_NAMED;
3452 temp.vnode.flags = 0;
3453 temp.uri = NULL;
3454
3455 return android_authority_name (&temp.vnode, name, length);
3456}
3457
3336 3458
3337 3459
3338/* SAF ``root'' vnode implementation. 3460/* SAF ``root'' vnode implementation.