diff options
| author | Paul Eggert | 2025-07-11 16:32:31 -0700 |
|---|---|---|
| committer | Paul Eggert | 2025-07-13 21:09:39 -0700 |
| commit | 6d09a339ceb986de6d2a49ac8c20a7360f57b7d6 (patch) | |
| tree | e63c76420384138e790d1354c2b2f2f669c72797 /src | |
| parent | b911029f96b14a03398198d5a849d6f4d0d1e071 (diff) | |
| download | emacs-6d09a339ceb986de6d2a49ac8c20a7360f57b7d6.tar.gz emacs-6d09a339ceb986de6d2a49ac8c20a7360f57b7d6.zip | |
Improve insert-file-contents on non-regular files
This is part of a fix for Bug#77315,
and improves on the fix for Bug#71258.
* src/fileio.c (Finsert_file_contents): Do not pretend that
directories are regular files. Instead, signal an error when
attempting to read from them in the usual case where the OS
prohibits that; and otherwise read from them. However, when
visiting a directory report an error right away rather than
waiting until later, as this function is documented to not allow
visiting non-regular files. Nest the struct stat into a small
code block, to keep the code simpler and so that the compiler can
see what parts are used and can issue better diagnostics if
uninitialized storage is accessed. Be more skeptical of st_size,
when the file is not regular.
Diffstat (limited to 'src')
| -rw-r--r-- | src/fileio.c | 68 |
1 files changed, 34 insertions, 34 deletions
diff --git a/src/fileio.c b/src/fileio.c index a3a49d179c4..cbdfeacf8e4 100644 --- a/src/fileio.c +++ b/src/fileio.c | |||
| @@ -4058,7 +4058,6 @@ by calling `format-decode', which see. */) | |||
| 4058 | (Lisp_Object filename, Lisp_Object visit, Lisp_Object beg, Lisp_Object end, | 4058 | (Lisp_Object filename, Lisp_Object visit, Lisp_Object beg, Lisp_Object end, |
| 4059 | Lisp_Object replace) | 4059 | Lisp_Object replace) |
| 4060 | { | 4060 | { |
| 4061 | struct stat st; | ||
| 4062 | struct timespec mtime; | 4061 | struct timespec mtime; |
| 4063 | emacs_fd fd; | 4062 | emacs_fd fd; |
| 4064 | ptrdiff_t inserted = 0; | 4063 | ptrdiff_t inserted = 0; |
| @@ -4067,7 +4066,7 @@ by calling `format-decode', which see. */) | |||
| 4067 | Lisp_Object handler, val, insval, orig_filename, old_undo; | 4066 | Lisp_Object handler, val, insval, orig_filename, old_undo; |
| 4068 | Lisp_Object p; | 4067 | Lisp_Object p; |
| 4069 | ptrdiff_t total = 0; | 4068 | ptrdiff_t total = 0; |
| 4070 | bool regular = true; | 4069 | bool regular; |
| 4071 | int save_errno = 0; | 4070 | int save_errno = 0; |
| 4072 | char read_buf[READ_BUF_SIZE]; | 4071 | char read_buf[READ_BUF_SIZE]; |
| 4073 | struct coding_system coding; | 4072 | struct coding_system coding; |
| @@ -4138,6 +4137,9 @@ by calling `format-decode', which see. */) | |||
| 4138 | orig_filename = filename; | 4137 | orig_filename = filename; |
| 4139 | filename = ENCODE_FILE (filename); | 4138 | filename = ENCODE_FILE (filename); |
| 4140 | 4139 | ||
| 4140 | /* A hint about the file size, or -1 if there is no hint. */ | ||
| 4141 | off_t file_size_hint; | ||
| 4142 | |||
| 4141 | fd = emacs_fd_open (SSDATA (filename), O_RDONLY, 0); | 4143 | fd = emacs_fd_open (SSDATA (filename), O_RDONLY, 0); |
| 4142 | if (!emacs_fd_valid_p (fd)) | 4144 | if (!emacs_fd_valid_p (fd)) |
| 4143 | { | 4145 | { |
| @@ -4145,7 +4147,7 @@ by calling `format-decode', which see. */) | |||
| 4145 | if (NILP (visit)) | 4147 | if (NILP (visit)) |
| 4146 | report_file_error ("Opening input file", orig_filename); | 4148 | report_file_error ("Opening input file", orig_filename); |
| 4147 | mtime = time_error_value (save_errno); | 4149 | mtime = time_error_value (save_errno); |
| 4148 | st.st_size = -1; | 4150 | file_size_hint = -1; |
| 4149 | if (!NILP (Vcoding_system_for_read)) | 4151 | if (!NILP (Vcoding_system_for_read)) |
| 4150 | { | 4152 | { |
| 4151 | /* Don't let invalid values into buffer-file-coding-system. */ | 4153 | /* Don't let invalid values into buffer-file-coding-system. */ |
| @@ -4167,21 +4169,27 @@ by calling `format-decode', which see. */) | |||
| 4167 | XCAR (XCAR (window_markers))); | 4169 | XCAR (XCAR (window_markers))); |
| 4168 | } | 4170 | } |
| 4169 | 4171 | ||
| 4170 | if (emacs_fd_fstat (fd, &st) != 0) | 4172 | { |
| 4171 | report_file_error ("Input file status", orig_filename); | 4173 | struct stat st; |
| 4172 | mtime = get_stat_mtime (&st); | 4174 | if (emacs_fd_fstat (fd, &st) < 0) |
| 4175 | report_file_error ("Input file status", orig_filename); | ||
| 4176 | regular = S_ISREG (st.st_mode) != 0; | ||
| 4177 | bool memory_object = S_TYPEISSHM (&st) || S_TYPEISTMO (&st); | ||
| 4178 | file_size_hint = regular | memory_object ? st.st_size : -1; | ||
| 4179 | mtime = (memory_object | ||
| 4180 | ? make_timespec (0, UNKNOWN_MODTIME_NSECS) | ||
| 4181 | : get_stat_mtime (&st)); | ||
| 4182 | } | ||
| 4173 | 4183 | ||
| 4174 | /* The REPLACE code will need to be changed in order to work on | 4184 | /* The REPLACE code will need to be changed in order to work on |
| 4175 | named pipes, and it's probably just not worth it. So we should | 4185 | named pipes, and it's probably just not worth it. So we should |
| 4176 | at least signal an error. */ | 4186 | at least signal an error. */ |
| 4177 | 4187 | ||
| 4178 | if (!(S_ISREG (st.st_mode) || S_ISDIR (st.st_mode))) | 4188 | if (!regular) |
| 4179 | { | 4189 | { |
| 4180 | regular = false; | 4190 | if (!NILP (visit) || !NILP (replace)) |
| 4181 | |||
| 4182 | if (!NILP (replace)) | ||
| 4183 | { | 4191 | { |
| 4184 | if (!EQ (replace, Qif_regular)) | 4192 | if (!NILP (visit) || !EQ (replace, Qif_regular)) |
| 4185 | xsignal2 (Qfile_error, | 4193 | xsignal2 (Qfile_error, |
| 4186 | build_string ("not a regular file"), orig_filename); | 4194 | build_string ("not a regular file"), orig_filename); |
| 4187 | else | 4195 | else |
| @@ -4191,12 +4199,13 @@ by calling `format-decode', which see. */) | |||
| 4191 | replace = Qunbound; | 4199 | replace = Qunbound; |
| 4192 | } | 4200 | } |
| 4193 | 4201 | ||
| 4194 | /* Forbid specifying BEG together with a special file, as per | 4202 | /* Forbid specifying BEG unless the file is regular, as per |
| 4195 | the doc string. */ | 4203 | the doc string. */ |
| 4196 | 4204 | ||
| 4197 | if (!NILP (beg)) | 4205 | if (!NILP (beg)) |
| 4198 | xsignal2 (Qfile_error, | 4206 | xsignal2 (Qfile_error, |
| 4199 | build_string ("cannot use a start position in a non-seekable file/device"), | 4207 | build_string ("can use a start position" |
| 4208 | " only in a regular file"), | ||
| 4200 | orig_filename); | 4209 | orig_filename); |
| 4201 | 4210 | ||
| 4202 | /* Now ascertain if this file is seekable, by detecting if | 4211 | /* Now ascertain if this file is seekable, by detecting if |
| @@ -4211,7 +4220,7 @@ by calling `format-decode', which see. */) | |||
| 4211 | end_offset = TYPE_MAXIMUM (off_t); | 4220 | end_offset = TYPE_MAXIMUM (off_t); |
| 4212 | else | 4221 | else |
| 4213 | { | 4222 | { |
| 4214 | end_offset = st.st_size; | 4223 | end_offset = file_size_hint; |
| 4215 | 4224 | ||
| 4216 | /* A negative size can happen on a platform that allows file | 4225 | /* A negative size can happen on a platform that allows file |
| 4217 | sizes greater than the maximum off_t value. */ | 4226 | sizes greater than the maximum off_t value. */ |
| @@ -4233,7 +4242,7 @@ by calling `format-decode', which see. */) | |||
| 4233 | { | 4242 | { |
| 4234 | /* The likely offset where we will stop reading. We could read | 4243 | /* The likely offset where we will stop reading. We could read |
| 4235 | more (or less), if the file grows (or shrinks) as we read it. */ | 4244 | more (or less), if the file grows (or shrinks) as we read it. */ |
| 4236 | off_t likely_end = min (end_offset, st.st_size); | 4245 | off_t likely_end = min (end_offset, file_size_hint); |
| 4237 | 4246 | ||
| 4238 | if (beg_offset < likely_end) | 4247 | if (beg_offset < likely_end) |
| 4239 | { | 4248 | { |
| @@ -4517,8 +4526,8 @@ by calling `format-decode', which see. */) | |||
| 4517 | 4526 | ||
| 4518 | /* Don't try to reuse the same piece of text twice. */ | 4527 | /* Don't try to reuse the same piece of text twice. */ |
| 4519 | overlap = (same_at_start - BEGV_BYTE | 4528 | overlap = (same_at_start - BEGV_BYTE |
| 4520 | - (same_at_end | 4529 | - (same_at_end - ZV_BYTE |
| 4521 | + (! NILP (end) ? end_offset : st.st_size) - ZV_BYTE)); | 4530 | + (!NILP (end) ? end_offset : file_size_hint))); |
| 4522 | if (overlap > 0) | 4531 | if (overlap > 0) |
| 4523 | same_at_end += overlap; | 4532 | same_at_end += overlap; |
| 4524 | same_at_end_charpos = BYTE_TO_CHAR (same_at_end); | 4533 | same_at_end_charpos = BYTE_TO_CHAR (same_at_end); |
| @@ -4737,18 +4746,17 @@ by calling `format-decode', which see. */) | |||
| 4737 | goto handled; | 4746 | goto handled; |
| 4738 | } | 4747 | } |
| 4739 | 4748 | ||
| 4740 | /* Don't believe st.st_size if it is zero. */ | 4749 | /* From here on, treat a file with zero or unknown size as not seekable. |
| 4741 | if ((regular && st.st_size > 0) || (!regular && seekable) || !NILP (end)) | 4750 | This causes us to read until we actually hit EOF. */ |
| 4751 | if (file_size_hint <= 0) | ||
| 4752 | seekable = false; | ||
| 4753 | |||
| 4754 | if (seekable || !NILP (end)) | ||
| 4742 | total = end_offset - beg_offset; | 4755 | total = end_offset - beg_offset; |
| 4743 | else | 4756 | else |
| 4744 | /* For a special file that is not seekable, all we can do is guess. */ | 4757 | /* For a file that is not seekable, all we can do is guess. */ |
| 4745 | total = READ_BUF_SIZE; | 4758 | total = READ_BUF_SIZE; |
| 4746 | 4759 | ||
| 4747 | /* From here on, treat a file with zero size as not seekable. This | ||
| 4748 | causes us to read until we actually hit EOF. */ | ||
| 4749 | if (regular && st.st_size == 0) | ||
| 4750 | seekable = false; | ||
| 4751 | |||
| 4752 | if (NILP (visit) && total > 0) | 4760 | if (NILP (visit) && total > 0) |
| 4753 | { | 4761 | { |
| 4754 | if (!NILP (BVAR (current_buffer, file_truename)) | 4762 | if (!NILP (BVAR (current_buffer, file_truename)) |
| @@ -5013,7 +5021,7 @@ by calling `format-decode', which see. */) | |||
| 5013 | if (NILP (handler)) | 5021 | if (NILP (handler)) |
| 5014 | { | 5022 | { |
| 5015 | current_buffer->modtime = mtime; | 5023 | current_buffer->modtime = mtime; |
| 5016 | current_buffer->modtime_size = st.st_size; | 5024 | current_buffer->modtime_size = file_size_hint; |
| 5017 | bset_filename (current_buffer, orig_filename); | 5025 | bset_filename (current_buffer, orig_filename); |
| 5018 | } | 5026 | } |
| 5019 | 5027 | ||
| @@ -5026,14 +5034,6 @@ by calling `format-decode', which see. */) | |||
| 5026 | Funlock_file (BVAR (current_buffer, file_truename)); | 5034 | Funlock_file (BVAR (current_buffer, file_truename)); |
| 5027 | Funlock_file (filename); | 5035 | Funlock_file (filename); |
| 5028 | } | 5036 | } |
| 5029 | |||
| 5030 | #if !defined HAVE_ANDROID || defined ANDROID_STUBIFY | ||
| 5031 | /* Under Android, modtime and st.st_size can be valid even if FD | ||
| 5032 | is not a regular file. */ | ||
| 5033 | if (!regular) | ||
| 5034 | xsignal2 (Qfile_error, | ||
| 5035 | build_string ("not a regular file"), orig_filename); | ||
| 5036 | #endif /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */ | ||
| 5037 | } | 5037 | } |
| 5038 | 5038 | ||
| 5039 | if (set_coding_system) | 5039 | if (set_coding_system) |