diff options
| author | Paul Eggert | 2020-02-23 16:19:42 -0800 |
|---|---|---|
| committer | Paul Eggert | 2020-02-23 16:45:50 -0800 |
| commit | 9d626dffc6ba62c0d7a1a5c712f576ed8684fd66 (patch) | |
| tree | 6cc8fbe8e5bc02c3bb74139710814a0400e91a8a | |
| parent | c4ca8219dd6b8f06e67a0b767475b1259653b8e0 (diff) | |
| download | emacs-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-x | admin/merge-gnulib | 2 | ||||
| -rw-r--r-- | doc/lispref/files.texi | 27 | ||||
| -rw-r--r-- | doc/lispref/os.texi | 2 | ||||
| -rw-r--r-- | etc/NEWS | 3 | ||||
| -rw-r--r-- | lib/fchmodat.c | 144 | ||||
| -rw-r--r-- | lib/gnulib.mk.in | 26 | ||||
| -rw-r--r-- | lib/lchmod.c | 110 | ||||
| -rw-r--r-- | lisp/dired-aux.el | 3 | ||||
| -rw-r--r-- | lisp/doc-view.el | 4 | ||||
| -rw-r--r-- | lisp/emacs-lisp/autoload.el | 2 | ||||
| -rw-r--r-- | lisp/emacs-lisp/bytecomp.el | 2 | ||||
| -rw-r--r-- | lisp/eshell/em-pred.el | 2 | ||||
| -rw-r--r-- | lisp/files.el | 12 | ||||
| -rw-r--r-- | lisp/gnus/gnus-util.el | 4 | ||||
| -rw-r--r-- | lisp/gnus/mail-source.el | 2 | ||||
| -rw-r--r-- | lisp/gnus/mm-decode.el | 2 | ||||
| -rw-r--r-- | lisp/gnus/nnmail.el | 2 | ||||
| -rw-r--r-- | lisp/net/ange-ftp.el | 3 | ||||
| -rw-r--r-- | lisp/net/tramp-adb.el | 9 | ||||
| -rw-r--r-- | lisp/net/tramp-gvfs.el | 4 | ||||
| -rw-r--r-- | lisp/net/tramp-sh.el | 10 | ||||
| -rw-r--r-- | lisp/net/tramp-smb.el | 3 | ||||
| -rw-r--r-- | lisp/net/tramp-sudoedit.el | 6 | ||||
| -rw-r--r-- | lisp/net/tramp.el | 13 | ||||
| -rw-r--r-- | lisp/server.el | 2 | ||||
| -rw-r--r-- | lisp/url/url-util.el | 4 | ||||
| -rw-r--r-- | m4/fchmodat.m4 | 82 | ||||
| -rw-r--r-- | m4/gnulib-comp.m4 | 35 | ||||
| -rw-r--r-- | m4/lchmod.m4 | 31 | ||||
| -rw-r--r-- | src/fileio.c | 46 |
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. | |||
| 928 | This function does not follow symbolic links. | 928 | This 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 | |||
| 946 | has read, write, and execute permission, the @acronym{SUID} bit is set | 946 | has read, write, and execute permission, the @acronym{SUID} bit is set |
| 947 | for both others and group, and the sticky bit is set. | 947 | for both others and group, and the sticky bit is set. |
| 948 | 948 | ||
| 949 | By default this function follows symbolic links. However, if the | ||
| 950 | optional argument @var{flag} is the symbol @code{nofollow}, this | ||
| 951 | function does not follow @var{filename} if it is a symbolic link; | ||
| 952 | this can help prevent inadvertently obtaining the mode bits of a file | ||
| 953 | somewhere 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 |
| 950 | can be used to set these permissions. | 957 | can 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 |
| 1805 | This function sets the @dfn{file mode} (or @dfn{permissions}) of | 1812 | This 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 | |||
| 1815 | By default this function follows symbolic links. However, if the | ||
| 1816 | optional argument @var{flag} is the symbol @code{nofollow}, this | ||
| 1817 | function does not follow @var{filename} if it is a symbolic link; | ||
| 1818 | this can help prevent inadvertently changing the mode bits of a file | ||
| 1819 | somewhere else. On platforms that do not support changing mode bits | ||
| 1820 | on a symbolic link, this function signals an error when @var{filename} | ||
| 1821 | is a symbolic link and @var{flag} is @code{nofollow}. | ||
| 1807 | 1822 | ||
| 1808 | If called non-interactively, @var{mode} must be an integer. Only the | 1823 | If called non-interactively, @var{mode} must be an integer. Only the |
| 1809 | lowest 12 bits of the integer are used; on most systems, only the | 1824 | lowest 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 | |||
| 1811 | octal numbers to enter @var{mode}. For example, | 1826 | octal 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 |
| @@ -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 | ||
| 202 | specifying 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, |
| 202 | such as "2020-01-15T16:12:21-08:00". | 205 | such 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 | ||
| 30 | static int | ||
| 31 | orig_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 | ||
| 63 | int | ||
| 64 | fchmodat (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@ | |||
| 1082 | gl_GNULIB_ENABLED_euidaccess = @gl_GNULIB_ENABLED_euidaccess@ | 1083 | gl_GNULIB_ENABLED_euidaccess = @gl_GNULIB_ENABLED_euidaccess@ |
| 1083 | gl_GNULIB_ENABLED_getdtablesize = @gl_GNULIB_ENABLED_getdtablesize@ | 1084 | gl_GNULIB_ENABLED_getdtablesize = @gl_GNULIB_ENABLED_getdtablesize@ |
| 1084 | gl_GNULIB_ENABLED_getgroups = @gl_GNULIB_ENABLED_getgroups@ | 1085 | gl_GNULIB_ENABLED_getgroups = @gl_GNULIB_ENABLED_getgroups@ |
| 1086 | gl_GNULIB_ENABLED_lchmod = @gl_GNULIB_ENABLED_lchmod@ | ||
| 1085 | gl_GNULIB_ENABLED_malloca = @gl_GNULIB_ENABLED_malloca@ | 1087 | gl_GNULIB_ENABLED_malloca = @gl_GNULIB_ENABLED_malloca@ |
| 1086 | gl_GNULIB_ENABLED_open = @gl_GNULIB_ENABLED_open@ | 1088 | gl_GNULIB_ENABLED_open = @gl_GNULIB_ENABLED_open@ |
| 1087 | gl_GNULIB_ENABLED_strtoll = @gl_GNULIB_ENABLED_strtoll@ | 1089 | gl_GNULIB_ENABLED_strtoll = @gl_GNULIB_ENABLED_strtoll@ |
| @@ -1586,6 +1588,17 @@ EXTRA_libgnu_a_SOURCES += at-func.c faccessat.c | |||
| 1586 | endif | 1588 | endif |
| 1587 | ## end gnulib module faccessat | 1589 | ## end gnulib module faccessat |
| 1588 | 1590 | ||
| 1591 | ## begin gnulib module fchmodat | ||
| 1592 | ifeq (,$(OMIT_GNULIB_MODULE_fchmodat)) | ||
| 1593 | |||
| 1594 | |||
| 1595 | EXTRA_DIST += at-func.c fchmodat.c | ||
| 1596 | |||
| 1597 | EXTRA_libgnu_a_SOURCES += at-func.c fchmodat.c | ||
| 1598 | |||
| 1599 | endif | ||
| 1600 | ## end gnulib module fchmodat | ||
| 1601 | |||
| 1589 | ## begin gnulib module fcntl | 1602 | ## begin gnulib module fcntl |
| 1590 | ifeq (,$(OMIT_GNULIB_MODULE_fcntl)) | 1603 | ifeq (,$(OMIT_GNULIB_MODULE_fcntl)) |
| 1591 | 1604 | ||
| @@ -1936,6 +1949,19 @@ EXTRA_DIST += inttypes.in.h | |||
| 1936 | endif | 1949 | endif |
| 1937 | ## end gnulib module inttypes-incomplete | 1950 | ## end gnulib module inttypes-incomplete |
| 1938 | 1951 | ||
| 1952 | ## begin gnulib module lchmod | ||
| 1953 | ifeq (,$(OMIT_GNULIB_MODULE_lchmod)) | ||
| 1954 | |||
| 1955 | ifneq (,$(gl_GNULIB_ENABLED_lchmod)) | ||
| 1956 | |||
| 1957 | endif | ||
| 1958 | EXTRA_DIST += lchmod.c | ||
| 1959 | |||
| 1960 | EXTRA_libgnu_a_SOURCES += lchmod.c | ||
| 1961 | |||
| 1962 | endif | ||
| 1963 | ## end gnulib module lchmod | ||
| 1964 | |||
| 1939 | ## begin gnulib module libc-config | 1965 | ## begin gnulib module libc-config |
| 1940 | ifeq (,$(OMIT_GNULIB_MODULE_libc-config)) | 1966 | ifeq (,$(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 | |||
| 45 | int | ||
| 46 | lchmod (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 | ||
| 2 | dnl Copyright (C) 2004-2020 Free Software Foundation, Inc. | ||
| 3 | dnl This file is free software; the Free Software Foundation | ||
| 4 | dnl gives unlimited permission to copy and/or distribute it, | ||
| 5 | dnl with or without modifications, as long as this notice is preserved. | ||
| 6 | |||
| 7 | # Written by Jim Meyering. | ||
| 8 | |||
| 9 | AC_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. | ||
| 78 | AC_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 | |||
| 3 | dnl Copyright (C) 2005-2006, 2008-2020 Free Software Foundation, Inc. | ||
| 4 | dnl This file is free software; the Free Software Foundation | ||
| 5 | dnl gives unlimited permission to copy and/or distribute it, | ||
| 6 | dnl with or without modifications, as long as this notice is preserved. | ||
| 7 | |||
| 8 | dnl From Paul Eggert. | ||
| 9 | dnl Provide a replacement for lchmod on hosts that lack a working version. | ||
| 10 | |||
| 11 | AC_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. | ||
| 27 | AC_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 | ||
| 3335 | DEFUN ("file-modes", Ffile_modes, Sfile_modes, 1, 1, 0, | 3335 | static int |
| 3336 | symlink_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 | |||
| 3342 | DEFUN ("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. |
| 3337 | Return nil if FILENAME does not exist. */) | 3344 | Return nil if FILENAME does not exist. If optional FLAG is `nofollow', |
| 3338 | (Lisp_Object filename) | 3345 | do 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 | ||
| 3354 | DEFUN ("set-file-modes", Fset_file_modes, Sset_file_modes, 2, 2, | 3364 | DEFUN ("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). |
| 3358 | Only the 12 low bits of MODE are used. | 3368 | Only the 12 low bits of MODE are used. If optional FLAG is `nofollow', |
| 3369 | do not follow FILENAME if it is a symbolic link. | ||
| 3359 | 3370 | ||
| 3360 | Interactively, mode bits are read by `read-file-modes', which accepts | 3371 | Interactively, mode bits are read by `read-file-modes', which accepts |
| 3361 | symbolic notation, like the `chmod' command from GNU Coreutils. */) | 3372 | symbolic 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; |