Update Android port

* 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.
This commit is contained in:
Po Lu 2023-07-27 17:13:39 +08:00
parent ce63f592f5
commit 65b58251b1
26 changed files with 7839 additions and 1677 deletions

View file

@ -2689,8 +2689,8 @@ for Android, but all API calls need to be stubbed out])
# sfntfont-android.o.
ANDROID_OBJ="$ANDROID_OBJ sfnt.o sfntfont.o sfntfont-android.o"
# Build androidselect.o.
ANDROID_OBJ="$ANDROID_OBJ androidselect.o"
# Build androidselect.o and androidvfs.o.
ANDROID_OBJ="$ANDROID_OBJ androidselect.o androidvfs.o"
# Check for some functions not always present in the NDK.
AC_CHECK_DECLS([android_get_device_api_level])

View file

@ -17,6 +17,7 @@ about using such devices with Emacs, @pxref{Other Input Devices}.
* What is Android?:: Preamble.
* Android Startup:: Starting up Emacs on Android.
* Android File System:: The Android file system.
* Android Document Providers:: Accessing files from other programs.
* Android Environment:: Running Emacs under Android.
* Android Windowing:: The Android window system.
* Android Fonts:: Font selection under Android.
@ -142,12 +143,12 @@ it starts Emacs and gives it the file to open as an argument. Note
that if that Emacs in turn does not start the Emacs server, subsequent
attempts to open the file with the wrapper will fail.
@cindex /content directory, android
@cindex /content/by-authority directory, android
Some files are given to Emacs as ``content identifiers'' that the
system provides access to outside the normal filesystem APIs. Emacs
uses a pseudo-directory named @file{/content} to access those files.
Do not make any assumptions about the contents of this directory, or
try to open files in it yourself.
uses a pseudo-directory named @file{/content/by-authority} to access
those files. Do not make any assumptions about the contents of this
directory, or try to open files in it yourself.
This feature is not provided on Android 4.3 and earlier, in which
case such files are copied to a temporary directory before being
@ -180,12 +181,13 @@ that result from such an implementation:
@item
Subprocesses (such as @command{ls}) can not run from the
@file{/assets} directory; if you try to run a subprocess with
@code{current-directory} set to @file{/assets} or a subdirectory
thereof, it will run from the home directory instead.
@code{current-directory} set to @file{/assets},
@file{/content/storage} or a subdirectory thereof, it will run from
the home directory instead.
@item
There are no @file{.} and @file{..} directories inside the
@file{/assets} directory.
@file{/assets} or @file{/content} directory.
@item
Files in the @file{/assets} directory are always read only, and may be
@ -193,7 +195,7 @@ read in to memory more than once each time they are opened.
@end itemize
Aside from the @file{/assets} directory, Android programs normally
have access to three other directories. They are:
have access to four other directories. They are:
@itemize @bullet
@item
@ -209,6 +211,12 @@ contains utility executables alongside Emacs itself.
The @dfn{external storage} directory. This is accessible to Emacs
when the user grants the ``Files and Media'' permission to Emacs via
system settings.
@item
Directories provided by @dfn{document providers} on Android 5.0 and
later. These directories exist outside the normal Unix filesystem,
containing files provided by external programs (@pxref{Android
Document Providers}.)
@end itemize
The external storage directory is found at @file{/sdcard}. The
@ -265,6 +273,46 @@ files under @file{/sdcard} as usual.
These settings are not present on many proprietary versions of
Android.
@node Android Document Providers
@section Accessing files from other programs under Android
@cindex document providers, Android
@cindex /content/storage directory, Android
Android 5.0 introduces a new sort of program, the ``document
provider'': these programs are small programs that provide access to
their own files outside both the asset manager and the Unix
filesystem. Emacs supports accessing files and directories they
provide, placing their files within the directory
@file{/content/storage}.
@findex android-request-directory-access
Before Emacs is granted access to any of these directories, it must
first request the right to access it. This is done by running the
command (@pxref{M-x}) @code{android-request-directory-access}, which
displays a file selection dialog.
If a directory is selected within this dialog, its contents are
subsequently made available within a new directory named
@file{/content/storage/@var{authority}/@var{id}}, where
@var{authority} is the name of the document provider, and @var{id} is
a unique identifier assigned to the directory by the document
provider.
Because these directories do not exist within the Unix file-system,
sub-processes cannot be created within them, just as with the
@file{/assets} directory (@pxref{Android File System}.) In addition,
although Emacs can normally write and create files inside these
directories, it cannot create symlinks or hard links.
@c TODO: fix this!
Since document providers are allowed to perform expensive network
operations to obtain file contents, a file access operation within one
of these directories will possibly take a significant amount of time.
Emacs presently does not support quitting out of such file system
operations, and the timeouts applied are fully subject to the
discretion of the system and the document provider that is responding
to these operations.
@node Android Environment
@section Running Emacs under Android

