From patchwork Wed Jun 9 15:55:43 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Max Reitz X-Patchwork-Id: 12310613 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0BD3EC48BCD for ; Wed, 9 Jun 2021 15:58:39 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id ABA4F60FEB for ; Wed, 9 Jun 2021 15:58:38 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org ABA4F60FEB Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:53078 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lr0bF-0001Vk-OO for qemu-devel@archiver.kernel.org; Wed, 09 Jun 2021 11:58:37 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:57914) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lr0Yy-0003IS-Bu for qemu-devel@nongnu.org; Wed, 09 Jun 2021 11:56:16 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:31542) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lr0Yv-0006tk-Ga for qemu-devel@nongnu.org; Wed, 09 Jun 2021 11:56:16 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1623254172; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=wShB3mfRQz3vX4+x9aHE+Xz5eNnWgcwUXViRK+9d3tg=; b=aJcoe6Y6/A35WEqWUZalOcXnZQjW/ct3m0jpmIhe/eLxVNVNZrLqmWoMF18gBD0kEfxivZ mk1RIklocUObq6aEq42xq8a2o9cVzGzWm/AqrxI+1PU3wWlI2srEkijByxE7zXjCuBQ5m5 6cSZ6IJfxgIOLW9iFzTqtHCfeSrBQBM= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-142-xKD7vJ8APU6ulxeKkEf1ew-1; Wed, 09 Jun 2021 11:56:11 -0400 X-MC-Unique: xKD7vJ8APU6ulxeKkEf1ew-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id BEF4D800C60 for ; Wed, 9 Jun 2021 15:56:10 +0000 (UTC) Received: from localhost (ovpn-114-102.ams2.redhat.com [10.36.114.102]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 4BA965D9C6; Wed, 9 Jun 2021 15:56:01 +0000 (UTC) From: Max Reitz To: qemu-devel@nongnu.org, virtio-fs@redhat.com Subject: [PATCH v2 1/9] virtiofsd: Add TempFd structure Date: Wed, 9 Jun 2021 17:55:43 +0200 Message-Id: <20210609155551.44437-2-mreitz@redhat.com> In-Reply-To: <20210609155551.44437-1-mreitz@redhat.com> References: <20210609155551.44437-1-mreitz@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mreitz@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Received-SPF: pass client-ip=170.10.133.124; envelope-from=mreitz@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -29 X-Spam_score: -3.0 X-Spam_bar: --- X-Spam_report: (-3.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.199, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr . David Alan Gilbert" , Stefan Hajnoczi , Max Reitz Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" We are planning to add file handles to lo_inode objects as an alternative to lo_inode.fd. That means that everywhere where we currently reference lo_inode.fd, we will have to open a temporary file descriptor that needs to be closed after use. So instead of directly accessing lo_inode.fd, there will be a helper function (lo_inode_fd()) that either returns lo_inode.fd, or opens a new file descriptor with open_by_handle_at(). It encapsulates this result in a TempFd structure to let the caller know whether the FD needs to be closed after use (opened from the handle) or not (copied from lo_inode.fd). By using g_auto(TempFd) to store this result, callers will not even have to care about closing a temporary FD after use. It will be done automatically once the object goes out of scope. Signed-off-by: Max Reitz Reviewed-by: Connor Kuehl --- tools/virtiofsd/passthrough_ll.c | 49 ++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index 49c21fd855..a4674aba80 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -174,6 +174,28 @@ struct lo_data { int user_killpriv_v2, killpriv_v2; }; +/** + * Represents a file descriptor that may either be owned by this + * TempFd, or only referenced (i.e. the ownership belongs to some + * other object, and the value has just been copied into this TempFd). + * + * The purpose of this encapsulation is to be used as g_auto(TempFd) + * to automatically clean up owned file descriptors when this object + * goes out of scope. + * + * Use temp_fd_steal() to get an owned file descriptor that will not + * be closed when the TempFd goes out of scope. + */ +typedef struct { + int fd; + bool owned; /* fd owned by this object? */ +} TempFd; + +#define TEMP_FD_INIT ((TempFd) { .fd = -1, .owned = false }) + +static void temp_fd_clear(TempFd *temp_fd); +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(TempFd, temp_fd_clear); + static const struct fuse_opt lo_opts[] = { { "sandbox=namespace", offsetof(struct lo_data, sandbox), @@ -249,6 +271,33 @@ static struct lo_data *lo_data(fuse_req_t req) return (struct lo_data *)fuse_req_userdata(req); } +/** + * Clean-up function for TempFds + */ +static void temp_fd_clear(TempFd *temp_fd) +{ + if (temp_fd->owned) { + close(temp_fd->fd); + *temp_fd = TEMP_FD_INIT; + } +} + +/** + * Return an owned fd from *temp_fd that will not be closed when + * *temp_fd goes out of scope. + * + * (TODO: Remove __attribute__ once this is used.) + */ +static __attribute__((unused)) int temp_fd_steal(TempFd *temp_fd) +{ + if (temp_fd->owned) { + temp_fd->owned = false; + return temp_fd->fd; + } else { + return dup(temp_fd->fd); + } +} + /* * Load capng's state from our saved state if the current thread * hadn't previously been loaded. From patchwork Wed Jun 9 15:55:44 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Max Reitz X-Patchwork-Id: 12310639 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7F3DCC48BCF for ; Wed, 9 Jun 2021 16:04:13 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 01C5661377 for ; Wed, 9 Jun 2021 16:04:12 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 01C5661377 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:45664 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lr0ge-0006wz-6I for qemu-devel@archiver.kernel.org; Wed, 09 Jun 2021 12:04:12 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:57964) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lr0Z9-00042G-Nm for qemu-devel@nongnu.org; Wed, 09 Jun 2021 11:56:28 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:59286) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lr0Z7-0006xe-N2 for qemu-devel@nongnu.org; Wed, 09 Jun 2021 11:56:27 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1623254185; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=zBNV20/rfrCAEeRG/biHZJ+6Oc8sUFzI+/cPDrPFG+k=; b=D93U9Axyn0QZKwSQSvjpgVTMLbIxHLLUCqChCZiQymPVV3Ljp8tM1KrCKr+twKBNp8awJ1 RfF6l/9qDSacr4PnWp74g3j7oDnF6+reGdBmu1MYbu2Ozk4ivUqcllYZa209jB84/dmq42 28yWVGT6Fw2q591SUqambyX4ZaisX4k= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-472-FEGHfs4wPhGR3W5Rv_WnmA-1; Wed, 09 Jun 2021 11:56:23 -0400 X-MC-Unique: FEGHfs4wPhGR3W5Rv_WnmA-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 048F8100CA8C for ; Wed, 9 Jun 2021 15:56:23 +0000 (UTC) Received: from localhost (ovpn-114-102.ams2.redhat.com [10.36.114.102]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 94E0D100238C; Wed, 9 Jun 2021 15:56:12 +0000 (UTC) From: Max Reitz To: qemu-devel@nongnu.org, virtio-fs@redhat.com Subject: [PATCH v2 2/9] virtiofsd: Use lo_inode_open() instead of openat() Date: Wed, 9 Jun 2021 17:55:44 +0200 Message-Id: <20210609155551.44437-3-mreitz@redhat.com> In-Reply-To: <20210609155551.44437-1-mreitz@redhat.com> References: <20210609155551.44437-1-mreitz@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mreitz@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Received-SPF: pass client-ip=170.10.133.124; envelope-from=mreitz@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -29 X-Spam_score: -3.0 X-Spam_bar: --- X-Spam_report: (-3.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.199, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr . David Alan Gilbert" , Stefan Hajnoczi , Max Reitz Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" The xattr functions want a non-O_PATH FD, so they reopen the lo_inode.fd with the flags they need through /proc/self/fd. Similarly, lo_opendir() needs an O_RDONLY FD. Instead of the /proc/self/fd trick, it just uses openat(fd, "."), because the FD is guaranteed to be a directory, so this works. All cases have one problem in common, though: In the future, when we may have a file handle in the lo_inode instead of an FD, querying an lo_inode FD may incur an open_by_handle_at() call. It does not make sense to then reopen that FD with custom flags, those should have been passed to open_by_handle_at() instead. Use lo_inode_open() instead of openat(). As part of the file handle change, lo_inode_open() will be made to invoke openat() only if lo_inode.fd is valid. Otherwise, it will invoke open_by_handle_at() with the right flags from the start. Consequently, after this patch, lo_inode_open() is the only place to invoke openat() to reopen an existing FD with different flags. Signed-off-by: Max Reitz Reviewed-by: Connor Kuehl --- tools/virtiofsd/passthrough_ll.c | 43 ++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index a4674aba80..436f771d2a 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -1645,18 +1645,26 @@ static void lo_opendir(fuse_req_t req, fuse_ino_t ino, { int error = ENOMEM; struct lo_data *lo = lo_data(req); - struct lo_dirp *d; + struct lo_inode *inode; + struct lo_dirp *d = NULL; int fd; ssize_t fh; + inode = lo_inode(req, ino); + if (!inode) { + error = EBADF; + goto out_err; + } + d = calloc(1, sizeof(struct lo_dirp)); if (d == NULL) { goto out_err; } - fd = openat(lo_fd(req, ino), ".", O_RDONLY); - if (fd == -1) { - goto out_errno; + fd = lo_inode_open(lo, inode, O_RDONLY); + if (fd < 0) { + error = -fd; + goto out_err; } d->dp = fdopendir(fd); @@ -1685,6 +1693,7 @@ static void lo_opendir(fuse_req_t req, fuse_ino_t ino, out_errno: error = errno; out_err: + lo_inode_put(lo, &inode); if (d) { if (d->dp) { closedir(d->dp); @@ -2827,7 +2836,6 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name, } } - sprintf(procname, "%i", inode->fd); /* * It is not safe to open() non-regular/non-dir files in file server * unless O_PATH is used, so use that method for regular files/dir @@ -2835,12 +2843,14 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name, * Otherwise, call fchdir() to avoid open(). */ if (S_ISREG(inode->filetype) || S_ISDIR(inode->filetype)) { - fd = openat(lo->proc_self_fd, procname, O_RDONLY); + fd = lo_inode_open(lo, inode, O_RDONLY); if (fd < 0) { - goto out_err; + saverr = -fd; + goto out; } ret = fgetxattr(fd, name, value, size); } else { + sprintf(procname, "%i", inode->fd); /* fchdir should not fail here */ FCHDIR_NOFAIL(lo->proc_self_fd); ret = getxattr(procname, name, value, size); @@ -2906,14 +2916,15 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) } } - sprintf(procname, "%i", inode->fd); if (S_ISREG(inode->filetype) || S_ISDIR(inode->filetype)) { - fd = openat(lo->proc_self_fd, procname, O_RDONLY); + fd = lo_inode_open(lo, inode, O_RDONLY); if (fd < 0) { - goto out_err; + saverr = -fd; + goto out; } ret = flistxattr(fd, value, size); } else { + sprintf(procname, "%i", inode->fd); /* fchdir should not fail here */ FCHDIR_NOFAIL(lo->proc_self_fd); ret = listxattr(procname, value, size); @@ -3039,15 +3050,15 @@ static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name, fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n", ino, name, value, size); - sprintf(procname, "%i", inode->fd); if (S_ISREG(inode->filetype) || S_ISDIR(inode->filetype)) { - fd = openat(lo->proc_self_fd, procname, O_RDONLY); + fd = lo_inode_open(lo, inode, O_RDONLY); if (fd < 0) { - saverr = errno; + saverr = -fd; goto out; } ret = fsetxattr(fd, name, value, size, flags); } else { + sprintf(procname, "%i", inode->fd); /* fchdir should not fail here */ FCHDIR_NOFAIL(lo->proc_self_fd); ret = setxattr(procname, name, value, size, flags); @@ -3105,15 +3116,15 @@ static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *in_name) fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n", ino, name); - sprintf(procname, "%i", inode->fd); if (S_ISREG(inode->filetype) || S_ISDIR(inode->filetype)) { - fd = openat(lo->proc_self_fd, procname, O_RDONLY); + fd = lo_inode_open(lo, inode, O_RDONLY); if (fd < 0) { - saverr = errno; + saverr = -fd; goto out; } ret = fremovexattr(fd, name); } else { + sprintf(procname, "%i", inode->fd); /* fchdir should not fail here */ FCHDIR_NOFAIL(lo->proc_self_fd); ret = removexattr(procname, name); From patchwork Wed Jun 9 15:55:45 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Max Reitz X-Patchwork-Id: 12310615 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 68640C48BCF for ; Wed, 9 Jun 2021 15:59:22 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id EDA7960FEB for ; Wed, 9 Jun 2021 15:59:21 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org EDA7960FEB Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:55012 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lr0bx-0002nc-2F for qemu-devel@archiver.kernel.org; Wed, 09 Jun 2021 11:59:21 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:57976) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lr0ZC-000463-9v for qemu-devel@nongnu.org; Wed, 09 Jun 2021 11:56:31 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:45485) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lr0ZA-0006yv-0p for qemu-devel@nongnu.org; Wed, 09 Jun 2021 11:56:30 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1623254153; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=r7R7d5n3WTjJM2LwGtOisp9zhqaDCggwON3l4k2CfLM=; b=cLYFKnzMhYazB5C/bT0onWm9rx1AHSIb/y39aJPiEVVkwhe3oCPVDpJNAOdE8inyt8jzfM pQJtu4iX0Fxej5HiS5VNTBe0KDGqZLYRsZlQvBkDwSgqrGWIKuROMgfKBKveGZ9GdcYWSi aLz1tVK4K8+WT4yHjy2q0jqdcTJ+G4c= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-146-lBbr7uG9PneSkt_Ec_uFyg-1; Wed, 09 Jun 2021 11:56:26 -0400 X-MC-Unique: lBbr7uG9PneSkt_Ec_uFyg-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 7114719251A3 for ; Wed, 9 Jun 2021 15:56:25 +0000 (UTC) Received: from localhost (ovpn-114-102.ams2.redhat.com [10.36.114.102]) by smtp.corp.redhat.com (Postfix) with ESMTPS id AFE5D100238C; Wed, 9 Jun 2021 15:56:24 +0000 (UTC) From: Max Reitz To: qemu-devel@nongnu.org, virtio-fs@redhat.com Subject: [PATCH v2 3/9] virtiofsd: Add lo_inode_fd() helper Date: Wed, 9 Jun 2021 17:55:45 +0200 Message-Id: <20210609155551.44437-4-mreitz@redhat.com> In-Reply-To: <20210609155551.44437-1-mreitz@redhat.com> References: <20210609155551.44437-1-mreitz@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mreitz@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Received-SPF: pass client-ip=170.10.133.124; envelope-from=mreitz@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -29 X-Spam_score: -3.0 X-Spam_bar: --- X-Spam_report: (-3.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.199, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr . David Alan Gilbert" , Stefan Hajnoczi , Max Reitz Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" Once we let lo_inode.fd be optional, we will need its users to open the file handle stored in lo_inode instead. This function will do that. For now, it just returns lo_inode.fd, though. Signed-off-by: Max Reitz Reviewed-by: Connor Kuehl --- tools/virtiofsd/passthrough_ll.c | 138 ++++++++++++++++++++++++++----- 1 file changed, 117 insertions(+), 21 deletions(-) diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index 436f771d2a..46c9dfe200 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -629,6 +629,16 @@ static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino) return elem->inode; } +static int lo_inode_fd(const struct lo_inode *inode, TempFd *tfd) +{ + *tfd = (TempFd) { + .fd = inode->fd, + .owned = false, + }; + + return 0; +} + /* * TODO Remove this helper and force callers to hold an inode refcount until * they are done with the fd. This will be done in a later patch to make @@ -790,11 +800,11 @@ static int lo_fi_fd(fuse_req_t req, struct fuse_file_info *fi) static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int valid, struct fuse_file_info *fi) { + g_auto(TempFd) inode_fd = TEMP_FD_INIT; int saverr; char procname[64]; struct lo_data *lo = lo_data(req); struct lo_inode *inode; - int ifd; int res; int fd = -1; @@ -804,7 +814,11 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, return; } - ifd = inode->fd; + res = lo_inode_fd(inode, &inode_fd); + if (res < 0) { + saverr = -res; + goto out_err; + } /* If fi->fh is invalid we'll report EBADF later */ if (fi) { @@ -815,7 +829,7 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, if (fi) { res = fchmod(fd, attr->st_mode); } else { - sprintf(procname, "%i", ifd); + sprintf(procname, "%i", inode_fd.fd); res = fchmodat(lo->proc_self_fd, procname, attr->st_mode, 0); } if (res == -1) { @@ -827,12 +841,13 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, uid_t uid = (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t)-1; gid_t gid = (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t)-1; - saverr = drop_security_capability(lo, ifd); + saverr = drop_security_capability(lo, inode_fd.fd); if (saverr) { goto out_err; } - res = fchownat(ifd, "", uid, gid, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); + res = fchownat(inode_fd.fd, "", uid, gid, + AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); if (res == -1) { saverr = errno; goto out_err; @@ -911,7 +926,7 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, if (fi) { res = futimens(fd, tv); } else { - sprintf(procname, "%i", inode->fd); + sprintf(procname, "%i", inode_fd.fd); res = utimensat(lo->proc_self_fd, procname, tv, 0); } if (res == -1) { @@ -1026,7 +1041,8 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, struct fuse_entry_param *e, struct lo_inode **inodep) { - int newfd; + g_auto(TempFd) dir_fd = TEMP_FD_INIT; + int newfd = -1; int res; int saverr; uint64_t mnt_id; @@ -1056,7 +1072,13 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, name = "."; } - newfd = openat(dir->fd, name, O_PATH | O_NOFOLLOW); + res = lo_inode_fd(dir, &dir_fd); + if (res < 0) { + saverr = -res; + goto out; + } + + newfd = openat(dir_fd.fd, name, O_PATH | O_NOFOLLOW); if (newfd == -1) { goto out_err; } @@ -1123,6 +1145,7 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, out_err: saverr = errno; +out: if (newfd != -1) { close(newfd); } @@ -1228,6 +1251,7 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev, const char *link) { + g_auto(TempFd) dir_fd = TEMP_FD_INIT; int res; int saverr; struct lo_data *lo = lo_data(req); @@ -1251,12 +1275,18 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, return; } + res = lo_inode_fd(dir, &dir_fd); + if (res < 0) { + saverr = -res; + goto out; + } + saverr = lo_change_cred(req, &old); if (saverr) { goto out; } - res = mknod_wrapper(dir->fd, name, link, mode, rdev); + res = mknod_wrapper(dir_fd.fd, name, link, mode, rdev); saverr = errno; @@ -1304,6 +1334,8 @@ static void lo_symlink(fuse_req_t req, const char *link, fuse_ino_t parent, static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, const char *name) { + g_auto(TempFd) inode_fd = TEMP_FD_INIT; + g_auto(TempFd) parent_fd = TEMP_FD_INIT; int res; struct lo_data *lo = lo_data(req); struct lo_inode *parent_inode; @@ -1329,18 +1361,31 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, goto out_err; } + res = lo_inode_fd(inode, &inode_fd); + if (res < 0) { + errno = -res; + goto out_err; + } + + res = lo_inode_fd(parent_inode, &parent_fd); + if (res < 0) { + errno = -res; + goto out_err; + } + memset(&e, 0, sizeof(struct fuse_entry_param)); e.attr_timeout = lo->timeout; e.entry_timeout = lo->timeout; - sprintf(procname, "%i", inode->fd); - res = linkat(lo->proc_self_fd, procname, parent_inode->fd, name, + sprintf(procname, "%i", inode_fd.fd); + res = linkat(lo->proc_self_fd, procname, parent_fd.fd, name, AT_SYMLINK_FOLLOW); if (res == -1) { goto out_err; } - res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); + res = fstatat(inode_fd.fd, "", &e.attr, + AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); if (res == -1) { goto out_err; } @@ -1369,6 +1414,7 @@ out_err: static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, const char *name) { + g_auto(TempFd) dir_fd = TEMP_FD_INIT; int res; uint64_t mnt_id; struct stat attr; @@ -1379,7 +1425,12 @@ static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, return NULL; } - res = do_statx(lo, dir->fd, name, &attr, AT_SYMLINK_NOFOLLOW, &mnt_id); + res = lo_inode_fd(dir, &dir_fd); + if (res < 0) { + return NULL; + } + + res = do_statx(lo, dir_fd.fd, name, &attr, AT_SYMLINK_NOFOLLOW, &mnt_id); lo_inode_put(lo, &dir); if (res == -1) { return NULL; @@ -1421,6 +1472,8 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname, unsigned int flags) { + g_auto(TempFd) parent_fd = TEMP_FD_INIT; + g_auto(TempFd) newparent_fd = TEMP_FD_INIT; int res; struct lo_inode *parent_inode; struct lo_inode *newparent_inode; @@ -1453,12 +1506,24 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, goto out; } + res = lo_inode_fd(parent_inode, &parent_fd); + if (res < 0) { + fuse_reply_err(req, -res); + goto out; + } + + res = lo_inode_fd(newparent_inode, &newparent_fd); + if (res < 0) { + fuse_reply_err(req, -res); + goto out; + } + if (flags) { #ifndef SYS_renameat2 fuse_reply_err(req, EINVAL); #else - res = syscall(SYS_renameat2, parent_inode->fd, name, - newparent_inode->fd, newname, flags); + res = syscall(SYS_renameat2, parent_fd.fd, name, + newparent_fd.fd, newname, flags); if (res == -1 && errno == ENOSYS) { fuse_reply_err(req, EINVAL); } else { @@ -1468,7 +1533,7 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, goto out; } - res = renameat(parent_inode->fd, name, newparent_inode->fd, newname); + res = renameat(parent_fd.fd, name, newparent_fd.fd, newname); fuse_reply_err(req, res == -1 ? errno : 0); out: @@ -1953,6 +2018,7 @@ static int lo_do_open(struct lo_data *lo, struct lo_inode *inode, static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi) { + g_auto(TempFd) parent_fd = TEMP_FD_INIT; int fd = -1; struct lo_data *lo = lo_data(req); struct lo_inode *parent_inode; @@ -1975,6 +2041,12 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, return; } + err = lo_inode_fd(parent_inode, &parent_fd); + if (err < 0) { + err = -err; + goto out; + } + err = lo_change_cred(req, &old); if (err) { goto out; @@ -1983,7 +2055,7 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, update_open_flags(lo->writeback, lo->allow_direct_io, fi); /* Try to create a new file but don't open existing files */ - fd = openat(parent_inode->fd, name, fi->flags | O_CREAT | O_EXCL, mode); + fd = openat(parent_fd.fd, name, fi->flags | O_CREAT | O_EXCL, mode); err = fd == -1 ? errno : 0; lo_restore_cred(&old); @@ -2788,6 +2860,7 @@ static int xattr_map_server(const struct lo_data *lo, const char *server_name, static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name, size_t size) { + g_auto(TempFd) inode_fd = TEMP_FD_INIT; struct lo_data *lo = lo_data(req); g_autofree char *value = NULL; char procname[64]; @@ -2850,7 +2923,12 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name, } ret = fgetxattr(fd, name, value, size); } else { - sprintf(procname, "%i", inode->fd); + ret = lo_inode_fd(inode, &inode_fd); + if (ret < 0) { + saverr = -ret; + goto out; + } + sprintf(procname, "%i", inode_fd.fd); /* fchdir should not fail here */ FCHDIR_NOFAIL(lo->proc_self_fd); ret = getxattr(procname, name, value, size); @@ -2887,6 +2965,7 @@ out: static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) { + g_auto(TempFd) inode_fd = TEMP_FD_INIT; struct lo_data *lo = lo_data(req); g_autofree char *value = NULL; char procname[64]; @@ -2924,7 +3003,12 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) } ret = flistxattr(fd, value, size); } else { - sprintf(procname, "%i", inode->fd); + ret = lo_inode_fd(inode, &inode_fd); + if (ret < 0) { + saverr = -ret; + goto out; + } + sprintf(procname, "%i", inode_fd.fd); /* fchdir should not fail here */ FCHDIR_NOFAIL(lo->proc_self_fd); ret = listxattr(procname, value, size); @@ -3013,6 +3097,7 @@ out: static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name, const char *value, size_t size, int flags) { + g_auto(TempFd) inode_fd = TEMP_FD_INIT; char procname[64]; const char *name; char *mapped_name; @@ -3058,7 +3143,12 @@ static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name, } ret = fsetxattr(fd, name, value, size, flags); } else { - sprintf(procname, "%i", inode->fd); + ret = lo_inode_fd(inode, &inode_fd); + if (ret < 0) { + saverr = -ret; + goto out; + } + sprintf(procname, "%i", inode_fd.fd); /* fchdir should not fail here */ FCHDIR_NOFAIL(lo->proc_self_fd); ret = setxattr(procname, name, value, size, flags); @@ -3079,6 +3169,7 @@ out: static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *in_name) { + g_auto(TempFd) inode_fd = TEMP_FD_INIT; char procname[64]; const char *name; char *mapped_name; @@ -3124,7 +3215,12 @@ static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *in_name) } ret = fremovexattr(fd, name); } else { - sprintf(procname, "%i", inode->fd); + ret = lo_inode_fd(inode, &inode_fd); + if (ret < 0) { + saverr = -ret; + goto out; + } + sprintf(procname, "%i", inode_fd.fd); /* fchdir should not fail here */ FCHDIR_NOFAIL(lo->proc_self_fd); ret = removexattr(procname, name); From patchwork Wed Jun 9 15:55:46 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Max Reitz X-Patchwork-Id: 12310679 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8593AC48BCD for ; Wed, 9 Jun 2021 16:08:52 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 04C1960FDC for ; Wed, 9 Jun 2021 16:08:51 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 04C1960FDC Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:53808 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lr0l9-00045C-7L for qemu-devel@archiver.kernel.org; Wed, 09 Jun 2021 12:08:51 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:58032) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lr0ZL-0004GQ-Qz for qemu-devel@nongnu.org; Wed, 09 Jun 2021 11:56:39 -0400 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:58904) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lr0ZJ-00073l-G3 for qemu-devel@nongnu.org; Wed, 09 Jun 2021 11:56:39 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1623254196; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ZUizWzXs5c0MZ8wm5ZT/JV/6GKrRY+uQddpvk5OGh2o=; b=YyUy7RwjSd2eSHgUO1SX94RQB/rIHywBiiZkIkas6HxmDV8H2udzThUa6r+B/M8cqiaGQ0 z1ILkp2DENC/x+USStazlu4Bz4OUnDVMl5za+CbgxycXOT2gewVwq4tgRkWYRtOJPVSfN0 JNHIWJ3ub3MbNCfgIGBOLn2ucIGwWvA= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-201-7i53dpBdOEWGM1J6K_vFMg-1; Wed, 09 Jun 2021 11:56:35 -0400 X-MC-Unique: 7i53dpBdOEWGM1J6K_vFMg-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 1F69719251A4 for ; Wed, 9 Jun 2021 15:56:34 +0000 (UTC) Received: from localhost (ovpn-114-102.ams2.redhat.com [10.36.114.102]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 3469919C45; Wed, 9 Jun 2021 15:56:27 +0000 (UTC) From: Max Reitz To: qemu-devel@nongnu.org, virtio-fs@redhat.com Subject: [PATCH v2 4/9] virtiofsd: Let lo_fd() return a TempFd Date: Wed, 9 Jun 2021 17:55:46 +0200 Message-Id: <20210609155551.44437-5-mreitz@redhat.com> In-Reply-To: <20210609155551.44437-1-mreitz@redhat.com> References: <20210609155551.44437-1-mreitz@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mreitz@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Received-SPF: pass client-ip=216.205.24.124; envelope-from=mreitz@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -29 X-Spam_score: -3.0 X-Spam_bar: --- X-Spam_report: (-3.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.199, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr . David Alan Gilbert" , Stefan Hajnoczi , Max Reitz Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" Accessing lo_inode.fd must generally happen through lo_inode_fd(), and lo_fd() is no exception; and then it must pass on the TempFd it has received from lo_inode_fd(). (Note that all lo_fd() calls now use proper error handling, where all of them were in-line before; i.e. they were used in place of the fd argument of some function call. This only worked because the only error that could occur was that lo_inode() failed to find the inode ID: Then -1 would be passed as the fd, which would result in an EBADF error, which is precisely what we would want to return to the guest for an invalid inode ID. Now, though, lo_inode_fd() might potentially invoke open_by_handle_at(), which can return many different errors, and they should be properly handled and returned to the guest. So we can no longer allow lo_fd() to be used in-line, and instead need to do proper error handling for it.) Signed-off-by: Max Reitz Reviewed-by: Connor Kuehl --- tools/virtiofsd/passthrough_ll.c | 55 +++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index 46c9dfe200..8f64bcd6c5 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -644,18 +644,19 @@ static int lo_inode_fd(const struct lo_inode *inode, TempFd *tfd) * they are done with the fd. This will be done in a later patch to make * review easier. */ -static int lo_fd(fuse_req_t req, fuse_ino_t ino) +static int lo_fd(fuse_req_t req, fuse_ino_t ino, TempFd *tfd) { struct lo_inode *inode = lo_inode(req, ino); - int fd; + int res; if (!inode) { - return -1; + return -EBADF; } - fd = inode->fd; + res = lo_inode_fd(inode, tfd); + lo_inode_put(lo_data(req), &inode); - return fd; + return res; } /* @@ -766,14 +767,19 @@ static void lo_init(void *userdata, struct fuse_conn_info *conn) static void lo_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { + g_auto(TempFd) ino_fd = TEMP_FD_INIT; int res; struct stat buf; struct lo_data *lo = lo_data(req); (void)fi; - res = - fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); + res = lo_fd(req, ino, &ino_fd); + if (res < 0) { + return (void)fuse_reply_err(req, -res); + } + + res = fstatat(ino_fd.fd, "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); if (res == -1) { return (void)fuse_reply_err(req, errno); } @@ -1441,6 +1447,7 @@ static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) { + g_auto(TempFd) parent_fd = TEMP_FD_INIT; int res; struct lo_inode *inode; struct lo_data *lo = lo_data(req); @@ -1455,13 +1462,19 @@ static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) return; } + res = lo_fd(req, parent, &parent_fd); + if (res < 0) { + fuse_reply_err(req, -res); + return; + } + inode = lookup_name(req, parent, name); if (!inode) { fuse_reply_err(req, EIO); return; } - res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR); + res = unlinkat(parent_fd.fd, name, AT_REMOVEDIR); fuse_reply_err(req, res == -1 ? errno : 0); unref_inode_lolocked(lo, inode, 1); @@ -1547,6 +1560,7 @@ out: static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) { + g_auto(TempFd) parent_fd = TEMP_FD_INIT; int res; struct lo_inode *inode; struct lo_data *lo = lo_data(req); @@ -1561,13 +1575,19 @@ static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) return; } + res = lo_fd(req, parent, &parent_fd); + if (res < 0) { + fuse_reply_err(req, -res); + return; + } + inode = lookup_name(req, parent, name); if (!inode) { fuse_reply_err(req, EIO); return; } - res = unlinkat(lo_fd(req, parent), name, 0); + res = unlinkat(parent_fd.fd, name, 0); fuse_reply_err(req, res == -1 ? errno : 0); unref_inode_lolocked(lo, inode, 1); @@ -1647,10 +1667,16 @@ static void lo_forget_multi(fuse_req_t req, size_t count, static void lo_readlink(fuse_req_t req, fuse_ino_t ino) { + g_auto(TempFd) ino_fd = TEMP_FD_INIT; char buf[PATH_MAX + 1]; int res; - res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf)); + res = lo_fd(req, ino, &ino_fd); + if (res < 0) { + return (void)fuse_reply_err(req, -res); + } + + res = readlinkat(ino_fd.fd, "", buf, sizeof(buf)); if (res == -1) { return (void)fuse_reply_err(req, errno); } @@ -2447,10 +2473,17 @@ static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, static void lo_statfs(fuse_req_t req, fuse_ino_t ino) { + g_auto(TempFd) ino_fd = TEMP_FD_INIT; int res; struct statvfs stbuf; - res = fstatvfs(lo_fd(req, ino), &stbuf); + res = lo_fd(req, ino, &ino_fd); + if (res < 0) { + fuse_reply_err(req, -res); + return; + } + + res = fstatvfs(ino_fd.fd, &stbuf); if (res == -1) { fuse_reply_err(req, errno); } else { From patchwork Wed Jun 9 15:55:47 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Max Reitz X-Patchwork-Id: 12310621 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BC3E6C48BCD for ; Wed, 9 Jun 2021 16:01:37 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 6101E6108D for ; Wed, 9 Jun 2021 16:01:37 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 6101E6108D Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:34716 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lr0e8-00088A-GI for qemu-devel@archiver.kernel.org; Wed, 09 Jun 2021 12:01:36 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:58072) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lr0ZT-0004mi-Nf for qemu-devel@nongnu.org; Wed, 09 Jun 2021 11:56:47 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:21866) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lr0ZR-00076u-Da for qemu-devel@nongnu.org; Wed, 09 Jun 2021 11:56:47 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1623254204; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=o6D4yTXUmptWWqJQrdPx8oWLxR5NxBLjQbwOmLBawuY=; b=EHM1U4SDJ4oXcvRSydeOio6HJXtpCkrUF+tYqDS3cB7hnuEQCFoE7niBWHY16J+mfVuSqG radrvk2bUMy5BLazdfUlQK4hlTcKbs0d7kuRJHMpGJpEefjt8w1Ne0YMuhXbo0XZkH6kS3 D+bqVB46rJ7ymX4tI+AQsH98VfDtcGQ= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-456-ZmeZf8IJOZi3UOvKiEbqjg-1; Wed, 09 Jun 2021 11:56:43 -0400 X-MC-Unique: ZmeZf8IJOZi3UOvKiEbqjg-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id E1E5919251A1 for ; Wed, 9 Jun 2021 15:56:42 +0000 (UTC) Received: from localhost (ovpn-114-102.ams2.redhat.com [10.36.114.102]) by smtp.corp.redhat.com (Postfix) with ESMTPS id C7FD4105C860; Wed, 9 Jun 2021 15:56:35 +0000 (UTC) From: Max Reitz To: qemu-devel@nongnu.org, virtio-fs@redhat.com Subject: [PATCH v2 5/9] virtiofsd: Let lo_inode_open() return a TempFd Date: Wed, 9 Jun 2021 17:55:47 +0200 Message-Id: <20210609155551.44437-6-mreitz@redhat.com> In-Reply-To: <20210609155551.44437-1-mreitz@redhat.com> References: <20210609155551.44437-1-mreitz@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mreitz@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Received-SPF: pass client-ip=170.10.133.124; envelope-from=mreitz@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -29 X-Spam_score: -3.0 X-Spam_bar: --- X-Spam_report: (-3.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.199, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_FILL_THIS_FORM_SHORT=0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr . David Alan Gilbert" , Stefan Hajnoczi , Max Reitz Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" Strictly speaking, this is not necessary, because lo_inode_open() will always return a new FD owned by the caller, so TempFd.owned will always be true. However, auto-cleanup is nice, and in some cases this plays nicely with an lo_inode_fd() call in another conditional branch (see lo_setattr()). Signed-off-by: Max Reitz Reviewed-by: Connor Kuehl --- tools/virtiofsd/passthrough_ll.c | 137 +++++++++++++------------------ 1 file changed, 59 insertions(+), 78 deletions(-) diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index 8f64bcd6c5..3014e8baf8 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -285,10 +285,8 @@ static void temp_fd_clear(TempFd *temp_fd) /** * Return an owned fd from *temp_fd that will not be closed when * *temp_fd goes out of scope. - * - * (TODO: Remove __attribute__ once this is used.) */ -static __attribute__((unused)) int temp_fd_steal(TempFd *temp_fd) +static int temp_fd_steal(TempFd *temp_fd) { if (temp_fd->owned) { temp_fd->owned = false; @@ -667,9 +665,12 @@ static int lo_fd(fuse_req_t req, fuse_ino_t ino, TempFd *tfd) * when a malicious client opens special files such as block device nodes. * Symlink inodes are also rejected since symlinks must already have been * traversed on the client side. + * + * The fd is returned in tfd->fd. The return value is 0 on success and -errno + * otherwise. */ -static int lo_inode_open(struct lo_data *lo, struct lo_inode *inode, - int open_flags) +static int lo_inode_open(const struct lo_data *lo, const struct lo_inode *inode, + int open_flags, TempFd *tfd) { g_autofree char *fd_str = g_strdup_printf("%d", inode->fd); int fd; @@ -688,7 +689,13 @@ static int lo_inode_open(struct lo_data *lo, struct lo_inode *inode, if (fd < 0) { return -errno; } - return fd; + + *tfd = (TempFd) { + .fd = fd, + .owned = true, + }; + + return 0; } static void lo_init(void *userdata, struct fuse_conn_info *conn) @@ -820,7 +827,12 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, return; } - res = lo_inode_fd(inode, &inode_fd); + if (!fi && (valid & FUSE_SET_ATTR_SIZE)) { + /* We need an O_RDWR FD for ftruncate() */ + res = lo_inode_open(lo, inode, O_RDWR, &inode_fd); + } else { + res = lo_inode_fd(inode, &inode_fd); + } if (res < 0) { saverr = -res; goto out_err; @@ -868,18 +880,11 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, if (fi) { truncfd = fd; } else { - truncfd = lo_inode_open(lo, inode, O_RDWR); - if (truncfd < 0) { - saverr = -truncfd; - goto out_err; - } + truncfd = inode_fd.fd; } saverr = drop_security_capability(lo, truncfd); if (saverr) { - if (!fi) { - close(truncfd); - } goto out_err; } @@ -887,9 +892,6 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, res = drop_effective_cap("FSETID", &cap_fsetid_dropped); if (res != 0) { saverr = res; - if (!fi) { - close(truncfd); - } goto out_err; } } @@ -902,9 +904,6 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, fuse_log(FUSE_LOG_ERR, "Failed to gain CAP_FSETID\n"); } } - if (!fi) { - close(truncfd); - } if (res == -1) { goto out_err; } @@ -1734,11 +1733,12 @@ static struct lo_dirp *lo_dirp(fuse_req_t req, struct fuse_file_info *fi) static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { + g_auto(TempFd) inode_fd = TEMP_FD_INIT; int error = ENOMEM; struct lo_data *lo = lo_data(req); struct lo_inode *inode; struct lo_dirp *d = NULL; - int fd; + int res; ssize_t fh; inode = lo_inode(req, ino); @@ -1752,13 +1752,13 @@ static void lo_opendir(fuse_req_t req, fuse_ino_t ino, goto out_err; } - fd = lo_inode_open(lo, inode, O_RDONLY); - if (fd < 0) { - error = -fd; + res = lo_inode_open(lo, inode, O_RDONLY, &inode_fd); + if (res < 0) { + error = -res; goto out_err; } - d->dp = fdopendir(fd); + d->dp = fdopendir(temp_fd_steal(&inode_fd)); if (d->dp == NULL) { goto out_errno; } @@ -1788,8 +1788,6 @@ out_err: if (d) { if (d->dp) { closedir(d->dp); - } else if (fd != -1) { - close(fd); } free(d); } @@ -1989,6 +1987,7 @@ static void update_open_flags(int writeback, int allow_direct_io, static int lo_do_open(struct lo_data *lo, struct lo_inode *inode, int existing_fd, struct fuse_file_info *fi) { + g_auto(TempFd) inode_fd = TEMP_FD_INIT; ssize_t fh; int fd = existing_fd; int err; @@ -2005,16 +2004,18 @@ static int lo_do_open(struct lo_data *lo, struct lo_inode *inode, } } - fd = lo_inode_open(lo, inode, fi->flags); + err = lo_inode_open(lo, inode, fi->flags, &inode_fd); if (cap_fsetid_dropped) { if (gain_effective_cap("FSETID")) { fuse_log(FUSE_LOG_ERR, "Failed to gain CAP_FSETID\n"); } } - if (fd < 0) { - return -fd; + if (err < 0) { + return -err; } + fd = temp_fd_steal(&inode_fd); + if (fi->flags & (O_TRUNC)) { int err = drop_security_capability(lo, fd); if (err) { @@ -2124,8 +2125,9 @@ static struct lo_inode_plock *lookup_create_plock_ctx(struct lo_data *lo, uint64_t lock_owner, pid_t pid, int *err) { + g_auto(TempFd) inode_fd = TEMP_FD_INIT; struct lo_inode_plock *plock; - int fd; + int res; plock = g_hash_table_lookup(inode->posix_locks, GUINT_TO_POINTER(lock_owner)); @@ -2142,15 +2144,15 @@ static struct lo_inode_plock *lookup_create_plock_ctx(struct lo_data *lo, /* Open another instance of file which can be used for ofd locks. */ /* TODO: What if file is not writable? */ - fd = lo_inode_open(lo, inode, O_RDWR); - if (fd < 0) { - *err = -fd; + res = lo_inode_open(lo, inode, O_RDWR, &inode_fd); + if (res < 0) { + *err = -res; free(plock); return NULL; } plock->lock_owner = lock_owner; - plock->fd = fd; + plock->fd = temp_fd_steal(&inode_fd); g_hash_table_insert(inode->posix_locks, GUINT_TO_POINTER(plock->lock_owner), plock); return plock; @@ -2366,6 +2368,7 @@ static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi) { + g_auto(TempFd) inode_fd = TEMP_FD_INIT; struct lo_inode *inode = lo_inode(req, ino); struct lo_data *lo = lo_data(req); int res; @@ -2380,11 +2383,12 @@ static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, } if (!fi) { - fd = lo_inode_open(lo, inode, O_RDWR); - if (fd < 0) { - res = -fd; + res = lo_inode_open(lo, inode, O_RDWR, &inode_fd); + if (res < 0) { + res = -res; goto out; } + fd = inode_fd.fd; } else { fd = lo_fi_fd(req, fi); } @@ -2394,9 +2398,6 @@ static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, } else { res = fsync(fd) == -1 ? errno : 0; } - if (!fi) { - close(fd); - } out: lo_inode_put(lo, &inode); fuse_reply_err(req, res); @@ -2902,7 +2903,6 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name, struct lo_inode *inode; ssize_t ret; int saverr; - int fd = -1; mapped_name = NULL; name = in_name; @@ -2949,12 +2949,12 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name, * Otherwise, call fchdir() to avoid open(). */ if (S_ISREG(inode->filetype) || S_ISDIR(inode->filetype)) { - fd = lo_inode_open(lo, inode, O_RDONLY); - if (fd < 0) { - saverr = -fd; + ret = lo_inode_open(lo, inode, O_RDONLY, &inode_fd); + if (ret < 0) { + saverr = -ret; goto out; } - ret = fgetxattr(fd, name, value, size); + ret = fgetxattr(inode_fd.fd, name, value, size); } else { ret = lo_inode_fd(inode, &inode_fd); if (ret < 0) { @@ -2981,10 +2981,6 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name, fuse_reply_xattr(req, ret); } out_free: - if (fd >= 0) { - close(fd); - } - lo_inode_put(lo, &inode); return; @@ -3005,7 +3001,6 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) struct lo_inode *inode; ssize_t ret; int saverr; - int fd = -1; inode = lo_inode(req, ino); if (!inode) { @@ -3029,12 +3024,12 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) } if (S_ISREG(inode->filetype) || S_ISDIR(inode->filetype)) { - fd = lo_inode_open(lo, inode, O_RDONLY); - if (fd < 0) { - saverr = -fd; + ret = lo_inode_open(lo, inode, O_RDONLY, &inode_fd); + if (ret < 0) { + saverr = -ret; goto out; } - ret = flistxattr(fd, value, size); + ret = flistxattr(inode_fd.fd, value, size); } else { ret = lo_inode_fd(inode, &inode_fd); if (ret < 0) { @@ -3113,10 +3108,6 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) fuse_reply_xattr(req, ret); } out_free: - if (fd >= 0) { - close(fd); - } - lo_inode_put(lo, &inode); return; @@ -3138,7 +3129,6 @@ static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name, struct lo_inode *inode; ssize_t ret; int saverr; - int fd = -1; mapped_name = NULL; name = in_name; @@ -3169,12 +3159,12 @@ static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name, ", name=%s value=%s size=%zd)\n", ino, name, value, size); if (S_ISREG(inode->filetype) || S_ISDIR(inode->filetype)) { - fd = lo_inode_open(lo, inode, O_RDONLY); - if (fd < 0) { - saverr = -fd; + ret = lo_inode_open(lo, inode, O_RDONLY, &inode_fd); + if (ret < 0) { + saverr = -ret; goto out; } - ret = fsetxattr(fd, name, value, size, flags); + ret = fsetxattr(inode_fd.fd, name, value, size, flags); } else { ret = lo_inode_fd(inode, &inode_fd); if (ret < 0) { @@ -3191,10 +3181,6 @@ static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name, saverr = ret == -1 ? errno : 0; out: - if (fd >= 0) { - close(fd); - } - lo_inode_put(lo, &inode); g_free(mapped_name); fuse_reply_err(req, saverr); @@ -3210,7 +3196,6 @@ static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *in_name) struct lo_inode *inode; ssize_t ret; int saverr; - int fd = -1; mapped_name = NULL; name = in_name; @@ -3241,12 +3226,12 @@ static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *in_name) name); if (S_ISREG(inode->filetype) || S_ISDIR(inode->filetype)) { - fd = lo_inode_open(lo, inode, O_RDONLY); - if (fd < 0) { - saverr = -fd; + ret = lo_inode_open(lo, inode, O_RDONLY, &inode_fd); + if (ret < 0) { + saverr = -ret; goto out; } - ret = fremovexattr(fd, name); + ret = fremovexattr(inode_fd.fd, name); } else { ret = lo_inode_fd(inode, &inode_fd); if (ret < 0) { @@ -3263,10 +3248,6 @@ static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *in_name) saverr = ret == -1 ? errno : 0; out: - if (fd >= 0) { - close(fd); - } - lo_inode_put(lo, &inode); g_free(mapped_name); fuse_reply_err(req, saverr); From patchwork Wed Jun 9 15:55:48 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Max Reitz X-Patchwork-Id: 12310635 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B2E58C48BCD for ; Wed, 9 Jun 2021 16:03:36 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 4019B6136D for ; Wed, 9 Jun 2021 16:03:36 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 4019B6136D Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:43104 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lr0g3-0005FZ-DP for qemu-devel@archiver.kernel.org; Wed, 09 Jun 2021 12:03:35 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:58106) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lr0Zb-00052O-Dq for qemu-devel@nongnu.org; Wed, 09 Jun 2021 11:56:55 -0400 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:50468) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lr0ZZ-0007BY-I0 for qemu-devel@nongnu.org; Wed, 09 Jun 2021 11:56:55 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1623254212; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=f9Av80cvFt78Dk611yUb9hejqkW3z96UAez0rSWF7rU=; b=JHT7nHhITiZ6yjYxYcokylS08IVQoZ4WhqAlSbp+hzsNRXUgZ5tsr3eZQv7RwfQxLdPCDR UECnFkRjBt+ecSmLTREBVQ7avduAb7uHN6psLxdeVJOwPpQYTLNZAI/5Zyigi0McxmBGx3 3QOjxqy0x6zWsBLBTh9pgEf+dsHXlcE= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-489-gNknNB-MMAKr0z3weDtnvA-1; Wed, 09 Jun 2021 11:56:46 -0400 X-MC-Unique: gNknNB-MMAKr0z3weDtnvA-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 227D5107ACF6 for ; Wed, 9 Jun 2021 15:56:45 +0000 (UTC) Received: from localhost (ovpn-114-102.ams2.redhat.com [10.36.114.102]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 8EE7B19726; Wed, 9 Jun 2021 15:56:44 +0000 (UTC) From: Max Reitz To: qemu-devel@nongnu.org, virtio-fs@redhat.com Subject: [PATCH v2 6/9] virtiofsd: Add lo_inode.fhandle Date: Wed, 9 Jun 2021 17:55:48 +0200 Message-Id: <20210609155551.44437-7-mreitz@redhat.com> In-Reply-To: <20210609155551.44437-1-mreitz@redhat.com> References: <20210609155551.44437-1-mreitz@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mreitz@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Received-SPF: pass client-ip=216.205.24.124; envelope-from=mreitz@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -29 X-Spam_score: -3.0 X-Spam_bar: --- X-Spam_report: (-3.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.199, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr . David Alan Gilbert" , Stefan Hajnoczi , Max Reitz Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" This new field is an alternative to lo_inode.fd: Either of the two must be set. In case an O_PATH FD is needed for some lo_inode, it is either taken from lo_inode.fd, if valid, or a temporary FD is opened with open_by_handle_at(). Using a file handle instead of an FD has the advantage of keeping the number of open file descriptors low. Because open_by_handle_at() requires a mount FD (i.e. a non-O_PATH FD opened on the filesystem to which the file handle refers), but every lo_fhandle only has a mount ID (as returned by name_to_handle_at()), we keep a hash map of such FDs in mount_fds (mapping ID to FD). get_file_handle(), which is added by a later patch, will ensure that every mount ID for which we have generated a handle has a corresponding entry in mount_fds. Signed-off-by: Max Reitz Reviewed-by: Connor Kuehl --- tools/virtiofsd/passthrough_ll.c | 116 ++++++++++++++++++++++---- tools/virtiofsd/passthrough_seccomp.c | 1 + 2 files changed, 102 insertions(+), 15 deletions(-) diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index 3014e8baf8..e665575401 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -88,8 +88,25 @@ struct lo_key { uint64_t mnt_id; }; +struct lo_fhandle { + union { + struct file_handle handle; + char padding[sizeof(struct file_handle) + MAX_HANDLE_SZ]; + }; + int mount_id; +}; + +/* Maps mount IDs to an FD that we can pass to open_by_handle_at() */ +static GHashTable *mount_fds; +pthread_rwlock_t mount_fds_lock = PTHREAD_RWLOCK_INITIALIZER; + struct lo_inode { + /* + * Either of fd or fhandle must be set (i.e. >= 0 or non-NULL, + * respectively). + */ int fd; + struct lo_fhandle *fhandle; /* * Atomic reference count for this object. The nlookup field holds a @@ -296,6 +313,44 @@ static int temp_fd_steal(TempFd *temp_fd) } } +/** + * Open the given file handle with the given flags. + * + * The mount FD to pass to open_by_handle_at() is taken from the + * mount_fds hash map. + * + * On error, return -errno. + */ +static int open_file_handle(const struct lo_fhandle *fh, int flags) +{ + gpointer mount_fd_ptr; + int mount_fd; + bool found; + int ret; + + ret = pthread_rwlock_rdlock(&mount_fds_lock); + if (ret) { + return -ret; + } + + /* mount_fd == 0 is valid, so we need lookup_extended */ + found = g_hash_table_lookup_extended(mount_fds, + GINT_TO_POINTER(fh->mount_id), + NULL, &mount_fd_ptr); + pthread_rwlock_unlock(&mount_fds_lock); + if (!found) { + return -EINVAL; + } + mount_fd = GPOINTER_TO_INT(mount_fd_ptr); + + ret = open_by_handle_at(mount_fd, (struct file_handle *)&fh->handle, flags); + if (ret < 0) { + return -errno; + } + + return ret; +} + /* * Load capng's state from our saved state if the current thread * hadn't previously been loaded. @@ -602,7 +657,11 @@ static void lo_inode_put(struct lo_data *lo, struct lo_inode **inodep) *inodep = NULL; if (g_atomic_int_dec_and_test(&inode->refcount)) { - close(inode->fd); + if (inode->fd >= 0) { + close(inode->fd); + } else { + g_free(inode->fhandle); + } free(inode); } } @@ -629,10 +688,25 @@ static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino) static int lo_inode_fd(const struct lo_inode *inode, TempFd *tfd) { - *tfd = (TempFd) { - .fd = inode->fd, - .owned = false, - }; + if (inode->fd >= 0) { + *tfd = (TempFd) { + .fd = inode->fd, + .owned = false, + }; + } else { + int fd; + + assert(inode->fhandle != NULL); + fd = open_file_handle(inode->fhandle, O_PATH); + if (fd < 0) { + return -errno; + } + + *tfd = (TempFd) { + .fd = fd, + .owned = true, + }; + } return 0; } @@ -672,22 +746,32 @@ static int lo_fd(fuse_req_t req, fuse_ino_t ino, TempFd *tfd) static int lo_inode_open(const struct lo_data *lo, const struct lo_inode *inode, int open_flags, TempFd *tfd) { - g_autofree char *fd_str = g_strdup_printf("%d", inode->fd); + g_autofree char *fd_str = NULL; int fd; if (!S_ISREG(inode->filetype) && !S_ISDIR(inode->filetype)) { return -EBADF; } - /* - * The file is a symlink so O_NOFOLLOW must be ignored. We checked earlier - * that the inode is not a special file but if an external process races - * with us then symlinks are traversed here. It is not possible to escape - * the shared directory since it is mounted as "/" though. - */ - fd = openat(lo->proc_self_fd, fd_str, open_flags & ~O_NOFOLLOW); - if (fd < 0) { - return -errno; + if (inode->fd >= 0) { + /* + * The file is a symlink so O_NOFOLLOW must be ignored. We checked + * earlier that the inode is not a special file but if an external + * process races with us then symlinks are traversed here. It is not + * possible to escape the shared directory since it is mounted as "/" + * though. + */ + fd_str = g_strdup_printf("%d", inode->fd); + fd = openat(lo->proc_self_fd, fd_str, open_flags & ~O_NOFOLLOW); + if (fd < 0) { + return -errno; + } + } else { + assert(inode->fhandle != NULL); + fd = open_file_handle(inode->fhandle, open_flags); + if (fd < 0) { + return fd; + } } *tfd = (TempFd) { @@ -3911,6 +3995,8 @@ int main(int argc, char *argv[]) lo.root.fuse_ino = FUSE_ROOT_ID; lo.cache = CACHE_AUTO; + mount_fds = g_hash_table_new(NULL, NULL); + /* * Set up the ino map like this: * [0] Reserved (will not be used) diff --git a/tools/virtiofsd/passthrough_seccomp.c b/tools/virtiofsd/passthrough_seccomp.c index 62441cfcdb..e948f25ac1 100644 --- a/tools/virtiofsd/passthrough_seccomp.c +++ b/tools/virtiofsd/passthrough_seccomp.c @@ -77,6 +77,7 @@ static const int syscall_allowlist[] = { SCMP_SYS(statx), SCMP_SYS(open), SCMP_SYS(openat), + SCMP_SYS(open_by_handle_at), SCMP_SYS(ppoll), SCMP_SYS(prctl), /* TODO restrict to just PR_SET_NAME? */ SCMP_SYS(preadv), From patchwork Wed Jun 9 15:55:49 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Max Reitz X-Patchwork-Id: 12310619 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7BFFEC48BCD for ; Wed, 9 Jun 2021 16:01:29 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 160186136D for ; Wed, 9 Jun 2021 16:01:29 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 160186136D Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:34368 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lr0e0-0007tx-6i for qemu-devel@archiver.kernel.org; Wed, 09 Jun 2021 12:01:28 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:58084) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lr0ZY-0004xz-0u for qemu-devel@nongnu.org; Wed, 09 Jun 2021 11:56:52 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:44819) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lr0ZV-000798-Sv for qemu-devel@nongnu.org; Wed, 09 Jun 2021 11:56:51 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1623254209; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=fOY/p0MGBX9XPrZafayZWZZXEq0E2abq59dOjooUW0w=; b=Emo+2XNkhnRrSCV9tokvOveraT6c1C5wuYlpmj2zWonZSEmm8ia/FI4nd+hOYjcYT6JkPW Nn+lkzQQfTNvUF9o8N1uG/ukoG+j6QwUcDLEe9vBxhyHG9Vx74S5FhTwXgRJkKAAT5MCPv 4ZFimjeYBLQInJN0ZJYGOrbs024CqGE= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-314-qFoZGCnzMVyJdZYCMvGBYw-1; Wed, 09 Jun 2021 11:56:48 -0400 X-MC-Unique: qFoZGCnzMVyJdZYCMvGBYw-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 6520F100CA8F for ; Wed, 9 Jun 2021 15:56:47 +0000 (UTC) Received: from localhost (ovpn-114-102.ams2.redhat.com [10.36.114.102]) by smtp.corp.redhat.com (Postfix) with ESMTPS id CE2A71037E81; Wed, 9 Jun 2021 15:56:46 +0000 (UTC) From: Max Reitz To: qemu-devel@nongnu.org, virtio-fs@redhat.com Subject: [PATCH v2 7/9] virtiofsd: Add inodes_by_handle hash table Date: Wed, 9 Jun 2021 17:55:49 +0200 Message-Id: <20210609155551.44437-8-mreitz@redhat.com> In-Reply-To: <20210609155551.44437-1-mreitz@redhat.com> References: <20210609155551.44437-1-mreitz@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mreitz@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Received-SPF: pass client-ip=170.10.133.124; envelope-from=mreitz@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -29 X-Spam_score: -3.0 X-Spam_bar: --- X-Spam_report: (-3.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.199, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr . David Alan Gilbert" , Stefan Hajnoczi , Max Reitz Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" Currently, lo_inode.fhandle is always NULL and so always keep an O_PATH FD in lo_inode.fd. Therefore, when the respective inode is unlinked, its inode ID will remain in use until we drop our lo_inode (and lo_inode_put() thus closes the FD). Therefore, lo_find() can safely use the inode ID as an lo_inode key, because any inode with an inode ID we find in lo_data.inodes (on the same filesystem) must be the exact same file. This will change when we start setting lo_inode.fhandle so we do not have to keep an O_PATH FD open. Then, unlinking such an inode will immediately remove it, so its ID can then be reused by newly created files, even while the lo_inode object is still there[1]. So creating a new file can then reuse the old file's inode ID, and looking up the new file would lead to us finding the old file's lo_inode, which is not ideal. Luckily, just as file handles cause this problem, they also solve it: A file handle contains a generation ID, which changes when an inode ID is reused, so the new file can be distinguished from the old one. So all we need to do is to add a second map besides lo_data.inodes that maps file handles to lo_inodes, namely lo_data.inodes_by_handle. For clarity, lo_data.inodes is renamed to lo_data.inodes_by_ids. Unfortunately, we cannot rely on being able to generate file handles every time. Therefore, we still enter every lo_inode object into inodes_by_ids, but having an entry in inodes_by_handle is optional. A potential inodes_by_handle entry then has precedence, the inodes_by_ids entry is just a fallback. Note that we do not generate lo_fhandle objects yet, and so we also do not enter anything into the inodes_by_handle map yet. Also, all lookups skip that map. We might manually create file handles with some code that is immediately removed by the next patch again, but that would break the assumption in lo_find() that every lo_inode with a non-NULL .fhandle must have an entry in inodes_by_handle and vice versa. So we leave actually using the inodes_by_handle map for the next patch. [1] If some application in the guest still has the file open, there is going to be a corresponding FD mapping in lo_data.fd_map. In such a case, the inode will only go away once every application in the guest has closed it. The problem described only applies to cases where the guest does not have the file open, and it is just in the dentry cache, basically. Signed-off-by: Max Reitz Reviewed-by: Connor Kuehl --- tools/virtiofsd/passthrough_ll.c | 80 +++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 16 deletions(-) diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index e665575401..793d2c333e 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -179,7 +179,8 @@ struct lo_data { int announce_submounts; bool use_statx; struct lo_inode root; - GHashTable *inodes; /* protected by lo->mutex */ + GHashTable *inodes_by_ids; /* protected by lo->mutex */ + GHashTable *inodes_by_handle; /* protected by lo->mutex */ struct lo_map ino_map; /* protected by lo->mutex */ struct lo_map dirp_map; /* protected by lo->mutex */ struct lo_map fd_map; /* protected by lo->mutex */ @@ -257,8 +258,9 @@ static struct { /* That we loaded cap-ng in the current thread from the saved */ static __thread bool cap_loaded = 0; -static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st, - uint64_t mnt_id); +static struct lo_inode *lo_find(struct lo_data *lo, + const struct lo_fhandle *fhandle, + struct stat *st, uint64_t mnt_id); static int xattr_map_client(const struct lo_data *lo, const char *client_name, char **out_name); @@ -1032,18 +1034,39 @@ out_err: fuse_reply_err(req, saverr); } -static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st, - uint64_t mnt_id) +static struct lo_inode *lo_find(struct lo_data *lo, + const struct lo_fhandle *fhandle, + struct stat *st, uint64_t mnt_id) { - struct lo_inode *p; - struct lo_key key = { + struct lo_inode *p = NULL; + struct lo_key ids_key = { .ino = st->st_ino, .dev = st->st_dev, .mnt_id = mnt_id, }; pthread_mutex_lock(&lo->mutex); - p = g_hash_table_lookup(lo->inodes, &key); + if (fhandle) { + p = g_hash_table_lookup(lo->inodes_by_handle, fhandle); + } + if (!p) { + p = g_hash_table_lookup(lo->inodes_by_ids, &ids_key); + /* + * When we had to fall back to looking up an inode by its IDs, + * ensure that we hit an entry that does not have a file + * handle. Entries with file handles must also have a handle + * alt key, so if we have not found it by that handle alt key, + * we must have found an entry with a mismatching handle; i.e. + * an entry for a different file, even though it has the same + * inode ID. + * (This can happen when we look up a new file that has reused + * the inode ID of some previously unlinked inode for which we + * still have an lo_inode object.) + */ + if (p && fhandle != NULL && p->fhandle != NULL) { + p = NULL; + } + } if (p) { assert(p->nlookup > 0); p->nlookup++; @@ -1183,7 +1206,7 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, e->attr_flags |= FUSE_ATTR_SUBMOUNT; } - inode = lo_find(lo, &e->attr, mnt_id); + inode = lo_find(lo, NULL, &e->attr, mnt_id); if (inode) { close(newfd); } else { @@ -1213,7 +1236,7 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, } pthread_mutex_lock(&lo->mutex); inode->fuse_ino = lo_add_inode_mapping(req, inode); - g_hash_table_insert(lo->inodes, &inode->key, inode); + g_hash_table_insert(lo->inodes_by_ids, &inode->key, inode); pthread_mutex_unlock(&lo->mutex); } e->ino = inode->fuse_ino; @@ -1525,7 +1548,7 @@ static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, return NULL; } - return lo_find(lo, &attr, mnt_id); + return lo_find(lo, NULL, &attr, mnt_id); } static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) @@ -1688,7 +1711,7 @@ static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n) inode->nlookup -= n; if (!inode->nlookup) { lo_map_remove(&lo->ino_map, inode->fuse_ino); - g_hash_table_remove(lo->inodes, &inode->key); + g_hash_table_remove(lo->inodes_by_ids, &inode->key); if (lo->posix_lock) { if (g_hash_table_size(inode->posix_locks)) { fuse_log(FUSE_LOG_WARNING, "Hash table is not empty\n"); @@ -3388,7 +3411,7 @@ static void lo_destroy(void *userdata) GHashTableIter iter; gpointer key, value; - g_hash_table_iter_init(&iter, lo->inodes); + g_hash_table_iter_init(&iter, lo->inodes_by_ids); if (!g_hash_table_iter_next(&iter, &key, &value)) { break; } @@ -3931,10 +3954,34 @@ static gboolean lo_key_equal(gconstpointer a, gconstpointer b) return la->ino == lb->ino && la->dev == lb->dev && la->mnt_id == lb->mnt_id; } +static guint lo_fhandle_hash(gconstpointer key) +{ + const struct lo_fhandle *fh = key; + guint hash; + size_t i; + + /* Basically g_str_hash() */ + hash = 5381; + for (i = 0; i < sizeof(fh->padding); i++) { + hash += hash * 33 + (unsigned char)fh->padding[i]; + } + hash += hash * 33 + fh->mount_id; + + return hash; +} + +static gboolean lo_fhandle_equal(gconstpointer a, gconstpointer b) +{ + return !memcmp(a, b, sizeof(struct lo_fhandle)); +} + static void fuse_lo_data_cleanup(struct lo_data *lo) { - if (lo->inodes) { - g_hash_table_destroy(lo->inodes); + if (lo->inodes_by_ids) { + g_hash_table_destroy(lo->inodes_by_ids); + } + if (lo->inodes_by_ids) { + g_hash_table_destroy(lo->inodes_by_handle); } if (lo->root.posix_locks) { @@ -3990,7 +4037,8 @@ int main(int argc, char *argv[]) qemu_init_exec_dir(argv[0]); pthread_mutex_init(&lo.mutex, NULL); - lo.inodes = g_hash_table_new(lo_key_hash, lo_key_equal); + lo.inodes_by_ids = g_hash_table_new(lo_key_hash, lo_key_equal); + lo.inodes_by_handle = g_hash_table_new(lo_fhandle_hash, lo_fhandle_equal); lo.root.fd = -1; lo.root.fuse_ino = FUSE_ROOT_ID; lo.cache = CACHE_AUTO; From patchwork Wed Jun 9 15:55:50 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Max Reitz X-Patchwork-Id: 12310637 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B8BDBC48BCF for ; Wed, 9 Jun 2021 16:03:42 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 4631C610F8 for ; Wed, 9 Jun 2021 16:03:42 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 4631C610F8 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:43640 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lr0g9-0005az-Co for qemu-devel@archiver.kernel.org; Wed, 09 Jun 2021 12:03:41 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:58108) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lr0Zc-00055R-68 for qemu-devel@nongnu.org; Wed, 09 Jun 2021 11:56:56 -0400 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:21819) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lr0ZZ-0007BX-I3 for qemu-devel@nongnu.org; Wed, 09 Jun 2021 11:56:55 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1623254212; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ANnvVbazworuV07fPux/9vXglx6E5eUyK+nYF4orxpQ=; b=Q1eq9HuVw4R6975NCskP97I+hPRZPYHQ0CziILIuNdOmJ2Bi1S63Xtr3XVQ83Hq3dNnxz0 /qks2Pqxd7H/RyU6QTRjVcYodHZRQ8AMqlw6tICMKZ5XHD6hSeJEHhqLLRjJojO5pJHKFN 3nMYYsl4u8L1zl8N58+lumS+DPviHZU= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-445-xNE6_w95OS2b_Nq0V19Qag-1; Wed, 09 Jun 2021 11:56:50 -0400 X-MC-Unique: xNE6_w95OS2b_Nq0V19Qag-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 8A544100C611 for ; Wed, 9 Jun 2021 15:56:49 +0000 (UTC) Received: from localhost (ovpn-114-102.ams2.redhat.com [10.36.114.102]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 060EE1000324; Wed, 9 Jun 2021 15:56:48 +0000 (UTC) From: Max Reitz To: qemu-devel@nongnu.org, virtio-fs@redhat.com Subject: [PATCH v2 8/9] virtiofsd: Optionally fill lo_inode.fhandle Date: Wed, 9 Jun 2021 17:55:50 +0200 Message-Id: <20210609155551.44437-9-mreitz@redhat.com> In-Reply-To: <20210609155551.44437-1-mreitz@redhat.com> References: <20210609155551.44437-1-mreitz@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mreitz@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Received-SPF: pass client-ip=216.205.24.124; envelope-from=mreitz@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -29 X-Spam_score: -3.0 X-Spam_bar: --- X-Spam_report: (-3.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.199, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr . David Alan Gilbert" , Stefan Hajnoczi , Max Reitz Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" When the inode_file_handles option is set, try to generate a file handle for new inodes instead of opening an O_PATH FD. Being able to open these again will require CAP_DAC_READ_SEARCH, so the description text tells the user they will also need to specify -o modcaps=+dac_read_search. Generating a file handle returns the mount ID it is valid for. Opening it will require an FD instead. We have mount_fds to map an ID to an FD. get_file_handle() fills the hash map by opening the file we have generated a handle for. To verify that the resulting FD indeed represents the handle's mount ID, we use statx(). Therefore, using file handles requires statx() support. Signed-off-by: Max Reitz --- tools/virtiofsd/helper.c | 3 + tools/virtiofsd/passthrough_ll.c | 197 ++++++++++++++++++++++++-- tools/virtiofsd/passthrough_seccomp.c | 1 + 3 files changed, 192 insertions(+), 9 deletions(-) diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c index 5e98ed702b..954f8639e6 100644 --- a/tools/virtiofsd/helper.c +++ b/tools/virtiofsd/helper.c @@ -186,6 +186,9 @@ void fuse_cmdline_help(void) " to virtiofsd from guest applications.\n" " default: no_allow_direct_io\n" " -o announce_submounts Announce sub-mount points to the guest\n" + " -o inode_file_handles Use file handles to reference inodes\n" + " instead of O_PATH file descriptors\n" + " (requires -o modcaps=+dac_read_search)\n" ); } diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index 793d2c333e..2e56c40b2f 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -190,6 +190,7 @@ struct lo_data { /* An O_PATH file descriptor to /proc/self/fd/ */ int proc_self_fd; int user_killpriv_v2, killpriv_v2; + int inode_file_handles; }; /** @@ -244,6 +245,10 @@ static const struct fuse_opt lo_opts[] = { { "announce_submounts", offsetof(struct lo_data, announce_submounts), 1 }, { "killpriv_v2", offsetof(struct lo_data, user_killpriv_v2), 1 }, { "no_killpriv_v2", offsetof(struct lo_data, user_killpriv_v2), 0 }, + { "inode_file_handles", offsetof(struct lo_data, inode_file_handles), 1 }, + { "no_inode_file_handles", + offsetof(struct lo_data, inode_file_handles), + 0 }, FUSE_OPT_END }; static bool use_syslog = false; @@ -315,6 +320,135 @@ static int temp_fd_steal(TempFd *temp_fd) } } +/** + * Generate a file handle for the given dirfd/name combination. + * + * If mount_fds does not yet contain an entry for the handle's mount + * ID, (re)open dirfd/name in O_RDONLY mode and add it to mount_fds + * as the FD for that mount ID. (That is the file that we have + * generated a handle for, so it should be representative for the + * mount ID. However, to be sure (and to rule out races), we use + * statx() to verify that our assumption is correct.) + */ +static struct lo_fhandle *get_file_handle(struct lo_data *lo, + int dirfd, const char *name) +{ + /* We need statx() to verify the mount ID */ +#if defined(CONFIG_STATX) && defined(STATX_MNT_ID) + struct lo_fhandle *fh; + int ret; + + if (!lo->use_statx || !lo->inode_file_handles) { + return NULL; + } + + fh = g_new0(struct lo_fhandle, 1); + + fh->handle.handle_bytes = sizeof(fh->padding) - sizeof(fh->handle); + ret = name_to_handle_at(dirfd, name, &fh->handle, &fh->mount_id, + AT_EMPTY_PATH); + if (ret < 0) { + goto fail; + } + + if (pthread_rwlock_rdlock(&mount_fds_lock)) { + goto fail; + } + if (!g_hash_table_contains(mount_fds, GINT_TO_POINTER(fh->mount_id))) { + g_auto(TempFd) path_fd = TEMP_FD_INIT; + struct statx stx; + char procname[64]; + int fd; + + pthread_rwlock_unlock(&mount_fds_lock); + + /* + * Before opening an O_RDONLY fd, check whether dirfd/name is a regular + * file or directory, because we must not open anything else with + * anything but O_PATH. + * (And we use that occasion to verify that the file has the mount ID we + * need.) + */ + if (name[0]) { + path_fd.fd = openat(dirfd, name, O_PATH); + if (path_fd.fd < 0) { + goto fail; + } + path_fd.owned = true; + } else { + path_fd.fd = dirfd; + path_fd.owned = false; + } + + ret = statx(path_fd.fd, "", AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, + STATX_TYPE | STATX_MNT_ID, &stx); + if (ret < 0) { + if (errno == ENOSYS) { + lo->use_statx = false; + fuse_log(FUSE_LOG_WARNING, + "statx() does not work: Will not be able to use file " + "handles for inodes\n"); + } + goto fail; + } + if (!(stx.stx_mask & STATX_MNT_ID) || stx.stx_mnt_id != fh->mount_id) { + /* + * One reason for stx_mnt_id != mount_id could be that dirfd/name + * is a directory, and some other filesystem was mounted there + * between us generating the file handle and then opening the FD. + * (Other kinds of races might be possible, too.) + * Failing this function is not fatal, though, because our caller + * (lo_do_lookup()) will just fall back to opening an O_PATH FD to + * store in lo_inode.fd instead of storing a file handle in + * lo_inode.fhandle. So we do not need to try too hard to get an + * FD for fh->mount_id so this function could succeed. + */ + goto fail; + } + if (!(stx.stx_mask & STATX_TYPE) || + !(S_ISREG(stx.stx_mode) || S_ISDIR(stx.stx_mode))) + { + /* + * We must not open special files with anything but O_PATH, so we + * cannot use this file for mount_fds. + * Just return a failure in such a case and let the lo_inode have + * an O_PATH fd instead of a file handle. + */ + goto fail; + } + + /* Now that we know this fd is safe to open, do it */ + snprintf(procname, sizeof(procname), "%i", path_fd.fd); + fd = openat(lo->proc_self_fd, procname, O_RDONLY); + if (fd < 0) { + goto fail; + } + + if (pthread_rwlock_wrlock(&mount_fds_lock)) { + goto fail; + } + + /* Check again, might have changed */ + if (g_hash_table_contains(mount_fds, GINT_TO_POINTER(fh->mount_id))) { + close(fd); + } else { + g_hash_table_insert(mount_fds, + GINT_TO_POINTER(fh->mount_id), + GINT_TO_POINTER(fd)); + } + } + pthread_rwlock_unlock(&mount_fds_lock); + + return fh; + +fail: + free(fh); + return NULL; +#else /* defined(CONFIG_STATX) && defined(STATX_MNT_ID) */ + return NULL; +#endif +} + /** * Open the given file handle with the given flags. * @@ -1132,6 +1266,11 @@ static int do_statx(struct lo_data *lo, int dirfd, const char *pathname, return -1; } lo->use_statx = false; + if (lo->inode_file_handles) { + fuse_log(FUSE_LOG_WARNING, + "statx() does not work: Will not be able to use file " + "handles for inodes\n"); + } /* fallback */ } #endif @@ -1161,6 +1300,7 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, struct lo_data *lo = lo_data(req); struct lo_inode *inode = NULL; struct lo_inode *dir = lo_inode(req, parent); + struct lo_fhandle *fh; if (inodep) { *inodep = NULL; /* in case there is an error */ @@ -1190,13 +1330,19 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, goto out; } - newfd = openat(dir_fd.fd, name, O_PATH | O_NOFOLLOW); - if (newfd == -1) { - goto out_err; - } + fh = get_file_handle(lo, dir_fd.fd, name); + if (fh) { + res = do_statx(lo, dir_fd.fd, name, &e->attr, + AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, &mnt_id); + } else { + newfd = openat(dir_fd.fd, name, O_PATH | O_NOFOLLOW); + if (newfd == -1) { + goto out_err; + } - res = do_statx(lo, newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, - &mnt_id); + res = do_statx(lo, newfd, "", &e->attr, + AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, &mnt_id); + } if (res == -1) { goto out_err; } @@ -1206,9 +1352,19 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, e->attr_flags |= FUSE_ATTR_SUBMOUNT; } - inode = lo_find(lo, NULL, &e->attr, mnt_id); + /* + * Note that fh is always NULL if lo->inode_file_handles is false, + * and so we will never do a lookup by file handle here, and + * lo->inodes_by_handle will always remain empty. We only need + * this map when we do not have an O_PATH fd open for every + * lo_inode, though, so if inode_file_handles is false, we do not + * need that map anyway. + */ + inode = lo_find(lo, fh, &e->attr, mnt_id); if (inode) { - close(newfd); + if (newfd != -1) { + close(newfd); + } } else { inode = calloc(1, sizeof(struct lo_inode)); if (!inode) { @@ -1226,6 +1382,7 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, inode->nlookup = 1; inode->fd = newfd; + inode->fhandle = fh; inode->key.ino = e->attr.st_ino; inode->key.dev = e->attr.st_dev; inode->key.mnt_id = mnt_id; @@ -1237,6 +1394,9 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, pthread_mutex_lock(&lo->mutex); inode->fuse_ino = lo_add_inode_mapping(req, inode); g_hash_table_insert(lo->inodes_by_ids, &inode->key, inode); + if (inode->fhandle) { + g_hash_table_insert(lo->inodes_by_handle, inode->fhandle, inode); + } pthread_mutex_unlock(&lo->mutex); } e->ino = inode->fuse_ino; @@ -1530,8 +1690,10 @@ static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, int res; uint64_t mnt_id; struct stat attr; + struct lo_fhandle *fh; struct lo_data *lo = lo_data(req); struct lo_inode *dir = lo_inode(req, parent); + struct lo_inode *inode; if (!dir) { return NULL; @@ -1542,13 +1704,19 @@ static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, return NULL; } + fh = get_file_handle(lo, dir_fd.fd, name); + /* Ignore errors, this is just an optional key for the lookup */ + res = do_statx(lo, dir_fd.fd, name, &attr, AT_SYMLINK_NOFOLLOW, &mnt_id); lo_inode_put(lo, &dir); if (res == -1) { return NULL; } - return lo_find(lo, NULL, &attr, mnt_id); + inode = lo_find(lo, fh, &attr, mnt_id); + g_free(fh); + + return inode; } static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) @@ -1712,6 +1880,9 @@ static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n) if (!inode->nlookup) { lo_map_remove(&lo->ino_map, inode->fuse_ino); g_hash_table_remove(lo->inodes_by_ids, &inode->key); + if (inode->fhandle) { + g_hash_table_remove(lo->inodes_by_handle, inode->fhandle); + } if (lo->posix_lock) { if (g_hash_table_size(inode->posix_locks)) { fuse_log(FUSE_LOG_WARNING, "Hash table is not empty\n"); @@ -4156,6 +4327,14 @@ int main(int argc, char *argv[]) lo.use_statx = true; +#if !defined(CONFIG_STATX) || !defined(STATX_MNT_ID) + if (lo.inode_file_handles) { + fuse_log(FUSE_LOG_WARNING, + "No statx() or mount ID support: Will not be able to use file " + "handles for inodes\n"); + } +#endif + se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo); if (se == NULL) { goto err_out1; diff --git a/tools/virtiofsd/passthrough_seccomp.c b/tools/virtiofsd/passthrough_seccomp.c index e948f25ac1..ed23e67ba8 100644 --- a/tools/virtiofsd/passthrough_seccomp.c +++ b/tools/virtiofsd/passthrough_seccomp.c @@ -73,6 +73,7 @@ static const int syscall_allowlist[] = { SCMP_SYS(mprotect), SCMP_SYS(mremap), SCMP_SYS(munmap), + SCMP_SYS(name_to_handle_at), SCMP_SYS(newfstatat), SCMP_SYS(statx), SCMP_SYS(open), From patchwork Wed Jun 9 15:55:51 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Max Reitz X-Patchwork-Id: 12310681 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A7F60C48BCD for ; Wed, 9 Jun 2021 16:11:51 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 4B75F610A2 for ; Wed, 9 Jun 2021 16:11:51 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 4B75F610A2 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:60636 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lr0o1-0000Uz-VN for qemu-devel@archiver.kernel.org; Wed, 09 Jun 2021 12:11:50 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:58134) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lr0Zk-0005EV-8T for qemu-devel@nongnu.org; Wed, 09 Jun 2021 11:57:04 -0400 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:28297) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lr0Zi-0007JB-A8 for qemu-devel@nongnu.org; Wed, 09 Jun 2021 11:57:03 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1623254221; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=59tV/9eGR8xY/eK4oT1wrIGt5zNlw4G3TmhR8Y+6xdQ=; b=TjJHgK1uIYGwmSNP62hMAB04mKRZcAYKdU9xcjT9E+0v9rtMk6KrFyW+ONhKxP0PHYuAPp UHfRshJZAV40h+fXl7pMGCQ4EzwKLR57yzxloQ9DmXSGE6PrHALB2lWtuG+Da5C+qgsmL9 isgYVmbap59vfZC/ml8PRi8sZQPt7KQ= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-51--s0ZRh50NwGsFI_rnSZI3A-1; Wed, 09 Jun 2021 11:57:00 -0400 X-MC-Unique: -s0ZRh50NwGsFI_rnSZI3A-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 724933636D for ; Wed, 9 Jun 2021 15:56:59 +0000 (UTC) Received: from localhost (ovpn-114-102.ams2.redhat.com [10.36.114.102]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 2B0035D9F2; Wed, 9 Jun 2021 15:56:51 +0000 (UTC) From: Max Reitz To: qemu-devel@nongnu.org, virtio-fs@redhat.com Subject: [PATCH v2 9/9] virtiofsd: Add lazy lo_do_find() Date: Wed, 9 Jun 2021 17:55:51 +0200 Message-Id: <20210609155551.44437-10-mreitz@redhat.com> In-Reply-To: <20210609155551.44437-1-mreitz@redhat.com> References: <20210609155551.44437-1-mreitz@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mreitz@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Received-SPF: pass client-ip=216.205.24.124; envelope-from=mreitz@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -29 X-Spam_score: -3.0 X-Spam_bar: --- X-Spam_report: (-3.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.199, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr . David Alan Gilbert" , Stefan Hajnoczi , Max Reitz Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" lo_find() right now takes two lookup keys for two maps, namely the file handle for inodes_by_handle and the statx information for inodes_by_ids. However, we only need the statx information if looking up the inode by the file handle failed. There are two callers of lo_find(): The first one, lo_do_lookup(), has both keys anyway, so passing them does not incur any additional cost. The second one, lookup_name(), though, needs to explicitly invoke name_to_handle_at() (through get_file_handle()) and statx() (through do_statx()). We need to try to get a file handle as the primary key, so we cannot get rid of get_file_handle(), but we only need the statx information if looking up an inode by handle failed; so we can defer that until the lookup has indeed failed. To this end, replace lo_find()'s st/mnt_id parameters by a get_ids() closure that is invoked to fill the lo_key struct if necessary. Also, lo_find() is renamed to lo_do_find(), so we can add a new lo_find() wrapper whose closure just initializes the lo_key from the st/mnt_id parameters, just like the old lo_find() did. lookup_name() directly calls lo_do_find() now and passes its own closure, which performs the do_statx() call. Signed-off-by: Max Reitz Reviewed-by: Connor Kuehl --- tools/virtiofsd/passthrough_ll.c | 93 ++++++++++++++++++++++++++------ 1 file changed, 76 insertions(+), 17 deletions(-) diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index 2e56c40b2f..8990fd5bd2 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -1168,22 +1168,23 @@ out_err: fuse_reply_err(req, saverr); } -static struct lo_inode *lo_find(struct lo_data *lo, - const struct lo_fhandle *fhandle, - struct stat *st, uint64_t mnt_id) +/* + * get_ids() will be called to get the key for lo->inodes_by_ids if + * the lookup by file handle has failed. + */ +static struct lo_inode *lo_do_find(struct lo_data *lo, + const struct lo_fhandle *fhandle, + int (*get_ids)(struct lo_key *, const void *), + const void *get_ids_opaque) { struct lo_inode *p = NULL; - struct lo_key ids_key = { - .ino = st->st_ino, - .dev = st->st_dev, - .mnt_id = mnt_id, - }; + struct lo_key ids_key; pthread_mutex_lock(&lo->mutex); if (fhandle) { p = g_hash_table_lookup(lo->inodes_by_handle, fhandle); } - if (!p) { + if (!p && get_ids(&ids_key, get_ids_opaque) == 0) { p = g_hash_table_lookup(lo->inodes_by_ids, &ids_key); /* * When we had to fall back to looking up an inode by its IDs, @@ -1211,6 +1212,36 @@ static struct lo_inode *lo_find(struct lo_data *lo, return p; } +struct lo_find_get_ids_key_opaque { + const struct stat *st; + uint64_t mnt_id; +}; + +static int lo_find_get_ids_key(struct lo_key *ids_key, const void *opaque) +{ + const struct lo_find_get_ids_key_opaque *stat_info = opaque; + + *ids_key = (struct lo_key){ + .ino = stat_info->st->st_ino, + .dev = stat_info->st->st_dev, + .mnt_id = stat_info->mnt_id, + }; + + return 0; +} + +static struct lo_inode *lo_find(struct lo_data *lo, + const struct lo_fhandle *fhandle, + struct stat *st, uint64_t mnt_id) +{ + const struct lo_find_get_ids_key_opaque stat_info = { + .st = st, + .mnt_id = mnt_id, + }; + + return lo_do_find(lo, fhandle, lo_find_get_ids_key, &stat_info); +} + /* value_destroy_func for posix_locks GHashTable */ static void posix_locks_value_destroy(gpointer data) { @@ -1682,14 +1713,41 @@ out_err: fuse_reply_err(req, saverr); } +struct lookup_name_get_ids_key_opaque { + struct lo_data *lo; + int parent_fd; + const char *name; +}; + +static int lookup_name_get_ids_key(struct lo_key *ids_key, const void *opaque) +{ + const struct lookup_name_get_ids_key_opaque *stat_params = opaque; + uint64_t mnt_id; + struct stat attr; + int res; + + res = do_statx(stat_params->lo, stat_params->parent_fd, stat_params->name, + &attr, AT_SYMLINK_NOFOLLOW, &mnt_id); + if (res < 0) { + return -errno; + } + + *ids_key = (struct lo_key){ + .ino = attr.st_ino, + .dev = attr.st_dev, + .mnt_id = mnt_id, + }; + + return 0; +} + /* Increments nlookup and caller must release refcount using lo_inode_put() */ static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, const char *name) { g_auto(TempFd) dir_fd = TEMP_FD_INIT; int res; - uint64_t mnt_id; - struct stat attr; + struct lookup_name_get_ids_key_opaque stat_params; struct lo_fhandle *fh; struct lo_data *lo = lo_data(req); struct lo_inode *dir = lo_inode(req, parent); @@ -1707,13 +1765,14 @@ static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, fh = get_file_handle(lo, dir_fd.fd, name); /* Ignore errors, this is just an optional key for the lookup */ - res = do_statx(lo, dir_fd.fd, name, &attr, AT_SYMLINK_NOFOLLOW, &mnt_id); - lo_inode_put(lo, &dir); - if (res == -1) { - return NULL; - } + stat_params = (struct lookup_name_get_ids_key_opaque){ + .lo = lo, + .parent_fd = dir_fd.fd, + .name = name, + }; - inode = lo_find(lo, fh, &attr, mnt_id); + inode = lo_do_find(lo, fh, lookup_name_get_ids_key, &stat_params); + lo_inode_put(lo, &dir); g_free(fh); return inode;