diff options
| author | Po Lu | 2024-03-19 12:08:17 +0800 |
|---|---|---|
| committer | Po Lu | 2024-03-19 12:08:17 +0800 |
| commit | f2e239c6a7d54ec3849a3bb783685953b6683752 (patch) | |
| tree | 0bb5d158eb1da4672d727057d2e045d8a9eb0bcc /src/androidvfs.c | |
| parent | ce29ae32d0b05cedbc9ba65c1a347ab7c34420ad (diff) | |
| download | emacs-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.c | 150 |
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 | ||
| 2437 | static struct android_vnode *android_authority_initial (char *, size_t); | 2439 | static struct android_vnode *android_authority_initial (char *, size_t); |
| 2440 | static struct android_vnode *android_authority_initial_name (char *, size_t); | ||
| 2438 | static struct android_vnode *android_saf_root_initial (char *, size_t); | 2441 | static 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 | ||
| 2452 | static struct android_vnode *android_content_name (struct android_vnode *, | 2455 | static 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 | ||
| 2491 | static const char *content_directory_contents[] = | 2494 | static 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 | ||
| 2872 | JNIEXPORT jstring JNICALL | ||
| 2873 | NATIVE_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 | ||
| 2865 | static char * | 2908 | static char * |
| 2866 | android_get_content_name (const char *filename) | 2909 | android_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 | |||
| 3445 | static struct android_vnode * | ||
| 3446 | android_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. |