View file

@ -1267,6 +1267,7 @@ Emacs and Android
* What is Android?:: Preamble.
* Android Startup:: Starting up Emacs on Android.
* Android File System:: The Android file system.
* Android Document Providers:: Accessing files from other programs.
* Android Environment:: Running Emacs under Android.
* Android Windowing:: The Android window system.
* Android Fonts:: Font selection under Android.

View file

@ -233,7 +233,8 @@ ifneq ($(NDK_BUILD_SHARED),)
endif
install_temp/assets/directory-tree: $(libsrc)/asset-directory-tool \
install_temp install_temp/assets/version
install_temp install_temp/assets/version \
install_temp/assets/build_info
$(AM_V_GEN) $(libsrc)/asset-directory-tool install_temp/assets \
install_temp/assets/directory-tree
@ -246,9 +247,9 @@ install_temp/assets/version: install_temp
install_temp/assets/build_info: install_temp
$(AM_V_GEN) { hostname; date +%s; } > $@
emacs.apk-in: install_temp install_temp/assets/directory-tree \
install_temp/assets/version install_temp/assets/build_info \
AndroidManifest.xml
emacs.apk-in: install_temp install_temp/assets/directory-tree \
AndroidManifest.xml install_temp/assets/version \
install_temp/assets/build_info
# Package everything. Specifying the assets on this command line is
# necessary for AAssetManager_getNextFileName to work on old versions
# of Android. Make sure not to generate R.java, as it's already been

View file

@ -25,6 +25,7 @@
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@ -33,6 +34,8 @@
import android.util.Log;
import android.net.Uri;
import android.view.Menu;
import android.view.View;
import android.view.ViewTreeObserver;
@ -48,6 +51,9 @@ public class EmacsActivity extends Activity
{
public static final String TAG = "EmacsActivity";
/* ID for URIs from a granted document tree. */
public static final int ACCEPT_DOCUMENT_TREE = 1;
/* The currently attached EmacsWindow, or null if none. */
private EmacsWindow window;
@ -431,4 +437,36 @@ public class EmacsActivity extends Activity
/* Update the window insets. */
syncFullscreenWith (window);
}
@Override
public final void
onActivityResult (int requestCode, int resultCode, Intent data)
{
ContentResolver resolver;
Uri uri;
int flags;
switch (requestCode)
{
case ACCEPT_DOCUMENT_TREE:
/* A document granted through
EmacsService.requestDirectoryAccess. */
if (resultCode == RESULT_OK)
{
resolver = getContentResolver ();
uri = data.getData ();
flags = (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (uri != null)
resolver.takePersistableUriPermission (uri, flags);
}
break;
}
}
};

View file

@ -0,0 +1,33 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
/* Structure holding a single ``directory entry'' from a document
provider. */
public class EmacsDirectoryEntry
{
/* The type of this directory entry. 0 means a regular file and 1
means a directory. */
public int d_type;
/* The display name of the file represented. */
public String d_name;
};

View file

@ -243,18 +243,8 @@ private class EmacsClientThread extends Thread
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
{
content = "/content/" + uri.getEncodedAuthority ();
for (String segment : uri.getPathSegments ())
content += "/" + Uri.encode (segment);
/* Append the URI query. */
if (uri.getEncodedQuery () != null)
content += "?" + uri.getEncodedQuery ();
content = EmacsService.buildContentName (uri);
Log.d (TAG, "checkReadableOrCopy: " + content);
return content;
}

File diff suppressed because it is too large Load diff

View file

