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.
This commit is contained in:
Po Lu 2023-08-08 13:37:00 +08:00
parent 594e03526b
commit 3fb2c174d3
4 changed files with 116 additions and 52 deletions

View file

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

View file

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

View file

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

View file

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