]> git.eshelyaron.com Git - emacs.git/commitdiff
Make some file I/O operations more robust on Android
authorPo Lu <luangruo@yahoo.com>
Sat, 19 Aug 2023 05:25:42 +0000 (13:25 +0800)
committerPo Lu <luangruo@yahoo.com>
Sat, 19 Aug 2023 05:25:42 +0000 (13:25 +0800)
* src/android.h (android_readlinkat): New prototype.
* src/androidvfs.c (struct android_vops): New function pointer
`readlink'.
(unix_vfs_ops, android_unix_readlink, afs_vfs_ops)
(android_afs_readlink, content_vfs_ops, android_content_mkdir)
(android_content_chmod, authority_vfs_ops)
(android_authority_readlink, saf_root_vfs_ops)
(android_saf_root_readlink, saf_tree_vfs_ops)
(android_saf_tree_readlink, saf_file_vfs_ops, saf_new_vfs_ops)
(android_saf_new_readlink, root_vfs_ops): Implement new
`readlink' vops for each vnode type.
(android_readlinkat): New function.

* src/fileio.c (Fcopy_file): Be more flexible about SELinux
errors under Android.
(emacs_readlinkat): Use `android_readlink' when built as
libemacs.so.

src/android.h
src/androidvfs.c
src/fileio.c

index a052d3a3b21d38bf457854212aac971311cf4c7d..e865d7da665f4a633f9b31127f55cfe21888109c 100644 (file)
@@ -75,6 +75,8 @@ extern int android_renameat_noreplace (int, const char *,
                                       int, const char *);
 extern int android_rename (const char *, const char *);
 extern int android_fchmodat (int, const char *, mode_t, int);
+extern ssize_t android_readlinkat (int, const char *restrict, char *restrict,
+                                  size_t);
 
 \f
 
index ec33687f34cc604d1c6ecf82cf6bd73c0f0b787a..d6b832d6cafd8659b251158c669178522673b005 100644 (file)
@@ -214,6 +214,10 @@ struct android_vops
      AT_SYMLINK_NOFOLLOW.  */
   int (*chmod) (struct android_vnode *, mode_t, int);
 
+  /* Return the target of VNODE if it is a symbolic link, or -1.
+     Value and errno are the same as with `readlink'.  */
+  ssize_t (*readlink) (struct android_vnode *, char *, size_t);
+
   /* Open the specified VNODE as a directory.
      Value is a ``directory handle'', or NULL upon failure.  */
   struct android_vdir *(*opendir) (struct android_vnode *);
@@ -625,6 +629,8 @@ static int android_unix_stat (struct android_vnode *, struct stat *);
 static int android_unix_access (struct android_vnode *, int);
 static int android_unix_mkdir (struct android_vnode *, mode_t);
 static int android_unix_chmod (struct android_vnode *, mode_t, int);
+static ssize_t android_unix_readlink (struct android_vnode *, char *,
+                                     size_t);
 static struct android_vdir *android_unix_opendir (struct android_vnode *);
 
 /* Vector of VFS operations associated with Unix filesystem VFS
@@ -643,6 +649,7 @@ static struct android_vops unix_vfs_ops =
     android_unix_access,
     android_unix_mkdir,
     android_unix_chmod,
+    android_unix_readlink,
     android_unix_opendir,
   };
 
@@ -898,6 +905,16 @@ android_unix_chmod (struct android_vnode *vnode, mode_t mode,
   return fchmodat (AT_FDCWD, vp->name, mode, flags);
 }
 
+static ssize_t
+android_unix_readlink (struct android_vnode *vnode, char *buffer,
+                      size_t size)
+{
+  struct android_unix_vnode *vp;
+
+  vp = (struct android_unix_vnode *) vnode;
+  return readlink (vp->name, buffer, size);
+}
+
 static struct dirent *
 android_unix_readdir (struct android_vdir *vdir)
 {
@@ -1606,6 +1623,8 @@ static int android_afs_stat (struct android_vnode *, struct stat *);
 static int android_afs_access (struct android_vnode *, int);
 static int android_afs_mkdir (struct android_vnode *, mode_t);
 static int android_afs_chmod (struct android_vnode *, mode_t, int);
+static ssize_t android_afs_readlink (struct android_vnode *, char *,
+                                    size_t);
 static struct android_vdir *android_afs_opendir (struct android_vnode *);
 
 /* Vector of VFS operations associated with asset VFS nodes.  */