@ -45,7 +45,12 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
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. */
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 `/'. */

File diff suppressed because it is too large Load diff

View file

@ -36,26 +36,46 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "androidgui.h"
#include "lisp.h"
#endif
#endif /* ANDROID_STUBIFY */
extern bool android_init_gui;
#ifndef ANDROID_STUBIFY
extern char *android_cache_dir;
extern int android_emacs_init (int, char **, char *);
extern int android_select (int, fd_set *, fd_set *, fd_set *,
struct timespec *);
extern char *android_user_full_name (struct passwd *);
/* File I/O operations. Many of these are defined in
androidvfs.c. */
extern const char *android_is_special_directory (const char *, const char *);
extern const char *android_get_home_directory (void);
extern void android_vfs_init (JNIEnv *, jobject);
extern int android_open (const char *, int, mode_t);
extern char *android_user_full_name (struct passwd *);
extern const char *android_is_special_directory (const char *, const char *);
extern int android_fstat (int, struct stat *);
extern int android_fstatat (int, const char *restrict,
struct stat *restrict, int);
extern int android_faccessat (int, const char *, int, int);
extern int android_close (int);
extern FILE *android_fdopen (int, const char *);
extern int android_fclose (FILE *);
extern const char *android_get_home_directory (void);
extern int android_unlink (const char *);
extern int android_symlink (const char *, const char *);
extern int android_rmdir (const char *);
extern int android_mkdir (const char *, mode_t);
extern int android_renameat_noreplace (int, const char *,
int, const char *);
extern int android_rename (const char *, const char *);
extern double android_pixel_density_x, android_pixel_density_y;
extern double android_scaled_pixel_density;
@ -89,6 +109,7 @@ extern jstring android_build_jstring (const char *);
extern void android_exception_check (void);
extern void android_exception_check_1 (jobject);
extern void android_exception_check_2 (jobject, jobject);
extern void android_exception_check_3 (jobject, jobject, jobject);
extern void android_exception_check_nonnull (void *, jobject);
extern void android_exception_check_nonnull_1 (void *, jobject, jobject);
@ -96,18 +117,29 @@ extern void android_get_keysym_name (int, char *, size_t);
extern void android_wait_event (void);
extern void android_toggle_on_screen_keyboard (android_window, bool);
extern _Noreturn void android_restart_emacs (void);
extern int android_get_current_api_level (void);
extern int android_request_directory_access (void);
extern int android_get_current_api_level (void)
__attribute__ ((pure));
/* Define `android_get_current_api_level' to a macro that the compiler
knows will always return at least __ANDROID_API__. */
#define android_get_current_api_level() \
({ int value; \
\
value = (android_get_current_api_level) (); \
eassume (value >= __ANDROID_API__); value; })
/* Directory listing emulation. */
struct android_dir;
struct android_vdir;
extern struct android_dir *android_opendir (const char *);
extern int android_dirfd (struct android_dir *);
extern struct dirent *android_readdir (struct android_dir *);
extern void android_closedir (struct android_dir *);
extern struct android_vdir *android_opendir (const char *);
extern int android_dirfd (struct android_vdir *);
extern struct dirent *android_readdir (struct android_vdir *);
extern void android_closedir (struct android_vdir *);
@ -211,8 +243,52 @@ extern int android_rewrite_spawn_argv (const char ***);
#ifndef ANDROID_STUBIFY
#include <jni.h>
struct android_emacs_service
{
jclass class;
jmethodID fill_rectangle;
jmethodID fill_polygon;
jmethodID draw_rectangle;
jmethodID draw_line;
jmethodID draw_point;
jmethodID clear_window;
jmethodID clear_area;
jmethodID ring_bell;
jmethodID query_tree;
jmethodID get_screen_width;
jmethodID get_screen_height;
jmethodID detect_mouse;
jmethodID name_keysym;
jmethodID browse_url;
jmethodID restart_emacs;
jmethodID update_ic;
jmethodID reset_ic;
jmethodID open_content_uri;
jmethodID check_content_uri;
jmethodID query_battery;
jmethodID update_extracted_text;
jmethodID update_cursor_anchor_info;
jmethodID get_document_authorities;
jmethodID request_directory_access;
jmethodID get_document_trees;
jmethodID document_id_from_name;
jmethodID get_tree_uri;
jmethodID stat_document;
jmethodID access_document;
jmethodID open_document_directory;
jmethodID read_directory_entry;
jmethodID open_document;
jmethodID create_document;
};
extern JNIEnv *android_java_env;
/* The EmacsService object. */
extern jobject emacs_service;
/* Various methods associated with the EmacsService. */
extern struct android_emacs_service service_class;
#define ANDROID_DELETE_LOCAL_REF(ref) \
((*android_java_env)->DeleteLocalRef (android_java_env, \
(ref)))

