From 2effd18c97bf542e43203c95dd88114159162dd5 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sat, 19 Aug 2023 13:25:42 +0800 Subject: [PATCH] Make some file I/O operations more robust on Android * 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 | 2 + src/androidvfs.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++- src/fileio.c | 34 ++++++++++-- 3 files changed, 170 insertions(+), 7 deletions(-) diff --git a/src/android.h b/src/android.h index a052d3a3b21..e865d7da665 100644 --- a/src/android.h +++ b/src/android.h @@ -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); diff --git a/src/androidvfs.c b/src/androidvfs.c index ec33687f34c..d6b832d6caf 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -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. diff --git a/src/fileio.c b/src/fileio.c index 869e1ea7e31..9294ea3e7d6 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -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. */) /* 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; -- 2.39.2