From 27113c22f77b7a409c33b956a1a8d8be2d5bc673 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Tue, 8 Aug 2023 16:02:49 +0800 Subject: [PATCH] Minor improvements to write-region heuristic * src/androidvfs.c (android_saf_stat): Set STATB->st_dev. (android_fstat): Likewise. (NATIVE_NAME): Seek to start of file after truncation. * src/fileio.c (write_region): Use stat instead of open+fstat to obtain updated mtime. --- src/androidvfs.c | 15 ++++++++++- src/fileio.c | 68 +++++++++++++++++++++++++----------------------- 2 files changed, 49 insertions(+), 34 deletions(-) diff --git a/src/androidvfs.c b/src/androidvfs.c index 93927ccc86b..8e742f8b26f 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -4013,6 +4013,7 @@ android_saf_stat (const char *uri_name, const char *id_name, memset (statb, 0, sizeof *statb); statb->st_size = MAX (0, MIN (TYPE_MAXIMUM (off_t), size)); statb->st_mode = mode; + statb->st_dev = -4; #ifdef STAT_TIMESPEC STAT_TIMESPEC (statb, st_mtim).tv_sec = mtim / 1000; STAT_TIMESPEC (statb, st_mtim).tv_nsec = (mtim % 1000) * 1000000; @@ -6169,7 +6170,14 @@ NATIVE_NAME (safPostRequest) (JNIEnv *env, jobject object) JNIEXPORT jboolean JNICALL NATIVE_NAME (ftruncate) (JNIEnv *env, jobject object, jint fd) { - return ftruncate (fd, 0) != -1; + if (ftruncate (fd, 0) < 0) + return false; + + /* Reset the file pointer. */ + if (lseek (fd, 0, SEEK_SET) < 0) + return false; + + return true; } #ifdef __clang__ @@ -6722,6 +6730,11 @@ android_fstat (int fd, struct stat *statb) parcel_fd = open_parcel_fds; for (; parcel_fd; parcel_fd = parcel_fd->next) { + if (parcel_fd->fd == fd) + /* Set STATB->st_dev to a negative device number, signifying + that it's contained within a content provider. */ + statb->st_dev = -4; + if (parcel_fd->fd == fd && timespec_valid_p (parcel_fd->mtime)) { diff --git a/src/fileio.c b/src/fileio.c index 26b7e193f0a..5d01e10f0ef 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -5582,42 +5582,44 @@ write_region (Lisp_Object start, Lisp_Object end, Lisp_Object filename, if (timespec_valid_p (modtime) && ! (valid_timestamp_file_system && st.st_dev == timestamp_file_system)) { - int desc1 = emacs_open (fn, O_WRONLY, 0); - if (desc1 >= 0) + struct stat st1; + + /* The code below previously tried to open FN O_WRONLY, + subsequently calling fstat on the opened file descriptor. + This proved inefficient and resulted in FN being truncated + under several Android filesystems, and as such has been + changed to a call to `stat'. */ + + if (emacs_fstatat (AT_FDCWD, fn, &st1, 0) == 0 + && st.st_dev == st1.st_dev && st.st_ino == st1.st_ino) { - struct stat st1; - if (sys_fstat (desc1, &st1) == 0 - && st.st_dev == st1.st_dev && st.st_ino == st1.st_ino) + /* Use the heuristic if it appears to be valid. With neither + O_EXCL nor O_TRUNC, if Emacs happened to write nothing to the + file, the time stamp won't change. Also, some non-POSIX + systems don't update an empty file's time stamp when + truncating it. Finally, file systems with 100 ns or worse + resolution sometimes seem to have bugs: on a system with ns + resolution, checking ns % 100 incorrectly avoids the heuristic + 1% of the time, but the problem should be temporary as we will + try again on the next time stamp. */ + bool use_heuristic + = ((open_flags & (O_EXCL | O_TRUNC)) != 0 + && st.st_size != 0 + && modtime.tv_nsec % 100 != 0); + + struct timespec modtime1 = get_stat_mtime (&st1); + if (use_heuristic + && timespec_cmp (modtime, modtime1) == 0 + && st.st_size == st1.st_size) { - /* Use the heuristic if it appears to be valid. With neither - O_EXCL nor O_TRUNC, if Emacs happened to write nothing to the - file, the time stamp won't change. Also, some non-POSIX - systems don't update an empty file's time stamp when - truncating it. Finally, file systems with 100 ns or worse - resolution sometimes seem to have bugs: on a system with ns - resolution, checking ns % 100 incorrectly avoids the heuristic - 1% of the time, but the problem should be temporary as we will - try again on the next time stamp. */ - bool use_heuristic - = ((open_flags & (O_EXCL | O_TRUNC)) != 0 - && st.st_size != 0 - && modtime.tv_nsec % 100 != 0); - - struct timespec modtime1 = get_stat_mtime (&st1); - if (use_heuristic - && timespec_cmp (modtime, modtime1) == 0 - && st.st_size == st1.st_size) - { - timestamp_file_system = st.st_dev; - valid_timestamp_file_system = 1; - } - else - { - st.st_size = st1.st_size; - modtime = modtime1; - } + timestamp_file_system = st.st_dev; + valid_timestamp_file_system = 1; + } + else + { + st.st_size = st1.st_size; + modtime = modtime1; } - emacs_close (desc1); } } -- 2.39.5