
* configure.ac (ANDROID_STUBIFY): Add androidvfs.o when building libemacs.so. * doc/emacs/android.texi (Android): Add `Android Document Providers'. (Android Startup): Update the location of the content identifier directory. (Android File System): Describe access to document provider directories. (Android Document Providers): New node. * doc/emacs/emacs.texi (Top): Update the menu for the Android appendix. * java/Makefile.in (filename, install_temp/assets/build_info): Make directory-tree depend on build_info. * java/org/gnu/emacs/EmacsActivity.java (onActivityResult): New function. When a document tree is accepted, persist access to it. * java/org/gnu/emacs/EmacsDirectoryEntry.java (EmacsDirectoryEntry): New struct. * java/org/gnu/emacs/EmacsOpenActivity.java (checkReadableOrCopy): Use EmacsService.buildContentName. * java/org/gnu/emacs/EmacsService.java (getEmacsView, openContentUri) (checkContentUri): Remove excessive debug logging. (buildContentName, getDocumentAuthorities, requestDirectoryAccess) (getDocumentTrees, decodeFileName, documentIdFromName, getTreeUri) (statDocument, accessDocument, openDocumentDirectory, readDirectoryEntry) (openDocument, createDocument): New functions. * lib-src/asset-directory-tool.c: Improve commentary by illustrating the difference between directory and ordinary files. * src/android.c (ANDROID_THROW, enum android_fd_table_entry_flags) (struct android_emacs_service, android_extract_long) (android_scan_directory_tree, android_is_directory) (android_get_asset_name, android_url_encode, android_content_name_p) (android_get_content_name, android_check_content_access, android_fstat) (android_fstatat, android_file_access_p, android_hack_asset_fd_fallback) (android_detect_ashmem, android_hack_asset_fd, android_close_on_exec) (android_open, android_close, android_fclose, android_create_lib_link) (android_faccessat, struct android_dir, android_opendir, android_dirfd) (android_readdir, android_closedir, android_lookup_asset_directory_fd) (android_exception_check_3, android_get_current_api_level) (android_open_asset, android_close_asset, android_asset_read_quit) (android_asset_read, android_asset_lseek, android_asset_fstat): Move content and asset related functions to androidvfs.c. (android_init_emacs_service): Obtain handles for new JNI functions. (initEmacsParams): Initialize the VFS layer. (android_request_directory_access): New function. (android_display_toast): Remove unused function. * src/android.h (android_get_current_api_level): Assume that this function never returns less than __ANDROID_API__. (struct android_emacs_service): Move `struct android_emacs_service' here. * src/androidfns.c (Fandroid_request_directory_access): New interactive function. (syms_of_androidfns): Register new subr. * src/androidvfs.c (struct android_vdir, struct android_vops) (struct android_vnode, struct android_special_vnode) (enum android_vnode_type, struct android_cursor_class) (struct emacs_directory_entry_class) (struct android_parcel_file_descriptor_class) (android_init_cursor_class, android_init_entry_class) (android_init_fd_class, android_vfs_canonicalize_name) (struct android_unix_vnode, struct android_unix_vdir, unix_vfs_ops) (android_unix_name, android_unix_vnode, android_unix_open) (android_unix_close, android_unix_unlink, android_unix_symlink) (android_unix_rmdir, android_unix_rename, android_unix_stat) (android_unix_access, android_unix_mkdir, android_unix_readdir) (android_unix_closedir, android_unix_dirfd, android_unix_opendir) (android_extract_long, android_scan_directory_tree) (android_is_directory, android_init_assets) (android_hack_asset_fd_fallback, android_detect_ashmem) (android_hack_asset_fd, struct android_afs_vnode) (struct android_afs_vdir, struct android_afs_open_fd, afs_vfs_ops) (android_afs_name, android_afs_initial, android_close_on_exec) (android_afs_open, android_afs_close, android_afs_unlink) (android_afs_symlink, android_afs_rmdir, android_afs_rename) (android_afs_stat, android_afs_access, android_afs_mkdir) (android_afs_readdir, android_afs_closedir, android_afs_dirfd) (android_afs_opendir, android_afs_get_directory_name) (struct android_content_vdir, content_vfs_ops) (content_directory_contents, android_content_name) (android_content_open, android_content_close) (android_content_unlink, android_content_symlink) (android_content_rmdir, android_content_rename) (android_content_stat, android_content_access) (android_content_mkdir, android_content_readdir) (android_content_closedir, android_content_dirfd) (android_content_opendir, android_content_get_directory_name) (android_content_initial, android_get_content_name) (android_check_content_access, struct android_authority_vnode) (authority_vfs_ops, android_authority_name, android_authority_open) (android_authority_close, android_authority_unlink) (android_authority_symlink, android_authority_rmdir) (android_authority_rename, android_authority_stat) (android_authority_access, android_authority_mkdir) (android_authority_opendir, android_authority_initial) (struct android_saf_root_vnode, struct android_saf_root_vdir) (saf_root_vfs_ops, android_saf_root_name, android_saf_root_open) (android_saf_root_close, android_saf_root_unlink) (android_saf_root_symlink, android_saf_root_rmdir) (android_saf_root_rename, android_saf_root_stat) (android_saf_root_access, android_saf_root_mkdir) (android_saf_root_readdir, android_saf_root_closedir) (android_saf_root_dirfd, android_saf_root_opendir) (android_saf_root_initial, android_saf_root_get_directory) (android_saf_stat, android_saf_access) (struct android_saf_tree_vnode, struct android_saf_tree_vdir) (saf_tree_vfs_ops, android_document_id_from_name) (android_saf_tree_name, android_saf_tree_open) (android_saf_tree_close, android_saf_tree_unlink) (android_saf_tree_symlink, android_saf_tree_rmdir) (android_saf_tree_rename, android_saf_tree_stat) (android_saf_tree_access, android_saf_tree_mkdir) (android_saf_tree_opendir_1, android_saf_tree_readdir) (android_saf_tree_closedir, android_saf_tree_dirfd) (android_saf_tree_opendir, android_saf_tree_from_name) (android_saf_tree_get_directory, android_saf_file_vnode) (saf_file_vfs_ops, android_saf_file_name, android_saf_file_open) (android_saf_file_unlink, android_saf_file_rmdir) (android_saf_file_opendir, android_close_parcel_fd) (android_saf_new_vnode, android_saf_new_name, android_saf_new_open) (android_saf_new_unlink, android_saf_new_symlink) (android_saf_new_rmdir, android_saf_new_rename) (android_saf_new_stat, android_saf_new_access) (android_saf_new_mkdir, android_saf_new_opendir, root_vfs_ops) (special_vnodes, android_root_name, android_name_file) (android_vfs_init, android_open, android_unlink, android_symlink) (android_rmdir, android_mkdir, android_renameat_noreplace) (android_rename, android_fstat, android_fstatat_1, android_fstatat) (android_faccessat, android_fdopen, android_close, android_fclose) (android_open_asset, android_close_asset, android_asset_read_quit) (android_asset_read, android_asset_lseek, android_asset_fstat) (android_opendir, android_dirfd, android_readdir) (android_closedir): Move file system emulation routines here. Introduce a new ``VFS'' layer for translating between Emacs-specific file names and the various disparate interfaces for accessing files on Android. * src/callproc.c (delete_temp_file): * src/charset.c (load_charset_map_from_file): * src/dired.c: * src/emacs.c (Fkill_emacs): * src/fileio.c (check_mutable_filename, Fcopy_file) (Fmake_directory_internal, Fdelete_directory_internal) (Fdelete_file, Frename_file, Fadd_name_to_file) (Fmake_symbolic_link, file_accessible_directory_p, Fset_file_modes) (Fset_file_times, write_region): * src/filelock.c (get_boot_time, rename_lock_file) (create_lock_file, current_lock_owner, unlock_file): * src/image.c (slurp_file, png_load_body, jpeg_load_body): * src/keyboard.c (Fopen_dribble_file): * src/lisp.h: * src/lread.c (Fload): * src/process.c (handle_child_signal): * src/sysdep.c (init_standard_fds, emacs_fopen, emacs_fdopen) (emacs_unlink, emacs_symlink, emacs_rmdir, emacs_mkdir) (emacs_renameat_noreplace, emacs_rename): * src/term.c (Fresume_tty, init_tty): Use and add new wrappers for fopen, fdopen, unlink, symlink, rmdir, mkdir, renameat_norepalce and rename.
289 lines
7.2 KiB
C
289 lines
7.2 KiB
C
/* Android asset directory tool.
|
||
|
||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||
|
||
This file is part of GNU Emacs.
|
||
|
||
GNU Emacs is free software: you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation, either version 3 of the License, or (at
|
||
your option) any later version.
|
||
|
||
GNU Emacs is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||
|
||
#include <config.h>
|
||
|
||
#include <stdio.h>
|
||
#include <fcntl.h>
|
||
#include <errno.h>
|
||
#include <byteswap.h>
|
||
#include <stdlib.h>
|
||
#include <dirent.h>
|
||
#include <string.h>
|
||
#include <unistd.h>
|
||
|
||
#include <sys/stat.h>
|
||
|
||
/* This program takes a directory as input, and generates a
|
||
``directory-tree'' file suitable for inclusion in an Android
|
||
application package.
|
||
|
||
Such a file records the layout of the `assets' directory in the
|
||
package. Emacs records this information itself and uses it in the
|
||
Android emulation of readdir, because the system asset manager APIs
|
||
are routinely buggy, and are often unable to locate directories or
|
||
files.
|
||
|
||
The file is packed, with no data alignment guarantees made. The
|
||
file starts with the bytes "EMACS", following which is the name of
|
||
the first file or directory, a NULL byte and an unsigned int
|
||
indicating the offset from the start of the file to the start of
|
||
the next sibling. Following that is a list of subdirectories or
|
||
files in the same format. The long is stored LSB first.
|
||
|
||
Directories can be distinguished from ordinary files through the
|
||
last bytes of their file names (immediately previous to their
|
||
terminating NULL bytes), which are set to the directory separator
|
||
character `/'. */
|
||
|
||
|
||
|
||
struct directory_tree
|
||
{
|
||
/* The offset to the next sibling. */
|
||
size_t offset;
|
||
|
||
/* The name of this directory or file. */
|
||
char *name;
|
||
|
||
/* Subdirectories and files inside this directory. */
|
||
struct directory_tree *children, *next;
|
||
};
|
||
|
||
/* Exit with EXIT_FAILURE, after printing a description of a failing
|
||
function WHAT along with the details of the error. */
|
||
|
||
static _Noreturn void
|
||
croak (const char *what)
|
||
{
|
||
perror (what);
|
||
exit (EXIT_FAILURE);
|
||
}
|
||
|
||
/* Like malloc, but aborts on failure. */
|
||
|
||
static void *
|
||
xmalloc (size_t size)
|
||
{
|
||
void *ptr;
|
||
|
||
ptr = malloc (size);
|
||
|
||
if (!ptr)
|
||
croak ("malloc");
|
||
|
||
return ptr;
|
||
}
|
||
|
||
/* Recursively build a struct directory_tree structure for each
|
||
subdirectory or file in DIR, in preparation for writing it out to
|
||
disk. PARENT should be the directory tree associated with the
|
||
parent directory, or else PARENT->offset must be initialized to
|
||
5. */
|
||
|
||
static void
|
||
main_1 (DIR *dir, struct directory_tree *parent)
|
||
{
|
||
struct dirent *dirent;
|
||
int dir_fd, fd;
|
||
struct stat statb;
|
||
struct directory_tree *this, **last;
|
||
size_t length;
|
||
DIR *otherdir;
|
||
|
||
dir_fd = dirfd (dir);
|
||
last = &parent->children;
|
||
|
||
while ((dirent = readdir (dir)))
|
||
{
|
||
/* Determine what kind of file DIRENT is. */
|
||
|
||
if (fstatat (dir_fd, dirent->d_name, &statb,
|
||
AT_SYMLINK_NOFOLLOW) == -1)
|
||
croak ("fstatat");
|
||
|
||
/* Ignore . and ... */
|
||
|
||
if (!strcmp (dirent->d_name, ".")
|
||
|| !strcmp (dirent->d_name, ".."))
|
||
continue;
|
||
|
||
length = strlen (dirent->d_name);
|
||
|
||
if (statb.st_mode & S_IFDIR)
|
||
{
|
||
/* This is a directory. Write its name followed by a
|
||
trailing slash, then a NULL byte, and the offset to the
|
||
next sibling. */
|
||
this = xmalloc (sizeof *this);
|
||
this->children = NULL;
|
||
this->next = NULL;
|
||
*last = this;
|
||
last = &this->next;
|
||
this->name = xmalloc (length + 2);
|
||
strcpy (this->name, dirent->d_name);
|
||
|
||
/* Now record the offset to the end of this directory. This
|
||
is length + 1, for the file name, and 5 more bytes for
|
||
the trailing NULL and long. */
|
||
this->offset = parent->offset + length + 6;
|
||
|
||
/* Terminate that with a slash and trailing NULL byte. */
|
||
this->name[length] = '/';
|
||
this->name[length + 1] = '\0';
|
||
|
||
/* Open and build that directory recursively. */
|
||
|
||
fd = openat (dir_fd, dirent->d_name, O_DIRECTORY,
|
||
O_RDONLY);
|
||
if (fd < 0)
|
||
croak ("openat");
|
||
otherdir = fdopendir (fd);
|
||
if (!otherdir)
|
||
croak ("fdopendir");
|
||
|
||
main_1 (otherdir, this);
|
||
|
||
/* Close this directory. */
|
||
closedir (otherdir);
|
||
|
||
/* Finally, set parent->offset to this->offset as well. */
|
||
parent->offset = this->offset;
|
||
}
|
||
else if (statb.st_mode & S_IFREG)
|
||
{
|
||
/* This is a regular file. */
|
||
this = xmalloc (sizeof *this);
|
||
this->children = NULL;
|
||
this->next = NULL;
|
||
*last = this;
|
||
last = &this->next;
|
||
this->name = xmalloc (length + 1);
|
||
strcpy (this->name, dirent->d_name);
|
||
|
||
/* This is one byte shorter because there is no trailing
|
||
slash. */
|
||
this->offset = parent->offset + length + 5;
|
||
parent->offset = this->offset;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Write the struct directory_tree TREE and all of is children to the
|
||
file descriptor FD. OFFSET is the offset of TREE and may be
|
||
modified; it is only used for checking purposes. */
|
||
|
||
static void
|
||
main_2 (int fd, struct directory_tree *tree, size_t *offset)
|
||
{
|
||
ssize_t size;
|
||
struct directory_tree *child;
|
||
unsigned int output;
|
||
|
||
/* Write tree->name with the trailing NULL byte. */
|
||
size = strlen (tree->name) + 1;
|
||
if (write (fd, tree->name, size) < size)
|
||
croak ("write");
|
||
|
||
/* Write the offset. */
|
||
#ifdef WORDS_BIGENDIAN
|
||
output = bswap_32 (tree->offset);
|
||
#else
|
||
output = tree->offset;
|
||
#endif
|
||
if (write (fd, &output, 4) < 1)
|
||
croak ("write");
|
||
size += 4;
|
||
|
||
/* Now update offset. */
|
||
*offset += size;
|
||
|
||
/* Write out each child. */
|
||
for (child = tree->children; child; child = child->next)
|
||
main_2 (fd, child, offset);
|
||
|
||
/* Verify the offset is correct. */
|
||
if (tree->offset != *offset)
|
||
{
|
||
fprintf (stderr,
|
||
"asset-directory-tool: invalid offset: expected %tu, "
|
||
"got %tu.\n"
|
||
"Please report this bug to bug-gnu-emacs@gnu.org, along\n"
|
||
"with an archive containing the contents of the java/inst"
|
||
"all_temp directory.\n",
|
||
tree->offset, *offset);
|
||
abort ();
|
||
}
|
||
}
|
||
|
||
int
|
||
main (int argc, char **argv)
|
||
{
|
||
int fd;
|
||
DIR *indir;
|
||
struct directory_tree tree;
|
||
size_t offset;
|
||
|
||
if (argc != 3)
|
||
{
|
||
fprintf (stderr, "usage: %s directory output-file\n",
|
||
argv[0]);
|
||
return EXIT_FAILURE;
|
||
}
|
||
|
||
fd = open (argv[2], O_CREAT | O_TRUNC | O_RDWR,
|
||
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
|
||
|
||
if (fd < 0)
|
||
{
|
||
perror ("open");
|
||
return EXIT_FAILURE;
|
||
}
|
||
|
||
indir = opendir (argv[1]);
|
||
|
||
if (!indir)
|
||
{
|
||
perror ("opendir");
|
||
return EXIT_FAILURE;
|
||
}
|
||
|
||
/* Write the first 5 byte header to FD. */
|
||
|
||
if (write (fd, "EMACS", 5) < 5)
|
||
{
|
||
perror ("write");
|
||
return EXIT_FAILURE;
|
||
}
|
||
|
||
/* Now iterate through children of INDIR, building the directory
|
||
tree. */
|
||
tree.offset = 5;
|
||
tree.children = NULL;
|
||
|
||
main_1 (indir, &tree);
|
||
closedir (indir);
|
||
|
||
/* Finally, write the directory tree to the output file. */
|
||
offset = 5;
|
||
for (; tree.children; tree.children = tree.children->next)
|
||
main_2 (fd, tree.children, &offset);
|
||
|
||
return 0;
|
||
}
|