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:
parent
594e03526b
commit
3fb2c174d3
4 changed files with 116 additions and 52 deletions
|
@ -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
|
||||
|
|
6
etc/NEWS
6
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
|
||||
|
|
|
@ -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.
|
||||
|
|
141
src/fileio.c
141
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");
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue