aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPo Lu2023-07-28 12:21:47 +0800
committerPo Lu2023-07-28 12:21:47 +0800
commit03cf3bbb5c38aa55abd6f7d4860025f7482fcfc3 (patch)
treea8d55088a6efed2cbe4b0bfad1c1bde61579b54e
parent7c0899586471d3649dfb468d2b8f7d6d9685fea1 (diff)
downloademacs-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.java2
-rw-r--r--java/org/gnu/emacs/EmacsService.java210
-rw-r--r--src/android.c5
-rw-r--r--src/android.h2
-rw-r--r--src/androidvfs.c316
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
25public class EmacsDirectoryEntry 25public 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
284extern JNIEnv *android_java_env; 286extern 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. */
277static struct android_parcel_file_descriptor_class fd_class; 277static struct android_parcel_file_descriptor_class fd_class;
278 278
279/* Global references to several exception classes. */
280static jclass file_not_found_exception, security_exception;
281static 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
3715static int
3716android_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
3934static int
3935android_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
4019static int 4149static int
4020android_document_id_from_name (const char *tree_uri, char *name, 4150android_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)
4354static int 4485static int
4355android_saf_tree_rmdir (struct android_vnode *vnode) 4486android_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
4362static int 4503static 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
4418static jobject 4559static jobject
4419android_saf_tree_opendir_1 (struct android_saf_tree_vnode *vp) 4560android_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,
4953static int 5098static int
4954android_saf_file_unlink (struct android_vnode *vnode) 5099android_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
4961static int 5107static 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)
5225static int 5373static int
5226android_saf_new_mkdir (struct android_vnode *vnode, mode_t mode) 5374android_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
5233static struct android_vdir * 5462static 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 }