[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
master 3fb2c174d3a 1/3: Enable visiting FIFOs as files
From: |
Po Lu |
Subject: |
master 3fb2c174d3a 1/3: Enable visiting FIFOs as files |
Date: |
Tue, 8 Aug 2023 01:50:12 -0400 (EDT) |
branch: master
commit 3fb2c174d3a73ee5a2670b438538a5c32ad9d7ac
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>
Enable visiting FIFOs as files
* doc/lispref/files.texi (Reading from Files): Document new
`if-regular' value of REPLACE.
* etc/NEWS: Announce the new value.
* lisp/files.el (basic-save-buffer-2): Demote errors saving
backup files, since FIFOs cannot be copied.
(revert-buffer-insert-file-contents--default-function): Supply
`if-regular' instead of t as REPLACE.
* src/fileio.c (selinux_enabled_p): New function.
(Fcopy_file, Ffile_selinux_context, Fset_file_selinux_context):
Call that function to ascertain if SELinux applies to a file.
(read_non_regular): Don't assume `emacs_fd_read' always returns
int.
(Finsert_file_contents): If REPLACE is if-regular and FILENAME
is a special non-seekable file, fall back to erasing the buffer
before inserting the contents of that file.
(syms_of_fileio) <Qif_regular>: New symbol.
---
doc/lispref/files.texi | 11 ++--
etc/NEWS | 6 +++
lisp/files.el | 10 ++--
src/fileio.c | 141 ++++++++++++++++++++++++++++++++++---------------
4 files changed, 116 insertions(+), 52 deletions(-)
diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi
index aaab4e455a0..afedf776c86 100644
--- a/doc/lispref/files.texi
+++ b/doc/lispref/files.texi
@@ -582,11 +582,12 @@ contents and inserting the whole file, because (1) it
preserves some
marker positions and (2) it puts less data in the undo list.
It is possible to read a special file (such as a FIFO or an I/O
-device) with @code{insert-file-contents}, as long as @var{replace},
-and @var{visit} and @var{beg} are @code{nil}. However, you should
-normally use an @var{end} argument for these files to avoid inserting
-(potentially) unlimited data into the buffer (for instance, when
-inserting data from @file{/dev/urandom}).
+device) with @code{insert-file-contents}, as long as @var{replace} is
+@code{nil} or @code{if-regular}, and @var{visit} and @var{beg} are
+@code{nil}. However, you should normally use an @var{end} argument
+for these files to avoid inserting (potentially) unlimited data into
+the buffer (for instance, when inserting data from
+@file{/dev/urandom}).
@end defun
@defun insert-file-contents-literally filename &optional visit beg end replace
diff --git a/etc/NEWS b/etc/NEWS
index 6cdeeacc158..ee1ce72db98 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -743,6 +743,12 @@ The compatibility aliases 'x-defined-colors',
'x-color-defined-p',
See (info "(elisp)Porting Old Advice") for help converting them
to use `advice-add` or `define-advice instead.
++++
+** New value 'if-regular' for the REPLACE argument to 'insert-file-contents'.
+It results in 'insert-file-contents' erasing the buffer instead of
+preserving markers if the file being inserted is not a regular file,
+rather than signaling an error.
+
+++
** New variable 'current-key-remap-sequence'.
It is bound to the key sequence that caused a call to a function bound
diff --git a/lisp/files.el b/lisp/files.el
index 58014665fbc..685eb5fc957 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -5871,8 +5871,10 @@ Before and after saving the buffer, this function runs
buffer-file-name)))
(setq tempsetmodes t)
(error "Attempt to save to a file that you aren't allowed to
write"))))))
- (or buffer-backed-up
- (setq setmodes (backup-buffer)))
+ (with-demoted-errors
+ "Backing up buffer: %s"
+ (or buffer-backed-up
+ (setq setmodes (backup-buffer))))
(let* ((dir (file-name-directory buffer-file-name))
(dir-writable (file-writable-p dir)))
(if (or (and file-precious-flag dir-writable)
@@ -6894,9 +6896,9 @@ an auto-save file."
(if revert-buffer-preserve-modes
(let ((buffer-file-format buffer-file-format))
(insert-file-contents file-name (not auto-save-p)
- nil nil t))
+ nil nil 'if-regular))
(insert-file-contents file-name (not auto-save-p)
- nil nil t))))))
+ nil nil 'if-regular))))))
(defvar revert-buffer-with-fine-grain-max-seconds 2.0
"Maximum time that `revert-buffer-with-fine-grain' should use.
diff --git a/src/fileio.c b/src/fileio.c
index b5c3add836e..26b7e193f0a 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -200,6 +200,25 @@ check_vfs_filename (Lisp_Object encoded, const char
*reason)
#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
}
+#ifdef HAVE_LIBSELINUX
+
+/* Return whether SELinux is enabled and pertinent to FILE. Provide
+ for cases where FILE is or is a constitutent of a special
+ directory, such as /assets or /content on Android. */
+
+static bool
+selinux_enabled_p (const char *file)
+{
+ return (is_selinux_enabled ()
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ && !android_is_special_directory (file, "/assets")
+ && !android_is_special_directory (file, "/content")
+#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
+ );
+}
+
+#endif /* HAVE_LIBSELINUX */
+
/* Test whether FILE is accessible for AMODE.
Return true if successful, false (setting errno) otherwise. */
@@ -2311,7 +2330,7 @@ permissions. */)
if (!NILP (preserve_permissions))
{
#if HAVE_LIBSELINUX
- if (is_selinux_enabled ()
+ if (selinux_enabled_p (SSDATA (encoded_file))
&& emacs_fd_to_int (ifd) != -1)
{
conlength = fgetfilecon (emacs_fd_to_int (ifd),
@@ -2319,7 +2338,7 @@ permissions. */)
if (conlength == -1)
report_file_error ("Doing fgetfilecon", file);
}
-#endif
+#endif /* HAVE_LIBSELINUX */
}
/* We can copy only regular files. */
@@ -3353,6 +3372,7 @@ or if SELinux is disabled, or if Emacs lacks SELinux
support. */)
{
Lisp_Object user = Qnil, role = Qnil, type = Qnil, range = Qnil;
Lisp_Object absname = expand_and_dir_to_file (filename);
+ const char *file;
/* If the file name has special constructs in it,
call the corresponding file name handler. */
@@ -3361,11 +3381,13 @@ or if SELinux is disabled, or if Emacs lacks SELinux
support. */)
if (!NILP (handler))
return call2 (handler, Qfile_selinux_context, absname);
+ file = SSDATA (ENCODE_FILE (absname));
+
#if HAVE_LIBSELINUX
- if (is_selinux_enabled ())
+ if (selinux_enabled_p (file))
{
char *con;
- int conlength = lgetfilecon (SSDATA (ENCODE_FILE (absname)), &con);
+ int conlength = lgetfilecon (file, &con);
if (conlength > 0)
{
context_t context = context_new (con);
@@ -3384,7 +3406,7 @@ or if SELinux is disabled, or if Emacs lacks SELinux
support. */)
|| errno == ENOTSUP))
report_file_error ("getting SELinux context", absname);
}
-#endif
+#endif /* HAVE_LIBSELINUX */
return list4 (user, role, type, range);
}
@@ -3410,10 +3432,11 @@ or if Emacs was not compiled with SELinux support. */)
Lisp_Object type = CAR_SAFE (CDR_SAFE (CDR_SAFE (context)));
Lisp_Object range = CAR_SAFE (CDR_SAFE (CDR_SAFE (CDR_SAFE (context))));
char *con;
+ const char *name;
bool fail;
int conlength;
context_t parsed_con;
-#endif
+#endif /* HAVE_LIBSELINUX */
absname = Fexpand_file_name (filename, BVAR (current_buffer, directory));
@@ -3424,11 +3447,13 @@ or if Emacs was not compiled with SELinux support. */)
return call3 (handler, Qset_file_selinux_context, absname, context);
#if HAVE_LIBSELINUX
- if (is_selinux_enabled ())
+ encoded_absname = ENCODE_FILE (absname);
+ name = SSDATA (encoded_absname);
+
+ if (selinux_enabled_p (name))
{
/* Get current file context. */
- encoded_absname = ENCODE_FILE (absname);
- conlength = lgetfilecon (SSDATA (encoded_absname), &con);
+ conlength = lgetfilecon (name, &con);
if (conlength > 0)
{
parsed_con = context_new (con);
@@ -3469,7 +3494,7 @@ or if Emacs was not compiled with SELinux support. */)
else
report_file_error ("Doing lgetfilecon", absname);
}
-#endif
+#endif /* HAVE_LIBSELINUX */
return Qnil;
}
@@ -3860,11 +3885,12 @@ static Lisp_Object
read_non_regular (Lisp_Object state)
{
union read_non_regular *data = XFIXNUMPTR (state);
- int nbytes = emacs_fd_read (data->s.fd,
- ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
- + data->s.inserted),
- data->s.trytry);
- return make_fixnum (nbytes);
+ intmax_t nbytes
+ = emacs_fd_read (data->s.fd,
+ ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
+ + data->s.inserted),
+ data->s.trytry);
+ return make_int (nbytes);
}
@@ -4002,13 +4028,19 @@ at the start and end of the buffer) and (2) it puts
less data in the
undo list. When REPLACE is non-nil, the second return value is the
number of characters that replace previous buffer contents.
+If REPLACE is the symbol `if-regular', then eschew preserving marker
+positions or the undo list if REPLACE is nil if FILENAME is not a
+regular file. Otherwise, signal an error if REPLACE is non-nil and
+FILENAME is not a regular file.
+
This function does code conversion according to the value of
`coding-system-for-read' or `file-coding-system-alist', and sets the
variable `last-coding-system-used' to the coding system actually used.
In addition, this function decodes the inserted text from known formats
by calling `format-decode', which see. */)
- (Lisp_Object filename, Lisp_Object visit, Lisp_Object beg, Lisp_Object end,
Lisp_Object replace)
+ (Lisp_Object filename, Lisp_Object visit, Lisp_Object beg, Lisp_Object end,
+ Lisp_Object replace)
{
struct stat st;
struct timespec mtime;
@@ -4123,24 +4155,27 @@ by calling `format-decode', which see. */)
report_file_error ("Input file status", orig_filename);
mtime = get_stat_mtime (&st);
- /* This code will need to be changed in order to work on named
- pipes, and it's probably just not worth it. So we should at
- least signal an error. */
+ /* The REPLACE code will need to be changed in order to work on
+ named pipes, and it's probably just not worth it. So we should
+ at least signal an error. */
+
if (!S_ISREG (st.st_mode))
{
regular = false;
- if (! NILP (visit))
- {
- eassert (inserted == 0);
- goto notfound;
- }
-
if (!NILP (replace))
- xsignal2 (Qfile_error,
- build_string ("not a regular file"), orig_filename);
+ {
+ if (!EQ (replace, Qif_regular))
+ xsignal2 (Qfile_error,
+ build_string ("not a regular file"), orig_filename);
+ else
+ /* Set REPLACE to Qunbound, indicating that we are trying
+ to replace the buffer contents with that of a
+ non-regular file. */
+ replace = Qunbound;
+ }
- seekable = emacs_fd_lseek (fd, 0, SEEK_CUR) < 0;
+ seekable = emacs_fd_lseek (fd, 0, SEEK_CUR) != (off_t) -1;
if (!NILP (beg) && !seekable)
xsignal2 (Qfile_error,
build_string ("cannot use a start position in a non-seekable
file/device"),
@@ -4316,7 +4351,8 @@ by calling `format-decode', which see. */)
method and hope for the best.
But if we discover the need for conversion, we give up on this method
and let the following if-statement handle the replace job. */
- if (!NILP (replace)
+ if ((!NILP (replace)
+ && !BASE_EQ (replace, Qunbound))
&& BEGV < ZV
&& (NILP (coding_system)
|| ! CODING_REQUIRE_DECODING (&coding)))
@@ -4503,7 +4539,9 @@ by calling `format-decode', which see. */)
is needed, in a simple way that needs a lot of memory.
The preceding if-statement handles the case of no conversion
in a more optimized way. */
- if (!NILP (replace) && ! replace_handled && BEGV < ZV)
+ if ((!NILP (replace)
+ && !BASE_EQ (replace, Qunbound))
+ && ! replace_handled && BEGV < ZV)
{
ptrdiff_t same_at_start_charpos;
ptrdiff_t inserted_chars;
@@ -4688,6 +4726,12 @@ by calling `format-decode', which see. */)
prepare_to_modify_buffer (PT, PT, NULL);
}
+ /* If REPLACE is Qunbound, buffer contents are being replaced with
+ text read from a FIFO. Erase the entire buffer. */
+
+ if (BASE_EQ (replace, Qunbound))
+ del_range (BEG, Z);
+
move_gap_both (PT, PT_BYTE);
/* Ensure the gap is at least one byte larger than needed for the
@@ -4696,7 +4740,8 @@ by calling `format-decode', which see. */)
if (GAP_SIZE <= total)
make_gap (total - GAP_SIZE + 1);
- if (beg_offset != 0 || !NILP (replace))
+ if (beg_offset != 0 || (!NILP (replace)
+ && !EQ (replace, Qunbound)))
{
if (emacs_fd_lseek (fd, beg_offset, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
@@ -4729,6 +4774,7 @@ by calling `format-decode', which see. */)
if (!seekable && NILP (end))
{
Lisp_Object nbytes;
+ intmax_t number;
/* Read from the file, capturing `quit'. When an
error occurs, end the loop, and arrange for a quit
@@ -4744,18 +4790,20 @@ by calling `format-decode', which see. */)
break;
}
- this = XFIXNUM (nbytes);
+ if (!integer_to_intmax (nbytes, &number)
+ && number > PTRDIFF_MAX)
+ buffer_overflow ();
+
+ this = number;
}
else
- {
- /* Allow quitting out of the actual I/O. We don't make text
- part of the buffer until all the reading is done, so a C-g
- here doesn't do any harm. */
- this = emacs_fd_read (fd,
- ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
- + inserted),
- trytry);
- }
+ /* Allow quitting out of the actual I/O. We don't make text
+ part of the buffer until all the reading is done, so a
+ C-g here doesn't do any harm. */
+ this = emacs_fd_read (fd,
+ ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
+ + inserted),
+ trytry);
if (this <= 0)
{
@@ -4940,9 +4988,14 @@ by calling `format-decode', which see. */)
Funlock_file (BVAR (current_buffer, file_truename));
Funlock_file (filename);
}
+
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+ /* Under Android, modtime and st.st_size can be valid even if FD
+ is not a regular file. */
if (!regular)
xsignal2 (Qfile_error,
build_string ("not a regular file"), orig_filename);
+#endif /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
}
if (set_coding_system)
@@ -6810,9 +6863,11 @@ This includes interactive calls to `delete-file' and
#ifndef DOS_NT
defsubr (&Sfile_system_info);
-#endif
+#endif /* DOS_NT */
#ifdef HAVE_SYNC
defsubr (&Sunix_sync);
-#endif
+#endif /* HAVE_SYNC */
+
+ DEFSYM (Qif_regular, "if-regular");
}