1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-11-28 07:45:00 +00:00
emacs/lib/lchmod.c
Paul Eggert 9d626dffc6 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.
2020-02-23 16:45:50 -08:00

111 lines
3.3 KiB
C

/* Implement lchmod on platforms where it does not work correctly.
Copyright 2020 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
/* written by Paul Eggert */
#include <config.h>
/* Specification. */
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#ifdef __osf__
/* Write "sys/stat.h" here, not <sys/stat.h>, otherwise OSF/1 5.1 DTK cc
eliminates this include because of the preliminary #include <sys/stat.h>
above. */
# include "sys/stat.h"
#else
# include <sys/stat.h>
#endif
#include <intprops.h>
/* Work like chmod, except when FILE is a symbolic link.
In that case, on systems where permissions on symbolic links are unsupported
(such as Linux), set errno to EOPNOTSUPP and return -1. */
int
lchmod (char const *file, mode_t mode)
{
#if defined O_PATH && defined AT_EMPTY_PATH
/* Open a file descriptor with O_NOFOLLOW, to make sure we don't
follow symbolic links, if /proc is mounted. O_PATH is used to
avoid a failure if the file is not readable.
Cf. <https://sourceware.org/bugzilla/show_bug.cgi?id=14578> */
int fd = open (file, O_PATH | O_NOFOLLOW | O_CLOEXEC);
if (fd < 0)
return fd;
/* Up to Linux 5.3 at least, when FILE refers to a symbolic link, the
chmod call below will change the permissions of the symbolic link
- which is undesired - and on many file systems (ext4, btrfs, jfs,
xfs, ..., but not reiserfs) fail with error EOPNOTSUPP - which is
misleading. Therefore test for a symbolic link explicitly.
Use fstatat because fstat does not work on O_PATH descriptors
before Linux 3.6. */
struct stat st;
if (fstatat (fd, "", &st, AT_EMPTY_PATH) != 0)
{
int stat_errno = errno;
close (fd);
errno = stat_errno;
return -1;
}
if (S_ISLNK (st.st_mode))
{
close (fd);
errno = EOPNOTSUPP;
return -1;
}
# if defined __linux__ || defined __ANDROID__
static char const fmt[] = "/proc/self/fd/%d";
char buf[sizeof fmt - sizeof "%d" + INT_BUFSIZE_BOUND (int)];
sprintf (buf, fmt, fd);
int chmod_result = chmod (buf, mode);
int chmod_errno = errno;
close (fd);
if (chmod_result == 0)
return chmod_result;
if (chmod_errno != ENOENT)
{
errno = chmod_errno;
return chmod_result;
}
# endif
/* /proc is not mounted or would not work as in GNU/Linux. */
#elif HAVE_LSTAT
struct stat st;
int lstat_result = lstat (file, &st);
if (lstat_result != 0)
return lstat_result;
if (S_ISLNK (st.st_mode))
{
errno = EOPNOTSUPP;
return -1;
}
#endif
/* Fall back on chmod, despite a possible race. */
return chmod (file, mode);
}