View file

@ -3036,6 +3036,32 @@ for more details about these values. */)
/* Directory access requests. */
DEFUN ("android-request-directory-access", Fandroid_request_directory_access,
Sandroid_request_directory_access, 0, 0, "",
doc: /* Request access to a directory within external storage.
On Android 5.0 and later, prompt for a directory within external or
application storage, and grant access to it; some of these directories
cannot be accessed through the regular `/sdcard' filesystem.
If access to the directory is granted, it will eventually appear
within the directory `/content/storage'. */)
(void)
{
if (android_get_current_api_level () < 21)
error ("Emacs can only access application storage on"
" Android 5.0 and later");
if (!android_init_gui)
return Qnil;
android_request_directory_access ();
return Qnil;
}
/* Miscellaneous input method related stuff. */
/* Report X, Y, by the phys cursor width and height as the cursor
@ -3062,7 +3088,7 @@ android_set_preeditarea (struct window *w, int x, int y)
y + w->phys_cursor_height);
}
#endif
#endif /* !ANDROID_STUBIFY */
@ -3220,6 +3246,7 @@ This option has no effect on Android 9 and earlier. */);
defsubr (&Sx_server_version);
#ifndef ANDROID_STUBIFY
defsubr (&Sandroid_query_battery);
defsubr (&Sandroid_request_directory_access);
tip_timer = Qnil;
staticpro (&tip_timer);
@ -3235,5 +3262,5 @@ This option has no effect on Android 9 and earlier. */);
staticpro (&tip_dx);
tip_dy = Qnil;
staticpro (&tip_dy);
#endif
#endif /* !ANDROID_STUBIFY */
}

6137
src/androidvfs.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -214,7 +214,7 @@ record_kill_process (struct Lisp_Process *p, Lisp_Object tempfile)
static void
delete_temp_file (Lisp_Object name)
{
unlink (SSDATA (name));
emacs_unlink (SSDATA (name));
}
static void

View file

