From patchwork Wed Jan 22 20:03:34 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 37470 Received: (qmail 101795 invoked by alias); 22 Jan 2020 20:03:44 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 101732 invoked by uid 89); 22 Jan 2020 20:03:43 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-18.7 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 spammy=releasing, accurately X-HELO: us-smtp-delivery-1.mimecast.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1579723420; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=eZsADYIvSrqoVluy1hpPbSC1pf3VQW4gS7DICCwGZuc=; b=haVt9erKXOB4ieWYDgqOzVGPINIJhFoUrXaQDBZ/Mt/jcPoVb/ruEplEOADNGi4BgSGvbU Oov8229qKQx6jfrDA7aU6zI0Bf9KL9w1rW/8vv9esrZkEHkiOgQ86/U6UYiWFFCFp5wujo qsW/Z4Vl/u+MI+Zl1pSn1Zdd8Ls+mcY= From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH 3/5] Linux: Emulate fchmodat with AT_SYMLINK_NOFOLLOW using O_PATH [BZ #14578] In-Reply-To: References: Message-Id: <3391fd4d8a6c8d6457985665b1b824dddf64e422.1579723048.git.fweimer@redhat.com> Date: Wed, 22 Jan 2020 21:03:34 +0100 User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.3 (gnu/linux) MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com /proc/self/fd files are special and chmod on O_PATH descriptors in that directory operates on the symbolic link itself (like lchmod). --- sysdeps/unix/sysv/linux/fchmodat.c | 61 +++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/sysdeps/unix/sysv/linux/fchmodat.c b/sysdeps/unix/sysv/linux/fchmodat.c index c41ebb290d..ac318ceb79 100644 --- a/sysdeps/unix/sysv/linux/fchmodat.c +++ b/sysdeps/unix/sysv/linux/fchmodat.c @@ -18,24 +18,65 @@ #include #include -#include +#include #include -#include -#include +#include #include -#include #include +#include int fchmodat (int fd, const char *file, mode_t mode, int flag) { - if (flag & ~AT_SYMLINK_NOFOLLOW) + if (flag == 0) + return INLINE_SYSCALL (fchmodat, 3, fd, file, mode); + else if (flag != AT_SYMLINK_NOFOLLOW) return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL); -#ifndef __NR_lchmod /* Linux so far has no lchmod syscall. */ - if (flag & AT_SYMLINK_NOFOLLOW) - return INLINE_SYSCALL_ERROR_RETURN_VALUE (ENOTSUP); -#endif + else + { + /* The kernel system call does not have a mode argument. + However, we can create an O_PATH descriptor and change that + via /proc (which does not resolve symbolic links). */ + + int pathfd = __openat_nocancel (fd, file, + O_PATH | O_NOFOLLOW | O_CLOEXEC); + if (pathfd < 0) + { + if (errno == ENFILE || errno == EMFILE) + /* These errors cannot happen with a straight fchmodat + operation because it does not create file descriptors, + so hide them. */ + __set_errno (EOPNOTSUPP); + /* Otherwise, this should accurately reflect the expected + error from fchmodat (e.g., EBADF or ENOENT). */ + return pathfd; + } + + char buf[32]; + if (__snprintf (buf, sizeof (buf), "/proc/self/fd/%d", pathfd) < 0) + { + __close_nocancel (pathfd); + return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOPNOTSUPP); + } - return INLINE_SYSCALL (fchmodat, 3, fd, file, mode); + /* This operates directly on the symbolic link if it is one. + /proc/self/fd files look like symbolic links, but they are + not. (fchmod and fchmodat do not work on O_PATH descriptors, + similar to fstat before Linux 3.6.) */ + int ret = __chmod (buf, mode); + if (ret != 0) + { + if (errno == ENOENT) + /* /proc has not been mounted. In general, we cannot use + openat with AT_EMPTY_PATH to upgrade the descriptor + because we may not have permission to open the file, + and opening files and closing them again may have side + effects (such as rewinding tape devices, or releasing + POSIX locks). */ + __set_errno (EOPNOTSUPP); + } + __close_nocancel (pathfd); + return ret; + } } libc_hidden_def (fchmodat)