@@ -1623,6 +1642,7 @@ static struct android_vops afs_vfs_ops =
     android_afs_access,
     android_afs_mkdir,
     android_afs_chmod,
+    android_afs_readlink,
     android_afs_opendir,
   };
 
@@ -2139,6 +2159,28 @@ android_afs_chmod (struct android_vnode *vnode, mode_t mode,
   return -1;
 }
 
+static ssize_t
+android_afs_readlink (struct android_vnode *vnode, char *buffer,
+                     size_t size)
+{
+  struct android_afs_vnode *vp;
+  const char *dir;
+
+  vp = (struct android_afs_vnode *) vnode;
+  dir = android_scan_directory_tree (vp->name, NULL);
+
+  /* As there are no symlinks in /assets, just return -1 with errno
+     set to a reasonable value contingent upon whether VP->name
+     actually exists.  */
+
+  if (dir)
+    errno = EINVAL;
+  else
+    errno = ENOENT;
+
+  return -1;
+}
+
 static struct dirent *
 android_afs_readdir (struct android_vdir *vdir)
 {
@@ -2379,6 +2421,8 @@ static int android_content_stat (struct android_vnode *, struct stat *);
 static int android_content_access (struct android_vnode *, int);
 static int android_content_mkdir (struct android_vnode *, mode_t);
 static int android_content_chmod (struct android_vnode *, mode_t, int);
+static ssize_t android_content_readlink (struct android_vnode *, char *,
+                                        size_t);
 static struct android_vdir *android_content_opendir (struct android_vnode *);
 
 /* Vector of VFS operations associated with the content VFS node.  */
@@ -2396,6 +2440,7 @@ static struct android_vops content_vfs_ops =
     android_content_access,
     android_content_mkdir,
     android_content_chmod,
+    android_content_readlink,
     android_content_opendir,
   };
 
@@ -2600,7 +2645,7 @@ static int
 android_content_mkdir (struct android_vnode *vnode, mode_t mode)
 {
   errno = EEXIST;
-  return 0;
+  return -1;
 }
 
 static int
@@ -2608,7 +2653,15 @@ android_content_chmod (struct android_vnode *vnode, mode_t mode,
                       int flags)
 {
   errno = EACCES;
-  return 0;
+  return -1;
+}
+
+static ssize_t
+android_content_readlink (struct android_vnode *vnode, char *buffer,
+                         size_t size)
+{
+  errno = EINVAL;
+  return -1;
 }
 
 static struct dirent *
@@ -2865,6 +2918,8 @@ static int android_authority_stat (struct android_vnode *, struct stat *);
 static int android_authority_access (struct android_vnode *, int);
 static int android_authority_mkdir (struct android_vnode *, mode_t);
 static int android_authority_chmod (struct android_vnode *, mode_t, int);
+static ssize_t android_authority_readlink (struct android_vnode *, char *,
+                                          size_t);
 static struct android_vdir *android_authority_opendir (struct android_vnode *);
 
 /* Vector of VFS operations associated with the content VFS node.  */
@@ -2882,6 +2937,7 @@ static struct android_vops authority_vfs_ops =
     android_authority_access,
     android_authority_mkdir,
     android_authority_chmod,
+    android_authority_readlink,
     android_authority_opendir,
   };
 
@@ -3176,6 +3232,14 @@ android_authority_chmod (struct android_vnode *vnode, mode_t mode,
   return -1;
 }
 
