aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Eggert2020-02-23 16:19:42 -0800
committerPaul Eggert2020-02-23 16:45:50 -0800
commit9d626dffc6ba62c0d7a1a5c712f576ed8684fd66 (patch)
tree6cc8fbe8e5bc02c3bb74139710814a0400e91a8a
parentc4ca8219dd6b8f06e67a0b767475b1259653b8e0 (diff)
downloademacs-9d626dffc6ba62c0d7a1a5c712f576ed8684fd66.tar.gz
emacs-9d626dffc6ba62c0d7a1a5c712f576ed8684fd66.zip
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.
-rwxr-xr-xadmin/merge-gnulib2
-rw-r--r--doc/lispref/files.texi27
-rw-r--r--doc/lispref/os.texi2
-rw-r--r--etc/NEWS3
-rw-r--r--lib/fchmodat.c144
-rw-r--r--lib/gnulib.mk.in26
-rw-r--r--lib/lchmod.c110
-rw-r--r--lisp/dired-aux.el3
-rw-r--r--lisp/doc-view.el4
-rw-r--r--lisp/emacs-lisp/autoload.el2
-rw-r--r--lisp/emacs-lisp/bytecomp.el2
-rw-r--r--lisp/eshell/em-pred.el2
-rw-r--r--lisp/files.el12
-rw-r--r--lisp/gnus/gnus-util.el4
-rw-r--r--lisp/gnus/mail-source.el2
-rw-r--r--lisp/gnus/mm-decode.el2
-rw-r--r--lisp/gnus/nnmail.el2
-rw-r--r--lisp/net/ange-ftp.el3
-rw-r--r--lisp/net/tramp-adb.el9
-rw-r--r--lisp/net/tramp-gvfs.el4
-rw-r--r--lisp/net/tramp-sh.el10
-rw-r--r--lisp/net/tramp-smb.el3
-rw-r--r--lisp/net/tramp-sudoedit.el6
-rw-r--r--lisp/net/tramp.el13
-rw-r--r--lisp/server.el2
-rw-r--r--lisp/url/url-util.el4
-rw-r--r--m4/fchmodat.m482
-rw-r--r--m4/gnulib-comp.m435
-rw-r--r--m4/lchmod.m431
-rw-r--r--src/fileio.c46
30 files changed, 533 insertions, 64 deletions
diff --git a/admin/merge-gnulib b/admin/merge-gnulib
index 48c81e61e2a..557119441e4 100755
--- a/admin/merge-gnulib
+++ b/admin/merge-gnulib
@@ -33,7 +33,7 @@ GNULIB_MODULES='
33 crypto/md5-buffer crypto/sha1-buffer crypto/sha256-buffer crypto/sha512-buffer 33 crypto/md5-buffer crypto/sha1-buffer crypto/sha256-buffer crypto/sha512-buffer
34 d-type diffseq dosname double-slash-root dtoastr dtotimespec dup2 34 d-type diffseq dosname double-slash-root dtoastr dtotimespec dup2
35 environ execinfo explicit_bzero faccessat 35 environ execinfo explicit_bzero faccessat
36 fcntl fcntl-h fdopendir 36 fchmodat fcntl fcntl-h fdopendir
37 filemode filevercmp flexmember fpieee fstatat fsusage fsync 37 filemode filevercmp flexmember fpieee fstatat fsusage fsync
38 getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog 38 getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog
39 ieee754-h ignore-value intprops largefile lstat 39 ieee754-h ignore-value intprops largefile lstat
diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi
index a93da39f174..a69a4e5dd38 100644
--- a/doc/lispref/files.texi
+++ b/doc/lispref/files.texi
@@ -928,7 +928,7 @@ also checks that the file's group would be unchanged.
928This function does not follow symbolic links. 928This function does not follow symbolic links.
929@end defun 929@end defun
930 930
931@defun file-modes filename 931@defun file-modes filename &optional flag
932@cindex mode bits 932@cindex mode bits
933@cindex file permissions 933@cindex file permissions
934@cindex permissions, file 934@cindex permissions, file
@@ -946,12 +946,19 @@ The highest possible value is 4095 (7777 octal), meaning that everyone
946has read, write, and execute permission, the @acronym{SUID} bit is set 946has read, write, and execute permission, the @acronym{SUID} bit is set
947for both others and group, and the sticky bit is set. 947for both others and group, and the sticky bit is set.
948 948
949By default this function follows symbolic links. However, if the
950optional argument @var{flag} is the symbol @code{nofollow}, this
951function does not follow @var{filename} if it is a symbolic link;
952this can help prevent inadvertently obtaining the mode bits of a file
953somewhere else, and is more consistent with @code{file-attributes}
954(@pxref{File Attributes}).
955
949@xref{Changing Files}, for the @code{set-file-modes} function, which 956@xref{Changing Files}, for the @code{set-file-modes} function, which
950can be used to set these permissions. 957can be used to set these permissions.
951 958
952@example 959@example
953@group 960@group
954(file-modes "~/junk/diffs") 961(file-modes "~/junk/diffs" 'nofollow)
955 @result{} 492 ; @r{Decimal integer.} 962 @result{} 492 ; @r{Decimal integer.}
956@end group 963@end group
957@group 964@group
@@ -960,7 +967,7 @@ can be used to set these permissions.
960@end group 967@end group
961 968
962@group 969@group
963(set-file-modes "~/junk/diffs" #o666) 970(set-file-modes "~/junk/diffs" #o666 'nofollow)
964 @result{} nil 971 @result{} nil
965@end group 972@end group
966 973
@@ -1801,9 +1808,17 @@ See also @code{delete-directory} in @ref{Create/Delete Dirs}.
1801@cindex file permissions, setting 1808@cindex file permissions, setting
1802@cindex permissions, file 1809@cindex permissions, file
1803@cindex file modes, setting 1810@cindex file modes, setting
1804@deffn Command set-file-modes filename mode 1811@deffn Command set-file-modes filename mode &optional flag
1805This function sets the @dfn{file mode} (or @dfn{permissions}) of 1812This function sets the @dfn{file mode} (or @dfn{permissions}) of
1806@var{filename} to @var{mode}. This function follows symbolic links. 1813@var{filename} to @var{mode}.
1814
1815By default this function follows symbolic links. However, if the
1816optional argument @var{flag} is the symbol @code{nofollow}, this
1817function does not follow @var{filename} if it is a symbolic link;
1818this can help prevent inadvertently changing the mode bits of a file
1819somewhere else. On platforms that do not support changing mode bits
1820on a symbolic link, this function signals an error when @var{filename}
1821is a symbolic link and @var{flag} is @code{nofollow}.
1807 1822
1808If called non-interactively, @var{mode} must be an integer. Only the 1823If called non-interactively, @var{mode} must be an integer. Only the
1809lowest 12 bits of the integer are used; on most systems, only the 1824lowest 12 bits of the integer are used; on most systems, only the
@@ -1811,7 +1826,7 @@ lowest 9 bits are meaningful. You can use the Lisp construct for
1811octal numbers to enter @var{mode}. For example, 1826octal numbers to enter @var{mode}. For example,
1812 1827
1813@example 1828@example
1814(set-file-modes #o644) 1829(set-file-modes "myfile" #o644 'nofollow)
1815@end example 1830@end example
1816 1831
1817@noindent 1832@noindent
diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi
index a034ccdcd5c..cf4ef52abfb 100644
--- a/doc/lispref/os.texi
+++ b/doc/lispref/os.texi
@@ -3127,7 +3127,7 @@ being reported. For example:
3127@end group 3127@end group
3128 3128
3129@group 3129@group
3130(set-file-modes "/tmp/foo" (default-file-modes)) 3130(set-file-modes "/tmp/foo" (default-file-modes) 'nofollow)
3131 @result{} Event (35025468 attribute-changed "/tmp/foo") 3131 @result{} Event (35025468 attribute-changed "/tmp/foo")
3132@end group 3132@end group
3133@end example 3133@end example
diff --git a/etc/NEWS b/etc/NEWS
index 02798798367..5ca054363d2 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -198,6 +198,9 @@ called when the function object is garbage-collected. Use
198'set_function_finalizer' to set the finalizer and 198'set_function_finalizer' to set the finalizer and
199'get_function_finalizer' to retrieve it. 199'get_function_finalizer' to retrieve it.
200 200
201** 'file-modes' and 'set-file-modes' now have an optional argument
202specifying whether to follow symbolic links.
203
201** 'parse-time-string' can now parse ISO 8601 format strings, 204** 'parse-time-string' can now parse ISO 8601 format strings,
202such as "2020-01-15T16:12:21-08:00". 205such as "2020-01-15T16:12:21-08:00".
203 206
diff --git a/lib/fchmodat.c b/lib/fchmodat.c
new file mode 100644
index 00000000000..8950168608f
--- /dev/null
+++ b/lib/fchmodat.c
@@ -0,0 +1,144 @@
1/* Change the protections of file relative to an open directory.
2 Copyright (C) 2006, 2009-2020 Free Software Foundation, Inc.
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17/* written by Jim Meyering and Paul Eggert */
18
19/* If the user's config.h happens to include <sys/stat.h>, let it include only
20 the system's <sys/stat.h> here, so that orig_fchmodat doesn't recurse to
21 rpl_fchmodat. */
22#define __need_system_sys_stat_h
23#include <config.h>
24
25/* Specification. */
26#include <sys/stat.h>
27#undef __need_system_sys_stat_h
28
29#if HAVE_FCHMODAT
30static int
31orig_fchmodat (int dir, char const *file, mode_t mode, int flags)
32{
33 return fchmodat (dir, file, mode, flags);
34}
35#endif
36
37#include <errno.h>
38#include <fcntl.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <unistd.h>
42
43#ifdef __osf__
44/* Write "sys/stat.h" here, not <sys/stat.h>, otherwise OSF/1 5.1 DTK cc
45 eliminates this include because of the preliminary #include <sys/stat.h>
46 above. */
47# include "sys/stat.h"
48#else
49# include <sys/stat.h>
50#endif
51
52#include <intprops.h>
53
54/* Invoke chmod or lchmod on FILE, using mode MODE, in the directory
55 open on descriptor FD. If possible, do it without changing the
56 working directory. Otherwise, resort to using save_cwd/fchdir,
57 then (chmod|lchmod)/restore_cwd. If either the save_cwd or the
58 restore_cwd fails, then give a diagnostic and exit nonzero.
59 Note that an attempt to use a FLAG value of AT_SYMLINK_NOFOLLOW
60 on a system without lchmod support causes this function to fail. */
61
62#if HAVE_FCHMODAT
63int
64fchmodat (int dir, char const *file, mode_t mode, int flags)
65{
66# if NEED_FCHMODAT_NONSYMLINK_FIX
67 if (flags == AT_SYMLINK_NOFOLLOW)
68 {
69 struct stat st;
70
71# if defined O_PATH && defined AT_EMPTY_PATH
72 /* Open a file descriptor with O_NOFOLLOW, to make sure we don't
73 follow symbolic links, if /proc is mounted. O_PATH is used to
74 avoid a failure if the file is not readable.
75 Cf. <https://sourceware.org/bugzilla/show_bug.cgi?id=14578> */
76 int fd = openat (dir, file, O_PATH | O_NOFOLLOW | O_CLOEXEC);
77 if (fd < 0)
78 return fd;
79
80 /* Up to Linux 5.3 at least, when FILE refers to a symbolic link, the
81 chmod call below will change the permissions of the symbolic link
82 - which is undesired - and on many file systems (ext4, btrfs, jfs,
83 xfs, ..., but not reiserfs) fail with error EOPNOTSUPP - which is
84 misleading. Therefore test for a symbolic link explicitly.
85 Use fstatat because fstat does not work on O_PATH descriptors
86 before Linux 3.6. */
87 if (fstatat (fd, "", &st, AT_EMPTY_PATH) != 0)
88 {
89 int stat_errno = errno;
90 close (fd);
91 errno = stat_errno;
92 return -1;
93 }
94 if (S_ISLNK (st.st_mode))
95 {
96 close (fd);
97 errno = EOPNOTSUPP;
98 return -1;
99 }
100
101# if defined __linux__ || defined __ANDROID__
102 static char const fmt[] = "/proc/self/fd/%d";
103 char buf[sizeof fmt - sizeof "%d" + INT_BUFSIZE_BOUND (int)];
104 sprintf (buf, fmt, fd);
105 int chmod_result = chmod (buf, mode);
106 int chmod_errno = errno;
107 close (fd);
108 if (chmod_result == 0)
109 return chmod_result;
110 if (chmod_errno != ENOENT)
111 {
112 errno = chmod_errno;
113 return chmod_result;
114 }
115# endif
116 /* /proc is not mounted or would not work as in GNU/Linux. */
117
118# else
119 int fstatat_result = fstatat (dir, file, &st, AT_SYMLINK_NOFOLLOW);
120 if (fstatat_result != 0)
121 return fstatat_result;
122 if (S_ISLNK (st.st_mode))
123 {
124 errno = EOPNOTSUPP;
125 return -1;
126 }
127# endif
128
129 /* Fall back on orig_fchmodat with no flags, despite a possible race. */
130 flags = 0;
131 }
132# endif
133
134 return orig_fchmodat (dir, file, mode, flags);
135}
136#else
137# define AT_FUNC_NAME fchmodat
138# define AT_FUNC_F1 lchmod
139# define AT_FUNC_F2 chmod
140# define AT_FUNC_USE_F1_COND AT_SYMLINK_NOFOLLOW
141# define AT_FUNC_POST_FILE_PARAM_DECLS , mode_t mode, int flag
142# define AT_FUNC_POST_FILE_ARGS , mode
143# include "at-func.c"
144#endif
diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in
index 3c01e61b266..d4dc6a3df33 100644
--- a/lib/gnulib.mk.in
+++ b/lib/gnulib.mk.in
@@ -95,6 +95,7 @@
95# execinfo \ 95# execinfo \
96# explicit_bzero \ 96# explicit_bzero \
97# faccessat \ 97# faccessat \
98# fchmodat \
98# fcntl \ 99# fcntl \
99# fcntl-h \ 100# fcntl-h \
100# fdopendir \ 101# fdopendir \
@@ -1082,6 +1083,7 @@ gl_GNULIB_ENABLED_dirfd = @gl_GNULIB_ENABLED_dirfd@
1082gl_GNULIB_ENABLED_euidaccess = @gl_GNULIB_ENABLED_euidaccess@ 1083gl_GNULIB_ENABLED_euidaccess = @gl_GNULIB_ENABLED_euidaccess@
1083gl_GNULIB_ENABLED_getdtablesize = @gl_GNULIB_ENABLED_getdtablesize@ 1084gl_GNULIB_ENABLED_getdtablesize = @gl_GNULIB_ENABLED_getdtablesize@
1084gl_GNULIB_ENABLED_getgroups = @gl_GNULIB_ENABLED_getgroups@ 1085gl_GNULIB_ENABLED_getgroups = @gl_GNULIB_ENABLED_getgroups@
1086gl_GNULIB_ENABLED_lchmod = @gl_GNULIB_ENABLED_lchmod@
1085gl_GNULIB_ENABLED_malloca = @gl_GNULIB_ENABLED_malloca@ 1087gl_GNULIB_ENABLED_malloca = @gl_GNULIB_ENABLED_malloca@
1086gl_GNULIB_ENABLED_open = @gl_GNULIB_ENABLED_open@ 1088gl_GNULIB_ENABLED_open = @gl_GNULIB_ENABLED_open@
1087gl_GNULIB_ENABLED_strtoll = @gl_GNULIB_ENABLED_strtoll@ 1089gl_GNULIB_ENABLED_strtoll = @gl_GNULIB_ENABLED_strtoll@
@@ -1586,6 +1588,17 @@ EXTRA_libgnu_a_SOURCES += at-func.c faccessat.c
1586endif 1588endif
1587## end gnulib module faccessat 1589## end gnulib module faccessat
1588 1590
1591## begin gnulib module fchmodat
1592ifeq (,$(OMIT_GNULIB_MODULE_fchmodat))
1593
1594
1595EXTRA_DIST += at-func.c fchmodat.c
1596
1597EXTRA_libgnu_a_SOURCES += at-func.c fchmodat.c
1598
1599endif
1600## end gnulib module fchmodat
1601
1589## begin gnulib module fcntl 1602## begin gnulib module fcntl
1590ifeq (,$(OMIT_GNULIB_MODULE_fcntl)) 1603ifeq (,$(OMIT_GNULIB_MODULE_fcntl))
1591 1604
@@ -1936,6 +1949,19 @@ EXTRA_DIST += inttypes.in.h
1936endif 1949endif
1937## end gnulib module inttypes-incomplete 1950## end gnulib module inttypes-incomplete
1938 1951
1952## begin gnulib module lchmod
1953ifeq (,$(OMIT_GNULIB_MODULE_lchmod))
1954
1955ifneq (,$(gl_GNULIB_ENABLED_lchmod))
1956
1957endif
1958EXTRA_DIST += lchmod.c
1959
1960EXTRA_libgnu_a_SOURCES += lchmod.c
1961
1962endif
1963## end gnulib module lchmod
1964
1939## begin gnulib module libc-config 1965## begin gnulib module libc-config
1940ifeq (,$(OMIT_GNULIB_MODULE_libc-config)) 1966ifeq (,$(OMIT_GNULIB_MODULE_libc-config))
1941 1967
diff --git a/lib/lchmod.c b/lib/lchmod.c
new file mode 100644
index 00000000000..e1132116234
--- /dev/null
+++ b/lib/lchmod.c
@@ -0,0 +1,110 @@
1/* Implement lchmod on platforms where it does not work correctly.
2
3 Copyright 2020 Free Software Foundation, Inc.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17
18/* written by Paul Eggert */
19
20#include <config.h>
21
22/* Specification. */
23#include <sys/stat.h>
24
25#include <errno.h>
26#include <fcntl.h>
27#include <stdio.h>
28#include <unistd.h>
29
30#ifdef __osf__
31/* Write "sys/stat.h" here, not <sys/stat.h>, otherwise OSF/1 5.1 DTK cc
32 eliminates this include because of the preliminary #include <sys/stat.h>
33 above. */
34# include "sys/stat.h"
35#else
36# include <sys/stat.h>
37#endif
38
39#include <intprops.h>
40
41/* Work like chmod, except when FILE is a symbolic link.
42 In that case, on systems where permissions on symbolic links are unsupported
43 (such as Linux), set errno to EOPNOTSUPP and return -1. */
44
45int
46lchmod (char const *file, mode_t mode)
47{
48#if defined O_PATH && defined AT_EMPTY_PATH
49 /* Open a file descriptor with O_NOFOLLOW, to make sure we don't
50 follow symbolic links, if /proc is mounted. O_PATH is used to
51 avoid a failure if the file is not readable.
52 Cf. <https://sourceware.org/bugzilla/show_bug.cgi?id=14578> */
53 int fd = open (file, O_PATH | O_NOFOLLOW | O_CLOEXEC);
54 if (fd < 0)
55 return fd;
56
57 /* Up to Linux 5.3 at least, when FILE refers to a symbolic link, the
58 chmod call below will change the permissions of the symbolic link
59 - which is undesired - and on many file systems (ext4, btrfs, jfs,
60 xfs, ..., but not reiserfs) fail with error EOPNOTSUPP - which is
61 misleading. Therefore test for a symbolic link explicitly.
62 Use fstatat because fstat does not work on O_PATH descriptors
63 before Linux 3.6. */
64 struct stat st;
65 if (fstatat (fd, "", &st, AT_EMPTY_PATH) != 0)
66 {
67 int stat_errno = errno;
68 close (fd);
69 errno = stat_errno;
70 return -1;
71 }
72 if (S_ISLNK (st.st_mode))
73 {
74 close (fd);
75 errno = EOPNOTSUPP;
76 return -1;
77 }
78
79# if defined __linux__ || defined __ANDROID__
80 static char const fmt[] = "/proc/self/fd/%d";
81 char buf[sizeof fmt - sizeof "%d" + INT_BUFSIZE_BOUND (int)];
82 sprintf (buf, fmt, fd);
83 int chmod_result = chmod (buf, mode);
84 int chmod_errno = errno;
85 close (fd);
86 if (chmod_result == 0)
87 return chmod_result;
88 if (chmod_errno != ENOENT)
89 {
90 errno = chmod_errno;
91 return chmod_result;
92 }
93# endif
94 /* /proc is not mounted or would not work as in GNU/Linux. */
95
96#elif HAVE_LSTAT
97 struct stat st;
98 int lstat_result = lstat (file, &st);
99 if (lstat_result != 0)
100 return lstat_result;
101 if (S_ISLNK (st.st_mode))
102 {
103 errno = EOPNOTSUPP;
104 return -1;
105 }
106#endif
107
108 /* Fall back on chmod, despite a possible race. */
109 return chmod (file, mode);
110}
diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index 0069c1744dc..8f00317c2b0 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -409,7 +409,8 @@ has no effect on MS-Windows."
409 (set-file-modes 409 (set-file-modes
410 file 410 file
411 (if num-modes num-modes 411 (if num-modes num-modes
412 (file-modes-symbolic-to-number modes (file-modes file))))) 412 (file-modes-symbolic-to-number modes (file-modes file 'nofollow)))
413 'nofollow))
413 (dired-do-redisplay arg))) 414 (dired-do-redisplay arg)))
414 415
415;;;###autoload 416;;;###autoload
diff --git a/lisp/doc-view.el b/lisp/doc-view.el
index 3788d797258..8b3d5527f08 100644
--- a/lisp/doc-view.el
+++ b/lisp/doc-view.el
@@ -683,8 +683,6 @@ at the top edge of the page moves to the previous page."
683 ;; time-window of loose permissions otherwise. 683 ;; time-window of loose permissions otherwise.
684 (with-file-modes #o0700 (make-directory dir)) 684 (with-file-modes #o0700 (make-directory dir))
685 (file-already-exists 685 (file-already-exists
686 (when (file-symlink-p dir)
687 (error "Danger: %s points to a symbolic link" dir))
688 ;; In case it was created earlier with looser rights. 686 ;; In case it was created earlier with looser rights.
689 ;; We could check the mode info returned by file-attributes, but it's 687 ;; We could check the mode info returned by file-attributes, but it's
690 ;; a pain to parse and it may not tell you what we want under 688 ;; a pain to parse and it may not tell you what we want under
@@ -694,7 +692,7 @@ at the top edge of the page moves to the previous page."
694 ;; sure we have write-access to the directory and that we own it, thus 692 ;; sure we have write-access to the directory and that we own it, thus
695 ;; closing a bunch of security holes. 693 ;; closing a bunch of security holes.
696 (condition-case error 694 (condition-case error
697 (set-file-modes dir #o0700) 695 (set-file-modes dir #o0700 'nofollow)
698 (file-error 696 (file-error
699 (error 697 (error
700 (format "Unable to use temporary directory %s: %s" 698 (format "Unable to use temporary directory %s: %s"
diff --git a/lisp/emacs-lisp/autoload.el b/lisp/emacs-lisp/autoload.el
index 785e350e0e5..e9f76583272 100644
--- a/lisp/emacs-lisp/autoload.el
+++ b/lisp/emacs-lisp/autoload.el
@@ -895,7 +895,7 @@ FILE's modification time."
895 (cons (lambda () (ignore-errors (delete-file tempfile))) 895 (cons (lambda () (ignore-errors (delete-file tempfile)))
896 kill-emacs-hook))) 896 kill-emacs-hook)))
897 (unless (= temp-modes desired-modes) 897 (unless (= temp-modes desired-modes)
898 (set-file-modes tempfile desired-modes)) 898 (set-file-modes tempfile desired-modes 'nofollow))
899 (write-region (point-min) (point-max) tempfile nil 1) 899 (write-region (point-min) (point-max) tempfile nil 1)
900 (backup-buffer) 900 (backup-buffer)
901 (rename-file tempfile buffer-file-name t)) 901 (rename-file tempfile buffer-file-name t))
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index fce5e4aed6d..24a36393b2e 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -2008,7 +2008,7 @@ The value is non-nil if there were no errors, nil if errors."
2008 (delete-file tempfile))) 2008 (delete-file tempfile)))
2009 kill-emacs-hook))) 2009 kill-emacs-hook)))
2010 (unless (= temp-modes desired-modes) 2010 (unless (= temp-modes desired-modes)
2011 (set-file-modes tempfile desired-modes)) 2011 (set-file-modes tempfile desired-modes 'nofollow))
2012 (write-region (point-min) (point-max) tempfile nil 1) 2012 (write-region (point-min) (point-max) tempfile nil 1)
2013 ;; This has the intentional side effect that any 2013 ;; This has the intentional side effect that any
2014 ;; hard-links to target-file continue to 2014 ;; hard-links to target-file continue to
diff --git a/lisp/eshell/em-pred.el b/lisp/eshell/em-pred.el
index 04bf3ff8998..7219af45f54 100644
--- a/lisp/eshell/em-pred.el
+++ b/lisp/eshell/em-pred.el
@@ -478,7 +478,7 @@ that `ls -l' will show in the first column of its display."
478(defsubst eshell-pred-file-mode (mode) 478(defsubst eshell-pred-file-mode (mode)
479 "Return a test which tests that MODE pertains to the file." 479 "Return a test which tests that MODE pertains to the file."
480 `(lambda (file) 480 `(lambda (file)
481 (let ((modes (file-modes file))) 481 (let ((modes (file-modes file 'nofollow)))
482 (if modes 482 (if modes
483 (logand ,mode modes))))) 483 (logand ,mode modes)))))
484 484
diff --git a/lisp/files.el b/lisp/files.el
index 683f4a8ce7c..2e7694d7677 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -4672,6 +4672,7 @@ BACKUPNAME is the backup file name, which is the old file renamed."
4672 ;; Create temp files with strict access rights. It's easy to 4672 ;; Create temp files with strict access rights. It's easy to
4673 ;; loosen them later, whereas it's impossible to close the 4673 ;; loosen them later, whereas it's impossible to close the
4674 ;; time-window of loose permissions otherwise. 4674 ;; time-window of loose permissions otherwise.
4675 (let (nofollow-flag)
4675 (with-file-modes ?\700 4676 (with-file-modes ?\700
4676 (when (condition-case nil 4677 (when (condition-case nil
4677 ;; Try to overwrite old backup first. 4678 ;; Try to overwrite old backup first.
@@ -4682,6 +4683,7 @@ BACKUPNAME is the backup file name, which is the old file renamed."
4682 (when (file-exists-p to-name) 4683 (when (file-exists-p to-name)
4683 (delete-file to-name)) 4684 (delete-file to-name))
4684 (copy-file from-name to-name nil t t) 4685 (copy-file from-name to-name nil t t)
4686 (setq nofollow-flag 'nofollow)
4685 nil) 4687 nil)
4686 (file-already-exists t)) 4688 (file-already-exists t))
4687 ;; The file was somehow created by someone else between 4689 ;; The file was somehow created by someone else between
@@ -4694,7 +4696,7 @@ BACKUPNAME is the backup file name, which is the old file renamed."
4694 (with-demoted-errors 4696 (with-demoted-errors
4695 (set-file-extended-attributes to-name extended-attributes))) 4697 (set-file-extended-attributes to-name extended-attributes)))
4696 (and modes 4698 (and modes
4697 (set-file-modes to-name (logand modes #o1777))))) 4699 (set-file-modes to-name (logand modes #o1777) nofollow-flag)))))
4698 4700
4699(defvar file-name-version-regexp 4701(defvar file-name-version-regexp
4700 "\\(?:~\\|\\.~[-[:alnum:]:#@^._]+\\(?:~[[:digit:]]+\\)?~\\)" 4702 "\\(?:~\\|\\.~[-[:alnum:]:#@^._]+\\(?:~[[:digit:]]+\\)?~\\)"
@@ -5900,7 +5902,8 @@ into NEWNAME instead."
5900 ;; If default-directory is a remote directory, make sure we find its 5902 ;; If default-directory is a remote directory, make sure we find its
5901 ;; copy-directory handler. 5903 ;; copy-directory handler.
5902 (let ((handler (or (find-file-name-handler directory 'copy-directory) 5904 (let ((handler (or (find-file-name-handler directory 'copy-directory)
5903 (find-file-name-handler newname 'copy-directory)))) 5905 (find-file-name-handler newname 'copy-directory)))
5906 (follow parents))
5904 (if handler 5907 (if handler
5905 (funcall handler 'copy-directory directory 5908 (funcall handler 'copy-directory directory
5906 newname keep-time parents copy-contents) 5909 newname keep-time parents copy-contents)
@@ -5920,7 +5923,8 @@ into NEWNAME instead."
5920 (or parents (not (file-directory-p newname))) 5923 (or parents (not (file-directory-p newname)))
5921 (setq newname (concat newname 5924 (setq newname (concat newname
5922 (file-name-nondirectory directory)))) 5925 (file-name-nondirectory directory))))
5923 (make-directory (directory-file-name newname) parents))) 5926 (make-directory (directory-file-name newname) parents))
5927 (t (setq follow t)))
5924 5928
5925 ;; Copy recursively. 5929 ;; Copy recursively.
5926 (dolist (file 5930 (dolist (file
@@ -5941,7 +5945,7 @@ into NEWNAME instead."
5941 (let ((modes (file-modes directory)) 5945 (let ((modes (file-modes directory))
5942 (times (and keep-time (file-attribute-modification-time 5946 (times (and keep-time (file-attribute-modification-time
5943 (file-attributes directory))))) 5947 (file-attributes directory)))))
5944 (if modes (set-file-modes newname modes)) 5948 (if modes (set-file-modes newname modes (unless follow 'nofollow)))
5945 (if times (set-file-times newname times)))))) 5949 (if times (set-file-times newname times))))))
5946 5950
5947 5951
diff --git a/lisp/gnus/gnus-util.el b/lisp/gnus/gnus-util.el
index eb0fd2522d3..83a85161aa0 100644
--- a/lisp/gnus/gnus-util.el
+++ b/lisp/gnus/gnus-util.el
@@ -1601,10 +1601,10 @@ empty directories from OLD-PATH."
1601 (file-truename 1601 (file-truename
1602 (concat old-dir ".."))))))))) 1602 (concat old-dir "..")))))))))
1603 1603
1604(defun gnus-set-file-modes (filename mode) 1604(defun gnus-set-file-modes (filename mode &optional flag)
1605 "Wrapper for set-file-modes." 1605 "Wrapper for set-file-modes."
1606 (ignore-errors 1606 (ignore-errors
1607 (set-file-modes filename mode))) 1607 (set-file-modes filename mode flag)))
1608 1608
1609(defun gnus-rescale-image (image size) 1609(defun gnus-rescale-image (image size)
1610 "Rescale IMAGE to SIZE if possible. 1610 "Rescale IMAGE to SIZE if possible.
diff --git a/lisp/gnus/mail-source.el b/lisp/gnus/mail-source.el
index f5b68789b85..acf35a376a9 100644
--- a/lisp/gnus/mail-source.el
+++ b/lisp/gnus/mail-source.el
@@ -695,7 +695,7 @@ Deleting old (> %s day(s)) incoming mail file `%s'." diff bfile)
695 mail-source-movemail-program 695 mail-source-movemail-program
696 nil errors nil from to))))) 696 nil errors nil from to)))))
697 (when (file-exists-p to) 697 (when (file-exists-p to)
698 (set-file-modes to mail-source-default-file-modes)) 698 (set-file-modes to mail-source-default-file-modes 'nofollow))
699 (if (and (or (not (buffer-modified-p errors)) 699 (if (and (or (not (buffer-modified-p errors))
700 (zerop (buffer-size errors))) 700 (zerop (buffer-size errors)))
701 (and (numberp result) 701 (and (numberp result)
diff --git a/lisp/gnus/mm-decode.el b/lisp/gnus/mm-decode.el
index 2dab278b373..96695aabfde 100644
--- a/lisp/gnus/mm-decode.el
+++ b/lisp/gnus/mm-decode.el
@@ -948,7 +948,7 @@ external if displayed external."
948 ;; The file is deleted after the viewer exists. If the users edits 948 ;; The file is deleted after the viewer exists. If the users edits
949 ;; the file, changes will be lost. Set file to read-only to make it 949 ;; the file, changes will be lost. Set file to read-only to make it
950 ;; clear. 950 ;; clear.
951 (set-file-modes file #o400) 951 (set-file-modes file #o400 'nofollow)
952 (message "Viewing with %s" method) 952 (message "Viewing with %s" method)
953 (cond 953 (cond
954 (needsterm 954 (needsterm
diff --git a/lisp/gnus/nnmail.el b/lisp/gnus/nnmail.el
index 6e01b5c4d0b..93e4b0e7a8f 100644
--- a/lisp/gnus/nnmail.el
+++ b/lisp/gnus/nnmail.el
@@ -1958,7 +1958,7 @@ If TIME is nil, then return the cutoff time for oldness instead."
1958 (let ((coding-system-for-write nnmail-file-coding-system) 1958 (let ((coding-system-for-write nnmail-file-coding-system)
1959 (file-name-coding-system nnmail-pathname-coding-system)) 1959 (file-name-coding-system nnmail-pathname-coding-system))
1960 (write-region start end filename append visit lockname) 1960 (write-region start end filename append visit lockname)
1961 (set-file-modes filename nnmail-default-file-modes))) 1961 (set-file-modes filename nnmail-default-file-modes 'nofollow)))
1962 1962
1963;;; 1963;;;
1964;;; Status functions 1964;;; Status functions
diff --git a/lisp/net/ange-ftp.el b/lisp/net/ange-ftp.el
index f28394260dd..e2d4d7dd057 100644
--- a/lisp/net/ange-ftp.el
+++ b/lisp/net/ange-ftp.el
@@ -4740,7 +4740,8 @@ NEWNAME should be the name to give the new compressed or uncompressed file.")
4740 (setq ange-ftp-ls-cache-file nil) ;Stop confusing Dired. 4740 (setq ange-ftp-ls-cache-file nil) ;Stop confusing Dired.
4741 0) 4741 0)
4742 4742
4743(defun ange-ftp-set-file-modes (filename mode) 4743(defun ange-ftp-set-file-modes (filename mode &optional flag)
4744 flag ;; FIXME: Support 'nofollow'.
4744 (ange-ftp-call-chmod (list (format "%o" mode) filename))) 4745 (ange-ftp-call-chmod (list (format "%o" mode) filename)))
4745 4746
4746(defun ange-ftp-make-symbolic-link (&rest _arguments) 4747(defun ange-ftp-make-symbolic-link (&rest _arguments)
diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el
index aa7fe147c20..96ef95dbe30 100644
--- a/lisp/net/tramp-adb.el
+++ b/lisp/net/tramp-adb.el
@@ -591,7 +591,8 @@ Emacs dired can't find files."
591 (ignore-errors (delete-file tmpfile)) 591 (ignore-errors (delete-file tmpfile))
592 (tramp-error 592 (tramp-error
593 v 'file-error "Cannot make local copy of file `%s'" filename)) 593 v 'file-error "Cannot make local copy of file `%s'" filename))
594 (set-file-modes tmpfile (logior (or (file-modes filename) 0) #o0400))) 594 (set-file-modes tmpfile (logior (or (file-modes filename) 0) #o0400)
595 'nofollow))
595 tmpfile))) 596 tmpfile)))
596 597
597(defun tramp-adb-handle-file-writable-p (filename) 598(defun tramp-adb-handle-file-writable-p (filename)
@@ -636,7 +637,8 @@ But handle the case, if the \"test\" command is not available."
636 (tmpfile (tramp-compat-make-temp-file filename))) 637 (tmpfile (tramp-compat-make-temp-file filename)))
637 (when (and append (file-exists-p filename)) 638 (when (and append (file-exists-p filename))
638 (copy-file filename tmpfile 'ok) 639 (copy-file filename tmpfile 'ok)
639 (set-file-modes tmpfile (logior (or (file-modes tmpfile) 0) #o0600))) 640 (set-file-modes tmpfile (logior (or (file-modes tmpfile) 0) #o0600)
641 'nofollow))
640 (tramp-run-real-handler 642 (tramp-run-real-handler
641 #'write-region (list start end tmpfile append 'no-message lockname)) 643 #'write-region (list start end tmpfile append 'no-message lockname))
642 (with-tramp-progress-reporter 644 (with-tramp-progress-reporter
@@ -665,8 +667,9 @@ But handle the case, if the \"test\" command is not available."
665 (tramp-message v 0 "Wrote %s" filename)) 667 (tramp-message v 0 "Wrote %s" filename))
666 (run-hooks 'tramp-handle-write-region-hook)))) 668 (run-hooks 'tramp-handle-write-region-hook))))
667 669
668(defun tramp-adb-handle-set-file-modes (filename mode) 670(defun tramp-adb-handle-set-file-modes (filename mode &optional flag)
669 "Like `set-file-modes' for Tramp files." 671 "Like `set-file-modes' for Tramp files."
672 flag ;; FIXME: Support 'nofollow'.
670 (with-parsed-tramp-file-name filename nil 673 (with-parsed-tramp-file-name filename nil
671 (tramp-flush-file-properties v localname) 674 (tramp-flush-file-properties v localname)
672 (tramp-adb-send-command-and-check v (format "chmod %o %s" mode localname)))) 675 (tramp-adb-send-command-and-check v (format "chmod %o %s" mode localname))))
diff --git a/lisp/net/tramp-gvfs.el b/lisp/net/tramp-gvfs.el
index 762c4fe4b3b..79835804bc0 100644
--- a/lisp/net/tramp-gvfs.el
+++ b/lisp/net/tramp-gvfs.el
@@ -1562,12 +1562,12 @@ If FILE-SYSTEM is non-nil, return file system attributes."
1562 (tramp-run-real-handler 1562 (tramp-run-real-handler
1563 #'rename-file (list filename newname ok-if-already-exists)))) 1563 #'rename-file (list filename newname ok-if-already-exists))))
1564 1564
1565(defun tramp-gvfs-handle-set-file-modes (filename mode) 1565(defun tramp-gvfs-handle-set-file-modes (filename mode &optional flag)
1566 "Like `set-file-modes' for Tramp files." 1566 "Like `set-file-modes' for Tramp files."
1567 (with-parsed-tramp-file-name filename nil 1567 (with-parsed-tramp-file-name filename nil
1568 (tramp-flush-file-properties v localname) 1568 (tramp-flush-file-properties v localname)
1569 (tramp-gvfs-send-command 1569 (tramp-gvfs-send-command
1570 v "gvfs-set-attribute" "-t" "uint32" 1570 v "gvfs-set-attribute" (if flag "-nt" "-t") "uint32"
1571 (tramp-gvfs-url-file-name (tramp-make-tramp-file-name v)) 1571 (tramp-gvfs-url-file-name (tramp-make-tramp-file-name v))
1572 "unix::mode" (number-to-string mode)))) 1572 "unix::mode" (number-to-string mode))))
1573 1573
diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el
index 5a3abc31ea6..f31d3615884 100644
--- a/lisp/net/tramp-sh.el
+++ b/lisp/net/tramp-sh.el
@@ -1478,10 +1478,11 @@ of."
1478 ;; only if that agrees with the buffer's record. 1478 ;; only if that agrees with the buffer's record.
1479 (t (tramp-compat-time-equal-p mt tramp-time-doesnt-exist))))))))) 1479 (t (tramp-compat-time-equal-p mt tramp-time-doesnt-exist)))))))))
1480 1480
1481(defun tramp-sh-handle-set-file-modes (filename mode) 1481(defun tramp-sh-handle-set-file-modes (filename mode &optional flag)
1482 "Like `set-file-modes' for Tramp files." 1482 "Like `set-file-modes' for Tramp files."
1483 (with-parsed-tramp-file-name filename nil 1483 (with-parsed-tramp-file-name filename nil
1484 (tramp-flush-file-properties v localname) 1484 (tramp-flush-file-properties v localname)
1485 flag ;; FIXME: Support 'nofollow'.
1485 ;; FIXME: extract the proper text from chmod's stderr. 1486 ;; FIXME: extract the proper text from chmod's stderr.
1486 (tramp-barf-unless-okay 1487 (tramp-barf-unless-okay
1487 v 1488 v
@@ -2279,7 +2280,7 @@ the uid and gid from FILENAME."
2279 ;; We must change the ownership as local user. 2280 ;; We must change the ownership as local user.
2280 ;; Since this does not work reliable, we also 2281 ;; Since this does not work reliable, we also
2281 ;; give read permissions. 2282 ;; give read permissions.
2282 (set-file-modes tmpfile #o0777) 2283 (set-file-modes tmpfile #o0777 'nofollow)
2283 (tramp-set-file-uid-gid 2284 (tramp-set-file-uid-gid
2284 tmpfile 2285 tmpfile
2285 (tramp-get-remote-uid v 'integer) 2286 (tramp-get-remote-uid v 'integer)
@@ -3221,7 +3222,8 @@ STDERR can also be a file name."
3221 (delete-file tmpfile2))))) 3222 (delete-file tmpfile2)))))
3222 3223
3223 ;; Set proper permissions. 3224 ;; Set proper permissions.
3224 (set-file-modes tmpfile (tramp-default-file-modes filename)) 3225 (set-file-modes tmpfile (tramp-default-file-modes filename)
3226 'nofollow)
3225 ;; Set local user ownership. 3227 ;; Set local user ownership.
3226 (tramp-set-file-uid-gid tmpfile)) 3228 (tramp-set-file-uid-gid tmpfile))
3227 3229
@@ -3320,7 +3322,7 @@ STDERR can also be a file name."
3320 ;; handles permissions. 3322 ;; handles permissions.
3321 ;; Ensure that it is still readable. 3323 ;; Ensure that it is still readable.
3322 (when modes 3324 (when modes
3323 (set-file-modes tmpfile (logior (or modes 0) #o0400))) 3325 (set-file-modes tmpfile (logior (or modes 0) #o0400) 'nofollow))
3324 3326
3325 ;; This is a bit lengthy due to the different methods 3327 ;; This is a bit lengthy due to the different methods
3326 ;; possible for file transfer. First, we check whether the 3328 ;; possible for file transfer. First, we check whether the
diff --git a/lisp/net/tramp-smb.el b/lisp/net/tramp-smb.el
index f02be394a7b..95505ea101f 100644
--- a/lisp/net/tramp-smb.el
+++ b/lisp/net/tramp-smb.el
@@ -1464,8 +1464,9 @@ component is used as the target of the symlink."
1464 (tramp-flush-connection-property v "process-name") 1464 (tramp-flush-connection-property v "process-name")
1465 (tramp-flush-connection-property v "process-buffer"))))))) 1465 (tramp-flush-connection-property v "process-buffer")))))))
1466 1466
1467(defun tramp-smb-handle-set-file-modes (filename mode) 1467(defun tramp-smb-handle-set-file-modes (filename mode &optional flag)
1468 "Like `set-file-modes' for Tramp files." 1468 "Like `set-file-modes' for Tramp files."
1469 flag ;; FIXME: Support 'nofollow'.
1469 (with-parsed-tramp-file-name filename nil 1470 (with-parsed-tramp-file-name filename nil
1470 (when (tramp-smb-get-cifs-capabilities v) 1471 (when (tramp-smb-get-cifs-capabilities v)
1471 (tramp-flush-file-properties v localname) 1472 (tramp-flush-file-properties v localname)
diff --git a/lisp/net/tramp-sudoedit.el b/lisp/net/tramp-sudoedit.el
index f258ad6b931..4654d633fab 100644
--- a/lisp/net/tramp-sudoedit.el
+++ b/lisp/net/tramp-sudoedit.el
@@ -463,8 +463,9 @@ the result will be a local, non-Tramp, file name."
463 (tramp-sudoedit-send-command 463 (tramp-sudoedit-send-command
464 v "test" "-r" (tramp-compat-file-name-unquote localname))))) 464 v "test" "-r" (tramp-compat-file-name-unquote localname)))))
465 465
466(defun tramp-sudoedit-handle-set-file-modes (filename mode) 466(defun tramp-sudoedit-handle-set-file-modes (filename mode &optional flag)
467 "Like `set-file-modes' for Tramp files." 467 "Like `set-file-modes' for Tramp files."
468 flag ;; FIXME: Support 'nofollow'.
468 (with-parsed-tramp-file-name filename nil 469 (with-parsed-tramp-file-name filename nil
469 (tramp-flush-file-properties v localname) 470 (tramp-flush-file-properties v localname)
470 (unless (tramp-sudoedit-send-command 471 (unless (tramp-sudoedit-send-command
@@ -735,7 +736,8 @@ ID-FORMAT valid values are `string' and `integer'."
735 (file-attributes filename 'integer)) 736 (file-attributes filename 'integer))
736 gid)) 737 gid))
737 (tramp-set-file-uid-gid filename uid gid)) 738 (tramp-set-file-uid-gid filename uid gid))
738 (set-file-modes filename modes))))) 739 (set-file-modes filename modes
740 (when (eq mustbenew 'excl) 'nofollow))))))
739 741
740 742
741;; Internal functions. 743;; Internal functions.
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index 409e1f7499a..64acaa95d47 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -3179,10 +3179,13 @@ User is always nil."
3179 (copy-file filename tmpfile 'ok-if-already-exists 'keep-time) 3179 (copy-file filename tmpfile 'ok-if-already-exists 'keep-time)
3180 tmpfile))) 3180 tmpfile)))
3181 3181
3182(defun tramp-handle-file-modes (filename) 3182(defun tramp-handle-file-modes (filename &optional flag)
3183 "Like `file-modes' for Tramp files." 3183 "Like `file-modes' for Tramp files."
3184 (when-let ((attrs (file-attributes (or (file-truename filename) filename)))) 3184 (when-let ((attrs (file-attributes filename)))
3185 (tramp-mode-string-to-int (tramp-compat-file-attribute-modes attrs)))) 3185 (let ((mode-string (tramp-compat-file-attribute-modes attrs)))
3186 (if (and (not flag) (eq ?l (aref mode-string 0)))
3187 (tramp-handle-file-modes (file-chase-links filename) 'nofollow)
3188 (tramp-mode-string-to-int mode-string)))))
3186 3189
3187;; Localname manipulation functions that grok Tramp localnames... 3190;; Localname manipulation functions that grok Tramp localnames...
3188(defun tramp-handle-file-name-as-directory (file) 3191(defun tramp-handle-file-name-as-directory (file)
@@ -3884,7 +3887,7 @@ of."
3884 ;; renamed to the backup file. This case `save-buffer' 3887 ;; renamed to the backup file. This case `save-buffer'
3885 ;; handles permissions. 3888 ;; handles permissions.
3886 ;; Ensure that it is still readable. 3889 ;; Ensure that it is still readable.
3887 (set-file-modes tmpfile (logior (or modes 0) #o0400)) 3890 (set-file-modes tmpfile (logior (or modes 0) #o0400) 'nofollow)
3888 ;; We say `no-message' here because we don't want the visited file 3891 ;; We say `no-message' here because we don't want the visited file
3889 ;; modtime data to be clobbered from the temp file. We call 3892 ;; modtime data to be clobbered from the temp file. We call
3890 ;; `set-visited-file-modtime' ourselves later on. 3893 ;; `set-visited-file-modtime' ourselves later on.
@@ -4664,7 +4667,7 @@ Return the local name of the temporary file."
4664 (setq result nil) 4667 (setq result nil)
4665 ;; This creates the file by side effect. 4668 ;; This creates the file by side effect.
4666 (set-file-times result) 4669 (set-file-times result)
4667 (set-file-modes result #o0700))) 4670 (set-file-modes result #o0700 'nofollow)))
4668 4671
4669 ;; Return the local part. 4672 ;; Return the local part.
4670 (tramp-file-local-name result))) 4673 (tramp-file-local-name result)))
diff --git a/lisp/server.el b/lisp/server.el
index e6d8b1783c9..18612181477 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -563,7 +563,7 @@ See variable `server-auth-dir' for details."
563 (format "it is not owned by you (owner = %s (%d))" 563 (format "it is not owned by you (owner = %s (%d))"
564 (user-full-name uid) uid)) 564 (user-full-name uid) uid))
565 (w32 nil) ; on NTFS? 565 (w32 nil) ; on NTFS?
566 ((let ((modes (file-modes dir))) 566 ((let ((modes (file-modes dir 'nofollow)))
567 (unless (zerop (logand (or modes 0) #o077)) 567 (unless (zerop (logand (or modes 0) #o077))
568 (format "it is accessible by others (%03o)" modes)))) 568 (format "it is accessible by others (%03o)" modes))))
569 (t nil)))) 569 (t nil))))
diff --git a/lisp/url/url-util.el b/lisp/url/url-util.el
index 645011a5783..6dd7a9c2aac 100644
--- a/lisp/url/url-util.el
+++ b/lisp/url/url-util.el
@@ -615,9 +615,7 @@ Creates FILE and its parent directories if they do not exist."
615 (with-temp-buffer 615 (with-temp-buffer
616 (write-region (point-min) (point-max) file nil 'silent nil 'excl))) 616 (write-region (point-min) (point-max) file nil 'silent nil 'excl)))
617 (file-already-exists 617 (file-already-exists
618 (if (file-symlink-p file) 618 (set-file-modes file #o0600 'nofollow))))
619 (error "Danger: `%s' is a symbolic link" file))
620 (set-file-modes file #o0600))))
621 619
622(autoload 'puny-encode-domain "puny") 620(autoload 'puny-encode-domain "puny")
623(autoload 'url-domsuf-cookie-allowed-p "url-domsuf") 621(autoload 'url-domsuf-cookie-allowed-p "url-domsuf")
diff --git a/m4/fchmodat.m4 b/m4/fchmodat.m4
new file mode 100644
index 00000000000..e3f2f048162
--- /dev/null
+++ b/m4/fchmodat.m4
@@ -0,0 +1,82 @@
1# fchmodat.m4 serial 4
2dnl Copyright (C) 2004-2020 Free Software Foundation, Inc.
3dnl This file is free software; the Free Software Foundation
4dnl gives unlimited permission to copy and/or distribute it,
5dnl with or without modifications, as long as this notice is preserved.
6
7# Written by Jim Meyering.
8
9AC_DEFUN([gl_FUNC_FCHMODAT],
10[
11 AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS])
12 AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
13 AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
14 AC_CHECK_FUNCS_ONCE([fchmodat lchmod])
15 if test $ac_cv_func_fchmodat != yes; then
16 HAVE_FCHMODAT=0
17 else
18 AC_CACHE_CHECK(
19 [whether fchmodat+AT_SYMLINK_NOFOLLOW works on non-symlinks],
20 [gl_cv_func_fchmodat_works],
21 [dnl This test fails on GNU/Linux with glibc 2.31 (but not on
22 dnl GNU/kFreeBSD nor GNU/Hurd) and Cygwin 2.9.
23 AC_RUN_IFELSE(
24 [AC_LANG_PROGRAM(
25 [
26 AC_INCLUDES_DEFAULT[
27 #include <fcntl.h>
28 #ifndef S_IRUSR
29 #define S_IRUSR 0400
30 #endif
31 #ifndef S_IWUSR
32 #define S_IWUSR 0200
33 #endif
34 #ifndef S_IRWXU
35 #define S_IRWXU 0700
36 #endif
37 #ifndef S_IRWXG
38 #define S_IRWXG 0070
39 #endif
40 #ifndef S_IRWXO
41 #define S_IRWXO 0007
42 #endif
43 ]],
44 [[
45 int permissive = S_IRWXU | S_IRWXG | S_IRWXO;
46 int desired = S_IRUSR | S_IWUSR;
47 static char const f[] = "conftest.fchmodat";
48 struct stat st;
49 if (creat (f, permissive) < 0)
50 return 1;
51 if (fchmodat (AT_FDCWD, f, desired, AT_SYMLINK_NOFOLLOW) != 0)
52 return 1;
53 if (stat (f, &st) != 0)
54 return 1;
55 return ! ((st.st_mode & permissive) == desired);
56 ]])],
57 [gl_cv_func_fchmodat_works=yes],
58 [gl_cv_func_fchmodat_works=no],
59 [case "$host_os" in
60 dnl Guess no on Linux with glibc and Cygwin, yes otherwise.
61 linux-gnu* | cygwin*) gl_cv_func_fchmodat_works="guessing no" ;;
62 *) gl_cv_func_fchmodat_works="$gl_cross_guess_normal" ;;
63 esac
64 ])
65 rm -f conftest.fchmodat])
66 case $gl_cv_func_fchmodat_works in
67 *yes) ;;
68 *)
69 AC_DEFINE([NEED_FCHMODAT_NONSYMLINK_FIX], [1],
70 [Define to 1 if fchmodat+AT_SYMLINK_NOFOLLOW does not work right on non-symlinks.])
71 REPLACE_FCHMODAT=1
72 ;;
73 esac
74 fi
75])
76
77# Prerequisites of lib/fchmodat.c.
78AC_DEFUN([gl_PREREQ_FCHMODAT],
79[
80 AC_CHECK_FUNCS_ONCE([lchmod])
81 :
82])
diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4
index fea32b544f9..1465ce811b8 100644
--- a/m4/gnulib-comp.m4
+++ b/m4/gnulib-comp.m4
@@ -82,6 +82,7 @@ AC_DEFUN([gl_EARLY],
82 # Code from module extensions: 82 # Code from module extensions:
83 # Code from module extern-inline: 83 # Code from module extern-inline:
84 # Code from module faccessat: 84 # Code from module faccessat:
85 # Code from module fchmodat:
85 # Code from module fcntl: 86 # Code from module fcntl:
86 # Code from module fcntl-h: 87 # Code from module fcntl-h:
87 # Code from module fdopendir: 88 # Code from module fdopendir:
@@ -111,6 +112,7 @@ AC_DEFUN([gl_EARLY],
111 # Code from module inttypes-incomplete: 112 # Code from module inttypes-incomplete:
112 # Code from module largefile: 113 # Code from module largefile:
113 AC_REQUIRE([AC_SYS_LARGEFILE]) 114 AC_REQUIRE([AC_SYS_LARGEFILE])
115 # Code from module lchmod:
114 # Code from module libc-config: 116 # Code from module libc-config:
115 # Code from module limits-h: 117 # Code from module limits-h:
116 # Code from module localtime-buffer: 118 # Code from module localtime-buffer:
@@ -255,6 +257,12 @@ AC_DEFUN([gl_INIT],
255 fi 257 fi
256 gl_MODULE_INDICATOR([faccessat]) 258 gl_MODULE_INDICATOR([faccessat])
257 gl_UNISTD_MODULE_INDICATOR([faccessat]) 259 gl_UNISTD_MODULE_INDICATOR([faccessat])
260 gl_FUNC_FCHMODAT
261 if test $HAVE_FCHMODAT = 0 || test $REPLACE_FCHMODAT = 1; then
262 AC_LIBOBJ([fchmodat])
263 gl_PREREQ_FCHMODAT
264 fi
265 gl_SYS_STAT_MODULE_INDICATOR([fchmodat])
258 gl_FUNC_FCNTL 266 gl_FUNC_FCNTL
259 if test $HAVE_FCNTL = 0 || test $REPLACE_FCNTL = 1; then 267 if test $HAVE_FCNTL = 0 || test $REPLACE_FCNTL = 1; then
260 AC_LIBOBJ([fcntl]) 268 AC_LIBOBJ([fcntl])
@@ -468,6 +476,7 @@ AC_DEFUN([gl_INIT],
468 gl_gnulib_enabled_getgroups=false 476 gl_gnulib_enabled_getgroups=false
469 gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false 477 gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false
470 gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=false 478 gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=false
479 gl_gnulib_enabled_lchmod=false
471 gl_gnulib_enabled_21ee726a3540c09237a8e70c0baf7467=false 480 gl_gnulib_enabled_21ee726a3540c09237a8e70c0baf7467=false
472 gl_gnulib_enabled_2049e887c7e5308faad27b3f894bb8c9=false 481 gl_gnulib_enabled_2049e887c7e5308faad27b3f894bb8c9=false
473 gl_gnulib_enabled_malloca=false 482 gl_gnulib_enabled_malloca=false
@@ -569,6 +578,18 @@ AC_DEFUN([gl_INIT],
569 fi 578 fi
570 fi 579 fi
571 } 580 }
581 func_gl_gnulib_m4code_lchmod ()
582 {
583 if ! $gl_gnulib_enabled_lchmod; then
584 gl_FUNC_LCHMOD
585 if test $HAVE_LCHMOD = 0; then
586 AC_LIBOBJ([lchmod])
587 gl_PREREQ_LCHMOD
588 fi
589 gl_SYS_STAT_MODULE_INDICATOR([lchmod])
590 gl_gnulib_enabled_lchmod=true
591 fi
592 }
572 func_gl_gnulib_m4code_21ee726a3540c09237a8e70c0baf7467 () 593 func_gl_gnulib_m4code_21ee726a3540c09237a8e70c0baf7467 ()
573 { 594 {
574 if ! $gl_gnulib_enabled_21ee726a3540c09237a8e70c0baf7467; then 595 if ! $gl_gnulib_enabled_21ee726a3540c09237a8e70c0baf7467; then
@@ -660,6 +681,15 @@ AC_DEFUN([gl_INIT],
660 if test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1; then 681 if test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1; then
661 func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 682 func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7
662 fi 683 fi
684 if test $HAVE_FCHMODAT = 0; then
685 func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
686 fi
687 if test $HAVE_FCHMODAT = 0; then
688 func_gl_gnulib_m4code_lchmod
689 fi
690 if test $HAVE_FCHMODAT = 0; then
691 func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7
692 fi
663 if test $HAVE_FCNTL = 0 || test $REPLACE_FCNTL = 1; then 693 if test $HAVE_FCNTL = 0 || test $REPLACE_FCNTL = 1; then
664 func_gl_gnulib_m4code_getdtablesize 694 func_gl_gnulib_m4code_getdtablesize
665 fi 695 fi
@@ -708,6 +738,7 @@ AC_DEFUN([gl_INIT],
708 AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups]) 738 AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups])
709 AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], [$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36]) 739 AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], [$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36])
710 AM_CONDITIONAL([gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1], [$gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1]) 740 AM_CONDITIONAL([gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1], [$gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1])
741 AM_CONDITIONAL([gl_GNULIB_ENABLED_lchmod], [$gl_gnulib_enabled_lchmod])
711 AM_CONDITIONAL([gl_GNULIB_ENABLED_21ee726a3540c09237a8e70c0baf7467], [$gl_gnulib_enabled_21ee726a3540c09237a8e70c0baf7467]) 742 AM_CONDITIONAL([gl_GNULIB_ENABLED_21ee726a3540c09237a8e70c0baf7467], [$gl_gnulib_enabled_21ee726a3540c09237a8e70c0baf7467])
712 AM_CONDITIONAL([gl_GNULIB_ENABLED_2049e887c7e5308faad27b3f894bb8c9], [$gl_gnulib_enabled_2049e887c7e5308faad27b3f894bb8c9]) 743 AM_CONDITIONAL([gl_GNULIB_ENABLED_2049e887c7e5308faad27b3f894bb8c9], [$gl_gnulib_enabled_2049e887c7e5308faad27b3f894bb8c9])
713 AM_CONDITIONAL([gl_GNULIB_ENABLED_malloca], [$gl_gnulib_enabled_malloca]) 744 AM_CONDITIONAL([gl_GNULIB_ENABLED_malloca], [$gl_gnulib_enabled_malloca])
@@ -908,6 +939,7 @@ AC_DEFUN([gl_FILE_LIST], [
908 lib/execinfo.in.h 939 lib/execinfo.in.h
909 lib/explicit_bzero.c 940 lib/explicit_bzero.c
910 lib/faccessat.c 941 lib/faccessat.c
942 lib/fchmodat.c
911 lib/fcntl.c 943 lib/fcntl.c
912 lib/fcntl.in.h 944 lib/fcntl.in.h
913 lib/fdopendir.c 945 lib/fdopendir.c
@@ -946,6 +978,7 @@ AC_DEFUN([gl_FILE_LIST], [
946 lib/ignore-value.h 978 lib/ignore-value.h
947 lib/intprops.h 979 lib/intprops.h
948 lib/inttypes.in.h 980 lib/inttypes.in.h
981 lib/lchmod.c
949 lib/libc-config.h 982 lib/libc-config.h
950 lib/limits.in.h 983 lib/limits.in.h
951 lib/localtime-buffer.c 984 lib/localtime-buffer.c
@@ -1058,6 +1091,7 @@ AC_DEFUN([gl_FILE_LIST], [
1058 m4/extensions.m4 1091 m4/extensions.m4
1059 m4/extern-inline.m4 1092 m4/extern-inline.m4
1060 m4/faccessat.m4 1093 m4/faccessat.m4
1094 m4/fchmodat.m4
1061 m4/fcntl-o.m4 1095 m4/fcntl-o.m4
1062 m4/fcntl.m4 1096 m4/fcntl.m4
1063 m4/fcntl_h.m4 1097 m4/fcntl_h.m4
@@ -1083,6 +1117,7 @@ AC_DEFUN([gl_FILE_LIST], [
1083 m4/include_next.m4 1117 m4/include_next.m4
1084 m4/inttypes.m4 1118 m4/inttypes.m4
1085 m4/largefile.m4 1119 m4/largefile.m4
1120 m4/lchmod.m4
1086 m4/limits-h.m4 1121 m4/limits-h.m4
1087 m4/localtime-buffer.m4 1122 m4/localtime-buffer.m4
1088 m4/lstat.m4 1123 m4/lstat.m4
diff --git a/m4/lchmod.m4 b/m4/lchmod.m4
new file mode 100644
index 00000000000..b9e8a97cb31
--- /dev/null
+++ b/m4/lchmod.m4
@@ -0,0 +1,31 @@
1#serial 7
2
3dnl Copyright (C) 2005-2006, 2008-2020 Free Software Foundation, Inc.
4dnl This file is free software; the Free Software Foundation
5dnl gives unlimited permission to copy and/or distribute it,
6dnl with or without modifications, as long as this notice is preserved.
7
8dnl From Paul Eggert.
9dnl Provide a replacement for lchmod on hosts that lack a working version.
10
11AC_DEFUN([gl_FUNC_LCHMOD],
12[
13 AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS])
14
15 dnl Persuade glibc <sys/stat.h> to declare lchmod().
16 AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
17
18 AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
19
20 AC_CHECK_FUNCS_ONCE([lchmod lstat])
21 if test "$ac_cv_func_lchmod" = no; then
22 HAVE_LCHMOD=0
23 fi
24])
25
26# Prerequisites of lib/lchmod.c.
27AC_DEFUN([gl_PREREQ_LCHMOD],
28[
29 AC_REQUIRE([AC_C_INLINE])
30 :
31])
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. */)
3332 return Qnil; 3332 return Qnil;
3333} 3333}
3334 3334
3335DEFUN ("file-modes", Ffile_modes, Sfile_modes, 1, 1, 0, 3335static int
3336symlink_nofollow_flag (Lisp_Object flag)
3337{
3338 /* For now, treat all non-nil FLAGs like 'nofollow'. */
3339 return !NILP (flag) ? AT_SYMLINK_NOFOLLOW : 0;
3340}
3341
3342DEFUN ("file-modes", Ffile_modes, Sfile_modes, 1, 2, 0,
3336 doc: /* Return mode bits of file named FILENAME, as an integer. 3343 doc: /* Return mode bits of file named FILENAME, as an integer.
3337Return nil if FILENAME does not exist. */) 3344Return nil if FILENAME does not exist. If optional FLAG is `nofollow',
3338 (Lisp_Object filename) 3345do not follow FILENAME if it is a symbolic link. */)
3346 (Lisp_Object filename, Lisp_Object flag)
3339{ 3347{
3340 struct stat st; 3348 struct stat st;
3349 int nofollow = symlink_nofollow_flag (flag);
3341 Lisp_Object absname = expand_and_dir_to_file (filename); 3350 Lisp_Object absname = expand_and_dir_to_file (filename);
3342 3351
3343 /* If the file name has special constructs in it, 3352 /* If the file name has special constructs in it,
3344 call the corresponding file name handler. */ 3353 call the corresponding file name handler. */
3345 Lisp_Object handler = Ffind_file_name_handler (absname, Qfile_modes); 3354 Lisp_Object handler = Ffind_file_name_handler (absname, Qfile_modes);
3346 if (!NILP (handler)) 3355 if (!NILP (handler))
3347 return call2 (handler, Qfile_modes, absname); 3356 return call3 (handler, Qfile_modes, absname, flag);
3348 3357
3349 if (emacs_fstatat (AT_FDCWD, SSDATA (ENCODE_FILE (absname)), &st, 0) != 0) 3358 char *fname = SSDATA (ENCODE_FILE (absname));
3359 if (emacs_fstatat (AT_FDCWD, fname, &st, nofollow) != 0)
3350 return file_attribute_errno (absname, errno); 3360 return file_attribute_errno (absname, errno);
3351 return make_fixnum (st.st_mode & 07777); 3361 return make_fixnum (st.st_mode & 07777);
3352} 3362}
3353 3363
3354DEFUN ("set-file-modes", Fset_file_modes, Sset_file_modes, 2, 2, 3364DEFUN ("set-file-modes", Fset_file_modes, Sset_file_modes, 2, 3,
3355 "(let ((file (read-file-name \"File: \"))) \ 3365 "(let ((file (read-file-name \"File: \"))) \
3356 (list file (read-file-modes nil file)))", 3366 (list file (read-file-modes nil file)))",
3357 doc: /* Set mode bits of file named FILENAME to MODE (an integer). 3367 doc: /* Set mode bits of file named FILENAME to MODE (an integer).
3358Only the 12 low bits of MODE are used. 3368Only the 12 low bits of MODE are used. If optional FLAG is `nofollow',
3369do not follow FILENAME if it is a symbolic link.
3359 3370
3360Interactively, mode bits are read by `read-file-modes', which accepts 3371Interactively, mode bits are read by `read-file-modes', which accepts
3361symbolic notation, like the `chmod' command from GNU Coreutils. */) 3372symbolic notation, like the `chmod' command from GNU Coreutils. */)
3362 (Lisp_Object filename, Lisp_Object mode) 3373 (Lisp_Object filename, Lisp_Object mode, Lisp_Object flag)
3363{ 3374{
3364 Lisp_Object absname, encoded_absname;
3365 Lisp_Object handler;
3366
3367 absname = Fexpand_file_name (filename, BVAR (current_buffer, directory));
3368 CHECK_FIXNUM (mode); 3375 CHECK_FIXNUM (mode);
3376 int nofollow = symlink_nofollow_flag (flag);
3377 Lisp_Object absname = Fexpand_file_name (filename,
3378 BVAR (current_buffer, directory));
3369 3379
3370 /* If the file name has special constructs in it, 3380 /* If the file name has special constructs in it,
3371 call the corresponding file name handler. */ 3381 call the corresponding file name handler. */
3372 handler = Ffind_file_name_handler (absname, Qset_file_modes); 3382 Lisp_Object handler = Ffind_file_name_handler (absname, Qset_file_modes);
3373 if (!NILP (handler)) 3383 if (!NILP (handler))
3374 return call3 (handler, Qset_file_modes, absname, mode); 3384 return call4 (handler, Qset_file_modes, absname, mode, flag);
3375
3376 encoded_absname = ENCODE_FILE (absname);
3377 3385
3378 if (chmod (SSDATA (encoded_absname), XFIXNUM (mode) & 07777) < 0) 3386 char *fname = SSDATA (ENCODE_FILE (absname));
3387 mode_t imode = XFIXNUM (mode) & 07777;
3388 if (fchmodat (AT_FDCWD, fname, imode, nofollow) != 0)
3379 report_file_error ("Doing chmod", absname); 3389 report_file_error ("Doing chmod", absname);
3380 3390
3381 return Qnil; 3391 return Qnil;
@@ -5740,7 +5750,7 @@ auto_save_1 (void)
5740 == 0) 5750 == 0)
5741 /* But make sure we can overwrite it later! */ 5751 /* But make sure we can overwrite it later! */
5742 auto_save_mode_bits = (st.st_mode | 0600) & 0777; 5752 auto_save_mode_bits = (st.st_mode | 0600) & 0777;
5743 else if (modes = Ffile_modes (BVAR (current_buffer, filename)), 5753 else if (modes = Ffile_modes (BVAR (current_buffer, filename), Qnil),
5744 FIXNUMP (modes)) 5754 FIXNUMP (modes))
5745 /* Remote files don't cooperate with fstatat. */ 5755 /* Remote files don't cooperate with fstatat. */
5746 auto_save_mode_bits = (XFIXNUM (modes) | 0600) & 0777; 5756 auto_save_mode_bits = (XFIXNUM (modes) | 0600) & 0777;