From nobody Sun Dec 14 02:18:34 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D9D7CC27C7C for ; Fri, 20 Jan 2023 15:24:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231389AbjATPYp (ORCPT ); Fri, 20 Jan 2023 10:24:45 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55518 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230087AbjATPYn (ORCPT ); Fri, 20 Jan 2023 10:24:43 -0500 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BCAB9D05EF for ; Fri, 20 Jan 2023 07:23:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1674228233; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=M+e8LXuZohWsWAKx1dvrBva8MxDrnRmzpeJF6iBcKD4=; b=eGsMP6o2okaxM4mgNZJIsIDwRKDULHo9+lF2jm7p8eu62jkhmwKNN1bNyx4mTF+Vb0YkSj 2lkUWzHNZmwQjdQtTrEamXHP8DQKmgtM4NJQJEFR8f9UA5oMtZTt9F/D9nUPJ7bgT/YZzr Y84Qlqj1T2hFjCQlmazu+UR1mn60Ovk= Received: from mail-ed1-f72.google.com (mail-ed1-f72.google.com [209.85.208.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-73-0Gxxc3-MNAayknGs_O_mcQ-1; Fri, 20 Jan 2023 10:23:51 -0500 X-MC-Unique: 0Gxxc3-MNAayknGs_O_mcQ-1 Received: by mail-ed1-f72.google.com with SMTP id x5-20020a05640226c500b0049e840f68feso2741483edd.23 for ; Fri, 20 Jan 2023 07:23:51 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=M+e8LXuZohWsWAKx1dvrBva8MxDrnRmzpeJF6iBcKD4=; b=fUZxkb5r3ApxFYws3Ibsn8kOL2LIiTJ/cn22yDQ2J1vh0uuWEMdDrFuf0Y4dHAMfNI n+jROVw7TtZOHlFxxblGads1sdyiuLcOUM3WrJkOcIgtL/6tDtGPDaLUlUZbI4Fo1w5t uD5scEEcqBuei7W5c1bni6ubyL7fF8yLJ5VqEJZAM0xo/Yy0zsJI6VBLnTf7aErHlqRo oozv1janF3cNdWz9dqzhjusi21kv+KzpTUvJmlbbBhmhR+Ez4WNbPrTMUPvTkzuo2vUY htM0GCV2BSE744oaxJ8YmpyHOW9DUobn8mN+J4fWgUHF0oAm5Q3WAqB9cJ+JjrpaFzOg 6Dkw== X-Gm-Message-State: AFqh2ko8PNUznRH6yKjTU2B3gUGAGeyRsmP1KqxbgoxSkarxx3au6OGe 77N+iSwpiFJF9g0v0+77M4dW6cBZzaG3ViqtjqJngW5FVPAcl617zWloDRAt0gz0tyCY2ENLGIB UPsXOwluwn37sabHLZaue7Nyf X-Received: by 2002:a17:907:c019:b0:867:ef3f:dd85 with SMTP id ss25-20020a170907c01900b00867ef3fdd85mr12923881ejc.56.1674228230751; Fri, 20 Jan 2023 07:23:50 -0800 (PST) X-Google-Smtp-Source: AMrXdXsMEOaz7fsloftAxJvTFdFe440vrPpM2DDvZFE55XjUBpJy1hWqEcm+5zW5lIHpijYVTDtVDQ== X-Received: by 2002:a17:907:c019:b0:867:ef3f:dd85 with SMTP id ss25-20020a170907c01900b00867ef3fdd85mr12923863ejc.56.1674228230607; Fri, 20 Jan 2023 07:23:50 -0800 (PST) Received: from localhost.localdomain (c-e6a5e255.022-110-73746f36.bbcust.telenor.se. [85.226.165.230]) by smtp.googlemail.com with ESMTPSA id s16-20020a1709067b9000b00872eb47f46dsm5706976ejo.19.2023.01.20.07.23.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Jan 2023 07:23:49 -0800 (PST) From: Alexander Larsson To: linux-fsdevel@vger.kernel.org Cc: linux-kernel@vger.kernel.org, gscrivan@redhat.com, david@fromorbit.com, brauner@kernel.org, viro@zeniv.linux.org.uk, Alexander Larsson Subject: [PATCH v3 1/6] fsverity: Export fsverity_get_digest Date: Fri, 20 Jan 2023 16:23:29 +0100 Message-Id: X-Mailer: git-send-email 2.39.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Composefs needs to call this when built in module form, so we need to export the symbol. This uses EXPORT_SYMBOL_GPL like the other fsverity functions do. Signed-off-by: Alexander Larsson --- fs/verity/measure.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/verity/measure.c b/fs/verity/measure.c index 5c79ea1b2468..875d143e0c7e 100644 --- a/fs/verity/measure.c +++ b/fs/verity/measure.c @@ -85,3 +85,4 @@ int fsverity_get_digest(struct inode *inode, *alg =3D hash_alg->algo_id; return 0; } +EXPORT_SYMBOL_GPL(fsverity_get_digest); --=20 2.39.0 From nobody Sun Dec 14 02:18:34 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1C4BFC05027 for ; Fri, 20 Jan 2023 15:25:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231451AbjATPZI (ORCPT ); Fri, 20 Jan 2023 10:25:08 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55610 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231416AbjATPYw (ORCPT ); Fri, 20 Jan 2023 10:24:52 -0500 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 705DA677B6 for ; Fri, 20 Jan 2023 07:23:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1674228236; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=4q+L6Afy5JT99kRsKQKl76nPtSUdZQ/nsXSjE4NKhco=; b=e61L4/gJJAdSbggBSWLl9RlOmKx47/XAdzahppWXwccsCctNHcQlteg7/miOMtK92lqbzj +eYqYbcIThYh6Mf/gOo0gjdVHySPItiGiOZ9R1IsrEIl+peaN17DD38huGGFEnUnLQ37hI WTHY+4g7ruQYdKK0MoSrn5/NQsgsmf8= Received: from mail-ed1-f69.google.com (mail-ed1-f69.google.com [209.85.208.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-628-xVLbElljPcO4guvFIQ8uHA-1; Fri, 20 Jan 2023 10:23:52 -0500 X-MC-Unique: xVLbElljPcO4guvFIQ8uHA-1 Received: by mail-ed1-f69.google.com with SMTP id h18-20020a056402281200b0049e5078a6c3so4105254ede.12 for ; Fri, 20 Jan 2023 07:23:52 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=4q+L6Afy5JT99kRsKQKl76nPtSUdZQ/nsXSjE4NKhco=; b=Eyr6cTyT1Yw9D/JnT3iYbKNVIzGi+VGTUlPa4GetEWXkPHLgH6otGXo6swhqX/VH4U j61kzMR5CfrGrpHvGCLfCAUVlI//h9qOozO+6oyKowWYSB/deWNbxwsN2m+HEHX1tE1o zywqbT4iDh/9RKL6ZjLsGzX7sIzqbXBFgKTFAWV6inQI7+EH5JPGnygZM6q22/1ef1VD qzzh0fVknYFFJoTBD+Pnt2Ror3DyJjtF5Yvt77X/RPHSf/R/jSC6LowsXYBSQj8n0Rgq Q1wngB+cK3QhQBecIiX7B/jlLsgmC+AfuAsDZP9XjhQsbE8h9w9f6iITiaQGiHqB+CoT nT5w== X-Gm-Message-State: AFqh2kqcxmchtRHIU+9C1J8D7dZNhQJfat2uJTBi6gOJccdqsp+Jl0kF JZK69SuwI0nhcMkdkQIT46AfFeFsciPCU3wId8+h4v02KHgWuK5Znh4sW7P3fhqICvIx7lee9Sc t7q65ssg5GHMXyLM1SP7wIXuY X-Received: by 2002:a17:907:2bdd:b0:857:b916:94c2 with SMTP id gv29-20020a1709072bdd00b00857b91694c2mr10703812ejc.35.1674228231801; Fri, 20 Jan 2023 07:23:51 -0800 (PST) X-Google-Smtp-Source: AMrXdXute8wTDnxMFCRBA7qrgVSJK+iTu1iuf8iBjGopV5NeM2ayALt6GVtdz2l0sr0jHwR09YFsyQ== X-Received: by 2002:a17:907:2bdd:b0:857:b916:94c2 with SMTP id gv29-20020a1709072bdd00b00857b91694c2mr10703792ejc.35.1674228231508; Fri, 20 Jan 2023 07:23:51 -0800 (PST) Received: from localhost.localdomain (c-e6a5e255.022-110-73746f36.bbcust.telenor.se. [85.226.165.230]) by smtp.googlemail.com with ESMTPSA id s16-20020a1709067b9000b00872eb47f46dsm5706976ejo.19.2023.01.20.07.23.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Jan 2023 07:23:51 -0800 (PST) From: Alexander Larsson To: linux-fsdevel@vger.kernel.org Cc: linux-kernel@vger.kernel.org, gscrivan@redhat.com, david@fromorbit.com, brauner@kernel.org, viro@zeniv.linux.org.uk, Alexander Larsson Subject: [PATCH v3 2/6] composefs: Add on-disk layout header Date: Fri, 20 Jan 2023 16:23:30 +0100 Message-Id: <19f39cabc13b646bf9084bfeacddc6599f7cd389.1674227308.git.alexl@redhat.com> X-Mailer: git-send-email 2.39.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" This header contains the on-disk layout of the composefs file format. The basic format is a simple subperblock with a version and magic number at the start for filetype detection. after that, a table of inodes (indexed by inode number) data containing all the fixed-size inode elements. After the inodes (at offset specified in superblock) is a variable data section that is linked to by the inodes for: * symlink targets, * backing filenames * xattrs * dirents The goal of this file format is to be simple and efficient to decode when mapped directly from the page cache. This allows an easy to understand and maintain codebase. Signed-off-by: Alexander Larsson Co-developed-by: Giuseppe Scrivano Signed-off-by: Giuseppe Scrivano --- fs/composefs/cfs.h | 172 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 fs/composefs/cfs.h diff --git a/fs/composefs/cfs.h b/fs/composefs/cfs.h new file mode 100644 index 000000000000..9209b80dd6ca --- /dev/null +++ b/fs/composefs/cfs.h @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * composefs + * + * Copyright (C) 2021 Giuseppe Scrivano + * Copyright (C) 2022 Alexander Larsson + * + * This file is released under the GPL. + */ + +#ifndef _CFS_H +#define _CFS_H + +#include +#include +#include +#include +#include + +/* Descriptor file layout: + * + * +-----------------------+ + * | cfs_superblock | + * | vdata_offfset |---| + * +-----------------------| | + * | Inode table | | + * | N * cfs_inode_data | | + * +-----------------------| | + * | Variable data section |<--/ + * | Used for: | + * | symlink targets | + * | backing file paths | + * | dirents | + * | xattrs | + * | digests | + * +-----------------------+ + * + * The superblock is at the start of the file, and the inode table + * directly follows it. The variable data section found via + * vdata_offset and all sections are 32bit aligned. All data is + * little endian. + * + * The inode table is a table of fixed size cfs_inode_data elements. + * The filesystem inode numbers are 32bit indexes into this table. + * Actual file content (for regular files) is referenced by a backing + * file path which is looked up relative to a given base dir. + * + * All variable size data are stored in the variable data section and + * are referenced using cfs_vdata (64bit offset from the start of the + * vdata section and 32bit lengths). + * + * Directory dirent data is stored in one 32bit aligned vdata chunk, + * staring with a table of fixed size cfs_dirents and which is + * followed by a string table. The dirents reference the strings by + * offsets form the string table. The dirents are sorted for efficient + * binary search lookups. + * + * Xattrs data are stored in a 32bit aligned vdata chunk. This is + * a table of cfs_xattr, followed by the key/value data. The + * xattrs are sorted by key. Note that many inodes can reference + * the same xattr data. + */ + +/* Current (and atm only) version of the image format. */ +#define CFS_VERSION 1 + +#define CFS_MAGIC 0xc078629aU + +#define CFS_SUPERBLOCK_OFFSET 0 +#define CFS_INODE_TABLE_OFFSET sizeof(struct cfs_superblock) +#define CFS_INODE_SIZE sizeof(struct cfs_inode_data) +#define CFS_DIRENT_SIZE sizeof(struct cfs_dirent) +#define CFS_XATTR_ELEM_SIZE sizeof(struct cfs_xattr_element) +#define CFS_ROOT_INO 0 + +/* Fits at least the root inode */ +#define CFS_DESCRIPTOR_MIN_SIZE = \ + (sizeof(struct cfs_superblock) + sizeof(struct cfs_inode_data)) + +/* More that this would overflow header size computation */ +#define CFS_MAX_DIRENTS (U32_MAX / CFS_DIRENT_SIZE - 1) + +#define CFS_MAX_XATTRS U16_MAX + +struct cfs_superblock { + __le32 version; /* CFS_VERSION */ + __le32 magic; /* CFS_MAGIC */ + + /* Offset of the variable data section from start of file */ + __le64 vdata_offset; + + /* For future use, and makes superblock 128 bytes to align + * inode table on cacheline boundary on most arches. + */ + __le32 unused[28]; +} __packed; + +struct cfs_vdata { + __le64 off; /* Offset into variable data section */ + __le32 len; +} __packed; + +struct cfs_inode_data { + __le32 st_mode; /* File type and mode. */ + __le32 st_nlink; /* Number of hard links, only for regular files. */ + __le32 st_uid; /* User ID of owner. */ + __le32 st_gid; /* Group ID of owner. */ + __le32 st_rdev; /* Device ID (if special file). */ + __le64 st_size; /* Size of file */ + __le64 st_mtim_sec; + __le32 st_mtim_nsec; + __le64 st_ctim_sec; + __le32 st_ctim_nsec; + + /* References to variable storage area: */ + + /* per-type variable data: + * S_IFDIR: dirents + * S_IFREG: backing file pathnem + * S_IFLNLK; symlink target + */ + struct cfs_vdata variable_data; + + struct cfs_vdata xattrs; + struct cfs_vdata digest; /* Expected fs-verity digest of backing file */ + + /* For future use, and makes inode_data 96 bytes which + * is semi-aligned with cacheline sizes. + */ + __le32 unused[2]; +} __packed; + +struct cfs_dirent { + __le32 inode_num; /* Index in inode table */ + __le32 name_offset; /* Offset from end of cfs_dir_header */ + u8 name_len; + u8 d_type; + u16 _padding; +} __packed; + +/* Directory entries, stored in variable data section, 32bit aligned, + * followed by name string table + */ +struct cfs_dir_header { + __le32 n_dirents; + struct cfs_dirent dirents[]; +} __packed; + +static inline size_t cfs_dir_header_size(size_t n_dirents) +{ + return sizeof(struct cfs_dir_header) + n_dirents * CFS_DIRENT_SIZE; +} + +struct cfs_xattr_element { + __le16 key_length; + __le16 value_length; +} __packed; + +/* Xattrs, stored in variable data section , 32bit aligned, followed + * by key/value table + */ +struct cfs_xattr_header { + __le16 n_attr; + struct cfs_xattr_element attr[0]; +} __packed; + +static inline size_t cfs_xattr_header_size(size_t n_element) +{ + return sizeof(struct cfs_xattr_header) + n_element * CFS_XATTR_ELEM_SIZE; +} + +#endif --=20 2.39.0 From nobody Sun Dec 14 02:18:34 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 17628C27C7C for ; Fri, 20 Jan 2023 15:25:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231458AbjATPZO (ORCPT ); Fri, 20 Jan 2023 10:25:14 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55614 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231430AbjATPYx (ORCPT ); Fri, 20 Jan 2023 10:24:53 -0500 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 868D0DA135 for ; Fri, 20 Jan 2023 07:23:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1674228235; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=s0/zcIGGKTApHMlSGG+YoMBJsFSTCsxQiIWlkpTWtTU=; b=RxLzr/GBcDkhRQNpV1giYdp6AQtQBLimVPaEQh2bf/hqmq0U7jQQcmnxAHPACBnRO22ZqG AcgXpLBLXG7XZZBSUsvKNqLWlpdqH1sW7iqTLqc8a4Xjz8IcdA2EVsjlGOgcoXTiRHyuGB 81j5DSdajWhEj8qZEN20sMnEAd4+odY= Received: from mail-ed1-f70.google.com (mail-ed1-f70.google.com [209.85.208.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-571-8Hb9IOugPuupIIIZFGg3Pw-1; Fri, 20 Jan 2023 10:23:54 -0500 X-MC-Unique: 8Hb9IOugPuupIIIZFGg3Pw-1 Received: by mail-ed1-f70.google.com with SMTP id z2-20020a056402274200b0049e48d86760so4094580edd.4 for ; Fri, 20 Jan 2023 07:23:54 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=s0/zcIGGKTApHMlSGG+YoMBJsFSTCsxQiIWlkpTWtTU=; b=XZAPP7hCpDunj1q5K2lQOi4rF7XOs8jitMSO0w8xy4DucqGDyN+l0Ox/hGaQ6MwkMo byDHmcbLTFH38x5lCGrSPHzAq/mQCQgTO7dIn3hXTS+XKZo4vwG0MZjgWdzamyzz3Zf7 uF9OuCQEzr8zTFD8b1/gWvtB8mGJgQ+OiF1ctSueuCgIcahvoInoTcfQvhVpf3JT1LGd CDmnUD2IUbSi86PrBkUWSgvFJDD3Xc+HarZd9gn5SZWGUwnpU9ziyzJEFL7RsC9yDqxa ME8y+MPj0rmRP7l/A19LglvH28dccjNzhem/qkcTqQ4NTmjDNVX9SEG0bbDpKGCQORX6 ufiQ== X-Gm-Message-State: AFqh2kozsXJm1gZFLh4id4VyKVnapkPjSduUuAmmN1Qb79r02KOorzU3 Y2nbvPf/fsYeWsj114rzcn4FmWKB1Zz8GVDGCx097+M1su36DyG7tVKwt4eAvVxYslBkesVCzXl 1WVX/0/50wQs/KuwvnNpTRVpQ X-Received: by 2002:a17:906:9e91:b0:84d:373b:39da with SMTP id fx17-20020a1709069e9100b0084d373b39damr13328812ejc.40.1674228233020; Fri, 20 Jan 2023 07:23:53 -0800 (PST) X-Google-Smtp-Source: AMrXdXsGcjWh87mhq41m34RJBZCY0zxbnw4W+rrq6YeNlwMvN/YQDsv7qURkx8RZ7LeLkM4j6fTfSA== X-Received: by 2002:a17:906:9e91:b0:84d:373b:39da with SMTP id fx17-20020a1709069e9100b0084d373b39damr13328796ejc.40.1674228232739; Fri, 20 Jan 2023 07:23:52 -0800 (PST) Received: from localhost.localdomain (c-e6a5e255.022-110-73746f36.bbcust.telenor.se. [85.226.165.230]) by smtp.googlemail.com with ESMTPSA id s16-20020a1709067b9000b00872eb47f46dsm5706976ejo.19.2023.01.20.07.23.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Jan 2023 07:23:52 -0800 (PST) From: Alexander Larsson To: linux-fsdevel@vger.kernel.org Cc: linux-kernel@vger.kernel.org, gscrivan@redhat.com, david@fromorbit.com, brauner@kernel.org, viro@zeniv.linux.org.uk, Alexander Larsson Subject: [PATCH v3 3/6] composefs: Add descriptor parsing code Date: Fri, 20 Jan 2023 16:23:31 +0100 Message-Id: X-Mailer: git-send-email 2.39.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" This adds the code to load and decode the filesystem descriptor file format. We open the descriptor at mount time and keep the struct file * around. Most accesses to it happens via cfs_get_buf() which reads the descriptor data directly from the page cache. Although in a few cases (like when we need to directly copy data) we use kernel_read() instead. Signed-off-by: Alexander Larsson Co-developed-by: Giuseppe Scrivano Signed-off-by: Giuseppe Scrivano --- fs/composefs/cfs-internals.h | 55 +++ fs/composefs/cfs-reader.c | 720 +++++++++++++++++++++++++++++++++++ 2 files changed, 775 insertions(+) create mode 100644 fs/composefs/cfs-internals.h create mode 100644 fs/composefs/cfs-reader.c diff --git a/fs/composefs/cfs-internals.h b/fs/composefs/cfs-internals.h new file mode 100644 index 000000000000..3524b977c8a8 --- /dev/null +++ b/fs/composefs/cfs-internals.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _CFS_INTERNALS_H +#define _CFS_INTERNALS_H + +#include "cfs.h" + +#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ + +struct cfs_inode_extra_data { + char *path_payload; /* Real pathname for files, target for symlinks */ + + u64 xattrs_offset; + u32 xattrs_len; + + u64 dirents_offset; + u32 dirents_len; + + bool has_digest; + u8 digest[SHA256_DIGEST_SIZE]; /* fs-verity digest */ +}; + +struct cfs_context { + struct file *descriptor; + u64 vdata_offset; + u32 num_inodes; + + u64 descriptor_len; +}; + +int cfs_init_ctx(const char *descriptor_path, const u8 *required_digest, + struct cfs_context *ctx); + +void cfs_ctx_put(struct cfs_context *ctx); + +int cfs_init_inode(struct cfs_context *ctx, u32 inode_num, struct inode *i= node, + struct cfs_inode_extra_data *data); + +ssize_t cfs_list_xattrs(struct cfs_context *ctx, + struct cfs_inode_extra_data *inode_data, char *names, + size_t size); +int cfs_get_xattr(struct cfs_context *ctx, struct cfs_inode_extra_data *in= ode_data, + const char *name, void *value, size_t size); + +typedef bool (*cfs_dir_iter_cb)(void *private, const char *name, int namel= en, + u64 ino, unsigned int dtype); + +int cfs_dir_iterate(struct cfs_context *ctx, u64 index, + struct cfs_inode_extra_data *inode_data, loff_t first, + cfs_dir_iter_cb cb, void *private); + +int cfs_dir_lookup(struct cfs_context *ctx, u64 index, + struct cfs_inode_extra_data *inode_data, const char *name, + size_t name_len, u64 *index_out); + +#endif diff --git a/fs/composefs/cfs-reader.c b/fs/composefs/cfs-reader.c new file mode 100644 index 000000000000..6ff7d3e70d39 --- /dev/null +++ b/fs/composefs/cfs-reader.c @@ -0,0 +1,720 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * composefs + * + * Copyright (C) 2021 Giuseppe Scrivano + * Copyright (C) 2022 Alexander Larsson + * + * This file is released under the GPL. + */ + +#include "cfs-internals.h" + +#include +#include +#include +#include + +/* When mapping buffers via page arrays this is an "arbitrary" limit + * to ensure we're not ballooning memory use for the page array and + * mapping. On a 4k page, 64bit machine this limit will make the page + * array fit in one page, and will allow a mapping of 2MB. When + * applied to e.g. dirents this will allow more than 27000 filenames + * of length 64, which seems ok. If we need to support more, at that + * point we should probably fall back to an approach that maps pages + * incrementally. + */ +#define CFS_BUF_MAXPAGES 512 + +#define CFS_BUF_PREALLOC_SIZE 4 + +/* Check if the element, which is supposed to be offset from section_start + * actually fits in the section starting at section_start ending at sectio= n_end, + * and doesn't wrap. + */ +static bool cfs_is_in_section(u64 section_start, u64 section_end, + u64 element_offset, u64 element_size) +{ + u64 element_end; + u64 element_start; + + element_start =3D section_start + element_offset; + if (element_start < section_start || element_start >=3D section_end) + return false; + + element_end =3D element_start + element_size; + if (element_end < element_start || element_end > section_end) + return false; + + return true; +} + +struct cfs_buf { + struct page **pages; + size_t n_pages; + void *base; + + /* Used as "pages" above to avoid allocation for small buffers */ + struct page *prealloc[CFS_BUF_PREALLOC_SIZE]; +}; + +static void cfs_buf_put(struct cfs_buf *buf) +{ + if (buf->pages) { + if (buf->n_pages =3D=3D 1) + kunmap_local(buf->base); + else + vm_unmap_ram(buf->base, buf->n_pages); + for (size_t i =3D 0; i < buf->n_pages; i++) + put_page(buf->pages[i]); + if (buf->n_pages > CFS_BUF_PREALLOC_SIZE) + kfree(buf->pages); + buf->pages =3D NULL; + } +} + +/* Map data from anywhere in the descriptor */ +static void *cfs_get_buf(struct cfs_context *ctx, u64 offset, u32 size, + struct cfs_buf *buf) +{ + struct inode *inode =3D ctx->descriptor->f_inode; + struct address_space *const mapping =3D inode->i_mapping; + size_t n_pages, read_pages; + u64 index, last_index; + struct page **pages; + void *base; + + if (buf->pages) + return ERR_PTR(-EINVAL); + + if (!cfs_is_in_section(0, ctx->descriptor_len, offset, size) || size =3D= =3D 0) + return ERR_PTR(-EFSCORRUPTED); + + index =3D offset >> PAGE_SHIFT; + last_index =3D (offset + size - 1) >> PAGE_SHIFT; + n_pages =3D last_index - index + 1; + + if (n_pages > CFS_BUF_MAXPAGES) + return ERR_PTR(-ENOMEM); + + if (n_pages > CFS_BUF_PREALLOC_SIZE) { + pages =3D kmalloc_array(n_pages, sizeof(struct page *), GFP_KERNEL); + if (!pages) + return ERR_PTR(-ENOMEM); + } else { + /* Avoid allocation in common (small) cases */ + pages =3D buf->prealloc; + } + + for (read_pages =3D 0; read_pages < n_pages; read_pages++) { + struct page *page =3D + read_cache_page(mapping, index + read_pages, NULL, NULL); + if (IS_ERR(page)) + goto nomem; + pages[read_pages] =3D page; + } + + if (n_pages =3D=3D 1) { + base =3D kmap_local_page(pages[0]); + } else { + base =3D vm_map_ram(pages, n_pages, -1); + if (!base) + goto nomem; + } + + buf->pages =3D pages; + buf->n_pages =3D n_pages; + buf->base =3D base; + + return base + (offset & (PAGE_SIZE - 1)); + +nomem: + for (size_t i =3D 0; i < read_pages; i++) + put_page(pages[i]); + if (n_pages > CFS_BUF_PREALLOC_SIZE) + kfree(pages); + + return ERR_PTR(-ENOMEM); +} + +/* Map data from the inode table */ +static void *cfs_get_inode_buf(struct cfs_context *ctx, u64 offset, u32 le= n, + struct cfs_buf *buf) +{ + if (!cfs_is_in_section(CFS_INODE_TABLE_OFFSET, ctx->vdata_offset, offset,= len)) + return ERR_PTR(-EINVAL); + + return cfs_get_buf(ctx, CFS_INODE_TABLE_OFFSET + offset, len, buf); +} + +/* Map data from the variable data section */ +static void *cfs_get_vdata_buf(struct cfs_context *ctx, u64 offset, u32 le= n, + struct cfs_buf *buf) +{ + if (!cfs_is_in_section(ctx->vdata_offset, ctx->descriptor_len, offset, le= n)) + return ERR_PTR(-EINVAL); + + return cfs_get_buf(ctx, ctx->vdata_offset + offset, len, buf); +} + +/* Read data from anywhere in the descriptor */ +static void *cfs_read_data(struct cfs_context *ctx, u64 offset, u32 size, = u8 *dest) +{ + loff_t pos =3D offset; + size_t copied; + + if (!cfs_is_in_section(0, ctx->descriptor_len, offset, size)) + return ERR_PTR(-EFSCORRUPTED); + + copied =3D 0; + while (copied < size) { + ssize_t bytes; + + bytes =3D kernel_read(ctx->descriptor, dest + copied, + size - copied, &pos); + if (bytes < 0) + return ERR_PTR(bytes); + if (bytes =3D=3D 0) + return ERR_PTR(-EINVAL); + + copied +=3D bytes; + } + + if (copied !=3D size) + return ERR_PTR(-EFSCORRUPTED); + return dest; +} + +/* Read data from the variable data section */ +static void *cfs_read_vdata(struct cfs_context *ctx, u64 offset, u32 len, = char *buf) +{ + void *res; + + if (!cfs_is_in_section(ctx->vdata_offset, ctx->descriptor_len, offset, le= n)) + return ERR_PTR(-EINVAL); + + res =3D cfs_read_data(ctx, ctx->vdata_offset + offset, len, buf); + if (IS_ERR(res)) + return ERR_CAST(res); + + return buf; +} + +/* Allocate, read and null-terminate paths from the variable data section = */ +static char *cfs_read_vdata_path(struct cfs_context *ctx, u64 offset, u32 = len) +{ + char *path; + void *res; + + if (len > PATH_MAX) + return ERR_PTR(-EINVAL); + + path =3D kmalloc(len + 1, GFP_KERNEL); + if (!path) + return ERR_PTR(-ENOMEM); + + res =3D cfs_read_vdata(ctx, offset, len, path); + if (IS_ERR(res)) { + kfree(path); + return ERR_CAST(res); + } + + /* zero terminate */ + path[len] =3D 0; + + return path; +} + +int cfs_init_ctx(const char *descriptor_path, const u8 *required_digest, + struct cfs_context *ctx_out) +{ + u8 verity_digest[FS_VERITY_MAX_DIGEST_SIZE]; + struct cfs_superblock superblock_buf; + struct cfs_superblock *superblock; + enum hash_algo verity_algo; + struct cfs_context ctx; + struct file *descriptor; + u64 num_inodes; + loff_t i_size; + int res; + + descriptor =3D filp_open(descriptor_path, O_RDONLY, 0); + if (IS_ERR(descriptor)) + return PTR_ERR(descriptor); + + if (required_digest) { + res =3D fsverity_get_digest(d_inode(descriptor->f_path.dentry), + verity_digest, &verity_algo); + if (res < 0) { + pr_err("ERROR: composefs descriptor has no fs-verity digest\n"); + goto fail; + } + if (verity_algo !=3D HASH_ALGO_SHA256 || + memcmp(required_digest, verity_digest, SHA256_DIGEST_SIZE) !=3D 0) { + pr_err("ERROR: composefs descriptor has wrong fs-verity digest\n"); + res =3D -EINVAL; + goto fail; + } + } + + i_size =3D i_size_read(file_inode(descriptor)); + if (i_size <=3D CFS_DESCRIPTOR_MIN_SIZE) { + res =3D -EINVAL; + goto fail; + } + + /* Need this temporary ctx for cfs_read_data() */ + ctx.descriptor =3D descriptor; + ctx.descriptor_len =3D i_size; + + superblock =3D cfs_read_data(&ctx, CFS_SUPERBLOCK_OFFSET, + sizeof(struct cfs_superblock), + (u8 *)&superblock_buf); + if (IS_ERR(superblock)) { + res =3D PTR_ERR(superblock); + goto fail; + } + + ctx.vdata_offset =3D le64_to_cpu(superblock->vdata_offset); + + /* Some basic validation of the format */ + if (le32_to_cpu(superblock->version) !=3D CFS_VERSION || + le32_to_cpu(superblock->magic) !=3D CFS_MAGIC || + /* vdata is in file */ + ctx.vdata_offset > ctx.descriptor_len || + ctx.vdata_offset <=3D CFS_INODE_TABLE_OFFSET || + /* vdata is aligned */ + ctx.vdata_offset % 4 !=3D 0) { + res =3D -EFSCORRUPTED; + goto fail; + } + + num_inodes =3D (ctx.vdata_offset - CFS_INODE_TABLE_OFFSET) / CFS_INODE_SI= ZE; + if (num_inodes > U32_MAX) { + res =3D -EFSCORRUPTED; + goto fail; + } + ctx.num_inodes =3D num_inodes; + + *ctx_out =3D ctx; + return 0; + +fail: + fput(descriptor); + return res; +} + +void cfs_ctx_put(struct cfs_context *ctx) +{ + if (ctx->descriptor) { + fput(ctx->descriptor); + ctx->descriptor =3D NULL; + } +} + +static bool cfs_validate_filename(const char *name, size_t name_len) +{ + if (name_len =3D=3D 0) + return false; + + if (name_len =3D=3D 1 && name[0] =3D=3D '.') + return false; + + if (name_len =3D=3D 2 && name[0] =3D=3D '.' && name[1] =3D=3D '.') + return false; + + if (memchr(name, '/', name_len)) + return false; + + return true; +} + +int cfs_init_inode(struct cfs_context *ctx, u32 inode_num, struct inode *i= node, + struct cfs_inode_extra_data *inode_data) +{ + struct cfs_buf vdata_buf =3D { NULL }; + struct cfs_inode_data *disk_data; + char *path_payload =3D NULL; + void *res; + int ret =3D 0; + u64 variable_data_off; + u32 variable_data_len; + u64 digest_off; + u32 digest_len; + u32 st_type; + u64 size; + + if (inode_num >=3D ctx->num_inodes) + return -EFSCORRUPTED; + + disk_data =3D cfs_get_inode_buf(ctx, inode_num * CFS_INODE_SIZE, + CFS_INODE_SIZE, &vdata_buf); + if (IS_ERR(disk_data)) + return PTR_ERR(disk_data); + + inode->i_ino =3D inode_num; + + inode->i_mode =3D le32_to_cpu(disk_data->st_mode); + set_nlink(inode, le32_to_cpu(disk_data->st_nlink)); + inode->i_uid =3D make_kuid(current_user_ns(), le32_to_cpu(disk_data->st_u= id)); + inode->i_gid =3D make_kgid(current_user_ns(), le32_to_cpu(disk_data->st_g= id)); + inode->i_rdev =3D le32_to_cpu(disk_data->st_rdev); + + size =3D le64_to_cpu(disk_data->st_size); + i_size_write(inode, size); + inode_set_bytes(inode, size); + + inode->i_mtime.tv_sec =3D le64_to_cpu(disk_data->st_mtim_sec); + inode->i_mtime.tv_nsec =3D le32_to_cpu(disk_data->st_mtim_nsec); + inode->i_ctime.tv_sec =3D le64_to_cpu(disk_data->st_ctim_sec); + inode->i_ctime.tv_nsec =3D le32_to_cpu(disk_data->st_ctim_nsec); + inode->i_atime =3D inode->i_mtime; + + variable_data_off =3D le64_to_cpu(disk_data->variable_data.off); + variable_data_len =3D le32_to_cpu(disk_data->variable_data.len); + + st_type =3D inode->i_mode & S_IFMT; + if (st_type =3D=3D S_IFDIR) { + inode_data->dirents_offset =3D variable_data_off; + inode_data->dirents_len =3D variable_data_len; + } else if ((st_type =3D=3D S_IFLNK || st_type =3D=3D S_IFREG) && + variable_data_len > 0) { + path_payload =3D cfs_read_vdata_path(ctx, variable_data_off, + variable_data_len); + if (IS_ERR(path_payload)) { + ret =3D PTR_ERR(path_payload); + goto fail; + } + inode_data->path_payload =3D path_payload; + } + + if (st_type =3D=3D S_IFLNK) { + /* Symbolic link must have a non-empty target */ + if (!inode_data->path_payload || *inode_data->path_payload =3D=3D 0) { + ret =3D -EFSCORRUPTED; + goto fail; + } + } else if (st_type =3D=3D S_IFREG) { + /* Regular file must have backing file except empty files */ + if ((inode_data->path_payload && size =3D=3D 0) || + (!inode_data->path_payload && size > 0)) { + ret =3D -EFSCORRUPTED; + goto fail; + } + } + + inode_data->xattrs_offset =3D le64_to_cpu(disk_data->xattrs.off); + inode_data->xattrs_len =3D le32_to_cpu(disk_data->xattrs.len); + + if (inode_data->xattrs_len !=3D 0) { + /* Validate xattr size */ + if (inode_data->xattrs_len < sizeof(struct cfs_xattr_header)) { + ret =3D -EFSCORRUPTED; + goto fail; + } + } + + digest_off =3D le64_to_cpu(disk_data->digest.off); + digest_len =3D le32_to_cpu(disk_data->digest.len); + + if (digest_len > 0) { + if (digest_len !=3D SHA256_DIGEST_SIZE) { + ret =3D -EFSCORRUPTED; + goto fail; + } + + res =3D cfs_read_vdata(ctx, digest_off, digest_len, inode_data->digest); + if (IS_ERR(res)) { + ret =3D PTR_ERR(res); + goto fail; + } + inode_data->has_digest =3D true; + } + + cfs_buf_put(&vdata_buf); + return 0; + +fail: + cfs_buf_put(&vdata_buf); + return ret; +} + +ssize_t cfs_list_xattrs(struct cfs_context *ctx, + struct cfs_inode_extra_data *inode_data, char *names, + size_t size) +{ + const struct cfs_xattr_header *xattrs; + struct cfs_buf vdata_buf =3D { NULL }; + size_t n_xattrs =3D 0; + u8 *data, *data_end; + ssize_t copied =3D 0; + + if (inode_data->xattrs_len =3D=3D 0) + return 0; + + /* xattrs_len basic size req was verified in cfs_init_inode_data */ + + xattrs =3D cfs_get_vdata_buf(ctx, inode_data->xattrs_offset, + inode_data->xattrs_len, &vdata_buf); + if (IS_ERR(xattrs)) + return PTR_ERR(xattrs); + + n_xattrs =3D le16_to_cpu(xattrs->n_attr); + if (n_xattrs =3D=3D 0 || n_xattrs > CFS_MAX_XATTRS || + inode_data->xattrs_len < cfs_xattr_header_size(n_xattrs)) { + copied =3D -EFSCORRUPTED; + goto exit; + } + + data =3D ((u8 *)xattrs) + cfs_xattr_header_size(n_xattrs); + data_end =3D ((u8 *)xattrs) + inode_data->xattrs_len; + + for (size_t i =3D 0; i < n_xattrs; i++) { + const struct cfs_xattr_element *e =3D &xattrs->attr[i]; + u16 this_value_len =3D le16_to_cpu(e->value_length); + u16 this_key_len =3D le16_to_cpu(e->key_length); + const char *this_key; + + if (this_key_len > XATTR_NAME_MAX || + /* key and data needs to fit in data */ + data_end - data < this_key_len + this_value_len) { + copied =3D -EFSCORRUPTED; + goto exit; + } + + this_key =3D data; + data +=3D this_key_len + this_value_len; + + if (size) { + if (size - copied < this_key_len + 1) { + copied =3D -E2BIG; + goto exit; + } + + memcpy(names + copied, this_key, this_key_len); + names[copied + this_key_len] =3D '\0'; + } + + copied +=3D this_key_len + 1; + } + +exit: + cfs_buf_put(&vdata_buf); + + return copied; +} + +int cfs_get_xattr(struct cfs_context *ctx, struct cfs_inode_extra_data *in= ode_data, + const char *name, void *value, size_t size) +{ + struct cfs_xattr_header *xattrs; + struct cfs_buf vdata_buf =3D { NULL }; + size_t name_len =3D strlen(name); + size_t n_xattrs =3D 0; + u8 *data, *data_end; + int res; + + if (inode_data->xattrs_len =3D=3D 0) + return -ENODATA; + + /* xattrs_len minimal size req was verified in cfs_init_inode_data */ + + xattrs =3D cfs_get_vdata_buf(ctx, inode_data->xattrs_offset, + inode_data->xattrs_len, &vdata_buf); + if (IS_ERR(xattrs)) + return PTR_ERR(xattrs); + + n_xattrs =3D le16_to_cpu(xattrs->n_attr); + if (n_xattrs =3D=3D 0 || n_xattrs > CFS_MAX_XATTRS || + inode_data->xattrs_len < cfs_xattr_header_size(n_xattrs)) { + res =3D -EFSCORRUPTED; + goto exit; + } + + data =3D ((u8 *)xattrs) + cfs_xattr_header_size(n_xattrs); + data_end =3D ((u8 *)xattrs) + inode_data->xattrs_len; + + for (size_t i =3D 0; i < n_xattrs; i++) { + const struct cfs_xattr_element *e =3D &xattrs->attr[i]; + u16 this_value_len =3D le16_to_cpu(e->value_length); + u16 this_key_len =3D le16_to_cpu(e->key_length); + const char *this_key, *this_value; + + if (this_key_len > XATTR_NAME_MAX || + /* key and data needs to fit in data */ + data_end - data < this_key_len + this_value_len) { + res =3D -EFSCORRUPTED; + goto exit; + } + + this_key =3D data; + this_value =3D data + this_key_len; + data +=3D this_key_len + this_value_len; + + if (this_key_len !=3D name_len || memcmp(this_key, name, name_len) !=3D = 0) + continue; + + if (size > 0) { + if (size < this_value_len) { + res =3D -E2BIG; + goto exit; + } + memcpy(value, this_value, this_value_len); + } + + res =3D this_value_len; + goto exit; + } + + res =3D -ENODATA; + +exit: + cfs_buf_put(&vdata_buf); + return res; +} + +/* This is essentially strmcp() for non-null-terminated strings */ +static inline int memcmp2(const void *a, const size_t a_size, const void *= b, + size_t b_size) +{ + size_t common_size =3D min(a_size, b_size); + int res; + + res =3D memcmp(a, b, common_size); + if (res !=3D 0 || a_size =3D=3D b_size) + return res; + + return a_size < b_size ? -1 : 1; +} + +int cfs_dir_iterate(struct cfs_context *ctx, u64 index, + struct cfs_inode_extra_data *inode_data, loff_t first, + cfs_dir_iter_cb cb, void *private) +{ + struct cfs_buf vdata_buf =3D { NULL }; + const struct cfs_dir_header *dir; + u32 n_dirents; + char *namedata, *namedata_end; + loff_t pos; + int res; + + if (inode_data->dirents_len =3D=3D 0) + return 0; + + dir =3D cfs_get_vdata_buf(ctx, inode_data->dirents_offset, + inode_data->dirents_len, &vdata_buf); + if (IS_ERR(dir)) + return PTR_ERR(dir); + + n_dirents =3D le32_to_cpu(dir->n_dirents); + if (n_dirents =3D=3D 0 || n_dirents > CFS_MAX_DIRENTS || + inode_data->dirents_len < cfs_dir_header_size(n_dirents)) { + res =3D -EFSCORRUPTED; + goto exit; + } + + if (first >=3D n_dirents) { + res =3D 0; + goto exit; + } + + namedata =3D ((u8 *)dir) + cfs_dir_header_size(n_dirents); + namedata_end =3D ((u8 *)dir) + inode_data->dirents_len; + pos =3D 0; + for (size_t i =3D 0; i < n_dirents; i++) { + const struct cfs_dirent *dirent =3D &dir->dirents[i]; + char *dirent_name =3D + (char *)namedata + le32_to_cpu(dirent->name_offset); + size_t dirent_name_len =3D dirent->name_len; + + /* name needs to fit in namedata */ + if (dirent_name >=3D namedata_end || + namedata_end - dirent_name < dirent_name_len) { + res =3D -EFSCORRUPTED; + goto exit; + } + + if (!cfs_validate_filename(dirent_name, dirent_name_len)) { + res =3D -EFSCORRUPTED; + goto exit; + } + + if (pos++ < first) + continue; + + if (!cb(private, dirent_name, dirent_name_len, + le32_to_cpu(dirent->inode_num), dirent->d_type)) { + break; + } + } + + res =3D 0; +exit: + cfs_buf_put(&vdata_buf); + return res; +} + +int cfs_dir_lookup(struct cfs_context *ctx, u64 index, + struct cfs_inode_extra_data *inode_data, const char *name, + size_t name_len, u64 *index_out) +{ + struct cfs_buf vdata_buf =3D { NULL }; + const struct cfs_dir_header *dir; + u32 start_dirent, end_dirent, n_dirents; + char *namedata, *namedata_end; + int cmp, res; + + if (inode_data->dirents_len =3D=3D 0) + return 0; + + dir =3D cfs_get_vdata_buf(ctx, inode_data->dirents_offset, + inode_data->dirents_len, &vdata_buf); + if (IS_ERR(dir)) + return PTR_ERR(dir); + + n_dirents =3D le32_to_cpu(dir->n_dirents); + if (n_dirents =3D=3D 0 || n_dirents > CFS_MAX_DIRENTS || + inode_data->dirents_len < cfs_dir_header_size(n_dirents)) { + res =3D -EFSCORRUPTED; + goto exit; + } + + namedata =3D ((u8 *)dir) + cfs_dir_header_size(n_dirents); + namedata_end =3D ((u8 *)dir) + inode_data->dirents_len; + + start_dirent =3D 0; + end_dirent =3D n_dirents - 1; + while (start_dirent <=3D end_dirent) { + int mid_dirent =3D start_dirent + (end_dirent - start_dirent) / 2; + const struct cfs_dirent *dirent =3D &dir->dirents[mid_dirent]; + char *dirent_name =3D + (char *)namedata + le32_to_cpu(dirent->name_offset); + size_t dirent_name_len =3D dirent->name_len; + + /* name needs to fit in namedata */ + if (dirent_name >=3D namedata_end || + namedata_end - dirent_name < dirent_name_len) { + res =3D -EFSCORRUPTED; + goto exit; + } + + cmp =3D memcmp2(name, name_len, dirent_name, dirent_name_len); + if (cmp =3D=3D 0) { + *index_out =3D le32_to_cpu(dirent->inode_num); + res =3D 1; + goto exit; + } + + if (cmp > 0) + start_dirent =3D mid_dirent + 1; + else + end_dirent =3D mid_dirent - 1; + } + + /* not found */ + res =3D 0; + +exit: + cfs_buf_put(&vdata_buf); + return res; +} --=20 2.39.0 From nobody Sun Dec 14 02:18:34 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1928FC05027 for ; Fri, 20 Jan 2023 15:25:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231468AbjATPZS (ORCPT ); Fri, 20 Jan 2023 10:25:18 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55652 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231443AbjATPYy (ORCPT ); Fri, 20 Jan 2023 10:24:54 -0500 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0F696DB78C for ; Fri, 20 Jan 2023 07:23:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1674228237; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=g1ysa6Es94S1oWhcD6Fh9rwJ4ajG2HDCUvsoQHDL/yE=; b=eLVQu8M9+p+rpJcSDXdk6Gi1YoeGAlehlxrSnRRad2FYt13hIu4FOanusHMwW/xY5KHhdF He/RyKWClihuCdtQuOXwZ9RbtTIYoGBtKEVkA0KVca7QaYCiK2j9iAjlhwi6MnOe1Sgk0G MFk/ZN0th6S0mx7vXqwWkBkIxPlRLa4= Received: from mail-ed1-f71.google.com (mail-ed1-f71.google.com [209.85.208.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-587-qFtS0vC1Mk-8sBnIeRX1SA-1; Fri, 20 Jan 2023 10:23:55 -0500 X-MC-Unique: qFtS0vC1Mk-8sBnIeRX1SA-1 Received: by mail-ed1-f71.google.com with SMTP id y20-20020a056402271400b0046c9a6ec30fso4103204edd.14 for ; Fri, 20 Jan 2023 07:23:55 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=g1ysa6Es94S1oWhcD6Fh9rwJ4ajG2HDCUvsoQHDL/yE=; b=ZWOEytn5T+fH09LsKUgv2K4jdFtAqq0xMVddsYJVMY0OM4LzJ9OzB6COTumzljx2Oz bMMun3qKWq0OQ/6yLcTrP7BrNgmke5C59RG8pPNumdujDAT5M5GNovNjZ7yA4TxdZOFM QK31o5TYOyRH/k9RfmI1Iz7oLbv9g65VFa1oXxXMS7ZNdgM6iKDIHbd2wqvIxMz7ltLI DsThh+F5dEHHL68Hql6A2YZlQL2YumnmnLzT6P152MVqFt0x2tarPjGBWDkqwo4xm+zF UKRncxzG2t8SOaeNxq2lnsF6/fkc1Sisrd/o1jogCbi0vqSWpFUr33LTWleNCslGTZdh JSHQ== X-Gm-Message-State: AFqh2kotDxd2uWMbvjwp5jyVymoSaGH+FH6iGntJc0HC3qx84dl4LRh+ tNYFoUmcQ8FQ8s0NU+Iu5Q+tuaRwxKnRfgKAQgo792uatJHDNYDIS//bKhO7NplC/cnJiezonF3 bdAbouKPFZRX7M/K+uPaPKHxj X-Received: by 2002:a17:906:7d5:b0:86d:9e43:e59 with SMTP id m21-20020a17090607d500b0086d9e430e59mr14169569ejc.39.1674228234085; Fri, 20 Jan 2023 07:23:54 -0800 (PST) X-Google-Smtp-Source: AMrXdXsVLIyaWnglSAJKoxQb335Mwq0fdr2kKLNdbqeEPg80oAWtYvypY6+m5zkZkZGCUHzZarbI9g== X-Received: by 2002:a17:906:7d5:b0:86d:9e43:e59 with SMTP id m21-20020a17090607d500b0086d9e430e59mr14169553ejc.39.1674228233733; Fri, 20 Jan 2023 07:23:53 -0800 (PST) Received: from localhost.localdomain (c-e6a5e255.022-110-73746f36.bbcust.telenor.se. [85.226.165.230]) by smtp.googlemail.com with ESMTPSA id s16-20020a1709067b9000b00872eb47f46dsm5706976ejo.19.2023.01.20.07.23.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Jan 2023 07:23:53 -0800 (PST) From: Alexander Larsson To: linux-fsdevel@vger.kernel.org Cc: linux-kernel@vger.kernel.org, gscrivan@redhat.com, david@fromorbit.com, brauner@kernel.org, viro@zeniv.linux.org.uk, Alexander Larsson Subject: [PATCH v3 4/6] composefs: Add filesystem implementation Date: Fri, 20 Jan 2023 16:23:32 +0100 Message-Id: <9b799ec7e403ba814e7bc097b1e8bd5f7662d596.1674227308.git.alexl@redhat.com> X-Mailer: git-send-email 2.39.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" This is the basic inode and filesystem implementation. We open a private mount for the specified base directories at the time of mount, and all backing data files are looked up with this mount as root, to protect against changes in the mount tables, or links pointing outside the base directories. Access to the backing data is done with the callers credentials, to the backing files need to be readable by all users of the filesystem. We also use open_with_fake_path() to ensure the backing filename is not visible in /proc/. Signed-off-by: Alexander Larsson Co-developed-by: Giuseppe Scrivano Signed-off-by: Giuseppe Scrivano --- fs/composefs/cfs.c | 750 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 750 insertions(+) create mode 100644 fs/composefs/cfs.c diff --git a/fs/composefs/cfs.c b/fs/composefs/cfs.c new file mode 100644 index 000000000000..cd54d3751186 --- /dev/null +++ b/fs/composefs/cfs.c @@ -0,0 +1,750 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * composefs + * + * Copyright (C) 2000 Linus Torvalds. + * 2000 Transmeta Corp. + * Copyright (C) 2021 Giuseppe Scrivano + * Copyright (C) 2022 Alexander Larsson + * + * This file is released under the GPL. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cfs-internals.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Giuseppe Scrivano "); + +#define CFS_MAX_STACK 500 + +/* Backing file fs-verity check policy, ordered in strictness */ +enum cfs_verity_policy { + CFS_VERITY_CHECK_NONE =3D 0, /* Never verify digest */ + CFS_VERITY_CHECK_IF_SPECIFIED =3D 1, /* Verify if specified in image */ + CFS_VERITY_CHECK_REQUIRED =3D 2, /* Always verify, fail if not specified = in image */ +}; + +#define CFS_VERITY_CHECK_MAX_POLICY 2 + +struct cfs_info { + struct cfs_context cfs_ctx; + + char *base_path; + + size_t n_bases; + struct vfsmount **bases; + + enum cfs_verity_policy verity_check; + bool has_digest; + u8 digest[SHA256_DIGEST_SIZE]; /* fs-verity digest */ +}; + +struct cfs_inode { + struct inode vfs_inode; + struct cfs_inode_extra_data inode_data; +}; + +static inline struct cfs_inode *CFS_I(struct inode *inode) +{ + return container_of(inode, struct cfs_inode, vfs_inode); +} + +static struct file empty_file; + +static const struct file_operations cfs_file_operations; + +static const struct super_operations cfs_ops; +static const struct file_operations cfs_dir_operations; +static const struct inode_operations cfs_dir_inode_operations; +static const struct inode_operations cfs_file_inode_operations; +static const struct inode_operations cfs_link_inode_operations; + +static const struct xattr_handler *cfs_xattr_handlers[]; + +static const struct address_space_operations cfs_aops =3D { + .direct_IO =3D noop_direct_IO, +}; + +static ssize_t cfs_listxattr(struct dentry *dentry, char *names, size_t si= ze); + +/* split array of basedirs at ':', copied from overlayfs. */ +static unsigned int cfs_split_basedirs(char *str) +{ + unsigned int ctr =3D 1; + char *s, *d; + + for (s =3D d =3D str;; s++, d++) { + if (*s =3D=3D '\\') { + s++; + } else if (*s =3D=3D ':') { + *d =3D '\0'; + ctr++; + continue; + } + *d =3D *s; + if (!*s) + break; + } + return ctr; +} + +static struct inode *cfs_make_inode(struct cfs_context *ctx, struct super_= block *sb, + ino_t ino_num, const struct inode *dir) +{ + struct inode *inode; + struct cfs_inode *cino; + int ret; + + inode =3D new_inode(sb); + if (!inode) + return ERR_PTR(-ENOMEM); + + cino =3D CFS_I(inode); + + ret =3D cfs_init_inode(ctx, ino_num, inode, &cino->inode_data); + if (ret < 0) + goto fail; + + inode_init_owner(&init_user_ns, inode, dir, inode->i_mode); + inode->i_mapping->a_ops =3D &cfs_aops; + + switch (inode->i_mode & S_IFMT) { + case S_IFREG: + inode->i_op =3D &cfs_file_inode_operations; + inode->i_fop =3D &cfs_file_operations; + break; + case S_IFLNK: + inode->i_link =3D cino->inode_data.path_payload; + inode->i_op =3D &cfs_link_inode_operations; + inode->i_fop =3D &cfs_file_operations; + break; + case S_IFDIR: + inode->i_op =3D &cfs_dir_inode_operations; + inode->i_fop =3D &cfs_dir_operations; + break; + case S_IFCHR: + case S_IFBLK: + if (current_user_ns() !=3D &init_user_ns) { + ret =3D -EPERM; + goto fail; + } + fallthrough; + default: + inode->i_op =3D &cfs_file_inode_operations; + init_special_inode(inode, inode->i_mode, inode->i_rdev); + break; + } + + return inode; + +fail: + iput(inode); + return ERR_PTR(ret); +} + +static bool cfs_iterate_cb(void *private, const char *name, int name_len, + u64 ino, unsigned int dtype) +{ + struct dir_context *ctx =3D private; + + if (!dir_emit(ctx, name, name_len, ino, dtype)) + return 0; + + ctx->pos++; + return 1; +} + +static int cfs_iterate(struct file *file, struct dir_context *ctx) +{ + struct inode *inode =3D file->f_inode; + struct cfs_info *fsi =3D inode->i_sb->s_fs_info; + struct cfs_inode *cino =3D CFS_I(inode); + + if (!dir_emit_dots(file, ctx)) + return 0; + + return cfs_dir_iterate(&fsi->cfs_ctx, inode->i_ino, &cino->inode_data, + ctx->pos - 2, cfs_iterate_cb, ctx); +} + +static struct dentry *cfs_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +{ + struct cfs_info *fsi =3D dir->i_sb->s_fs_info; + struct cfs_inode *cino =3D CFS_I(dir); + struct inode *inode =3D NULL; + u64 index; + int ret; + + if (dentry->d_name.len > NAME_MAX) + return ERR_PTR(-ENAMETOOLONG); + + ret =3D cfs_dir_lookup(&fsi->cfs_ctx, dir->i_ino, &cino->inode_data, + dentry->d_name.name, dentry->d_name.len, &index); + if (ret) { + if (ret < 0) + return ERR_PTR(ret); + inode =3D cfs_make_inode(&fsi->cfs_ctx, dir->i_sb, index, dir); + } + + return d_splice_alias(inode, dentry); +} + +static const struct file_operations cfs_dir_operations =3D { + .llseek =3D generic_file_llseek, + .read =3D generic_read_dir, + .iterate_shared =3D cfs_iterate, +}; + +static const struct inode_operations cfs_dir_inode_operations =3D { + .lookup =3D cfs_lookup, + .listxattr =3D cfs_listxattr, +}; + +static const struct inode_operations cfs_link_inode_operations =3D { + .get_link =3D simple_get_link, + .listxattr =3D cfs_listxattr, +}; + +static int digest_from_string(const char *digest_str, u8 *digest) +{ + int res; + + res =3D hex2bin(digest, digest_str, SHA256_DIGEST_SIZE); + if (res < 0) + return res; + + if (digest_str[2 * SHA256_DIGEST_SIZE] !=3D 0) + return -EINVAL; /* Too long string */ + + return 0; +} + +/* + * Display the mount options in /proc/mounts. + */ +static int cfs_show_options(struct seq_file *m, struct dentry *root) +{ + struct cfs_info *fsi =3D root->d_sb->s_fs_info; + + if (fsi->base_path) + seq_show_option(m, "basedir", fsi->base_path); + if (fsi->has_digest) + seq_printf(m, ",digest=3D%*phN", SHA256_DIGEST_SIZE, fsi->digest); + if (fsi->verity_check !=3D 0) + seq_printf(m, ",verity_check=3D%u", fsi->verity_check); + + return 0; +} + +static struct kmem_cache *cfs_inode_cachep; + +static struct inode *cfs_alloc_inode(struct super_block *sb) +{ + struct cfs_inode *cino =3D alloc_inode_sb(sb, cfs_inode_cachep, GFP_KERNE= L); + + if (!cino) + return NULL; + + memset(&cino->inode_data, 0, sizeof(cino->inode_data)); + + return &cino->vfs_inode; +} + +static void cfs_free_inode(struct inode *inode) +{ + struct cfs_inode *cino =3D CFS_I(inode); + + kfree(cino->inode_data.path_payload); + kmem_cache_free(cfs_inode_cachep, cino); +} + +static void cfs_put_super(struct super_block *sb) +{ + struct cfs_info *fsi =3D sb->s_fs_info; + + cfs_ctx_put(&fsi->cfs_ctx); + if (fsi->bases) { + kern_unmount_array(fsi->bases, fsi->n_bases); + kfree(fsi->bases); + } + kfree(fsi->base_path); + + kfree(fsi); +} + +static int cfs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct cfs_info *fsi =3D dentry->d_sb->s_fs_info; + int err =3D 0; + + /* We return the free space, etc from the first base dir. */ + if (fsi->n_bases > 0) { + struct path root =3D { .mnt =3D fsi->bases[0], + .dentry =3D fsi->bases[0]->mnt_root }; + err =3D vfs_statfs(&root, buf); + } + + if (!err) { + buf->f_namelen =3D NAME_MAX; + buf->f_type =3D dentry->d_sb->s_magic; + } + + return err; +} + +static const struct super_operations cfs_ops =3D { + .statfs =3D cfs_statfs, + .drop_inode =3D generic_delete_inode, + .show_options =3D cfs_show_options, + .put_super =3D cfs_put_super, + .alloc_inode =3D cfs_alloc_inode, + .free_inode =3D cfs_free_inode, +}; + +enum cfs_param { + Opt_base_path, + Opt_digest, + Opt_verity_check, +}; + +const struct fs_parameter_spec cfs_parameters[] =3D { + fsparam_string("basedir", Opt_base_path), + fsparam_string("digest", Opt_digest), + fsparam_u32("verity_check", Opt_verity_check), + {} +}; + +static int cfs_parse_param(struct fs_context *fc, struct fs_parameter *par= am) +{ + struct cfs_info *fsi =3D fc->s_fs_info; + struct fs_parse_result result; + int opt, r; + + opt =3D fs_parse(fc, cfs_parameters, param, &result); + if (opt =3D=3D -ENOPARAM) + return vfs_parse_fs_param_source(fc, param); + if (opt < 0) + return opt; + + switch (opt) { + case Opt_base_path: + kfree(fsi->base_path); + /* Take ownership. */ + fsi->base_path =3D param->string; + param->string =3D NULL; + break; + case Opt_digest: + r =3D digest_from_string(param->string, fsi->digest); + if (r < 0) + return r; + fsi->has_digest =3D true; + fsi->verity_check =3D CFS_VERITY_CHECK_REQUIRED; /* Default to full veri= ty check */ + break; + case Opt_verity_check: + if (result.uint_32 > CFS_VERITY_CHECK_MAX_POLICY) + return invalfc(fc, "Invalid verity_check mode"); + fsi->verity_check =3D result.uint_32; + break; + } + + return 0; +} + +static struct vfsmount *resolve_basedir(const char *name) +{ + struct path path =3D {}; + struct vfsmount *mnt; + int err =3D -EINVAL; + + if (!*name) { + pr_err("empty basedir\n"); + return ERR_PTR(-EINVAL); + } + err =3D kern_path(name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); + if (err) { + pr_err("failed to resolve '%s': %i\n", name, err); + return ERR_PTR(-EINVAL); + } + + mnt =3D clone_private_mount(&path); + path_put(&path); + if (!IS_ERR(mnt)) { + /* Don't inherit atime flags */ + mnt->mnt_flags &=3D ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME); + } + + return mnt; +} + +static int cfs_fill_super(struct super_block *sb, struct fs_context *fc) +{ + struct cfs_info *fsi =3D sb->s_fs_info; + struct vfsmount **bases =3D NULL; + size_t numbasedirs =3D 0; + struct inode *inode; + struct vfsmount *mnt; + int ret; + + /* Set up the inode allocator early */ + sb->s_op =3D &cfs_ops; + sb->s_flags |=3D SB_RDONLY; + sb->s_magic =3D CFS_MAGIC; + sb->s_xattr =3D cfs_xattr_handlers; + + if (fsi->base_path) { + char *lower, *splitlower =3D NULL; + + ret =3D -ENOMEM; + splitlower =3D kstrdup(fsi->base_path, GFP_KERNEL); + if (!splitlower) + goto fail; + + ret =3D -EINVAL; + numbasedirs =3D cfs_split_basedirs(splitlower); + if (numbasedirs > CFS_MAX_STACK) { + pr_err("too many lower directories, limit is %d\n", + CFS_MAX_STACK); + kfree(splitlower); + goto fail; + } + + ret =3D -ENOMEM; + bases =3D kcalloc(numbasedirs, sizeof(struct vfsmount *), GFP_KERNEL); + if (!bases) { + kfree(splitlower); + goto fail; + } + + lower =3D splitlower; + for (size_t i =3D 0; i < numbasedirs; i++) { + mnt =3D resolve_basedir(lower); + if (IS_ERR(mnt)) { + ret =3D PTR_ERR(mnt); + kfree(splitlower); + goto fail; + } + bases[i] =3D mnt; + + lower =3D strchr(lower, '\0') + 1; + } + kfree(splitlower); + } + + /* Must be inited before calling cfs_get_inode. */ + ret =3D cfs_init_ctx(fc->source, fsi->has_digest ? fsi->digest : NULL, + &fsi->cfs_ctx); + if (ret < 0) + goto fail; + + inode =3D cfs_make_inode(&fsi->cfs_ctx, sb, CFS_ROOT_INO, NULL); + if (IS_ERR(inode)) { + ret =3D PTR_ERR(inode); + goto fail; + } + sb->s_root =3D d_make_root(inode); + + ret =3D -ENOMEM; + if (!sb->s_root) + goto fail; + + sb->s_maxbytes =3D MAX_LFS_FILESIZE; + sb->s_blocksize =3D PAGE_SIZE; + sb->s_blocksize_bits =3D PAGE_SHIFT; + + fsi->bases =3D bases; + fsi->n_bases =3D numbasedirs; + return 0; +fail: + if (bases) { + for (size_t i =3D 0; i < numbasedirs; i++) { + if (bases[i]) + kern_unmount(bases[i]); + } + kfree(bases); + } + cfs_ctx_put(&fsi->cfs_ctx); + return ret; +} + +static int cfs_get_tree(struct fs_context *fc) +{ + return get_tree_nodev(fc, cfs_fill_super); +} + +static const struct fs_context_operations cfs_context_ops =3D { + .parse_param =3D cfs_parse_param, + .get_tree =3D cfs_get_tree, +}; + +static struct file *open_base_file(struct cfs_info *fsi, struct inode *ino= de, + struct file *file) +{ + struct cfs_inode *cino =3D CFS_I(inode); + struct file *real_file; + char *real_path =3D cino->inode_data.path_payload; + + for (size_t i =3D 0; i < fsi->n_bases; i++) { + real_file =3D file_open_root_mnt(fsi->bases[i], real_path, + file->f_flags, 0); + if (real_file !=3D ERR_PTR(-ENOENT)) + return real_file; + } + + return ERR_PTR(-ENOENT); +} + +static int cfs_open_file(struct inode *inode, struct file *file) +{ + struct cfs_info *fsi =3D inode->i_sb->s_fs_info; + struct cfs_inode *cino =3D CFS_I(inode); + char *real_path =3D cino->inode_data.path_payload; + struct file *faked_file; + struct file *real_file; + + if (file->f_flags & (O_WRONLY | O_RDWR | O_CREAT | O_EXCL | O_TRUNC)) + return -EROFS; + + if (!real_path) { + file->private_data =3D &empty_file; + return 0; + } + + if (fsi->verity_check >=3D CFS_VERITY_CHECK_REQUIRED && + !cino->inode_data.has_digest) { + pr_warn("WARNING: composefs image file '%pD' specified no fs-verity dige= st\n", + file); + return -EIO; + } + + real_file =3D open_base_file(fsi, inode, file); + + if (IS_ERR(real_file)) + return PTR_ERR(real_file); + + /* If metadata records a digest for the file, ensure it is there + * and correct before using the contents. + */ + if (cino->inode_data.has_digest && + fsi->verity_check >=3D CFS_VERITY_CHECK_IF_SPECIFIED) { + u8 verity_digest[FS_VERITY_MAX_DIGEST_SIZE]; + enum hash_algo verity_algo; + int res; + + res =3D fsverity_get_digest(d_inode(real_file->f_path.dentry), + verity_digest, &verity_algo); + if (res < 0) { + pr_warn("WARNING: composefs backing file '%pD' has no fs-verity digest\= n", + real_file); + fput(real_file); + return -EIO; + } + if (verity_algo !=3D HASH_ALGO_SHA256 || + memcmp(cino->inode_data.digest, verity_digest, + SHA256_DIGEST_SIZE) !=3D 0) { + pr_warn("WARNING: composefs backing file '%pD' has the wrong fs-verity = digest\n", + real_file); + fput(real_file); + return -EIO; + } + } + + faked_file =3D open_with_fake_path(&file->f_path, file->f_flags, + real_file->f_inode, current_cred()); + fput(real_file); + + if (IS_ERR(faked_file)) + return PTR_ERR(faked_file); + + file->private_data =3D faked_file; + return 0; +} + +#ifdef CONFIG_MMU +static unsigned long cfs_mmu_get_unmapped_area(struct file *file, unsigned= long addr, + unsigned long len, unsigned long pgoff, + unsigned long flags) +{ + struct file *realfile =3D file->private_data; + + if (realfile =3D=3D &empty_file) + return 0; + + return current->mm->get_unmapped_area(file, addr, len, pgoff, flags); +} +#endif + +static int cfs_release_file(struct inode *inode, struct file *file) +{ + struct file *realfile =3D file->private_data; + + if (WARN_ON(!realfile)) + return -EIO; + + if (realfile =3D=3D &empty_file) + return 0; + + fput(realfile); + + return 0; +} + +static int cfs_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct file *realfile =3D file->private_data; + int ret; + + if (realfile =3D=3D &empty_file) + return 0; + + if (!realfile->f_op->mmap) + return -ENODEV; + + if (WARN_ON(file !=3D vma->vm_file)) + return -EIO; + + vma_set_file(vma, realfile); + + ret =3D call_mmap(vma->vm_file, vma); + + return ret; +} + +static ssize_t cfs_read_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + struct file *file =3D iocb->ki_filp; + struct file *realfile =3D file->private_data; + int ret; + + if (realfile =3D=3D &empty_file) + return 0; + + if (!realfile->f_op->read_iter) + return -ENODEV; + + iocb->ki_filp =3D realfile; + ret =3D call_read_iter(realfile, iocb, iter); + iocb->ki_filp =3D file; + + return ret; +} + +static int cfs_fadvise(struct file *file, loff_t offset, loff_t len, int a= dvice) +{ + struct file *realfile =3D file->private_data; + + if (realfile =3D=3D &empty_file) + return 0; + + return vfs_fadvise(realfile, offset, len, advice); +} + +static int cfs_getxattr(const struct xattr_handler *handler, + struct dentry *unused2, struct inode *inode, + const char *name, void *value, size_t size) +{ + struct cfs_info *fsi =3D inode->i_sb->s_fs_info; + struct cfs_inode *cino =3D CFS_I(inode); + + return cfs_get_xattr(&fsi->cfs_ctx, &cino->inode_data, name, value, size); +} + +static ssize_t cfs_listxattr(struct dentry *dentry, char *names, size_t si= ze) +{ + struct inode *inode =3D d_inode(dentry); + struct cfs_info *fsi =3D inode->i_sb->s_fs_info; + struct cfs_inode *cino =3D CFS_I(inode); + + return cfs_list_xattrs(&fsi->cfs_ctx, &cino->inode_data, names, size); +} + +static const struct file_operations cfs_file_operations =3D { + .read_iter =3D cfs_read_iter, + .mmap =3D cfs_mmap, + .fadvise =3D cfs_fadvise, + .fsync =3D noop_fsync, + .splice_read =3D generic_file_splice_read, + .llseek =3D generic_file_llseek, +#ifdef CONFIG_MMU + .get_unmapped_area =3D cfs_mmu_get_unmapped_area, +#endif + .release =3D cfs_release_file, + .open =3D cfs_open_file, +}; + +static const struct xattr_handler cfs_xattr_handler =3D { + .prefix =3D "", /* catch all */ + .get =3D cfs_getxattr, +}; + +static const struct xattr_handler *cfs_xattr_handlers[] =3D { + &cfs_xattr_handler, + NULL, +}; + +static const struct inode_operations cfs_file_inode_operations =3D { + .listxattr =3D cfs_listxattr, +}; + +static int cfs_init_fs_context(struct fs_context *fc) +{ + struct cfs_info *fsi; + + fsi =3D kzalloc(sizeof(*fsi), GFP_KERNEL); + if (!fsi) + return -ENOMEM; + + fc->s_fs_info =3D fsi; + fc->ops =3D &cfs_context_ops; + return 0; +} + +static struct file_system_type cfs_type =3D { + .owner =3D THIS_MODULE, + .name =3D "composefs", + .init_fs_context =3D cfs_init_fs_context, + .parameters =3D cfs_parameters, + .kill_sb =3D kill_anon_super, +}; + +static void cfs_inode_init_once(void *foo) +{ + struct cfs_inode *cino =3D foo; + + inode_init_once(&cino->vfs_inode); +} + +static int __init init_cfs(void) +{ + cfs_inode_cachep =3D kmem_cache_create( + "cfs_inode", sizeof(struct cfs_inode), 0, + (SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD | SLAB_ACCOUNT), + cfs_inode_init_once); + if (!cfs_inode_cachep) + return -ENOMEM; + + return register_filesystem(&cfs_type); +} + +static void __exit exit_cfs(void) +{ + unregister_filesystem(&cfs_type); + + /* Ensure all RCU free inodes are safe to be destroyed. */ + rcu_barrier(); + + kmem_cache_destroy(cfs_inode_cachep); +} + +module_init(init_cfs); +module_exit(exit_cfs); --=20 2.39.0 From nobody Sun Dec 14 02:18:34 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0934FC05027 for ; Fri, 20 Jan 2023 15:26:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231370AbjATP04 (ORCPT ); Fri, 20 Jan 2023 10:26:56 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55650 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231402AbjATPYv (ORCPT ); Fri, 20 Jan 2023 10:24:51 -0500 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0D376C79FE for ; Fri, 20 Jan 2023 07:23:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1674228238; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=QF5J0+urAsLTcSPqBu0CZ6ZRujp7qsmZ8yBe1kN5g6U=; b=eqjSvtqLB4gUQ2nACD7z5jBE/yGjnVmgs/CHGQVwQl54W4q2ph46aEc8196K+BpB+GZ1qG jF6517xR36D3vBMlE2Wp1OrQ6NZlH/DVR/8gt7tdCxLpj8fJXGmX4PiZGbmX+TGloOJ+/N LCns7ogJeJqmlkSFRD5B8Ux1xX7oyaI= Received: from mail-ej1-f69.google.com (mail-ej1-f69.google.com [209.85.218.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-142-dpPWdrnWMwaGY_KOhhoNhg-1; Fri, 20 Jan 2023 10:23:57 -0500 X-MC-Unique: dpPWdrnWMwaGY_KOhhoNhg-1 Received: by mail-ej1-f69.google.com with SMTP id sh37-20020a1709076ea500b0087760a6acceso3993952ejc.17 for ; Fri, 20 Jan 2023 07:23:56 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=QF5J0+urAsLTcSPqBu0CZ6ZRujp7qsmZ8yBe1kN5g6U=; b=VziAO13jTSKCXXBkpOEKesbPz5tAQtX/toMihDHaLOe5BY1QL6c5Q3orn0qflmRc3x CQOyIK1rNKF2F3C2Y/nim3TwL8jm8M3HWDfAROmR1/sOBJRP5uHlOjItrEmV36m+ISpp vyzMu21LqjzK25bzSVwLz9Yjg7jFrvGz9aBgZXHWDaWXMkHcusElLvC1Id9ZpvtWz6hc n9KBZoYUTAhaPUT153O8L0UeZwFkQ4siDq8NzK1YCk/aTx+uuGu4NpBRRQXnzUTz3OS0 vDgUZP703Hf2DxO/SGW3oe2JuAWDostBv8BgLyPUdg3uH78wFEyQQjFaP7Uv4l3aobtO JxwQ== X-Gm-Message-State: AFqh2koKJBg9k0k4rYSCnOBjGwS+H+PAVo1KK4ogsly0mOiMqQSb4iJv HgFPdmJc1CIDlAxJaxRs6tRD/ZiGIXXjsrnvJ+uv3LJd23kP7QGLh/lUIbyS4u2HhQAXxuWN1VA 8Sacpc71TCKtmQk8/QuVUZoWQ X-Received: by 2002:a17:907:94ce:b0:870:ab89:3dd3 with SMTP id dn14-20020a17090794ce00b00870ab893dd3mr15032263ejc.70.1674228234979; Fri, 20 Jan 2023 07:23:54 -0800 (PST) X-Google-Smtp-Source: AMrXdXu9l14RqKgZGgwS+5Q69X+B7w0S3j2d7ypp/QCEwKT5hv3rjo4LziJLs4jhOsXdUrvIIrw6zQ== X-Received: by 2002:a17:907:94ce:b0:870:ab89:3dd3 with SMTP id dn14-20020a17090794ce00b00870ab893dd3mr15032221ejc.70.1674228234720; Fri, 20 Jan 2023 07:23:54 -0800 (PST) Received: from localhost.localdomain (c-e6a5e255.022-110-73746f36.bbcust.telenor.se. [85.226.165.230]) by smtp.googlemail.com with ESMTPSA id s16-20020a1709067b9000b00872eb47f46dsm5706976ejo.19.2023.01.20.07.23.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Jan 2023 07:23:54 -0800 (PST) From: Alexander Larsson To: linux-fsdevel@vger.kernel.org Cc: linux-kernel@vger.kernel.org, gscrivan@redhat.com, david@fromorbit.com, brauner@kernel.org, viro@zeniv.linux.org.uk, Alexander Larsson , linux-doc@vger.kernel.org Subject: [PATCH v3 5/6] composefs: Add documentation Date: Fri, 20 Jan 2023 16:23:33 +0100 Message-Id: <20baca7da01c285b2a77c815c9d4b3080ce4b279.1674227308.git.alexl@redhat.com> X-Mailer: git-send-email 2.39.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Add documentation about the composefs filesystem and how to use it. Signed-off-by: Alexander Larsson --- Documentation/filesystems/composefs.rst | 159 ++++++++++++++++++++++++ Documentation/filesystems/index.rst | 1 + 2 files changed, 160 insertions(+) create mode 100644 Documentation/filesystems/composefs.rst diff --git a/Documentation/filesystems/composefs.rst b/Documentation/filesy= stems/composefs.rst new file mode 100644 index 000000000000..f270a66f4204 --- /dev/null +++ b/Documentation/filesystems/composefs.rst @@ -0,0 +1,159 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Composefs Filesystem +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Introduction +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Composefs is a read-only file system that is backed by regular files +(rather than a block device). It is designed to help easily share +content between different directory trees, such as container images in +a local store or ostree checkouts. In addition it also has support for +integrity validation of file content and directory metadata, in an +efficient way (using fs-verity). + +The filesystem mount source is a binary blob called the descriptor. It +contains all the inode and directory entry data for the entire +filesystem. However, instead of storing the file content each regular +file inode stores a relative path name, and the filesystem gets the +file content from the filesystem by looking up that filename in a set +of base directories. + +Given such a descriptor called "image.cfs" and a directory with files +called "/dir" you can mount it like:: + + mount -t composefs image.cfs -o basedir=3D/dir /mnt + +Content sharing +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Suppose you have a single basedir where the files are content +addressed (i.e. named by content digest), and a set of composefs +descriptors using this basedir. Any file that happens to be shared +between two images (same content, so same digest) will now only be +stored once on the disk. + +Such sharing is possible even if the metadata for the file in the +image differs (common reasons for metadata difference are mtime, +permissions, xattrs, etc). The sharing is also anonymous in the sense +that you can't tell the difference on the mounted files from a +non-shared file (for example by looking at the link count for a +hardlinked file). + +In addition, any shared files that are actively in use will share +page-cache, because the page cache for the file contents will be +addressed by the backing file in the basedir, This means (for example) +that shared libraries between images will only be mmap:ed once across +all mounts. + +Integrity validation +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Composefs uses :doc:`fs-verity ` for integrity validation, +and extends it by making the validation also apply to the directory +metadata. This happens on two levels, validation of the descriptor +and validation of the backing files. + +For descriptor validation, the idea is that you enable fs-verity on +the descriptor file which seals it from changes that would affect the +directory metadata. Additionally you can pass a "digest" mount option, +which composefs verifies against the descriptor fs-verity measure. Such +an option could be embedded in a trusted source (like a signed kernel +command line) and be used as a root of trust if using composefs for the +root filesystem. + +For file validation, the descriptor can contain digests for each +backing file, and you can enable fs-verity on them too. Composefs will +validate the digest before using the backing files. This means any +(accidental or malicious) modification of the basedir will be detected +at the time the file is used. + +Expected use-cases +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Container Image Storage +``````````````````````` + +Typically a container image is stored as a set of "layer" directories, +merged into one mount by using overlayfs. The lower layers are +read-only image and the upper layer is the writable directory of a +running container. Multiple uses of the same layer can be shared this +way, but it is hard to share individual files between unrelated layers. + +Using composefs, we can instead use a shared, content-addressed +store for all the images in the system, and use composefs +for the read-only image of each container, pointing into the +shared store. Then for a running container we use an overlayfs +with the lower dir being the composefs and the upper dir being +the writable directory. + + +Ostree root filesystem validation +````````````````````````````````` + +Ostree uses a content-addressed on-disk store for file content, +allowing efficient updates and sharing of content. However to actually +use these as a root filesystem it needs to create a real +"chroot-style" directory, containing hard links into the store. The +store itself is validated when created, but once the hard-link +directory is created, nothing validates the directory structure for +post-creation changes. + +Instead of a chroot we can use composefs. The composefs image pointing +to the object store is created, then fs-verity is enabled for +everything and the descriptor digest is encoded in the +kernel-command line. This will allow booting a trusted system where +all directory metadata and file content is validated lazily at use. + + +Mount options +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +basedir + A colon separated list of directories to use as a base when resolving + relative content paths. + +verity_check=3D[0,1,2] + When to verify backing file fs-verity: + + * 0: never verify + * 1: if the digest is specified in image + * 2: always verify the file (and require digests in image) + +digest + A fs-verity sha256 digest that the descriptor file must match. If set, + "verity_check" defaults to 2. + + +Filesystem format +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The format of the descriptor contains three sections: superblock, +inodes and variable data. All data in the file is stored in +little-endian form. + +The superblock starts at the beginning of the file and contains +version, magic value, and offsets to the variable data section. + +The inode table starts at a fixed location right after the +header. It is a array of fixed size inode data. The first inode +is the root inode, and inode numbers are index into this array. + +The variable data section is stored after the inode section, and you +can find it from the offset in the header. It contains paths, digests, +dirents and Xattrs data. The xattrs are referred to by offset and size +in the xattr attribute in the inode data. Each xattr data can be used +by many inodes in the filesystem. + +For more details, see cfs.h. + +Tools +=3D=3D=3D=3D=3D + +Tools for composefs can be found at https://github.com/containers/composefs + +There is a mkcomposefs tool which can be used to create images on the +CLI, and a library that applications can use to create composefs +images. diff --git a/Documentation/filesystems/index.rst b/Documentation/filesystem= s/index.rst index bee63d42e5ec..9b7cf136755d 100644 --- a/Documentation/filesystems/index.rst +++ b/Documentation/filesystems/index.rst @@ -75,6 +75,7 @@ Documentation for filesystem implementations. cifs/index ceph coda + composefs configfs cramfs dax --=20 2.39.0 From nobody Sun Dec 14 02:18:34 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C4153C25B4E for ; Fri, 20 Jan 2023 15:25:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231429AbjATPZA (ORCPT ); Fri, 20 Jan 2023 10:25:00 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55660 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231420AbjATPYw (ORCPT ); Fri, 20 Jan 2023 10:24:52 -0500 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CB2CBDA127 for ; Fri, 20 Jan 2023 07:23:59 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1674228239; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=M9kcFm4bZIrWxqb92kaZI81madTzRGEizfXuC8UiNAg=; b=FEiZuFbRK8ptJmmJI4EGINeo3zSRMF8AAFbx3kyX1KiZH8HeZN+ZxTZeq6wpq//XW2Hdhv 4GQmuN/mfX3deZJCPnGEg2b1AD5n75wb9DMWkxjBUAbV6XkFAv0tPVd7p+FpBKWXa/yntw 2SjjnsBMMUbghwHE5aV4uE7vdexTIVg= Received: from mail-ej1-f71.google.com (mail-ej1-f71.google.com [209.85.218.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-647-h8K_wuihOz-N6GP2AeTkQA-1; Fri, 20 Jan 2023 10:23:58 -0500 X-MC-Unique: h8K_wuihOz-N6GP2AeTkQA-1 Received: by mail-ej1-f71.google.com with SMTP id wz4-20020a170906fe4400b0084c7e7eb6d0so3999615ejb.19 for ; Fri, 20 Jan 2023 07:23:57 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=M9kcFm4bZIrWxqb92kaZI81madTzRGEizfXuC8UiNAg=; b=E8N4wygG05/CY7hqO+ye15uwDvukUSa4wCtpZL4uyjS6FTiKnfZ1eJ5qixx/jZVvec qSBC/UyURlzTnIfedArSAQNpKZFR14SFigUTUwn7RFPTELHbilSzdZGnldAm3xbL/gFM nzRz7F7VHrUMb7v+RUjNXBZUJzO1o2/LsaH6JRBcB+6gAhO3x96DGvVEtIm6rB8DYJc5 QCKPaTtl78Pi3j6iY3TyApMuejf/1F40nPG+df6sbmAs9wSdKTaDL9wzN/SrRfoRExsg q5v9pHzeiTKjfcHKNtRQtmbX+1cQPaJ/M1PcB08R7tdUm56oo1pZkNxcwidxTasoGkV5 jGlQ== X-Gm-Message-State: AFqh2kpGsORg76srOrvc9WwSQy7TKo0ACzYY5xhcitQlm83ogvE6OVb8 rddxfbpxZKd1wxZudQESD1uuM/K9/1ITkJTyR5jQcqQTfUL+U87CaGe+Fjo5so+2wtiQMGOV5Cu dJKLx899nP8F+8BpceHBeuR0c X-Received: by 2002:a17:907:8e8c:b0:86f:fbcf:f30a with SMTP id tx12-20020a1709078e8c00b0086ffbcff30amr17256937ejc.58.1674228236684; Fri, 20 Jan 2023 07:23:56 -0800 (PST) X-Google-Smtp-Source: AMrXdXu9qkCLMflGLnlHw4DtZmrXLGQEq54VRSN4Eh5U4Z+xTENpAmtCMu5VVcOv2DCIdzj6GA3G8Q== X-Received: by 2002:a17:907:8e8c:b0:86f:fbcf:f30a with SMTP id tx12-20020a1709078e8c00b0086ffbcff30amr17256918ejc.58.1674228236482; Fri, 20 Jan 2023 07:23:56 -0800 (PST) Received: from localhost.localdomain (c-e6a5e255.022-110-73746f36.bbcust.telenor.se. [85.226.165.230]) by smtp.googlemail.com with ESMTPSA id s16-20020a1709067b9000b00872eb47f46dsm5706976ejo.19.2023.01.20.07.23.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Jan 2023 07:23:55 -0800 (PST) From: Alexander Larsson To: linux-fsdevel@vger.kernel.org Cc: linux-kernel@vger.kernel.org, gscrivan@redhat.com, david@fromorbit.com, brauner@kernel.org, viro@zeniv.linux.org.uk, Alexander Larsson Subject: [PATCH v3 6/6] composefs: Add kconfig and build support Date: Fri, 20 Jan 2023 16:23:34 +0100 Message-Id: <44514e018d5fa27ace5c97c6c30e95a9cca89032.1674227308.git.alexl@redhat.com> X-Mailer: git-send-email 2.39.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" This commit adds Makefile and Kconfig for composefs, and updates Makefile and Kconfig files in the fs directory Signed-off-by: Alexander Larsson --- fs/Kconfig | 1 + fs/Makefile | 1 + fs/composefs/Kconfig | 18 ++++++++++++++++++ fs/composefs/Makefile | 5 +++++ 4 files changed, 25 insertions(+) create mode 100644 fs/composefs/Kconfig create mode 100644 fs/composefs/Makefile diff --git a/fs/Kconfig b/fs/Kconfig index 2685a4d0d353..de8493fc2b1e 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -127,6 +127,7 @@ source "fs/quota/Kconfig" source "fs/autofs/Kconfig" source "fs/fuse/Kconfig" source "fs/overlayfs/Kconfig" +source "fs/composefs/Kconfig" =20 menu "Caches" =20 diff --git a/fs/Makefile b/fs/Makefile index 4dea17840761..d16974e02468 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -137,3 +137,4 @@ obj-$(CONFIG_EFIVAR_FS) +=3D efivarfs/ obj-$(CONFIG_EROFS_FS) +=3D erofs/ obj-$(CONFIG_VBOXSF_FS) +=3D vboxsf/ obj-$(CONFIG_ZONEFS_FS) +=3D zonefs/ +obj-$(CONFIG_COMPOSEFS_FS) +=3D composefs/ diff --git a/fs/composefs/Kconfig b/fs/composefs/Kconfig new file mode 100644 index 000000000000..88c5b55380e6 --- /dev/null +++ b/fs/composefs/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config COMPOSEFS_FS + tristate "Composefs filesystem support" + select EXPORTFS + help + Composefs is a filesystem that allows combining file content from + existing regular files with a metadata directory structure from + a separate binary file. This is useful to share file content between + many different directory trees, such as in a local container image stor= e. + + Composefs also allows using fs-verity to validate the content of the + content-files as well as the metadata file which allows dm-verity + like validation with the flexibility of regular files. + + For more information see Documentation/filesystems/composefs.rst + + If unsure, say N. diff --git a/fs/composefs/Makefile b/fs/composefs/Makefile new file mode 100644 index 000000000000..eac8445e7d25 --- /dev/null +++ b/fs/composefs/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_COMPOSEFS_FS) +=3D composefs.o + +composefs-objs +=3D cfs-reader.o cfs.o --=20 2.39.0