diff options
| author | Po Lu | 2023-07-28 12:21:47 +0800 |
|---|---|---|
| committer | Po Lu | 2023-07-28 12:21:47 +0800 |
| commit | 03cf3bbb5c38aa55abd6f7d4860025f7482fcfc3 (patch) | |
| tree | a8d55088a6efed2cbe4b0bfad1c1bde61579b54e | |
| parent | 7c0899586471d3649dfb468d2b8f7d6d9685fea1 (diff) | |
| download | emacs-03cf3bbb5c38aa55abd6f7d4860025f7482fcfc3.tar.gz emacs-03cf3bbb5c38aa55abd6f7d4860025f7482fcfc3.zip | |
Update Android port
* java/org/gnu/emacs/EmacsDirectoryEntry.java
(EmacsDirectoryEntry): Make class final.
* java/org/gnu/emacs/EmacsService.java (accessDocument)
(openDocumentDirectory, openDocument, createDocument): Throw
access and IO error exceptions instead of catching them.
(createDirectory, deleteDocument): New functions.
* src/android.c (android_init_emacs_service): Add new functions.
* src/android.h (struct android_emacs_service): Likewise.
* src/androidvfs.c (android_saf_exception_check): New function.
Translate between Java exceptions and errno values.
(android_saf_stat, android_saf_access, android_saf_delete_document)
(struct android_saf_tree_vnode, android_document_id_from_name)
(android_saf_tree_name, android_saf_tree_rmdir)
(android_saf_tree_opendir_1, android_saf_tree_opendir)
(android_saf_file_open, android_saf_file_unlink)
(android_saf_new_open, android_saf_new_mkdir): Implement missing
VFS operations and derive errno values from the type of any
exceptions thrown.
(android_vfs_init): Initialize exception classes.
(android_mkdir, android_fstat): Remove trailing whitespace.
| -rw-r--r-- | java/org/gnu/emacs/EmacsDirectoryEntry.java | 2 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsService.java | 210 | ||||
| -rw-r--r-- | src/android.c | 5 | ||||
| -rw-r--r-- | src/android.h | 2 | ||||
| -rw-r--r-- | src/androidvfs.c | 316 |
5 files changed, 410 insertions, 125 deletions
diff --git a/java/org/gnu/emacs/EmacsDirectoryEntry.java b/java/org/gnu/emacs/EmacsDirectoryEntry.java index 9c10f2e8771..75c52e48002 100644 --- a/java/org/gnu/emacs/EmacsDirectoryEntry.java +++ b/java/org/gnu/emacs/EmacsDirectoryEntry.java | |||
| @@ -22,7 +22,7 @@ package org.gnu.emacs; | |||
| 22 | /* Structure holding a single ``directory entry'' from a document | 22 | /* Structure holding a single ``directory entry'' from a document |
| 23 | provider. */ | 23 | provider. */ |
| 24 | 24 | ||
| 25 | public class EmacsDirectoryEntry | 25 | public final class EmacsDirectoryEntry |
| 26 | { | 26 | { |
| 27 | /* The type of this directory entry. 0 means a regular file and 1 | 27 | /* The type of this directory entry. 0 means a regular file and 1 |
| 28 | means a directory. */ | 28 | means a directory. */ |
diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index bc62e050345..aa672994f12 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java | |||
| @@ -1723,9 +1723,12 @@ public final class EmacsService extends Service | |||
| 1723 | not: | 1723 | not: |
| 1724 | 1724 | ||
| 1725 | -1, if the file does not exist. | 1725 | -1, if the file does not exist. |
| 1726 | -2, upon a security exception or if WRITABLE the file | 1726 | -2, if WRITABLE and the file is not writable. |
| 1727 | is not writable. | 1727 | -3, upon any other error. |
| 1728 | -3, upon any other error. */ | 1728 | |
| 1729 | In addition, arbitrary runtime exceptions (such as | ||
| 1730 | SecurityException or UnsupportedOperationException) may be | ||
| 1731 | thrown. */ | ||
| 1729 | 1732 | ||
| 1730 | public int | 1733 | public int |
| 1731 | accessDocument (String uri, String documentId, boolean writable) | 1734 | accessDocument (String uri, String documentId, boolean writable) |
| @@ -1754,24 +1757,8 @@ public final class EmacsService extends Service | |||
| 1754 | Document.COLUMN_MIME_TYPE, | 1757 | Document.COLUMN_MIME_TYPE, |
| 1755 | }; | 1758 | }; |
| 1756 | 1759 | ||
| 1757 | try | 1760 | cursor = resolver.query (uriObject, projection, null, |
| 1758 | { | 1761 | null, null); |
| 1759 | cursor = resolver.query (uriObject, projection, null, | ||
| 1760 | null, null); | ||
| 1761 | } | ||
| 1762 | catch (SecurityException exception) | ||
| 1763 | { | ||
| 1764 | /* A SecurityException can be thrown if Emacs doesn't have | ||
| 1765 | access to uriObject. */ | ||
| 1766 | return -2; | ||
| 1767 | } | ||
| 1768 | catch (UnsupportedOperationException exception) | ||
| 1769 | { | ||
| 1770 | exception.printStackTrace (); | ||
| 1771 | |||
| 1772 | /* Why is this? */ | ||
| 1773 | return -3; | ||
| 1774 | } | ||
| 1775 | 1762 | ||
| 1776 | if (cursor == null || !cursor.moveToFirst ()) | 1763 | if (cursor == null || !cursor.moveToFirst ()) |
| 1777 | return -1; | 1764 | return -1; |
| @@ -1816,13 +1803,10 @@ public final class EmacsService extends Service | |||
| 1816 | if (writable && (tem & Document.FLAG_SUPPORTS_WRITE) == 0) | 1803 | if (writable && (tem & Document.FLAG_SUPPORTS_WRITE) == 0) |
| 1817 | return -3; | 1804 | return -3; |
| 1818 | } | 1805 | } |
| 1819 | catch (Exception exception) | 1806 | finally |
| 1820 | { | 1807 | { |
| 1821 | /* Whether or not type errors cause exceptions to be signaled | 1808 | /* Close the cursor if an exception occurs. */ |
| 1822 | is defined ``by the implementation of Cursor'', whatever | 1809 | cursor.close (); |
| 1823 | that means. */ | ||
| 1824 | exception.printStackTrace (); | ||
| 1825 | return -3; | ||
| 1826 | } | 1810 | } |
| 1827 | 1811 | ||
| 1828 | return 0; | 1812 | return 0; |
| @@ -1832,7 +1816,11 @@ public final class EmacsService extends Service | |||
| 1832 | designated by the specified DOCUMENTID within the tree URI. | 1816 | designated by the specified DOCUMENTID within the tree URI. |
| 1833 | 1817 | ||
| 1834 | If DOCUMENTID is NULL, use the document ID within URI itself. | 1818 | If DOCUMENTID is NULL, use the document ID within URI itself. |
| 1835 | Value is NULL upon failure. */ | 1819 | Value is NULL upon failure. |
| 1820 | |||
| 1821 | In addition, arbitrary runtime exceptions (such as | ||
| 1822 | SecurityException or UnsupportedOperationException) may be | ||
| 1823 | thrown. */ | ||
| 1836 | 1824 | ||
| 1837 | public Cursor | 1825 | public Cursor |
| 1838 | openDocumentDirectory (String uri, String documentId) | 1826 | openDocumentDirectory (String uri, String documentId) |
| @@ -1861,25 +1849,8 @@ public final class EmacsService extends Service | |||
| 1861 | Document.COLUMN_MIME_TYPE, | 1849 | Document.COLUMN_MIME_TYPE, |
| 1862 | }; | 1850 | }; |
| 1863 | 1851 | ||
| 1864 | try | 1852 | cursor = resolver.query (uriObject, projection, null, null, |
| 1865 | { | 1853 | null); |
| 1866 | cursor = resolver.query (uriObject, projection, null, null, | ||
| 1867 | null); | ||
| 1868 | } | ||
| 1869 | catch (SecurityException exception) | ||
| 1870 | { | ||
| 1871 | /* A SecurityException can be thrown if Emacs doesn't have | ||
| 1872 | access to uriObject. */ | ||
| 1873 | return null; | ||
| 1874 | } | ||
| 1875 | catch (UnsupportedOperationException exception) | ||
| 1876 | { | ||
| 1877 | exception.printStackTrace (); | ||
| 1878 | |||
| 1879 | /* Why is this? */ | ||
| 1880 | return null; | ||
| 1881 | } | ||
| 1882 | |||
| 1883 | /* Return the cursor. */ | 1854 | /* Return the cursor. */ |
| 1884 | return cursor; | 1855 | return cursor; |
| 1885 | } | 1856 | } |
| @@ -1966,11 +1937,15 @@ public final class EmacsService extends Service | |||
| 1966 | 1937 | ||
| 1967 | Value is NULL upon failure or a parcel file descriptor upon | 1938 | Value is NULL upon failure or a parcel file descriptor upon |
| 1968 | success. Call `ParcelFileDescriptor.close' on this file | 1939 | success. Call `ParcelFileDescriptor.close' on this file |
| 1969 | descriptor instead of using the `close' system call. */ | 1940 | descriptor instead of using the `close' system call. |
| 1941 | |||
| 1942 | FileNotFoundException and/or SecurityException and | ||
| 1943 | UnsupportedOperationException may be thrown upon failure. */ | ||
| 1970 | 1944 | ||
| 1971 | public ParcelFileDescriptor | 1945 | public ParcelFileDescriptor |
| 1972 | openDocument (String uri, String documentId, boolean write, | 1946 | openDocument (String uri, String documentId, boolean write, |
| 1973 | boolean truncate) | 1947 | boolean truncate) |
| 1948 | throws FileNotFoundException | ||
| 1974 | { | 1949 | { |
| 1975 | Uri treeUri, documentUri; | 1950 | Uri treeUri, documentUri; |
| 1976 | String mode; | 1951 | String mode; |
| @@ -1984,40 +1959,33 @@ public final class EmacsService extends Service | |||
| 1984 | documentUri | 1959 | documentUri |
| 1985 | = DocumentsContract.buildDocumentUriUsingTree (treeUri, documentId); | 1960 | = DocumentsContract.buildDocumentUriUsingTree (treeUri, documentId); |
| 1986 | 1961 | ||
| 1987 | try | 1962 | if (write || Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) |
| 1988 | { | 1963 | { |
| 1989 | if (write || Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) | 1964 | /* Select the mode used to open the file. `rw' means open |
| 1990 | { | 1965 | a stat-able file, while `rwt' means that and to |
| 1991 | /* Select the mode used to open the file. `rw' means open | 1966 | truncate the file as well. */ |
| 1992 | a stat-able file, while `rwt' means that and to | ||
| 1993 | truncate the file as well. */ | ||
| 1994 | |||
| 1995 | if (truncate) | ||
| 1996 | mode = "rwt"; | ||
| 1997 | else | ||
| 1998 | mode = "rw"; | ||
| 1999 | |||
| 2000 | fileDescriptor | ||
| 2001 | = resolver.openFileDescriptor (documentUri, mode, | ||
| 2002 | null); | ||
| 2003 | } | ||
| 2004 | else | ||
| 2005 | { | ||
| 2006 | /* Select the mode used to open the file. `openFile' | ||
| 2007 | below means always open a stat-able file. */ | ||
| 2008 | 1967 | ||
| 2009 | if (truncate) | 1968 | if (truncate) |
| 2010 | /* Invalid mode! */ | 1969 | mode = "rwt"; |
| 2011 | return null; | 1970 | else |
| 2012 | else | 1971 | mode = "rw"; |
| 2013 | mode = "r"; | ||
| 2014 | 1972 | ||
| 2015 | fileDescriptor = resolver.openFile (documentUri, mode, null); | 1973 | fileDescriptor |
| 2016 | } | 1974 | = resolver.openFileDescriptor (documentUri, mode, |
| 1975 | null); | ||
| 2017 | } | 1976 | } |
| 2018 | catch (Exception exception) | 1977 | else |
| 2019 | { | 1978 | { |
| 2020 | return null; | 1979 | /* Select the mode used to open the file. `openFile' |
| 1980 | below means always open a stat-able file. */ | ||
| 1981 | |||
| 1982 | if (truncate) | ||
| 1983 | /* Invalid mode! */ | ||
| 1984 | return null; | ||
| 1985 | else | ||
| 1986 | mode = "r"; | ||
| 1987 | |||
| 1988 | fileDescriptor = resolver.openFile (documentUri, mode, null); | ||
| 2021 | } | 1989 | } |
| 2022 | 1990 | ||
| 2023 | return fileDescriptor; | 1991 | return fileDescriptor; |
| @@ -2030,11 +1998,15 @@ public final class EmacsService extends Service | |||
| 2030 | If DOCUMENTID is NULL, create the document inside the root of | 1998 | If DOCUMENTID is NULL, create the document inside the root of |
| 2031 | that tree. | 1999 | that tree. |
| 2032 | 2000 | ||
| 2001 | Either FileNotFoundException, SecurityException or | ||
| 2002 | UnsupportedOperationException may be thrown upon failure. | ||
| 2003 | |||
| 2033 | Return the document ID of the new file upon success, NULL | 2004 | Return the document ID of the new file upon success, NULL |
| 2034 | otherwise. */ | 2005 | otherwise. */ |
| 2035 | 2006 | ||
| 2036 | public String | 2007 | public String |
| 2037 | createDocument (String uri, String documentId, String name) | 2008 | createDocument (String uri, String documentId, String name) |
| 2009 | throws FileNotFoundException | ||
| 2038 | { | 2010 | { |
| 2039 | String mimeType, separator, mime, extension; | 2011 | String mimeType, separator, mime, extension; |
| 2040 | int index; | 2012 | int index; |
| @@ -2072,23 +2044,77 @@ public final class EmacsService extends Service | |||
| 2072 | = DocumentsContract.buildChildDocumentsUriUsingTree (directoryUri, | 2044 | = DocumentsContract.buildChildDocumentsUriUsingTree (directoryUri, |
| 2073 | documentId); | 2045 | documentId); |
| 2074 | 2046 | ||
| 2075 | try | 2047 | docUri = DocumentsContract.createDocument (resolver, |
| 2076 | { | 2048 | directoryUri, |
| 2077 | docUri = DocumentsContract.createDocument (resolver, | 2049 | mimeType, name); |
| 2078 | directoryUri, | ||
| 2079 | mimeType, name); | ||
| 2080 | 2050 | ||
| 2081 | if (docUri == null) | 2051 | if (docUri == null) |
| 2082 | return null; | 2052 | return null; |
| 2083 | 2053 | ||
| 2084 | /* Return the ID of the new document. */ | 2054 | /* Return the ID of the new document. */ |
| 2085 | return DocumentsContract.getDocumentId (docUri); | 2055 | return DocumentsContract.getDocumentId (docUri); |
| 2086 | } | 2056 | } |
| 2087 | catch (Exception exception) | ||
| 2088 | { | ||
| 2089 | exception.printStackTrace (); | ||
| 2090 | } | ||
| 2091 | 2057 | ||
| 2092 | return null; | 2058 | /* Like `createDocument', but create a directory instead of an |
| 2059 | ordinary document. */ | ||
| 2060 | |||
| 2061 | public String | ||
| 2062 | createDirectory (String uri, String documentId, String name) | ||
| 2063 | throws FileNotFoundException | ||
| 2064 | { | ||
| 2065 | int index; | ||
| 2066 | Uri directoryUri, docUri; | ||
| 2067 | |||
| 2068 | /* Now parse URI. */ | ||
| 2069 | directoryUri = Uri.parse (uri); | ||
| 2070 | |||
| 2071 | if (documentId == null) | ||
| 2072 | documentId = DocumentsContract.getTreeDocumentId (directoryUri); | ||
| 2073 | |||
| 2074 | /* And build a file URI referring to the directory. */ | ||
| 2075 | |||
| 2076 | directoryUri | ||
| 2077 | = DocumentsContract.buildChildDocumentsUriUsingTree (directoryUri, | ||
| 2078 | documentId); | ||
| 2079 | |||
| 2080 | /* If name ends with a directory separator character, delete | ||
| 2081 | it. */ | ||
| 2082 | |||
| 2083 | if (name.endsWith ("/")) | ||
| 2084 | name = name.substring (0, name.length () - 1); | ||
| 2085 | |||
| 2086 | /* From Android's perspective, directories are just ordinary | ||
| 2087 | documents with the `MIME_TYPE_DIR' type. */ | ||
| 2088 | |||
| 2089 | docUri = DocumentsContract.createDocument (resolver, | ||
| 2090 | directoryUri, | ||
| 2091 | Document.MIME_TYPE_DIR, | ||
| 2092 | name); | ||
| 2093 | |||
| 2094 | if (docUri == null) | ||
| 2095 | return null; | ||
| 2096 | |||
| 2097 | /* Return the ID of the new document. */ | ||
| 2098 | return DocumentsContract.getDocumentId (docUri); | ||
| 2099 | } | ||
| 2100 | |||
| 2101 | /* Delete the document identified by ID from the document tree | ||
| 2102 | identified by URI. Return 0 upon success and -1 upon | ||
| 2103 | failure. */ | ||
| 2104 | |||
| 2105 | public int | ||
| 2106 | deleteDocument (String uri, String id) | ||
| 2107 | throws FileNotFoundException | ||
| 2108 | { | ||
| 2109 | Uri uriObject; | ||
| 2110 | |||
| 2111 | uriObject = Uri.parse (uri); | ||
| 2112 | uriObject = DocumentsContract.buildDocumentUriUsingTree (uriObject, | ||
| 2113 | id); | ||
| 2114 | |||
| 2115 | if (DocumentsContract.deleteDocument (resolver, uriObject)) | ||
| 2116 | return 0; | ||
| 2117 | |||
| 2118 | return -1; | ||
| 2093 | } | 2119 | } |
| 2094 | }; | 2120 | }; |
diff --git a/src/android.c b/src/android.c index 098fa6c383d..687c0b48a2a 100644 --- a/src/android.c +++ b/src/android.c | |||
| @@ -1577,6 +1577,11 @@ android_init_emacs_service (void) | |||
| 1577 | FIND_METHOD (create_document, "createDocument", | 1577 | FIND_METHOD (create_document, "createDocument", |
| 1578 | "(Ljava/lang/String;Ljava/lang/String;" | 1578 | "(Ljava/lang/String;Ljava/lang/String;" |
| 1579 | "Ljava/lang/String;)Ljava/lang/String;"); | 1579 | "Ljava/lang/String;)Ljava/lang/String;"); |
| 1580 | FIND_METHOD (create_directory, "createDirectory", | ||
| 1581 | "(Ljava/lang/String;Ljava/lang/String;" | ||
| 1582 | "Ljava/lang/String;)Ljava/lang/String;"); | ||
| 1583 | FIND_METHOD (delete_document, "deleteDocument", | ||
| 1584 | "(Ljava/lang/String;Ljava/lang/String;)I"); | ||
| 1580 | #undef FIND_METHOD | 1585 | #undef FIND_METHOD |
| 1581 | } | 1586 | } |
| 1582 | 1587 | ||
diff --git a/src/android.h b/src/android.h index 94a3ad46e74..fd391fa6435 100644 --- a/src/android.h +++ b/src/android.h | |||
| @@ -279,6 +279,8 @@ struct android_emacs_service | |||
| 279 | jmethodID read_directory_entry; | 279 | jmethodID read_directory_entry; |
| 280 | jmethodID open_document; | 280 | jmethodID open_document; |
| 281 | jmethodID create_document; | 281 | jmethodID create_document; |
| 282 | jmethodID create_directory; | ||
| 283 | jmethodID delete_document; | ||
| 282 | }; | 284 | }; |
| 283 | 285 | ||
| 284 | extern JNIEnv *android_java_env; | 286 | extern JNIEnv *android_java_env; |
diff --git a/src/androidvfs.c b/src/androidvfs.c index c174c35f02b..2cd50963e97 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c | |||
| @@ -276,6 +276,10 @@ static struct emacs_directory_entry_class entry_class; | |||
| 276 | class. */ | 276 | class. */ |
| 277 | static struct android_parcel_file_descriptor_class fd_class; | 277 | static struct android_parcel_file_descriptor_class fd_class; |
| 278 | 278 | ||
| 279 | /* Global references to several exception classes. */ | ||
| 280 | static jclass file_not_found_exception, security_exception; | ||
| 281 | static jclass unsupported_operation_exception, out_of_memory_error; | ||
| 282 | |||
| 279 | /* Initialize `cursor_class' using the given JNI environment ENV. | 283 | /* Initialize `cursor_class' using the given JNI environment ENV. |
| 280 | Calling this function is not necessary on Android 4.4 and | 284 | Calling this function is not necessary on Android 4.4 and |
| 281 | earlier. */ | 285 | earlier. */ |
| @@ -3688,6 +3692,85 @@ android_saf_root_get_directory (int dirfd) | |||
| 3688 | 3692 | ||
| 3689 | /* Functions common to both SAF directory and file nodes. */ | 3693 | /* Functions common to both SAF directory and file nodes. */ |
| 3690 | 3694 | ||
| 3695 | /* Check for JNI exceptions, clear them, and set errno accordingly. | ||
| 3696 | Also, free each of the N local references given as arguments if an | ||
| 3697 | exception takes place. | ||
| 3698 | |||
| 3699 | Value is 1 if an exception has taken place, 0 otherwise. | ||
| 3700 | |||
| 3701 | If the exception thrown derives from FileNotFoundException, set | ||
| 3702 | errno to ENOENT. | ||
| 3703 | |||
| 3704 | If the exception thrown derives from SecurityException, set errno | ||
| 3705 | to EACCES. | ||
| 3706 | |||
| 3707 | If the exception thrown derives from UnsupportedOperationException, | ||
| 3708 | set errno to ENOSYS. | ||
| 3709 | |||
| 3710 | If the exception thrown derives from OutOfMemoryException, call | ||
| 3711 | `memory_full'. | ||
| 3712 | |||
| 3713 | If the exception thrown is anything else, set errno to EIO. */ | ||
| 3714 | |||
| 3715 | static int | ||
| 3716 | android_saf_exception_check (int n, ...) | ||
| 3717 | { | ||
| 3718 | jthrowable exception; | ||
| 3719 | JNIEnv *env; | ||
| 3720 | va_list ap; | ||
| 3721 | |||
| 3722 | env = android_java_env; | ||
| 3723 | va_start (ap, n); | ||
| 3724 | |||
| 3725 | /* First, check for an exception. */ | ||
| 3726 | |||
| 3727 | if (!(*env)->ExceptionCheck (env)) | ||
| 3728 | /* No exception has taken place. Return 0. */ | ||
| 3729 | return 0; | ||
| 3730 | |||
| 3731 | exception = (*env)->ExceptionOccurred (env); | ||
| 3732 | |||
| 3733 | if (!exception) | ||
| 3734 | /* JNI couldn't return a local reference to the exception. */ | ||
| 3735 | memory_full (0); | ||
| 3736 | |||
| 3737 | /* Clear the exception, making it safe to subsequently call other | ||
| 3738 | JNI functions. */ | ||
| 3739 | (*env)->ExceptionClear (env); | ||
| 3740 | |||
| 3741 | /* Delete each of the N arguments. */ | ||
| 3742 | |||
| 3743 | while (n > 0) | ||
| 3744 | { | ||
| 3745 | ANDROID_DELETE_LOCAL_REF (va_arg (ap, jobject)); | ||
| 3746 | n--; | ||
| 3747 | } | ||
| 3748 | |||
| 3749 | /* Now set errno or signal memory_full as required. */ | ||
| 3750 | |||
| 3751 | if ((*env)->IsInstanceOf (env, (jobject) exception, | ||
| 3752 | file_not_found_exception)) | ||
| 3753 | errno = ENOENT; | ||
| 3754 | else if ((*env)->IsInstanceOf (env, (jobject) exception, | ||
| 3755 | security_exception)) | ||
| 3756 | errno = EACCES; | ||
| 3757 | else if ((*env)->IsInstanceOf (env, (jobject) exception, | ||
| 3758 | unsupported_operation_exception)) | ||
| 3759 | errno = ENOSYS; | ||
| 3760 | else if ((*env)->IsInstanceOf (env, (jobject) exception, | ||
| 3761 | out_of_memory_error)) | ||
| 3762 | { | ||
| 3763 | ANDROID_DELETE_LOCAL_REF ((jobject) exception); | ||
| 3764 | memory_full (0); | ||
| 3765 | } | ||
| 3766 | else | ||
| 3767 | errno = EIO; | ||
| 3768 | |||
| 3769 | /* expression is still a local reference! */ | ||
| 3770 | ANDROID_DELETE_LOCAL_REF ((jobject) exception); | ||
| 3771 | return 1; | ||
| 3772 | } | ||
| 3773 | |||
| 3691 | /* Return file status for the document designated by ID_NAME within | 3774 | /* Return file status for the document designated by ID_NAME within |
| 3692 | the document tree identified by URI_NAME. | 3775 | the document tree identified by URI_NAME. |
| 3693 | 3776 | ||
| @@ -3727,11 +3810,13 @@ android_saf_stat (const char *uri_name, const char *id_name, | |||
| 3727 | 3810 | ||
| 3728 | if (id) | 3811 | if (id) |
| 3729 | { | 3812 | { |
| 3730 | android_exception_check_2 (uri, id); | 3813 | if (android_saf_exception_check (2, uri, id)) |
| 3814 | return -1; | ||
| 3815 | |||
| 3731 | ANDROID_DELETE_LOCAL_REF (id); | 3816 | ANDROID_DELETE_LOCAL_REF (id); |
| 3732 | } | 3817 | } |
| 3733 | else | 3818 | else if (android_saf_exception_check (1, uri)) |
| 3734 | android_exception_check_1 (uri); | 3819 | return -1; |
| 3735 | 3820 | ||
| 3736 | ANDROID_DELETE_LOCAL_REF (uri); | 3821 | ANDROID_DELETE_LOCAL_REF (uri); |
| 3737 | 3822 | ||
| @@ -3810,11 +3895,13 @@ android_saf_access (const char *uri_name, const char *id_name, | |||
| 3810 | 3895 | ||
| 3811 | if (id) | 3896 | if (id) |
| 3812 | { | 3897 | { |
| 3813 | android_exception_check_2 (uri, id); | 3898 | if (android_saf_exception_check (2, uri, id)) |
| 3899 | return -1; | ||
| 3900 | |||
| 3814 | ANDROID_DELETE_LOCAL_REF (id); | 3901 | ANDROID_DELETE_LOCAL_REF (id); |
| 3815 | } | 3902 | } |
| 3816 | else | 3903 | else if (android_saf_exception_check (1, uri)) |
| 3817 | android_exception_check_1 (uri); | 3904 | return -1; |
| 3818 | 3905 | ||
| 3819 | ANDROID_DELETE_LOCAL_REF (uri); | 3906 | ANDROID_DELETE_LOCAL_REF (uri); |
| 3820 | 3907 | ||
| @@ -3840,6 +3927,46 @@ android_saf_access (const char *uri_name, const char *id_name, | |||
| 3840 | return 0; | 3927 | return 0; |
| 3841 | } | 3928 | } |
| 3842 | 3929 | ||
| 3930 | /* Delete the document designated by DOC_ID within the tree identified | ||
| 3931 | through the URI TREE. Return 0 if the document has been deleted, | ||
| 3932 | set errno and return -1 upon failure. */ | ||
| 3933 | |||
| 3934 | static int | ||
| 3935 | android_saf_delete_document (const char *tree, const char *doc_id) | ||
| 3936 | { | ||
| 3937 | jobject id, uri; | ||
| 3938 | jmethodID method; | ||
| 3939 | jint rc; | ||
| 3940 | |||
| 3941 | /* Build the strings holding the ID and URI. */ | ||
| 3942 | id = (*android_java_env)->NewStringUTF (android_java_env, | ||
| 3943 | doc_id); | ||
| 3944 | android_exception_check (); | ||
| 3945 | uri = (*android_java_env)->NewStringUTF (android_java_env, | ||
| 3946 | tree); | ||
| 3947 | android_exception_check_1 (id); | ||
| 3948 | |||
| 3949 | /* Now, try to delete the document. */ | ||
| 3950 | method = service_class.delete_document; | ||
| 3951 | rc = (*android_java_env)->CallIntMethod (android_java_env, | ||
| 3952 | emacs_service, | ||
| 3953 | method, uri, id); | ||
| 3954 | |||
| 3955 | if (android_saf_exception_check (2, id, uri)) | ||
| 3956 | return -1; | ||
| 3957 | |||
| 3958 | ANDROID_DELETE_LOCAL_REF (id); | ||
| 3959 | ANDROID_DELETE_LOCAL_REF (uri); | ||
| 3960 | |||
| 3961 | if (rc) | ||
| 3962 | { | ||
| 3963 | errno = EACCES; | ||
| 3964 | return -1; | ||
| 3965 | } | ||
| 3966 | |||
| 3967 | return 0; | ||
| 3968 | } | ||
| 3969 | |||
| 3843 | 3970 | ||
| 3844 | 3971 | ||
| 3845 | /* SAF directory vnode. A file within a SAF directory tree is | 3972 | /* SAF directory vnode. A file within a SAF directory tree is |
| @@ -3863,7 +3990,9 @@ struct android_saf_tree_vnode | |||
| 3863 | char *tree_id; | 3990 | char *tree_id; |
| 3864 | 3991 | ||
| 3865 | /* The document ID of the directory represented, or NULL if this is | 3992 | /* The document ID of the directory represented, or NULL if this is |
| 3866 | the root directory of the tree. */ | 3993 | the root directory of the tree. Since file and new vnodes don't |
| 3994 | represent the root directory, this field is always set in | ||
| 3995 | them. */ | ||
| 3867 | char *document_id; | 3996 | char *document_id; |
| 3868 | 3997 | ||
| 3869 | /* The file name of this tree vnode. This is a ``path'' to the | 3998 | /* The file name of this tree vnode. This is a ``path'' to the |
| @@ -4004,7 +4133,7 @@ android_verify_jni_string (const char *name) | |||
| 4004 | must name a directory file within TREE_URI. | 4133 | must name a directory file within TREE_URI. |
| 4005 | 4134 | ||
| 4006 | If NAME is not correct for the Java ``modified UTF-8'' coding | 4135 | If NAME is not correct for the Java ``modified UTF-8'' coding |
| 4007 | system, return -1. | 4136 | system, return -1 and set errno to ENOENT. |
| 4008 | 4137 | ||
| 4009 | Upon success, return 0 or 1 (contingent upon whether or not the | 4138 | Upon success, return 0 or 1 (contingent upon whether or not the |
| 4010 | last component within NAME is a directory) and place the document | 4139 | last component within NAME is a directory) and place the document |
| @@ -4014,7 +4143,8 @@ android_verify_jni_string (const char *name) | |||
| 4014 | within NAME does and is also a directory, return -2 and place the | 4143 | within NAME does and is also a directory, return -2 and place the |
| 4015 | document ID of that directory within *ID. | 4144 | document ID of that directory within *ID. |
| 4016 | 4145 | ||
| 4017 | If the designated file can't be located, return -1. */ | 4146 | If the designated file can't be located, return -1 and set errno |
| 4147 | accordingly. */ | ||
| 4018 | 4148 | ||
| 4019 | static int | 4149 | static int |
| 4020 | android_document_id_from_name (const char *tree_uri, char *name, | 4150 | android_document_id_from_name (const char *tree_uri, char *name, |
| @@ -4023,7 +4153,6 @@ android_document_id_from_name (const char *tree_uri, char *name, | |||
| 4023 | jobjectArray result; | 4153 | jobjectArray result; |
| 4024 | jstring uri; | 4154 | jstring uri; |
| 4025 | jbyteArray java_name; | 4155 | jbyteArray java_name; |
| 4026 | size_t length; | ||
| 4027 | jint rc; | 4156 | jint rc; |
| 4028 | jmethodID method; | 4157 | jmethodID method; |
| 4029 | const char *doc_id; | 4158 | const char *doc_id; |
| @@ -4055,7 +4184,10 @@ android_document_id_from_name (const char *tree_uri, char *name, | |||
| 4055 | method, | 4184 | method, |
| 4056 | uri, java_name, | 4185 | uri, java_name, |
| 4057 | result); | 4186 | result); |
| 4058 | android_exception_check_3 (result, uri, java_name); | 4187 | |
| 4188 | if (android_saf_exception_check (3, result, uri, java_name)) | ||
| 4189 | goto finish; | ||
| 4190 | |||
| 4059 | ANDROID_DELETE_LOCAL_REF (uri); | 4191 | ANDROID_DELETE_LOCAL_REF (uri); |
| 4060 | ANDROID_DELETE_LOCAL_REF (java_name); | 4192 | ANDROID_DELETE_LOCAL_REF (java_name); |
| 4061 | 4193 | ||
| @@ -4178,7 +4310,6 @@ android_saf_tree_name (struct android_vnode *vnode, char *name, | |||
| 4178 | 4310 | ||
| 4179 | /* The document ID can't be found. */ | 4311 | /* The document ID can't be found. */ |
| 4180 | xfree (filename); | 4312 | xfree (filename); |
| 4181 | errno = ENOENT; | ||
| 4182 | return NULL; | 4313 | return NULL; |
| 4183 | } | 4314 | } |
| 4184 | 4315 | ||
| @@ -4354,9 +4485,19 @@ android_saf_tree_symlink (const char *target, struct android_vnode *vnode) | |||
| 4354 | static int | 4485 | static int |
| 4355 | android_saf_tree_rmdir (struct android_vnode *vnode) | 4486 | android_saf_tree_rmdir (struct android_vnode *vnode) |
| 4356 | { | 4487 | { |
| 4357 | /* TODO */ | 4488 | struct android_saf_tree_vnode *vp; |
| 4358 | errno = ENOSYS; | 4489 | |
| 4359 | return -1; | 4490 | vp = (struct android_saf_tree_vnode *) vnode; |
| 4491 | |||
| 4492 | /* Don't allow deleting the root directory. */ | ||
| 4493 | |||
| 4494 | if (!vp->document_id) | ||
| 4495 | { | ||
| 4496 | errno = EROFS; | ||
| 4497 | return -1; | ||
| 4498 | } | ||
| 4499 | |||
| 4500 | return android_saf_delete_document (vp->tree_uri, vp->document_id); | ||
| 4360 | } | 4501 | } |
| 4361 | 4502 | ||
| 4362 | static int | 4503 | static int |
| @@ -4412,8 +4553,8 @@ android_saf_tree_mkdir (struct android_vnode *vnode, mode_t mode) | |||
| 4412 | /* Open a database Cursor containing each directory entry within the | 4553 | /* Open a database Cursor containing each directory entry within the |
| 4413 | supplied SAF tree vnode VP. | 4554 | supplied SAF tree vnode VP. |
| 4414 | 4555 | ||
| 4415 | Value is NULL upon failure, a local reference to the Cursor object | 4556 | Value is NULL upon failure with errno set to a suitable value, a |
| 4416 | otherwise. */ | 4557 | local reference to the Cursor object otherwise. */ |
| 4417 | 4558 | ||
| 4418 | static jobject | 4559 | static jobject |
| 4419 | android_saf_tree_opendir_1 (struct android_saf_tree_vnode *vp) | 4560 | android_saf_tree_opendir_1 (struct android_saf_tree_vnode *vp) |
| @@ -4445,11 +4586,13 @@ android_saf_tree_opendir_1 (struct android_saf_tree_vnode *vp) | |||
| 4445 | 4586 | ||
| 4446 | if (id) | 4587 | if (id) |
| 4447 | { | 4588 | { |
| 4448 | android_exception_check_2 (id, uri); | 4589 | if (android_saf_exception_check (2, id, uri)) |
| 4590 | return NULL; | ||
| 4591 | |||
| 4449 | ANDROID_DELETE_LOCAL_REF (id); | 4592 | ANDROID_DELETE_LOCAL_REF (id); |
| 4450 | } | 4593 | } |
| 4451 | else | 4594 | else if (android_saf_exception_check (1, uri)) |
| 4452 | android_exception_check_1 (uri); | 4595 | return NULL; |
| 4453 | 4596 | ||
| 4454 | ANDROID_DELETE_LOCAL_REF (uri); | 4597 | ANDROID_DELETE_LOCAL_REF (uri); |
| 4455 | 4598 | ||
| @@ -4657,7 +4800,6 @@ android_saf_tree_opendir (struct android_vnode *vnode) | |||
| 4657 | { | 4800 | { |
| 4658 | xfree (dir); | 4801 | xfree (dir); |
| 4659 | xfree (dir->name); | 4802 | xfree (dir->name); |
| 4660 | errno = ENOENT; | ||
| 4661 | return NULL; | 4803 | return NULL; |
| 4662 | } | 4804 | } |
| 4663 | 4805 | ||
| @@ -4880,7 +5022,10 @@ android_saf_file_open (struct android_vnode *vnode, int flags, | |||
| 4880 | service_class.class, | 5022 | service_class.class, |
| 4881 | method, uri, id, | 5023 | method, uri, id, |
| 4882 | write, trunc); | 5024 | write, trunc); |
| 4883 | android_exception_check_2 (uri, id); | 5025 | |
| 5026 | if (android_saf_exception_check (2, uri, id)) | ||
| 5027 | return -1; | ||
| 5028 | |||
| 4884 | ANDROID_DELETE_LOCAL_REF (uri); | 5029 | ANDROID_DELETE_LOCAL_REF (uri); |
| 4885 | ANDROID_DELETE_LOCAL_REF (id); | 5030 | ANDROID_DELETE_LOCAL_REF (id); |
| 4886 | 5031 | ||
| @@ -4953,9 +5098,10 @@ android_saf_file_open (struct android_vnode *vnode, int flags, | |||
| 4953 | static int | 5098 | static int |
| 4954 | android_saf_file_unlink (struct android_vnode *vnode) | 5099 | android_saf_file_unlink (struct android_vnode *vnode) |
| 4955 | { | 5100 | { |
| 4956 | /* TODO */ | 5101 | struct android_saf_file_vnode *vp; |
| 4957 | errno = ENOSYS; | 5102 | |
| 4958 | return -1; | 5103 | vp = (struct android_saf_file_vnode *) vnode; |
| 5104 | return android_saf_delete_document (vp->tree_uri, vp->document_id); | ||
| 4959 | } | 5105 | } |
| 4960 | 5106 | ||
| 4961 | static int | 5107 | static int |
| @@ -5114,7 +5260,7 @@ android_saf_new_open (struct android_vnode *vnode, int flags, | |||
| 5114 | { | 5260 | { |
| 5115 | errno = ENOENT; | 5261 | errno = ENOENT; |
| 5116 | return -1; | 5262 | return -1; |
| 5117 | } | 5263 | } |
| 5118 | 5264 | ||
| 5119 | /* Otherwise, try to create a new document. First, build strings | 5265 | /* Otherwise, try to create a new document. First, build strings |
| 5120 | for the name, ID and document URI. */ | 5266 | for the name, ID and document URI. */ |
| @@ -5137,7 +5283,9 @@ android_saf_new_open (struct android_vnode *vnode, int flags, | |||
| 5137 | service_class.class, | 5283 | service_class.class, |
| 5138 | method, uri, id, | 5284 | method, uri, id, |
| 5139 | name); | 5285 | name); |
| 5140 | android_exception_check_3 (name, id, uri); | 5286 | |
| 5287 | if (android_saf_exception_check (3, name, id, uri)) | ||
| 5288 | return -1; | ||
| 5141 | 5289 | ||
| 5142 | /* Delete unused local references. */ | 5290 | /* Delete unused local references. */ |
| 5143 | ANDROID_DELETE_LOCAL_REF (name); | 5291 | ANDROID_DELETE_LOCAL_REF (name); |
| @@ -5225,9 +5373,90 @@ android_saf_new_access (struct android_vnode *vnode, int mode) | |||
| 5225 | static int | 5373 | static int |
| 5226 | android_saf_new_mkdir (struct android_vnode *vnode, mode_t mode) | 5374 | android_saf_new_mkdir (struct android_vnode *vnode, mode_t mode) |
| 5227 | { | 5375 | { |
| 5228 | /* TODO */ | 5376 | struct android_saf_new_vnode *vp; |
| 5229 | errno = ENOSYS; | 5377 | jstring name, id, uri, new_id; |
| 5230 | return -1; | 5378 | jmethodID method; |
| 5379 | const char *new_doc_id; | ||
| 5380 | char *end; | ||
| 5381 | |||
| 5382 | vp = (struct android_saf_tree_vnode *) vnode; | ||
| 5383 | |||
| 5384 | /* Find the last component of vp->name. */ | ||
| 5385 | end = strrchr (vp->name, '/'); | ||
| 5386 | |||
| 5387 | /* VP->name must contain at least one directory separator. */ | ||
| 5388 | eassert (end); | ||
| 5389 | |||
| 5390 | if (end[1] == '\0') | ||
| 5391 | { | ||
| 5392 | /* There's a trailing directory separator. Search | ||
| 5393 | backwards. */ | ||
| 5394 | |||
| 5395 | end--; | ||
| 5396 | while (end != vp->name && *end != '/') | ||
| 5397 | end--; | ||
| 5398 | |||
| 5399 | /* vp->name[0] is always a directory separator. */ | ||
| 5400 | eassert (*end == '/'); | ||
| 5401 | } | ||
| 5402 | |||
| 5403 | /* Otherwise, try to create a new document. First, build strings | ||
| 5404 | for the name, ID and document URI. */ | ||
| 5405 | |||
| 5406 | name = (*android_java_env)->NewStringUTF (android_java_env, | ||
| 5407 | end + 1); | ||
| 5408 | android_exception_check (); | ||
| 5409 | id = (*android_java_env)->NewStringUTF (android_java_env, | ||
| 5410 | vp->document_id); | ||
| 5411 | android_exception_check_1 (name); | ||
| 5412 | uri = (*android_java_env)->NewStringUTF (android_java_env, | ||
| 5413 | vp->tree_uri); | ||
| 5414 | android_exception_check_2 (name, id); | ||
| 5415 | |||
| 5416 | /* Next, try to create a new document and retrieve its ID. */ | ||
| 5417 | |||
| 5418 | method = service_class.create_directory; | ||
| 5419 | new_id = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env, | ||
| 5420 | emacs_service, | ||
| 5421 | service_class.class, | ||
| 5422 | method, uri, id, | ||
| 5423 | name); | ||
| 5424 | |||
| 5425 | if (android_saf_exception_check (3, name, id, uri)) | ||
| 5426 | return -1; | ||
| 5427 | |||
| 5428 | /* Delete unused local references. */ | ||
| 5429 | ANDROID_DELETE_LOCAL_REF (name); | ||
| 5430 | ANDROID_DELETE_LOCAL_REF (id); | ||
| 5431 | ANDROID_DELETE_LOCAL_REF (uri); | ||
| 5432 | |||
| 5433 | if (!new_id) | ||
| 5434 | { | ||
| 5435 | /* The file couldn't be created for some reason. */ | ||
| 5436 | errno = EIO; | ||
| 5437 | return -1; | ||
| 5438 | } | ||
| 5439 | |||
| 5440 | /* Now, free VP->document_id and replace it with the service | ||
| 5441 | document ID. */ | ||
| 5442 | |||
| 5443 | new_doc_id = (*android_java_env)->GetStringUTFChars (android_java_env, | ||
| 5444 | new_id, NULL); | ||
| 5445 | |||
| 5446 | if (android_saf_exception_check (3, name, id, uri)) | ||
| 5447 | return -1; | ||
| 5448 | |||
| 5449 | xfree (vp->document_id); | ||
| 5450 | vp->document_id = xstrdup (new_doc_id); | ||
| 5451 | |||
| 5452 | (*android_java_env)->ReleaseStringUTFChars (android_java_env, | ||
| 5453 | new_id, new_doc_id); | ||
| 5454 | ANDROID_DELETE_LOCAL_REF (new_id); | ||
| 5455 | |||
| 5456 | /* Finally, transform this vnode into a directory vnode. */ | ||
| 5457 | vp->vnode.type = ANDROID_VNODE_SAF_TREE; | ||
| 5458 | vp->vnode.ops = &saf_tree_vfs_ops; | ||
| 5459 | return 0; | ||
| 5231 | } | 5460 | } |
| 5232 | 5461 | ||
| 5233 | static struct android_vdir * | 5462 | static struct android_vdir * |
| @@ -5449,6 +5678,29 @@ android_vfs_init (JNIEnv *env, jobject manager) | |||
| 5449 | android_init_cursor_class (env); | 5678 | android_init_cursor_class (env); |
| 5450 | android_init_entry_class (env); | 5679 | android_init_entry_class (env); |
| 5451 | android_init_fd_class (env); | 5680 | android_init_fd_class (env); |
| 5681 | |||
| 5682 | /* Initialize each of the exception classes used by | ||
| 5683 | `android_saf_exception_check'. */ | ||
| 5684 | |||
| 5685 | old = (*env)->FindClass (env, "java/io/FileNotFoundException"); | ||
| 5686 | file_not_found_exception = (*env)->NewGlobalRef (env, old); | ||
| 5687 | (*env)->DeleteLocalRef (env, old); | ||
| 5688 | eassert (file_not_found_exception); | ||
| 5689 | |||
| 5690 | old = (*env)->FindClass (env, "java/lang/SecurityException"); | ||
| 5691 | security_exception = (*env)->NewGlobalRef (env, old); | ||
| 5692 | (*env)->DeleteLocalRef (env, old); | ||
| 5693 | eassert (security_exception); | ||
| 5694 | |||
| 5695 | old = (*env)->FindClass (env, "java/lang/UnsupportedOperationException"); | ||
| 5696 | unsupported_operation_exception = (*env)->NewGlobalRef (env, old); | ||
| 5697 | (*env)->DeleteLocalRef (env, old); | ||
| 5698 | eassert (unsupported_operation_exception); | ||
| 5699 | |||
| 5700 | old = (*env)->FindClass (env, "java/lang/OutOfMemoryError"); | ||
| 5701 | out_of_memory_error = (*env)->NewGlobalRef (env, old); | ||
| 5702 | (*env)->DeleteLocalRef (env, old); | ||
| 5703 | eassert (out_of_memory_error); | ||
| 5452 | } | 5704 | } |
| 5453 | 5705 | ||
| 5454 | /* The replacement functions that follow have several major | 5706 | /* The replacement functions that follow have several major |
| @@ -5606,7 +5858,7 @@ android_mkdir (const char *name, mode_t mode) | |||
| 5606 | 5858 | ||
| 5607 | rc = (*vp->ops->mkdir) (vp, mode); | 5859 | rc = (*vp->ops->mkdir) (vp, mode); |
| 5608 | (*vp->ops->close) (vp); | 5860 | (*vp->ops->close) (vp); |
| 5609 | return rc; | 5861 | return rc; |
| 5610 | } | 5862 | } |
| 5611 | 5863 | ||
| 5612 | /* Rename the vnode designated by SRC to the vnode designated by DST. | 5864 | /* Rename the vnode designated by SRC to the vnode designated by DST. |
| @@ -5724,7 +5976,7 @@ android_fstat (int fd, struct stat *statb) | |||
| 5724 | for (tem = afs_file_descriptors; tem; tem = tem->next) | 5976 | for (tem = afs_file_descriptors; tem; tem = tem->next) |
| 5725 | { | 5977 | { |
| 5726 | if (tem->fd == fd) | 5978 | if (tem->fd == fd) |
| 5727 | { | 5979 | { |
| 5728 | memcpy (statb, &tem->statb, sizeof *statb); | 5980 | memcpy (statb, &tem->statb, sizeof *statb); |
| 5729 | return 0; | 5981 | return 0; |
| 5730 | } | 5982 | } |