From: Po Lu Date: Sun, 31 Mar 2024 07:33:40 +0000 (+0800) Subject: List special directories when reading root directory on Android X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=1f19876ce58ad76402f805fbf8178f9548148f03;p=emacs.git List special directories when reading root directory on Android * src/androidvfs.c (root_vfs_ops): Substitute android_root_opendir for android_root_opendir. (struct android_root_vdir): New structure. (root_fd, root_fd_references): New variables. (android_root_readdir, android_root_closedir, android_root_dirfd) (android_root_opendir): New functions. (android_fstatat_1): Test provided fd against root_fd, and if they match, prefix FILENAME with the name of the root directory. * lisp/ls-lisp.el (ls-lisp-insert-directory): If d-f-a-a signals an error while retrieving attributes, compile the alist of directory contents by hand. (cherry picked from commit 7f377407b4b7d6ac9994ed983d7516bc42139885) --- diff --git a/lisp/ls-lisp.el b/lisp/ls-lisp.el index 89f0238cf74..d09b53b1cc3 100644 --- a/lisp/ls-lisp.el +++ b/lisp/ls-lisp.el @@ -328,11 +328,39 @@ not contain `d', so that a full listing is expected." full-directory-p) (let* ((dir (file-name-as-directory file)) (default-directory dir) ; so that file-attributes works + (id-format (if (memq ?n switches) + 'integer + 'string)) (file-alist - (directory-files-and-attributes dir nil wildcard-regexp t - (if (memq ?n switches) - 'integer - 'string))) + (catch 'new-list + (handler-bind + ((error + (lambda (error) + ;; `directory-files-and-attributes' signals + ;; failure on Unix systems if even a single + ;; file's attributes cannot be accessed. + ;; + ;; Detect errors signaled while retrieving file + ;; attributes and resolve them by creating the + ;; attribute list manually, ignoring the + ;; attributes of files that cannot be accessed + ;; in this sense. + (when (member (cadr error) + '("Getting attributes" + "Reading symbolic link")) + (let ((file-list (directory-files dir nil + wildcard-regexp + t))) + (throw 'new-list + (mapcar (lambda (file) + (cons file + (or (ignore-errors + (file-attributes + file id-format)) + nil))) + file-list))))))) + (directory-files-and-attributes + dir nil wildcard-regexp t id-format)))) (sum 0) (max-uid-len 0) (max-gid-len 0) diff --git a/src/androidvfs.c b/src/androidvfs.c index a9035ae53c6..e8eb9f2d41c 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -6525,11 +6525,33 @@ NATIVE_NAME (ftruncate) (JNIEnv *env, jobject object, jint fd) /* Root vnode. This vnode represents the root inode, and is a regular - Unix vnode with modifications to `name' that make it return asset - vnodes. */ + Unix vnode with modifications to `name' so that it returns asset and + content vnodes, and to `opendir', so that asset and content vnodes + are read from the root directory, whether or not Emacs holds rights + to access the underlying filesystem. */ + +struct android_root_vdir +{ + /* The directory function table. */ + struct android_vdir vdir; + + /* The directory stream, or NULL if it could not be opened. */ + DIR *directory; + + /* Index of the next directory to return in `special_vnodes'. */ + int index; +}; + +/* File descriptor for instances of the foregoing structure when the + true root is unavailable. */ +static int root_fd = -1; + +/* Number of open instances referencing this file descriptor. */ +static ptrdiff_t root_fd_references; static struct android_vnode *android_root_name (struct android_vnode *, char *, size_t); +static struct android_vdir *android_root_opendir (struct android_vnode *); /* Vector of VFS operations associated with Unix root filesystem VFS nodes. */ @@ -6548,7 +6570,7 @@ static struct android_vops root_vfs_ops = android_unix_mkdir, android_unix_chmod, android_unix_readlink, - android_unix_opendir, + android_root_opendir, }; /* Array of special named vnodes. */ @@ -6676,6 +6698,97 @@ android_root_name (struct android_vnode *vnode, char *name, return android_unix_name (vnode, name, length); } +static struct dirent * +android_root_readdir (struct android_vdir *vdir) +{ + struct android_root_vdir *dir; + static struct dirent dirent, *p; + + dir = (struct android_root_vdir *) vdir; + p = dir->directory ? readdir (dir->directory) : NULL; + + if (p || dir->index >= ARRAYELTS (special_vnodes)) + return p; + + dirent.d_ino = 0; + dirent.d_off = 0; + dirent.d_reclen = sizeof dirent; + dirent.d_type = DT_DIR; + + /* No element in special_vnode must overflow dirent.d_name. */ + strcpy ((char *) &dirent.d_name, + special_vnodes[dir->index++].name); + return &dirent; +} + +static void +android_root_closedir (struct android_vdir *vdir) +{ + struct android_root_vdir *dir; + + dir = (struct android_root_vdir *) vdir; + + if (dir->directory) + closedir (dir->directory); + else if (root_fd_references--) + ; + else + { + /* Close root_fd, for which no references remain. */ + close (root_fd); + root_fd = -1; + } + + xfree (vdir); +} + +static int +android_root_dirfd (struct android_vdir *vdir) +{ + struct android_unix_vdir *dir; + + dir = (struct android_unix_vdir *) vdir; + + if (dir->directory) + return dirfd (dir->directory); + + return root_fd; +} + +static struct android_vdir * +android_root_opendir (struct android_vnode *vnode) +{ + struct android_unix_vnode *vp; + struct android_root_vdir *dir; + DIR *directory; + + /* Try to opendir the vnode. */ + vp = (struct android_unix_vnode *) vnode; + + directory = opendir (vp->name); + + /* Proceed with the remaining code if directory is nil, in which event + directory functions will simply forgo listing files inside the real + root directory. */ + + dir = xmalloc (sizeof *dir); + dir->vdir.readdir = android_root_readdir; + dir->vdir.closedir = android_root_closedir; + dir->vdir.dirfd = android_root_dirfd; + dir->directory = directory; + dir->index = 0; + + if (!directory) + { + /* Allocate a temporary file descriptor for this ersatz root. */ + if (root_fd < 0) + root_fd = open ("/dev/null", O_RDONLY | O_CLOEXEC); + root_fd_references++; + } + + return &dir->vdir; +} + /* File system lookup. */ @@ -7223,6 +7336,14 @@ android_fstatat_1 (int dirfd, const char *filename, return 0; } + /* /foo... */ + + if (root_fd >= 0 && dirfd == root_fd) + { + snprintf (buffer, size, "/%s", filename); + return 0; + } + return 1; }