@ -488,7 +488,7 @@ load_charset_map_from_file (struct charset *charset, Lisp_Object mapfile,
specbind (Qfile_name_handler_alist, Qnil);
fd = openp (Vcharset_map_path, mapfile, suffixes, NULL, Qnil, false, false,
NULL);
fp = fd < 0 ? 0 : fdopen (fd, "r");
fp = fd < 0 ? 0 : emacs_fdopen (fd, "r");
if (!fp)
{
int open_errno = errno;

View file

@ -54,7 +54,7 @@ typedef DIR emacs_dir;
/* The Android emulation of dirent stuff is required to be able to
list the /assets special directory. */
typedef struct android_dir emacs_dir;
typedef struct android_vdir emacs_dir;
#define emacs_readdir android_readdir
#define emacs_closedir android_closedir
#endif

View file

@ -3012,7 +3012,7 @@ killed. */
{
Lisp_Object listfile;
listfile = Fexpand_file_name (Vauto_save_list_file_name, Qnil);
unlink (SSDATA (listfile));
emacs_unlink (SSDATA (listfile));
}
#ifdef HAVE_NATIVE_COMP

View file

@ -182,15 +182,12 @@ static bool e_write (int, Lisp_Object, ptrdiff_t, ptrdiff_t,
/* Check that ENCODED does not lie on any special directory whose
contents are read only. Signal a `file-error' if it does.
If WRITE, then don't check that the file lies on `/content' on
Android. This special exception allows writing to content
provider-supplied files. */
/* Establish that ENCODED is not contained within a special directory
whose contents are not eligible for Unix VFS operations. Signal a
`file-error' with REASON if it does. */
static void
check_mutable_filename (Lisp_Object encoded, bool write)
check_vfs_filename (Lisp_Object encoded, const char *reason)
{
#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
const char *name;
@ -198,17 +195,10 @@ check_mutable_filename (Lisp_Object encoded, bool write)
name = SSDATA (encoded);
if (android_is_special_directory (name, "/assets"))
xsignal2 (Qfile_error,
build_string ("File lies on read-only directory"),
encoded);
if (write)
return;
xsignal2 (Qfile_error, build_string (reason), encoded);
if (android_is_special_directory (name, "/content"))
xsignal2 (Qfile_error,
build_string ("File lies on read-only directory"),
encoded);
xsignal2 (Qfile_error, build_string (reason), encoded);
#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
}
@ -2287,7 +2277,6 @@ permissions. */)
encoded_file = ENCODE_FILE (file);
encoded_newname = ENCODE_FILE (newname);
check_mutable_filename (encoded_newname, true);
#ifdef WINDOWSNT
if (NILP (ok_if_already_exists)
@ -2553,7 +2542,7 @@ DEFUN ("make-directory-internal", Fmake_directory_internal,
dir = SSDATA (encoded_dir);
if (mkdir (dir, 0777 & ~auto_saving_dir_umask) != 0)
if (emacs_mkdir (dir, 0777 & ~auto_saving_dir_umask) != 0)
report_file_error ("Creating directory", directory);
return Qnil;
@ -2572,9 +2561,7 @@ DEFUN ("delete-directory-internal", Fdelete_directory_internal,
encoded_dir = ENCODE_FILE (directory);
dir = SSDATA (encoded_dir);
check_mutable_filename (encoded_dir, false);
if (rmdir (dir) != 0)
if (emacs_rmdir (dir) != 0)
report_file_error ("Removing directory", directory);
return Qnil;
@ -2613,9 +2600,9 @@ With a prefix argument, TRASH is nil. */)
return call1 (Qmove_file_to_trash, filename);
encoded_file = ENCODE_FILE (filename);
check_mutable_filename (encoded_file, false);
if (unlink (SSDATA (encoded_file)) != 0 && errno != ENOENT)
if (emacs_unlink (SSDATA (encoded_file)) != 0
&& errno != ENOENT)
report_file_error ("Removing old name", filename);
return Qnil;
}
@ -2771,8 +2758,6 @@ This is what happens in interactive use with M-x. */)
encoded_file = ENCODE_FILE (file);
encoded_newname = ENCODE_FILE (newname);
check_mutable_filename (encoded_file, false);
check_mutable_filename (encoded_newname, false);
bool plain_rename = (case_only_rename
|| (!NILP (ok_if_already_exists)
@ -2780,8 +2765,10 @@ This is what happens in interactive use with M-x. */)
int rename_errno UNINIT;
if (!plain_rename)
{
if (renameat_noreplace (AT_FDCWD, SSDATA (encoded_file),
AT_FDCWD, SSDATA (encoded_newname))
if (emacs_renameat_noreplace (AT_FDCWD,
SSDATA (encoded_file),
AT_FDCWD,
SSDATA (encoded_newname))
== 0)
return Qnil;
@ -2803,7 +2790,8 @@ This is what happens in interactive use with M-x. */)
if (plain_rename)
{
if (rename (SSDATA (encoded_file), SSDATA (encoded_newname)) == 0)
if (emacs_rename (SSDATA (encoded_file),
SSDATA (encoded_newname)) == 0)
return Qnil;
rename_errno = errno;
/* Don't prompt again. */
@ -2884,8 +2872,10 @@ This is what happens in interactive use with M-x. */)
encoded_file = ENCODE_FILE (file);
encoded_newname = ENCODE_FILE (newname);
check_mutable_filename (encoded_file, false);
check_mutable_filename (encoded_newname, false);
check_vfs_filename (encoded_file, "Trying to create hard link to "
"file within special directory");
check_vfs_filename (encoded_newname, "Trying to create hard link"
" within special directory");
if (link (SSDATA (encoded_file), SSDATA (encoded_newname)) == 0)
return Qnil;
@ -2896,7 +2886,7 @@ This is what happens in interactive use with M-x. */)
|| FIXNUMP (ok_if_already_exists))
barf_or_query_if_file_exists (newname, true, "make it a new name",
FIXNUMP (ok_if_already_exists), false);
unlink (SSDATA (newname));
emacs_unlink (SSDATA (newname));
if (link (SSDATA (encoded_file), SSDATA (encoded_newname)) == 0)
return Qnil;
}
@ -2939,10 +2929,9 @@ This happens for interactive use with M-x. */)
encoded_target = ENCODE_FILE (target);
encoded_linkname = ENCODE_FILE (linkname);
check_mutable_filename (encoded_target, false);
check_mutable_filename (encoded_linkname, false);
if (symlink (SSDATA (encoded_target), SSDATA (encoded_linkname)) == 0)
if (emacs_symlink (SSDATA (encoded_target),
SSDATA (encoded_linkname)) == 0)
return Qnil;
if (errno == ENOSYS)
@ -2955,8 +2944,9 @@ This happens for interactive use with M-x. */)
|| FIXNUMP (ok_if_already_exists))
barf_or_query_if_file_exists (linkname, true, "make it a link",
FIXNUMP (ok_if_already_exists), false);
unlink (SSDATA (encoded_linkname));
if (symlink (SSDATA (encoded_target), SSDATA (encoded_linkname)) == 0)
emacs_unlink (SSDATA (encoded_linkname));
if (emacs_symlink (SSDATA (encoded_target),
SSDATA (encoded_linkname)) == 0)
return Qnil;
}
@ -3328,27 +3318,12 @@ file_accessible_directory_p (Lisp_Object file)
special cases "/" and "//", and it's a safe optimization
here. After appending '.', append another '/' to work around
a macOS bug (Bug#30350). */
#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
if (!strncmp ("/assets/", data,
sizeof "/assets" - 1))
{
static char const appended[] = "/";
char *buf = SAFE_ALLOCA (len + sizeof appended);
memcpy (buf, data, len);
strcpy (buf + len, &appended[data[len - 1] == '/']);
dir = buf;
}
else
{
#endif
static char const appended[] = "/./";
char *buf = SAFE_ALLOCA (len + sizeof appended);
memcpy (buf, data, len);
strcpy (buf + len, &appended[data[len - 1] == '/']);
dir = buf;
#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
}
#endif
static char const appended[] = "/./";
char *buf = SAFE_ALLOCA (len + sizeof appended);
memcpy (buf, data, len);
strcpy (buf + len, &appended[data[len - 1] == '/']);
dir = buf;
}
ok = file_access_p (dir, F_OK);
@ -3682,7 +3657,8 @@ command from GNU Coreutils. */)
return call4 (handler, Qset_file_modes, absname, mode, flag);
encoded = ENCODE_FILE (absname);
check_mutable_filename (encoded, false);
check_vfs_filename (encoded, "Trying to change access modes of file"
" within special directory");
char *fname = SSDATA (encoded);
mode_t imode = XFIXNUM (mode) & 07777;
if (fchmodat (AT_FDCWD, fname, imode, nofollow) != 0)
@ -3755,7 +3731,8 @@ TIMESTAMP is in the format of `current-time'. */)
return call4 (handler, Qset_file_times, absname, timestamp, flag);
Lisp_Object encoded_absname = ENCODE_FILE (absname);
check_mutable_filename (encoded_absname, false);
check_vfs_filename (encoded_absname, "Trying to set access times of"
" file within special directory");
if (utimensat (AT_FDCWD, SSDATA (encoded_absname), ts, nofollow) != 0)
{
@ -5456,7 +5433,6 @@ write_region (Lisp_Object start, Lisp_Object end, Lisp_Object filename,
}
encoded_filename = ENCODE_FILE (filename);
check_mutable_filename (encoded_filename, false);
fn = SSDATA (encoded_filename);
open_flags = O_WRONLY | O_CREAT;

View file

@ -228,7 +228,7 @@ get_boot_time (void)
{
get_boot_time_1 (SSDATA (filename), 1);
if (delete_flag)
unlink (SSDATA (filename));
emacs_unlink (SSDATA (filename));
}
}
@ -323,11 +323,12 @@ rename_lock_file (char const *old, char const *new, bool force)
{
struct stat st;
int r = renameat_noreplace (AT_FDCWD, old, AT_FDCWD, new);
int r = emacs_renameat_noreplace (AT_FDCWD, old,
AT_FDCWD, new);
if (! (r < 0 && errno == ENOSYS))
return r;
if (link (old, new) == 0)
return unlink (old) == 0 || errno == ENOENT ? 0 : -1;
return emacs_unlink (old) == 0 || errno == ENOENT ? 0 : -1;
if (errno != ENOSYS && errno != LINKS_MIGHT_NOT_WORK)
return -1;
@ -347,7 +348,7 @@ rename_lock_file (char const *old, char const *new, bool force)
return -1;
}
return rename (old, new);
return emacs_rename (old, new);
#endif
}
@ -365,13 +366,13 @@ create_lock_file (char *lfname, char *lock_info_str, bool force)
pretending that 'symlink' does not work. */
int err = ENOSYS;
#else
int err = symlink (lock_info_str, lfname) == 0 ? 0 : errno;
int err = emacs_symlink (lock_info_str, lfname) == 0 ? 0 : errno;
#endif
if (err == EEXIST && force)
{
unlink (lfname);
err = symlink (lock_info_str, lfname) == 0 ? 0 : errno;
emacs_unlink (lfname);
err = emacs_symlink (lock_info_str, lfname) == 0 ? 0 : errno;
}
if (err == ENOSYS || err == LINKS_MIGHT_NOT_WORK || err == ENAMETOOLONG)
@ -409,7 +410,7 @@ create_lock_file (char *lfname, char *lock_info_str, bool force)
if (!err && rename_lock_file (nonce, lfname, force) != 0)
err = errno;
if (err)
unlink (nonce);
emacs_unlink (nonce);
}
SAFE_FREE ();
@ -622,7 +623,7 @@ current_lock_owner (lock_info_type *owner, Lisp_Object lfname)
/* The owner process is dead or has a strange pid, so try to
zap the lockfile. */
else
return unlink (SSDATA (lfname)) < 0 ? errno : 0;
return emacs_unlink (SSDATA (lfname)) < 0 ? errno : 0;
}
else
{ /* If we wanted to support the check for stale locks on remote machines,
@ -770,7 +771,8 @@ unlock_file (Lisp_Object fn)
int err = current_lock_owner (0, lfname);
if (! (err == 0 || err == ANOTHER_OWNS_IT
|| (err == I_OWN_IT
&& (unlink (SSDATA (lfname)) == 0 || (err = errno) == ENOENT))))
&& (emacs_unlink (SSDATA (lfname)) == 0
|| (err = errno) == ENOENT))))
report_file_errno ("Unlocking file", fn, err);
return Qnil;

View file

@ -4234,7 +4234,7 @@ static char *
slurp_file (image_fd fd, ptrdiff_t *size)
{
#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
FILE *fp = fdopen (fd, "rb");
FILE *fp = emacs_fdopen (fd, "rb");
char *buf = NULL;
struct stat st;
@ -8021,7 +8021,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c)
}
/* Open the image file. */
fp = fdopen (fd, "rb");
fp = emacs_fdopen (fd, "rb");
if (!fp)
{
image_error ("Cannot open image file `%s'", file);
@ -8750,7 +8750,7 @@ jpeg_load_body (struct frame *f, struct image *img,
return 0;
}
fp = fdopen (fd, "rb");
fp = emacs_fdopen (fd, "rb");
if (fp == NULL)
{
image_error ("Cannot open `%s'", file);

View file

@ -11740,9 +11740,9 @@ This may include sensitive information such as passwords. */)
encfile = ENCODE_FILE (file);
fd = emacs_open (SSDATA (encfile), O_WRONLY | O_CREAT | O_EXCL, 0600);
if (fd < 0 && errno == EEXIST
&& (unlink (SSDATA (encfile)) == 0 || errno == ENOENT))
&& (emacs_unlink (SSDATA (encfile)) == 0 || errno == ENOENT))
fd = emacs_open (SSDATA (encfile), O_WRONLY | O_CREAT | O_EXCL, 0600);
dribble = fd < 0 ? 0 : fdopen (fd, "w");
dribble = fd < 0 ? 0 : emacs_fdopen (fd, "w");
if (dribble == 0)
report_file_error ("Opening dribble", file);
}

