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.
This commit is contained in:
Po Lu 2024-03-31 15:33:40 +08:00
parent de8cae30bc
commit 7f377407b4
2 changed files with 156 additions and 7 deletions

View file

@ -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)

View file

@ -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;
}