aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2025-07-11 16:32:31 -0700
committerPaul Eggert2025-07-13 21:09:39 -0700
commit6d09a339ceb986de6d2a49ac8c20a7360f57b7d6 (patch)
treee63c76420384138e790d1354c2b2f2f669c72797 /src
parentb911029f96b14a03398198d5a849d6f4d0d1e071 (diff)
downloademacs-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.c68
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)