View file

@ -5086,7 +5086,15 @@ extern int emacs_open (const char *, int, int);
extern int emacs_open_noquit (const char *, int, int);
extern int emacs_pipe (int[2]);
extern int emacs_close (int);
extern FILE *emacs_fdopen (int, const char *);
extern int emacs_fclose (FILE *);
extern int emacs_unlink (const char *);
extern int emacs_symlink (const char *, const char *);
extern int emacs_rmdir (const char *);
extern int emacs_mkdir (const char *, mode_t);
extern int emacs_renameat_noreplace (int, const char *, int,
const char *);
extern int emacs_rename (const char *, const char *);
extern ptrdiff_t emacs_read (int, void *, ptrdiff_t);
extern ptrdiff_t emacs_read_quit (int, void *, ptrdiff_t);
extern ptrdiff_t emacs_write (int, void const *, ptrdiff_t);

View file

@ -1702,7 +1702,7 @@ Return t if the file exists and loads successfully. */)
stream = emacs_fopen (SSDATA (efound), fmode);
#else
#if !defined USE_ANDROID_ASSETS
stream = fdopen (fd, fmode);
stream = emacs_fdopen (fd, fmode);
#else
/* Android systems use special file descriptors which can point
into compressed data and double as file streams. FMODE is

View file

@ -7472,6 +7472,16 @@ handle_child_signal (int sig)
{
changed = true;
if (STRINGP (XCDR (head)))
/* handle_child_signal is called in an async signal
handler but needs to unlink temporary files which
might've been created in an Android content
provider.
emacs_unlink is not async signal safe because
deleting files from content providers must proceed
through Java code. Consequentially, if XCDR (head)
lies on a content provider it will not be removed,
which is a bug. */
unlink (SSDATA (XCDR (head)));
XSETCAR (tail, Qnil);
}