+static ssize_t
+android_authority_readlink (struct android_vnode *vnode, char *buffer,
+                           size_t size)
+{
+  errno = EINVAL;
+  return -1;
+}
+
 static struct android_vdir *
 android_authority_opendir (struct android_vnode *vnode)
 {
@@ -3282,6 +3346,8 @@ static int android_saf_root_stat (struct android_vnode *, struct stat *);
 static int android_saf_root_access (struct android_vnode *, int);
 static int android_saf_root_mkdir (struct android_vnode *, mode_t);
 static int android_saf_root_chmod (struct android_vnode *, mode_t, int);
+static ssize_t android_saf_root_readlink (struct android_vnode *, char *,
+                                         size_t);
 static struct android_vdir *android_saf_root_opendir (struct android_vnode *);
 
 /* Vector of VFS operations associated with the SAF root VFS node.  */
@@ -3299,6 +3365,7 @@ static struct android_vops saf_root_vfs_ops =
     android_saf_root_access,
     android_saf_root_mkdir,
     android_saf_root_chmod,
+    android_saf_root_readlink,
     android_saf_root_opendir,
   };
 
@@ -3585,6 +3652,14 @@ android_saf_root_chmod (struct android_vnode *vnode, mode_t mode,
   return -1;
 }
 
+static ssize_t
+android_saf_root_readlink (struct android_vnode *vnode, char *buffer,
+                          size_t size)
+{
+  errno = EINVAL;
+  return -1;
+}
+
 static struct dirent *
 android_saf_root_readdir (struct android_vdir *vdir)
 {
@@ -4426,6 +4501,8 @@ static int android_saf_tree_stat (struct android_vnode *, struct stat *);
 static int android_saf_tree_access (struct android_vnode *, int);
 static int android_saf_tree_mkdir (struct android_vnode *, mode_t);
 static int android_saf_tree_chmod (struct android_vnode *, mode_t, int);
+static ssize_t android_saf_tree_readlink (struct android_vnode *, char *,
+                                         size_t);
 static struct android_vdir *android_saf_tree_opendir (struct android_vnode *);
 
 /* Vector of VFS operations associated with SAF tree VFS nodes.  */
@@ -4443,6 +4520,7 @@ static struct android_vops saf_tree_vfs_ops =
     android_saf_tree_access,
     android_saf_tree_mkdir,
     android_saf_tree_chmod,
+    android_saf_tree_readlink,
     android_saf_tree_opendir,
   };
 
@@ -5138,6 +5216,16 @@ android_saf_tree_chmod (struct android_vnode *vnode, mode_t mode,
   return 0;
 }
 
+static ssize_t
+android_saf_tree_readlink (struct android_vnode *vnode, char *buffer,
+                          size_t size)
+{
+  /* Return EINVAL.  Symlinks aren't exposed to clients by the
+     SAF.  */
+  errno = EINVAL;
+  return -1;
+}
+
 /* Open a database Cursor containing each directory entry within the
    supplied SAF tree vnode VP.
 
@@ -5562,6 +5650,7 @@ static struct android_vops saf_file_vfs_ops =
     android_saf_tree_access,
     android_saf_tree_mkdir,
     android_saf_tree_chmod,
+    android_saf_tree_readlink,
     android_saf_file_opendir,
   };
 
@@ -5842,6 +5931,8 @@ static int android_saf_new_stat (struct android_vnode *, struct stat *);
 static int android_saf_new_access (struct android_vnode *, int);
 static int android_saf_new_mkdir (struct android_vnode *, mode_t);
 static int android_saf_new_chmod (struct android_vnode *, mode_t, int);
+static ssize_t android_saf_new_readlink (struct android_vnode *, char *,
+                                        size_t);
 static struct android_vdir *android_saf_new_opendir (struct android_vnode *);
 
 /* Vector of VFS operations associated with SAF new VFS nodes.  */
@@ -5859,6 +5950,7 @@ static struct android_vops saf_new_vfs_ops =
     android_saf_new_access,
     android_saf_new_mkdir,
     android_saf_new_chmod,
+    android_saf_new_readlink,
     android_saf_new_opendir,
   };
 
@@ -6128,6 +6220,14 @@ android_saf_new_chmod (struct android_vnode *vnode, mode_t mode,
   return -1;
 }
 
+static ssize_t
+android_saf_new_readlink (struct android_vnode *vnode, char *buffer,
+                         size_t size)
+{
+  errno = ENOENT;
+  return -1;
+}
+
 static struct android_vdir *
 android_saf_new_opendir (struct android_vnode *vnode)
 {
@@ -6229,6 +6329,7 @@ static struct android_vops root_vfs_ops =
     android_unix_access,
     android_unix_mkdir,
     android_unix_chmod,
+    android_unix_readlink,
     android_unix_opendir,
   };
 
