summaryrefslogtreecommitdiff
path: root/src/fileio.c
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2020-02-23 16:19:42 -0800
committerPaul Eggert <eggert@cs.ucla.edu>2020-02-23 16:45:50 -0800
commit9d626dffc6ba62c0d7a1a5c712f576ed8684fd66 (patch)
tree6cc8fbe8e5bc02c3bb74139710814a0400e91a8a /src/fileio.c
parentc4ca8219dd6b8f06e67a0b767475b1259653b8e0 (diff)
downloademacs-9d626dffc6ba62c0d7a1a5c712f576ed8684fd66.tar.gz
Add 'nofollow' flag to set-file-modes etc.
This avoids some race conditions (Bug#39683). E.g., if some other program changes a file to a symlink between the time Emacs creates the file and the time it changes the file’s permissions, using the new flag prevents Emacs from inadvertently changing the permissions of a victim in some completely unrelated directory. * admin/merge-gnulib (GNULIB_MODULES): Add fchmodat. * doc/lispref/files.texi (Testing Accessibility, Changing Files): * doc/lispref/os.texi (File Notifications): * etc/NEWS: Adjust documentation accordingly. * lib/chmodat.c, lib/fchmodat.c, lib/lchmod.c, m4/fchmodat.m4: * m4/lchmod.m4: New files, copied from Gnulib. * lib/gnulib.mk.in: Regenerate. * lisp/dired-aux.el (dired-do-chmod): * lisp/doc-view.el (doc-view-make-safe-dir): * lisp/emacs-lisp/autoload.el (autoload--save-buffer): * lisp/emacs-lisp/bytecomp.el (byte-compile-file): * lisp/eshell/em-pred.el (eshell-pred-file-mode): * lisp/files.el (backup-buffer-copy, copy-directory): * lisp/gnus/mail-source.el (mail-source-movemail): * lisp/gnus/mm-decode.el (mm-display-external): * lisp/gnus/nnmail.el (nnmail-write-region): * lisp/net/tramp-adb.el (tramp-adb-handle-file-local-copy) (tramp-adb-handle-write-region): * lisp/net/tramp-sh.el (tramp-do-copy-or-rename-file-directly): * lisp/net/tramp-sudoedit.el (tramp-sudoedit-handle-write-region): * lisp/net/tramp.el (tramp-handle-write-region) (tramp-make-tramp-temp-file): * lisp/server.el (server-ensure-safe-dir): * lisp/url/url-util.el (url-make-private-file): When getting or setting file modes, avoid following symbolic links when the file is not supposed to be a symbolic link. * lisp/doc-view.el (doc-view-make-safe-dir): Omit no-longer-needed separate symlink test. * lisp/gnus/gnus-util.el (gnus-set-file-modes): * lisp/net/tramp.el (tramp-handle-file-modes): * lisp/net/tramp-gvfs.el (tramp-gvfs-handle-set-file-modes): * src/fileio.c (symlink_nofollow_flag): New function. (Ffile_modes, Fset_file_modes): Support an optional FLAG arg. All C callers changed. * lisp/net/ange-ftp.el (ange-ftp-set-file-modes): * lisp/net/tramp-adb.el (tramp-adb-handle-set-file-modes): * lisp/net/tramp-sh.el (tramp-sh-handle-set-file-modes): * lisp/net/tramp-smb.el (tramp-smb-handle-set-file-modes): * lisp/net/tramp-sudoedit.el (tramp-sudoedit-handle-set-file-modes): Accept an optional FLAG arg that is currently ignored, and add a FIXME comment for it. * m4/gnulib-comp.m4: Regenerate.
Diffstat (limited to 'src/fileio.c')
-rw-r--r--src/fileio.c46
1 files changed, 28 insertions, 18 deletions
diff --git a/src/fileio.c b/src/fileio.c
index 6b56c473abf..2532f5233c4 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -3332,50 +3332,60 @@ support. */)
return Qnil;
}
-DEFUN ("file-modes", Ffile_modes, Sfile_modes, 1, 1, 0,
+static int
+symlink_nofollow_flag (Lisp_Object flag)
+{
+ /* For now, treat all non-nil FLAGs like 'nofollow'. */
+ return !NILP (flag) ? AT_SYMLINK_NOFOLLOW : 0;
+}
+
+DEFUN ("file-modes", Ffile_modes, Sfile_modes, 1, 2, 0,
doc: /* Return mode bits of file named FILENAME, as an integer.
-Return nil if FILENAME does not exist. */)
- (Lisp_Object filename)
+Return nil if FILENAME does not exist. If optional FLAG is `nofollow',
+do not follow FILENAME if it is a symbolic link. */)
+ (Lisp_Object filename, Lisp_Object flag)
{
struct stat st;
+ int nofollow = symlink_nofollow_flag (flag);
Lisp_Object absname = expand_and_dir_to_file (filename);
/* If the file name has special constructs in it,
call the corresponding file name handler. */
Lisp_Object handler = Ffind_file_name_handler (absname, Qfile_modes);
if (!NILP (handler))
- return call2 (handler, Qfile_modes, absname);
+ return call3 (handler, Qfile_modes, absname, flag);
- if (emacs_fstatat (AT_FDCWD, SSDATA (ENCODE_FILE (absname)), &st, 0) != 0)
+ char *fname = SSDATA (ENCODE_FILE (absname));
+ if (emacs_fstatat (AT_FDCWD, fname, &st, nofollow) != 0)
return file_attribute_errno (absname, errno);
return make_fixnum (st.st_mode & 07777);
}
-DEFUN ("set-file-modes", Fset_file_modes, Sset_file_modes, 2, 2,
+DEFUN ("set-file-modes", Fset_file_modes, Sset_file_modes, 2, 3,
"(let ((file (read-file-name \"File: \"))) \
(list file (read-file-modes nil file)))",
doc: /* Set mode bits of file named FILENAME to MODE (an integer).
-Only the 12 low bits of MODE are used.
+Only the 12 low bits of MODE are used. If optional FLAG is `nofollow',
+do not follow FILENAME if it is a symbolic link.
Interactively, mode bits are read by `read-file-modes', which accepts
symbolic notation, like the `chmod' command from GNU Coreutils. */)
- (Lisp_Object filename, Lisp_Object mode)
+ (Lisp_Object filename, Lisp_Object mode, Lisp_Object flag)
{
- Lisp_Object absname, encoded_absname;
- Lisp_Object handler;
-
- absname = Fexpand_file_name (filename, BVAR (current_buffer, directory));
CHECK_FIXNUM (mode);
+ int nofollow = symlink_nofollow_flag (flag);
+ Lisp_Object absname = Fexpand_file_name (filename,
+ BVAR (current_buffer, directory));
/* If the file name has special constructs in it,
call the corresponding file name handler. */
- handler = Ffind_file_name_handler (absname, Qset_file_modes);
+ Lisp_Object handler = Ffind_file_name_handler (absname, Qset_file_modes);
if (!NILP (handler))
- return call3 (handler, Qset_file_modes, absname, mode);
-
- encoded_absname = ENCODE_FILE (absname);
+ return call4 (handler, Qset_file_modes, absname, mode, flag);
- if (chmod (SSDATA (encoded_absname), XFIXNUM (mode) & 07777) < 0)
+ char *fname = SSDATA (ENCODE_FILE (absname));
+ mode_t imode = XFIXNUM (mode) & 07777;
+ if (fchmodat (AT_FDCWD, fname, imode, nofollow) != 0)
report_file_error ("Doing chmod", absname);
return Qnil;
@@ -5740,7 +5750,7 @@ auto_save_1 (void)
== 0)
/* But make sure we can overwrite it later! */
auto_save_mode_bits = (st.st_mode | 0600) & 0777;
- else if (modes = Ffile_modes (BVAR (current_buffer, filename)),
+ else if (modes = Ffile_modes (BVAR (current_buffer, filename), Qnil),
FIXNUMP (modes))
/* Remote files don't cooperate with fstatat. */
auto_save_mode_bits = (XFIXNUM (modes) | 0600) & 0777;