View file

@ -260,7 +260,7 @@ init_standard_fds (void)
/* Set buferr if possible on platforms defining _PC_PIPE_BUF, as
they support the notion of atomic writes to pipes. */
#ifdef _PC_PIPE_BUF
buferr = fdopen (STDERR_FILENO, "w");
buferr = emacs_fdopen (STDERR_FILENO, "w");
if (buferr)
setvbuf (buferr, NULL, _IOLBF, 0);
#endif
@ -2545,7 +2545,7 @@ emacs_fopen (char const *file, char const *mode)
}
fd = emacs_open (file, omode | oflags | bflag, 0666);
return fd < 0 ? 0 : fdopen (fd, mode);
return fd < 0 ? 0 : emacs_fdopen (fd, mode);
}
/* Create a pipe for Emacs use. */
@ -2627,6 +2627,19 @@ emacs_close (int fd)
}
}
/* Wrapper around fdopen. On Android, this calls `android_fclose' to
clear information associated with FD if necessary. */
FILE *
emacs_fdopen (int fd, const char *mode)
{
#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
return fdopen (fd, mode);
#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
return android_fdopen (fd, mode);
#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */
}
/* Wrapper around fclose. On Android, this calls `android_fclose' to
clear information associated with the FILE's file descriptor if
necessary. */
@ -2641,6 +2654,71 @@ emacs_fclose (FILE *stream)
#endif
}
/* Wrappers around unlink, symlink, rename, renameat_noreplace, and
rmdir. These operations handle asset and content directories on
Android. */
int
emacs_unlink (const char *name)
{
#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
return unlink (name);
#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
return android_unlink (name);
#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */
}
int
emacs_symlink (const char *target, const char *linkname)
{
#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
return symlink (target, linkname);
#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
return android_symlink (target, linkname);
#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */
}
int
emacs_rmdir (const char *dirname)
{
#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
return rmdir (dirname);
#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
return android_rmdir (dirname);
#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */
}
int
emacs_mkdir (const char *dirname, mode_t mode)
{
#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
return mkdir (dirname, mode);
#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
return android_mkdir (dirname, mode);
#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */
}
int
emacs_renameat_noreplace (int srcfd, const char *src,
int dstfd, const char *dst)
{
#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
return renameat_noreplace (srcfd, src, dstfd, dst);
#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
return android_renameat_noreplace (srcfd, src, dstfd, dst);
#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */
}
int
emacs_rename (const char *src, const char *dst)
{
#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
return rename (src, dst);
#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
return android_rename (src, dst);
#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */
}
/* Maximum number of bytes to read or write in a single system call.
This works around a serious bug in Linux kernels before 2.6.16; see
<https://bugzilla.redhat.com/show_bug.cgi?format=multiple&id=612839>.

View file

@ -2415,7 +2415,7 @@ frame's terminal). */)
#else /* !MSDOS */
fd = emacs_open (t->display_info.tty->name, O_RDWR | O_NOCTTY, 0);
t->display_info.tty->input = t->display_info.tty->output
= fd < 0 ? 0 : fdopen (fd, "w+");
= fd < 0 ? 0 : emacs_fdopen (fd, "w+");
if (! t->display_info.tty->input)
{
@ -4128,7 +4128,7 @@ init_tty (const char *name, const char *terminal_type, bool must_succeed)
tty->input = tty->output
= ((fd < 0 || ! isatty (fd))
? NULL
: fdopen (fd, "w+"));
: emacs_fdopen (fd, "w+"));
if (! tty->input)
{