@@ -6962,6 +7063,42 @@ android_fchmodat (int dirfd, const char *pathname, mode_t mode,
   return rc;
 }
 
+/* Like `android_fstatat', but return the target of any symbolic link
+   at PATHNAME instead of checking file status.  */
+
+ssize_t
+android_readlinkat (int dirfd, const char *restrict pathname,
+                   char *restrict buf, size_t bufsiz)
+{
+  char buffer[PATH_MAX + 1];
+  struct android_vnode *vp;
+  ssize_t rc;
+
+  if (dirfd == AT_FDCWD || pathname[0] == '/')
+    goto vfs;
+
+  /* Now establish whether DIRFD is a file descriptor corresponding to
+     an open VFS directory stream.  */
+
+  if (!android_fstatat_1 (dirfd, pathname, buffer, PATH_MAX + 1))
+    {
+      pathname = buffer;
+      goto vfs;
+    }
+
+  /* Fall back to readlinkat.  */
+  return readlinkat (dirfd, pathname, buf, bufsiz);
+
+ vfs:
+  vp = android_name_file (pathname);
+  if (!vp)
+    return -1;
+
+  rc = (*vp->ops->readlink) (vp, buf, bufsiz);
+  (*vp->ops->close) (vp);
+  return rc;
+}
+
 /* Like `fdopen', but if FD is a parcel file descriptor, ``detach'' it
    from the original.
 
index 869e1ea7e317aafe58b12ae4f6a804c5e56db95a..9294ea3e7d6a5fe8d80d44e1ea93b0f3064247d2 100644 (file)
@@ -2331,6 +2331,9 @@ permissions.  */)
     {
 #if HAVE_LIBSELINUX
       if (selinux_enabled_p (SSDATA (encoded_file))
+         /* Eschew copying SELinux contexts if they're inapplicable
+            to the destination file.  */
+         && selinux_enabled_p (SSDATA (encoded_newname))
          && emacs_fd_to_int (ifd) != -1)
        {
          conlength = fgetfilecon (emacs_fd_to_int (ifd),
@@ -2494,7 +2497,14 @@ permissions.  */)
       /* Set the modified context back to the file.  */
       bool fail = fsetfilecon (ofd, con) != 0;
       /* See https://debbugs.gnu.org/11245 for ENOTSUP.  */
-      if (fail && errno != ENOTSUP)
+      if (fail
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+         /* Treat SELinux errors copying files leniently on Android,
+            since the system usually forbids user programs from
+            changing file contexts.  */
+         && errno != EACCES
+#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
+         && errno != ENOTSUP)
        report_file_error ("Doing fsetfilecon", newname);
 
       freecon (con);
@@ -3090,15 +3100,29 @@ If there is no error, returns nil.  */)
 \f
 /* Relative to directory FD, return the symbolic link value of FILENAME.
    On failure, return nil (setting errno).  */
+
 static Lisp_Object
 emacs_readlinkat (int fd, char const *filename)
 {
-  static struct allocator const emacs_norealloc_allocator =
-    { xmalloc, NULL, xfree, memory_full };
+  static struct allocator const emacs_norealloc_allocator = {
+    xmalloc,
+    NULL,
+    xfree,
+    memory_full,
+  };
+
   Lisp_Object val;
   char readlink_buf[1024];
-  char *buf = careadlinkat (fd, filename, readlink_buf, sizeof readlink_buf,
-                           &emacs_norealloc_allocator, readlinkat);
+  char *buf;
+
+  buf = careadlinkat (fd, filename, readlink_buf, sizeof readlink_buf,
+                     &emacs_norealloc_allocator,
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+                     android_readlinkat
+#else /* !HAVE_ANDROID || ANDROID_STUBIFY */
+                     readlinkat
+#endif /* HAVE_ANDROID && !ANDROID_STUBIFY */
+                     );
   if (!buf)
     return Qnil;