From patchwork Thu Apr 16 16:56:51 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2197 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358732; bh=iJiyg5mzeCQxTbfWUAu7ppQci0wb/g6Aqzf2jY9U/LA=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=nSQXQPlk3peHzd7LcaNwDIEUqx0+mjwkJViXnzy3dcElb0zYub3Tj9/cbRmiUVFwZ 9CyWb/8bHNiFiwprLI4Dl8gsZtnYCljU5/sjeHNyJPEvSBCn2pUKOyoOgr+otqGkpO 1Ixq1nWNmaiQD5RTTeRWfhoW67BHf2AHVwNW2ehk= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 129F66A4DC for ; Thu, 16 Apr 2026 10:58:52 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id hWA1gYauUaM6 for ; Thu, 16 Apr 2026 10:58:52 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358732; bh=iJiyg5mzeCQxTbfWUAu7ppQci0wb/g6Aqzf2jY9U/LA=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=nSQXQPlk3peHzd7LcaNwDIEUqx0+mjwkJViXnzy3dcElb0zYub3Tj9/cbRmiUVFwZ 9CyWb/8bHNiFiwprLI4Dl8gsZtnYCljU5/sjeHNyJPEvSBCn2pUKOyoOgr+otqGkpO 1Ixq1nWNmaiQD5RTTeRWfhoW67BHf2AHVwNW2ehk= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 028266A4D8 for ; Thu, 16 Apr 2026 10:58:52 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358729; bh=RQ/Egvugw1nBWhFnrpQkHB4YkSIjEDUmHnO62xGce8w=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SRrEnRSER3h40H4JZYQpqzJiXOftT2m2YRSLA0PsOWpyZoU7VzktJaZk+FzKaby6Z CV0MhWJJWyHt604DY55qCTbPdbUpJGRUVFlKvCoTYFNOIEoFg5vevgnoUPGvHUiD9o DGr/z4dUTQYiWFxFVWTmyueK3VPJpIESmrmRP6PE= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id E350E6A4A5; Thu, 16 Apr 2026 10:58:49 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id 4ftxDjwj79Ms; Thu, 16 Apr 2026 10:58:49 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358727; bh=AJt8aeiNhkPk6824jFWQ950nEmsDxkBMimZR5hwv6qo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=C/uPoHODst2ZJBc2f6PN/dZUwrGb1fQnc/n8HJifmvWmWuZPT4PmBX1EkUhxiADUw IbNQw/QkvQHuB0vEm0tjkcR0+AW0lrvfCU32xm8eDghEj2gO7HWIyRDnjAYVf9c4wG vIoaIE9f4tCmnYG6QKJtkfGD8ehuH+Wmpx0nh+YA= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 988346A4DD; Thu, 16 Apr 2026 10:58:47 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:56:51 -0600 Message-ID: <20260416165733.2923423-2-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: BFCBB3ESFYZAW4ELGMH5X2H2OP6YOUN2 X-Message-ID-Hash: BFCBB3ESFYZAW4ELGMH5X2H2OP6YOUN2 X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 01/21] linux: Add magic.h with filesystem magic numbers List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass The Linux kernel defines filesystem magic numbers in U-Boot's linux/iso_fs.h needs ISOFS_SUPER_MAGIC from this header. Add a minimal magic.h with EXT4_SUPER_MAGIC and ISOFS_SUPER_MAGIC for use by Linux-ported filesystem drivers. Also remove the duplicate EXT4_SUPER_MAGIC definition from ext4_uboot.h replacing it with an include of Signed-off-by: Simon Glass --- fs/ext4l/ext4_uboot.h | 2 +- include/linux/magic.h | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 include/linux/magic.h diff --git a/fs/ext4l/ext4_uboot.h b/fs/ext4l/ext4_uboot.h index 01868087ec4..8face975101 100644 --- a/fs/ext4l/ext4_uboot.h +++ b/fs/ext4l/ext4_uboot.h @@ -112,6 +112,7 @@ #include #include #include +#include #include #include #include @@ -174,7 +175,6 @@ /* ext4-specific constants */ #define EXT4_FIEMAP_EXTENT_HOLE 0x08000000 -#define EXT4_SUPER_MAGIC 0xEF53 #define EXT4_GOING_FLAGS_DEFAULT 0 #define EXT4_GOING_FLAGS_LOGFLUSH 1 diff --git a/include/linux/magic.h b/include/linux/magic.h new file mode 100644 index 00000000000..65e85114a6e --- /dev/null +++ b/include/linux/magic.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_MAGIC_H +#define _LINUX_MAGIC_H + +#define EXT4_SUPER_MAGIC 0xef53 +#define ISOFS_SUPER_MAGIC 0x9660 + +#endif /* _LINUX_MAGIC_H */ From patchwork Thu Apr 16 16:56:52 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2198 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358735; bh=9dLiWk+ZPyX+Qb4ftBo+UleUnfH7aeQIEnoGihUz82k=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=J/gEFe+uf3Y58SpCilmbJlvScJXcBuDB8ZEvXPFx3dCegN3gn7h6yb2EX+OAQwzC2 uD9DIaiLbWTFRt6YQe9B8/t73VbrJl/pvQz1R+HZQkqJEmfq+wjBnRoyjK+ccMgM2/ uAxyId2vVA2VpJ90dstT7gRRKBceEuM3SeCDR7hY= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id D25D56A4DC for ; Thu, 16 Apr 2026 10:58:55 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id mQSqBE_oSQzV for ; Thu, 16 Apr 2026 10:58:55 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358735; bh=9dLiWk+ZPyX+Qb4ftBo+UleUnfH7aeQIEnoGihUz82k=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=J/gEFe+uf3Y58SpCilmbJlvScJXcBuDB8ZEvXPFx3dCegN3gn7h6yb2EX+OAQwzC2 uD9DIaiLbWTFRt6YQe9B8/t73VbrJl/pvQz1R+HZQkqJEmfq+wjBnRoyjK+ccMgM2/ uAxyId2vVA2VpJ90dstT7gRRKBceEuM3SeCDR7hY= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id C13BE6A4D6 for ; Thu, 16 Apr 2026 10:58:55 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358734; bh=2RzdwcdhGm5cEwuLDvOhH0evXDGvmJN1zDqqhZ6YVnk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=A46jMf+gxsGq2MdGE8RuNRXt9NCcym4XfU5cVAZtdxkNzI0o8tv00VRaNdKniE7t/ E7eem4ZUdqecoWE0XbiZea60LjQKqjkAhgoP36STimHMS8Waoki2RJZ/RSaDCe9l+j 8ZSU2jbOFXVcTBWlXzKTSMM1VyGtW29CMbdWEw4g= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 714D26A4DA; Thu, 16 Apr 2026 10:58:54 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id 9e2qJVlXNuId; Thu, 16 Apr 2026 10:58:54 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358729; bh=7l6fQZ7vzGYC/8r/fblrLx258X21Rw8wqASMQeT0Wzc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FgzWJR/8a454YdxloczHnFQWTZn0mxrLZFL21iJ1YuySFJOwKtIzkq1vpsfruGOaX PSlRqzfAxHgWEhomMMXAQHiBevrMxT4niRxGZGDw4LiFqT6KXjBxP5mUvzaRWgV6x6 xYPUTpcBuYoexRVurGuC8VecfKiI8kxSY7MJriNY= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id C9D776A4D6; Thu, 16 Apr 2026 10:58:48 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:56:52 -0600 Message-ID: <20260416165733.2923423-3-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: L74GOBBTV6SJY2IOKB2EV2BRG43HTVM6 X-Message-ID-Hash: L74GOBBTV6SJY2IOKB2EV2BRG43HTVM6 X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 02/21] linux: Add unaligned.h redirect header List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Linux 6.x moved unaligned access helpers from to Add a redirect header so that Linux-ported drivers using the new path find the existing U-Boot implementation. Signed-off-by: Simon Glass --- include/linux/unaligned.h | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 include/linux/unaligned.h diff --git a/include/linux/unaligned.h b/include/linux/unaligned.h new file mode 100644 index 00000000000..1949a7b7a49 --- /dev/null +++ b/include/linux/unaligned.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Linux 6.x moved unaligned access helpers to + * In U-Boot the implementation lives in + */ +#ifndef _LINUX_UNALIGNED_H +#define _LINUX_UNALIGNED_H + +#include + +#endif From patchwork Thu Apr 16 16:56:53 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2199 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358738; bh=1FkmUc7K8rNoVp/W2fSmf1w+S3U4ClZFEHkP9kP/eho=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=KaJUx3Izx4vDYcQJAVqrG8ZDj81rtNOSoZdjLYxQLmAmsA8zpp02g1G8rtIOvtNtT akcCvuAp0/3WeGuli+g0mfUWzkLH/eUB4JHu1025pcjPx5xNeHvfj37wqdkqd3Bazt VgPfly6OdsS2SiiNMqVKPtS7sdG2ORnIssUhFMEI= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 857CE6A4DA for ; Thu, 16 Apr 2026 10:58:58 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id B1sd7KJyCrr0 for ; Thu, 16 Apr 2026 10:58:58 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358736; bh=1FkmUc7K8rNoVp/W2fSmf1w+S3U4ClZFEHkP9kP/eho=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=prZHV08qv8VywiPpUxkaR55AXEluv6FHkDWIGQ2mIJnn1Wb8D96DD6NMRvKdEfTAK qvVurxXCeawmRYGtlzcMN8TqoHsxaKxnbRkqGvq83ySG4pUhV4VvH2DWxeguNr4uIh 8wyBsneJ0KXUMIO92VKA5YW1GJcuUO6pT+8S8xt8= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 8CBC26A4E4 for ; Thu, 16 Apr 2026 10:58:56 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358734; bh=pwatgte31XBJezwfrTBVxYYH+Fcjgk7p2l+NHI5bzFk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kCvrxWBYy24+RnSCenE9HoOXzfQA9vrF4WRh5NCBcxpV1YCJ31ZFkfJ6LskzAnBAC ZDb/d6IEs4H1b9wC9/9jCiOQVScJIBjm5QOgOGL6MK9Qp0qZXtZlfOvZvZJVxarO+M VLz9ZJfnfAgBCozNjhVG/UTAFIhR8tkXKJS22m40= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 83A766A4D6; Thu, 16 Apr 2026 10:58:54 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id lQiHt9vd3WMz; Thu, 16 Apr 2026 10:58:54 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358734; bh=0UK6mzXU27EXjonTKCskgECsTwlPyJtIvBOzK89J6pI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=QWLDJyoSffIwl/2QwqUnuMi1uVG4/SjWG8i0uMRaGbErBkkBLzjqyaZdz5Dbgu2At zHNsQ9/AxWFsYE8hV4uAD3K0ib3UOVt7JNf20EAy5DjmgVe3aWqgLS0erfVApnJ1uS 96OvZxzdtYrsQuJvg5f3kQIcBO1FO4hwLOZA9GFY= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id BD59D6A4D8; Thu, 16 Apr 2026 10:58:53 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:56:53 -0600 Message-ID: <20260416165733.2923423-4-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: ICSMEMPMCTTMRBMHET35NKKDCX2AORSK X-Message-ID-Hash: ICSMEMPMCTTMRBMHET35NKKDCX2AORSK X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 03/21] linux: Add NLS stubs with UTF-16 to UTF-8 conversion List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass The Linux kernel's NLS (National Language Support) subsystem provides character set conversion for filesystems with Unicode support. Add minimal stubs for struct nls_table, load_nls(), unload_nls(), and a basic utf16s_to_utf8s() implementation. These are needed by the isofs Joliet extension for Unicode filename support. Signed-off-by: Simon Glass --- include/charset.h | 20 ++++++++++++++++++ include/linux/nls.h | 50 +++++++++++++++++++++++++++++++++++++++++++++ lib/charset.c | 29 ++++++++++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 include/linux/nls.h diff --git a/include/charset.h b/include/charset.h index 348bad5883a..2d1964287d8 100644 --- a/include/charset.h +++ b/include/charset.h @@ -303,6 +303,26 @@ size_t u16_strlcat(u16 *dest, const u16 *src, size_t count); */ uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size); +enum utf16_endian; + +/** + * utf16s_to_utf8s() - convert a UTF-16 string to UTF-8 with explicit endianness + * + * Linux NLS-compatible interface that wraps utf16_to_utf8(). Converts at + * most @inlen UTF-16 code units from @pwcs to UTF-8, stopping at a null + * character or when @maxout bytes have been written. Surrogate pairs are + * handled by the underlying utf16_to_utf8() implementation. + * + * @pwcs: source UTF-16 string + * @inlen: number of UTF-16 code units to convert + * @endian: byte order of the source string (UTF16_BIG_ENDIAN, etc.) + * @s: destination buffer for UTF-8 output + * @maxout: size of the destination buffer in bytes + * Return: number of bytes written to @s + */ +int utf16s_to_utf8s(const u16 *pwcs, int inlen, enum utf16_endian endian, + u8 *s, int maxout); + /** * utf_to_cp() - translate Unicode code point to 8bit codepage * diff --git a/include/linux/nls.h b/include/linux/nls.h new file mode 100644 index 00000000000..939dc1afaaa --- /dev/null +++ b/include/linux/nls.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Minimal NLS (National Language Support) stubs for U-Boot + * + * Based on the interface from Linux's include/linux/nls.h but heavily + * simplified: struct nls_table is trimmed to the fields used by isofs, + * load_nls() and unload_nls() are no-ops, and utf16s_to_utf8s() is a + * implementation wrapping utf16_to_utf8() in lib/charset.c + * + * Joliet support requires NLS for character set conversion. These stubs + * allow the code to compile without full NLS infrastructure. + */ +#ifndef _LINUX_NLS_H +#define _LINUX_NLS_H + +#include + +#define NLS_MAX_CHARSET_SIZE 6 + +/* UTF-16 byte order */ +enum utf16_endian { + UTF16_HOST_ENDIAN, + UTF16_LITTLE_ENDIAN, + UTF16_BIG_ENDIAN, +}; + +struct nls_table { + const char *charset; + int (*uni2char)(wchar_t uni, unsigned char *out, int boundlen); + int (*char2uni)(const unsigned char *rawstring, int boundlen, + wchar_t *uni); +}; + +static inline struct nls_table *load_nls(const char *charset) +{ + return NULL; +} + +static inline struct nls_table *load_nls_default(void) +{ + return NULL; +} + +static inline void unload_nls(struct nls_table *nls) +{ +} + +#include + +#endif /* _LINUX_NLS_H */ diff --git a/lib/charset.c b/lib/charset.c index 182c92a50c4..911659199d7 100644 --- a/lib/charset.c +++ b/lib/charset.c @@ -10,6 +10,7 @@ #include #include #include +#include #include /** @@ -311,6 +312,34 @@ int utf16_utf8_strncpy(char **dst, const u16 *src, size_t count) return 0; } +int utf16s_to_utf8s(const u16 *pwcs, int inlen, enum utf16_endian endian, + u8 *s, int maxout) +{ + u16 *tmp; + u8 *start = s; + int i; + + tmp = malloc(inlen * sizeof(u16)); + if (!tmp) + return 0; + + for (i = 0; i < inlen; i++) { + if (endian == UTF16_BIG_ENDIAN) + tmp[i] = __be16_to_cpu(pwcs[i]); + else + tmp[i] = __le16_to_cpu(pwcs[i]); + if (!tmp[i]) { + inlen = i; + break; + } + } + + s = utf16_to_utf8(s, tmp, inlen); + free(tmp); + + return min((int)(s - start), maxout); +} + s32 utf_to_lower(const s32 code) { struct capitalization_table *pos = capitalization_table; From patchwork Thu Apr 16 16:56:54 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2200 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358738; bh=T1KBGlejgR6aBe5J6ev1DdDVPd3t5GEE5ZDVDicQ5oc=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=pKoqUKE5BvHc1cXehvUBRvTeIvPjipwT0aQg8yD9O3sk3R6YUllvB1Qhw/IfnzUKQ 888OGQJyLB9EAp8GG+UBOzvZljlJGqPAp4RTDSAlQLLvFgOsFhkWcXOXr4Tg4t6O3v Ut6COtthSFmLZOQaXmG5AV9//m/prfqKHXsb6Cxc= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id CB46F6A4D8 for ; Thu, 16 Apr 2026 10:58:58 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id Q6x__f-7z0EJ for ; Thu, 16 Apr 2026 10:58:58 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358737; bh=T1KBGlejgR6aBe5J6ev1DdDVPd3t5GEE5ZDVDicQ5oc=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=ixe6KouzoYHZP7njSVnU2ueuH+0/zU9v3jffYAI3NkWNHKIGO2wKMLMBdHssfpRah dCRZ6IllseZ07omDkwx1GDvusIo1JpaWC/2mTLvL6k8geKn/T6/Gf1T/VMq6c4eIBN HkJ9mF1xe4ILy2RYH3qQsbK/h7OftwzYUj6WNhgk= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 1A79B6A4E9 for ; Thu, 16 Apr 2026 10:58:57 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358735; bh=KaHZ/K1dnVMt+L/1J1n9PZygkjfjHE7jQPdvKBLPy+4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=E2vv4P/YBv9GaItlRvlzk2uLpZDXJRdtVZrnH+SDUQ5tXyTB9pCagxNQ97wa6cpHE T57I2ed5ZlPDYy17YGS7nK5wtNBrNNfYodnEoIjX3LdUBkhzAUWhnzzDevhhA/LsOs my73n4fWeVH+aTQXa/L7IYd+KO9t3MMl8Ix/Iu1U= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 8358A6A4DA; Thu, 16 Apr 2026 10:58:55 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id e8nl5n9G6fKo; Thu, 16 Apr 2026 10:58:55 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358735; bh=/OGxPJemD6afiABqW72+QzaXztTRN9uYHzJEqJ0iafE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RoR9o1teDLw2TKUvMeD2LVm5Sfroqorjhb/stqB2n9Nljd3ybIz5e30cIeEyU9ZLu LWNvPx1lJG+k37CrM/R98YZtbea1IafMaMpdGx+UgcvY6Q3UfIf6Pd1f7E2lAJ8yFC kYgJdPb746uYkRpHhJNW5WZIm3oDbTHbe763MTbg= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id D2D406A4D8; Thu, 16 Apr 2026 10:58:54 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:56:54 -0600 Message-ID: <20260416165733.2923423-5-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: KJ4AZ3OLLD6TEK426PAFSYO6LP5FTLVY X-Message-ID-Hash: KJ4AZ3OLLD6TEK426PAFSYO6LP5FTLVY X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 04/21] linux: Add ISO 9660 on-disk structure definitions List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Add the ISO 9660 filesystem structure definitions from the Linux UAPI header. This includes volume descriptors, directory records, High Sierra format support, and the ISOFS_BLOCK_SIZE / ISOFS_BUFFER_SIZE macros needed by the isofs driver. Taken from Linux v7.0 include/uapi/linux/iso_fs.h without modification. Signed-off-by: Simon Glass --- include/linux/iso_fs.h | 166 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 include/linux/iso_fs.h diff --git a/include/linux/iso_fs.h b/include/linux/iso_fs.h new file mode 100644 index 00000000000..758178f5b52 --- /dev/null +++ b/include/linux/iso_fs.h @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _ISOFS_FS_H +#define _ISOFS_FS_H + +#include +#include + +/* + * The isofs filesystem constants/structures + */ + +/* This part borrowed from the bsd386 isofs */ +#define ISODCL(from, to) (to - from + 1) + +struct iso_volume_descriptor { + __u8 type[ISODCL(1,1)]; /* 711 */ + char id[ISODCL(2,6)]; + __u8 version[ISODCL(7,7)]; + __u8 data[ISODCL(8,2048)]; +}; + +/* volume descriptor types */ +#define ISO_VD_PRIMARY 1 +#define ISO_VD_SUPPLEMENTARY 2 +#define ISO_VD_END 255 + +#define ISO_STANDARD_ID "CD001" + +struct iso_primary_descriptor { + __u8 type [ISODCL ( 1, 1)]; /* 711 */ + char id [ISODCL ( 2, 6)]; + __u8 version [ISODCL ( 7, 7)]; /* 711 */ + __u8 unused1 [ISODCL ( 8, 8)]; + char system_id [ISODCL ( 9, 40)]; /* achars */ + char volume_id [ISODCL ( 41, 72)]; /* dchars */ + __u8 unused2 [ISODCL ( 73, 80)]; + __u8 volume_space_size [ISODCL ( 81, 88)]; /* 733 */ + __u8 unused3 [ISODCL ( 89, 120)]; + __u8 volume_set_size [ISODCL (121, 124)]; /* 723 */ + __u8 volume_sequence_number [ISODCL (125, 128)]; /* 723 */ + __u8 logical_block_size [ISODCL (129, 132)]; /* 723 */ + __u8 path_table_size [ISODCL (133, 140)]; /* 733 */ + __u8 type_l_path_table [ISODCL (141, 144)]; /* 731 */ + __u8 opt_type_l_path_table [ISODCL (145, 148)]; /* 731 */ + __u8 type_m_path_table [ISODCL (149, 152)]; /* 732 */ + __u8 opt_type_m_path_table [ISODCL (153, 156)]; /* 732 */ + __u8 root_directory_record [ISODCL (157, 190)]; /* 9.1 */ + char volume_set_id [ISODCL (191, 318)]; /* dchars */ + char publisher_id [ISODCL (319, 446)]; /* achars */ + char preparer_id [ISODCL (447, 574)]; /* achars */ + char application_id [ISODCL (575, 702)]; /* achars */ + char copyright_file_id [ISODCL (703, 739)]; /* 7.5 dchars */ + char abstract_file_id [ISODCL (740, 776)]; /* 7.5 dchars */ + char bibliographic_file_id [ISODCL (777, 813)]; /* 7.5 dchars */ + __u8 creation_date [ISODCL (814, 830)]; /* 8.4.26.1 */ + __u8 modification_date [ISODCL (831, 847)]; /* 8.4.26.1 */ + __u8 expiration_date [ISODCL (848, 864)]; /* 8.4.26.1 */ + __u8 effective_date [ISODCL (865, 881)]; /* 8.4.26.1 */ + __u8 file_structure_version [ISODCL (882, 882)]; /* 711 */ + __u8 unused4 [ISODCL (883, 883)]; + __u8 application_data [ISODCL (884, 1395)]; + __u8 unused5 [ISODCL (1396, 2048)]; +}; + +/* Almost the same as the primary descriptor but two fields are specified */ +struct iso_supplementary_descriptor { + __u8 type [ISODCL ( 1, 1)]; /* 711 */ + char id [ISODCL ( 2, 6)]; + __u8 version [ISODCL ( 7, 7)]; /* 711 */ + __u8 flags [ISODCL ( 8, 8)]; /* 853 */ + char system_id [ISODCL ( 9, 40)]; /* achars */ + char volume_id [ISODCL ( 41, 72)]; /* dchars */ + __u8 unused2 [ISODCL ( 73, 80)]; + __u8 volume_space_size [ISODCL ( 81, 88)]; /* 733 */ + __u8 escape [ISODCL ( 89, 120)]; /* 856 */ + __u8 volume_set_size [ISODCL (121, 124)]; /* 723 */ + __u8 volume_sequence_number [ISODCL (125, 128)]; /* 723 */ + __u8 logical_block_size [ISODCL (129, 132)]; /* 723 */ + __u8 path_table_size [ISODCL (133, 140)]; /* 733 */ + __u8 type_l_path_table [ISODCL (141, 144)]; /* 731 */ + __u8 opt_type_l_path_table [ISODCL (145, 148)]; /* 731 */ + __u8 type_m_path_table [ISODCL (149, 152)]; /* 732 */ + __u8 opt_type_m_path_table [ISODCL (153, 156)]; /* 732 */ + __u8 root_directory_record [ISODCL (157, 190)]; /* 9.1 */ + char volume_set_id [ISODCL (191, 318)]; /* dchars */ + char publisher_id [ISODCL (319, 446)]; /* achars */ + char preparer_id [ISODCL (447, 574)]; /* achars */ + char application_id [ISODCL (575, 702)]; /* achars */ + char copyright_file_id [ISODCL (703, 739)]; /* 7.5 dchars */ + char abstract_file_id [ISODCL (740, 776)]; /* 7.5 dchars */ + char bibliographic_file_id [ISODCL (777, 813)]; /* 7.5 dchars */ + __u8 creation_date [ISODCL (814, 830)]; /* 8.4.26.1 */ + __u8 modification_date [ISODCL (831, 847)]; /* 8.4.26.1 */ + __u8 expiration_date [ISODCL (848, 864)]; /* 8.4.26.1 */ + __u8 effective_date [ISODCL (865, 881)]; /* 8.4.26.1 */ + __u8 file_structure_version [ISODCL (882, 882)]; /* 711 */ + __u8 unused4 [ISODCL (883, 883)]; + __u8 application_data [ISODCL (884, 1395)]; + __u8 unused5 [ISODCL (1396, 2048)]; +}; + + +#define HS_STANDARD_ID "CDROM" + +struct hs_volume_descriptor { + __u8 foo [ISODCL ( 1, 8)]; /* 733 */ + __u8 type [ISODCL ( 9, 9)]; /* 711 */ + char id [ISODCL ( 10, 14)]; + __u8 version [ISODCL ( 15, 15)]; /* 711 */ + __u8 data[ISODCL(16,2048)]; +}; + + +struct hs_primary_descriptor { + __u8 foo [ISODCL ( 1, 8)]; /* 733 */ + __u8 type [ISODCL ( 9, 9)]; /* 711 */ + __u8 id [ISODCL ( 10, 14)]; + __u8 version [ISODCL ( 15, 15)]; /* 711 */ + __u8 unused1 [ISODCL ( 16, 16)]; /* 711 */ + char system_id [ISODCL ( 17, 48)]; /* achars */ + char volume_id [ISODCL ( 49, 80)]; /* dchars */ + __u8 unused2 [ISODCL ( 81, 88)]; /* 733 */ + __u8 volume_space_size [ISODCL ( 89, 96)]; /* 733 */ + __u8 unused3 [ISODCL ( 97, 128)]; /* 733 */ + __u8 volume_set_size [ISODCL (129, 132)]; /* 723 */ + __u8 volume_sequence_number [ISODCL (133, 136)]; /* 723 */ + __u8 logical_block_size [ISODCL (137, 140)]; /* 723 */ + __u8 path_table_size [ISODCL (141, 148)]; /* 733 */ + __u8 type_l_path_table [ISODCL (149, 152)]; /* 731 */ + __u8 unused4 [ISODCL (153, 180)]; /* 733 */ + __u8 root_directory_record [ISODCL (181, 214)]; /* 9.1 */ +}; + +/* We use this to help us look up the parent inode numbers. */ + +struct iso_path_table{ + __u8 name_len[2]; /* 721 */ + __u8 extent[4]; /* 731 */ + __u8 parent[2]; /* 721 */ + char name[]; +} __attribute__((packed)); + +/* high sierra is identical to iso, except that the date is only 6 bytes, and + there is an extra reserved byte after the flags */ + +struct iso_directory_record { + __u8 length [ISODCL (1, 1)]; /* 711 */ + __u8 ext_attr_length [ISODCL (2, 2)]; /* 711 */ + __u8 extent [ISODCL (3, 10)]; /* 733 */ + __u8 size [ISODCL (11, 18)]; /* 733 */ + __u8 date [ISODCL (19, 25)]; /* 7 by 711 */ + __u8 flags [ISODCL (26, 26)]; + __u8 file_unit_size [ISODCL (27, 27)]; /* 711 */ + __u8 interleave [ISODCL (28, 28)]; /* 711 */ + __u8 volume_sequence_number [ISODCL (29, 32)]; /* 723 */ + __u8 name_len [ISODCL (33, 33)]; /* 711 */ + char name []; +} __attribute__((packed)); + +#define ISOFS_BLOCK_BITS 11 +#define ISOFS_BLOCK_SIZE 2048 + +#define ISOFS_BUFFER_SIZE(INODE) ((INODE)->i_sb->s_blocksize) +#define ISOFS_BUFFER_BITS(INODE) ((INODE)->i_sb->s_blocksize_bits) + +#endif /* _ISOFS_FS_H */ From patchwork Thu Apr 16 16:56:55 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2201 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358742; bh=bDf1q9QRRJW08GBsTI98zUTobIbeOqfD3eO8tQWvRJg=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=SM/gY2S0RvuChtUKYk/NaS041PlbW/prxhEogwcE4f8FsbBtgqIc5/MRljG2p/Yzj I5dLvDh/HpJRdSBnZw3I4L8zkm/3ac2anFaXtyrPw8Ob5DZOgviJTjxA66rqSV6pXq HTiiBJUJjLOFfdZ40KLNS/+wCQlyht8pRvfnK9Qg= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id E3D7B6A4DA for ; Thu, 16 Apr 2026 10:59:02 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id LPvHeHErNFpz for ; Thu, 16 Apr 2026 10:59:02 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358742; bh=bDf1q9QRRJW08GBsTI98zUTobIbeOqfD3eO8tQWvRJg=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=SM/gY2S0RvuChtUKYk/NaS041PlbW/prxhEogwcE4f8FsbBtgqIc5/MRljG2p/Yzj I5dLvDh/HpJRdSBnZw3I4L8zkm/3ac2anFaXtyrPw8Ob5DZOgviJTjxA66rqSV6pXq HTiiBJUJjLOFfdZ40KLNS/+wCQlyht8pRvfnK9Qg= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id D2E8F6A4DC for ; Thu, 16 Apr 2026 10:59:02 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358741; bh=8EtjoH2kPfns7qpxmdcxGHRL1PhzbBTjf9qw88fjmqM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NHsJC8ZRqy/jxAuZtQjiEglL0vOOW/u4Hi2bB/H0OSH65rhEeERfH3TABSIE3VLRU nofL1sAcV4ZGv3WTWbqhfAcRV4BHx8djiZ1uYBNLcwULTUH/Ixk4pQnzh01MTfncYc 34btRuSkl6ZkF6xxW2HasRuAY+NvjTnNu4c7b28U= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 249DF6A4DA; Thu, 16 Apr 2026 10:59:01 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id TkMyxZCp2ZmO; Thu, 16 Apr 2026 10:59:01 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358736; bh=HzTcpD9hciZd0sZZRQNCFIOG/oqYlD36xbsoa/U98E4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=odCMbHQWwdDY+yrSDuJi+CMtBDuhWWZI7kECPTxWvC6vg2hZzy0W4ZxEwNEdBtTkP EKl22lRfPIAAADWevf487hFN2dtwa+MQ5cZi6wAq8R8Zk94JIZiS8CxYJgnQCk9CoE wD4az2UY6me3XNKVHapYM0ieK8hoVx2H5KcFe2oc= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 160B96A4A5; Thu, 16 Apr 2026 10:58:56 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:56:55 -0600 Message-ID: <20260416165733.2923423-6-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: PK5AMVT73TLTUWEJMTDABM3S3CTUMRIK X-Message-ID-Hash: PK5AMVT73TLTUWEJMTDABM3S3CTUMRIK X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 05/21] linux: dcache: Add d_op field to struct dentry List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass The isofs driver uses dentry_operations for case-insensitive filename matching (needed for ISO 9660 and Joliet extensions). Add the d_op field and a forward declaration of struct dentry_operations to the U-Boot dentry stub. Signed-off-by: Simon Glass --- include/linux/dcache.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/dcache.h b/include/linux/dcache.h index bf8d1e4755e..bf6253ee60f 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -48,11 +48,14 @@ static const struct qstr dotdot_name = QSTR_INIT("..", 2); * * U-Boot stub - minimal fields for ext4l. */ +struct dentry_operations; + struct dentry { struct qstr d_name; struct inode *d_inode; struct super_block *d_sb; struct dentry *d_parent; + const struct dentry_operations *d_op; }; /** From patchwork Thu Apr 16 16:56:56 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2202 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358745; bh=2BG86rQXdkQipoK49RLlexkjpBs6IeyvfRnsFGd9bDI=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=pydxLh47rxhBqelnrWDt2wozbr7u3IPyr4QiuZMiN4vniSv8xZRtjFA1AkJko6lC/ Yh6K2BbzI45WzwrTiQm//zudmwzk3XgrRkwf77O04yxvNHUA3X4I4wt+KPjQqL8QQj vtAupEvgO7iYPKfANtqK3ve9+fcrrE4wxSFZhUqI= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 58BEA6A4F1 for ; Thu, 16 Apr 2026 10:59:05 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id 0qF4YL-NC9_r for ; Thu, 16 Apr 2026 10:59:05 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358743; bh=2BG86rQXdkQipoK49RLlexkjpBs6IeyvfRnsFGd9bDI=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=Ywo6D//mDCUKsgyWyaOXp40gvyu4wW1qrdggQa1qYjS9yvCUcJIrcVtw9QbFYvDcn LREK2MeNL93gFJw0Ru6BRLXf8aomRxqXC0JKBaQPo8EP/1ekDpibO2rnKJGFOuT7iZ zuLPc37dcDjbp8CJakxMowI8UXcl5vwJ2h4DqEx0= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 572026A4E8 for ; Thu, 16 Apr 2026 10:59:03 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358741; bh=lS12DOzQQLZ8AjfOVNgbNYQZCjP97oilhsq+GUxMWPU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FzTeZiBC2XHk++DqeoxwHDNJyG0sCW9ipxiWRFPeX4357dmpUUKnFh8Hr9llaUC/G zkKQ1Y1HaaIF5dYHFMvtTIcug8BaC1751z/DTP+XwnZDQRz5K0ZVfhdXWwNZKfMQFs yirbfehGw8aSbNrIvznznN4zYdJOei8Yuc1YzhFw= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 83D026A4A5; Thu, 16 Apr 2026 10:59:01 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id lc3PAvU-P0Oa; Thu, 16 Apr 2026 10:59:01 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358741; bh=0PBJbBNQGTxg+MrXo+k26XFicooyTpM7j6/48vSdxmk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IcogwMCMs5U7pZY/o6xd3HoPmVkRMDozWP9fvp1/hrtQGgt52HHM8R8GQa30K1gr1 L3wiyM+9sNme3niMrLW7i9tGngxUKvj+2qViH+qznkEBBwMygPAwKk9RyUsiK3sJT9 V4nMduML2NKuXNTnD5IZzB4XCHutgnm2rUixNo1I= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 0F1896A4D8; Thu, 16 Apr 2026 10:59:01 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:56:56 -0600 Message-ID: <20260416165733.2923423-7-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 5BV3UUDCRGFUK6TV2BE67F7CIYYZRXBZ X-Message-ID-Hash: 5BV3UUDCRGFUK6TV2BE67F7CIYYZRXBZ X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 06/21] linux: buffer_head: Implement map_bh() properly List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass The map_bh() macro is used by isofs_get_blocks() to record the physical block number for a mapped buffer head. The current no-op stub causes isofs_bmap() to return uninitialised block numbers, leading to reads from invalid disk locations. Implement map_bh() to set b_blocknr and mark the buffer as mapped. Signed-off-by: Simon Glass --- include/linux/buffer_head.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index ea317a6488d..3b829e035aa 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -283,7 +283,8 @@ static inline void end_buffer_read_sync(struct buffer_head *bh, int uptodate) #define try_to_free_buffers(f) ({ (void)(f); true; }) #define block_commit_write(f, f2, t) do { } while (0) #define block_page_mkwrite(v, f, g) ((vm_fault_t)0) -#define map_bh(bh, sb, block) do { } while (0) +#define map_bh(bh, sb, blk) \ + do { (bh)->b_blocknr = (blk); set_buffer_mapped(bh); } while (0) #define block_read_full_folio(folio, get_block) \ ({ (void)(folio); (void)(get_block); 0; }) From patchwork Thu Apr 16 16:56:57 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2203 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358745; bh=nC2Zo3/toKVHHaZyF9hhWP/utjEs3uvS7wYDO/iGmlw=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=N3HFQAYJ3ohGj+eVYfwo4ZyL7pr4U9iIzC6qYWrYEO5h4BjLnb37NXU+VePjkj4WF Jwhc1JhmGALYLLzdHZXqjkBX6ek8dv2Q6KjyHhdiV+FtrqDMeJZxdwNST1vTeublwA chzXu4im7gATd2mTuHc4hFIyYrQs6xHLOyhY44i8= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id CF7266A4ED for ; Thu, 16 Apr 2026 10:59:05 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id T-GrOXrz0I61 for ; Thu, 16 Apr 2026 10:59:05 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358744; bh=nC2Zo3/toKVHHaZyF9hhWP/utjEs3uvS7wYDO/iGmlw=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=AEEFrFyGHbKd0oXO1ABqsV8nhy3TkGxMsWu39v8vUUFvGoOTW8HvprAAJvmWXcXes UQWYMEF1Ohbo5TlM7XEQ3ZOSADjzBpSmjaOLtut7FE5pXDHuTq4hH6OpPj5i1jSvaT dK1Z+aflvW97HkT59lV1pBSdQZ/kvRKtSI5wvA8U= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 8FDE56A4DE for ; Thu, 16 Apr 2026 10:59:04 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358742; bh=B8Uqp11Dhk7p559mu59RozRmm0johytLPmDE4UWahw4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jWlt+F2gM2Sg7nhxmcFe6AKafdzN8TO98/WO9VaNQgMsGAGtYVgnu28zKpDVjs16K wvWLc4bPQU2fQMKwJRToqu/LmraLxqmGpckwvpvmkdJO7rFGheXpJ+MvYzjAArYBkb nlxXKyWX3+RFATUKah7eikWpwY3EKJidlGDfpRWo= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 90E856A4A5; Thu, 16 Apr 2026 10:59:02 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id i0GBz7gEEEI7; Thu, 16 Apr 2026 10:59:02 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358742; bh=NmNPoLvyMX4bF8xFeNrr9QatlJf159NzR8PjBUNRNl8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=PtjpKeF2OtEl5+2tHriCdcheQnESEl9zcNs+e9HCMxmc7f++qbmvgYV/Xj8qk4cCb NDN2T1gKKNY2HaUTYKkr17qp5HF/2G+Dav0KDr6cZFVjBHV8ec3jH1YTvjlcvjPirX 4sI4qbNJTH3GiRnKOZJkKx7FtVwEeYGAi0R0Cx+8= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 1235F6A4D8; Thu, 16 Apr 2026 10:59:02 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:56:57 -0600 Message-ID: <20260416165733.2923423-8-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 2GHTFYTK5DZQVQZ27LRSJECT2GCHLQTB X-Message-ID-Hash: 2GHTFYTK5DZQVQZ27LRSJECT2GCHLQTB X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 07/21] linux: fs: Add shared VFS function declarations List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Declare the common VFS helpers shared across Linux-ported filesystem drivers: dir_emit_dot(), dir_emit_dotdot(), generic_ro_fops and page_symlink_inode_operations. Update the source-file comments on generic_read_dir() and generic_check_addressable() to point at fs/linux_fs.c, where the implementations move in a follow-up patch. Signed-off-by: Simon Glass --- include/linux/fs.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/include/linux/fs.h b/include/linux/fs.h index 1bc50d46281..c8b95528013 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -472,6 +472,10 @@ static inline bool dir_emit(struct dir_context *ctx, const char *name, int len, return ctx->actor(ctx, name, len, ctx->pos, ino, type) == 0; } +/* dir_emit_dot/dotdot - implemented in fs/linux_fs.c */ +bool dir_emit_dot(struct file *file, struct dir_context *ctx); +bool dir_emit_dotdot(struct file *file, struct dir_context *ctx); + #define dir_relax_shared(i) ({ (void)(i); 1; }) /* Inode mutex nesting classes */ @@ -729,11 +733,17 @@ struct inode_operations { /* Case-insensitive name validation - not supported */ #define generic_ci_validate_strict_name(d, n) ({ (void)(d); (void)(n); 1; }) -/* Generic directory read - implemented in ext4l/stub.c */ +/* Generic directory read - implemented in fs/linux_fs.c */ ssize_t generic_read_dir(struct file *f, char __user *buf, size_t count, loff_t *ppos); -/* Block addressability check - implemented in ext4l/stub.c */ +/* Block addressability check - implemented in fs/linux_fs.c */ int generic_check_addressable(unsigned int blocksize_bits, u64 num_blocks); +/* Read-only file operations stub - implemented in fs/linux_fs.c */ +extern const struct file_operations generic_ro_fops; + +/* Symlink inode operations stub - implemented in fs/linux_fs.c */ +extern const struct inode_operations page_symlink_inode_operations; + #endif /* _LINUX_FS_H */ From patchwork Thu Apr 16 16:56:58 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2204 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358748; bh=J3C4AFrChuhFJLeIYwrDGsL72v0EgLLvPGDyeI3d+zU=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=VmFh0eOFcXuNck0SRqOuHeI4yqs7teiWMWtAfq4wMFqesEhFTM6BLuioHCDToTAzJ 7zyGhRUeSbd8oiravPMLaDYVXxFC5EQkgmYUqStAycsJcii0kcPF4Su5jUzfGn10vm ylwDLqySZdIt3cCjvE76KZykJ6Rr5K8b4RN8caKc= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 5BF1D6A4DA for ; Thu, 16 Apr 2026 10:59:08 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id QTOQOoEHkG1N for ; Thu, 16 Apr 2026 10:59:08 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358748; bh=J3C4AFrChuhFJLeIYwrDGsL72v0EgLLvPGDyeI3d+zU=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=VmFh0eOFcXuNck0SRqOuHeI4yqs7teiWMWtAfq4wMFqesEhFTM6BLuioHCDToTAzJ 7zyGhRUeSbd8oiravPMLaDYVXxFC5EQkgmYUqStAycsJcii0kcPF4Su5jUzfGn10vm ylwDLqySZdIt3cCjvE76KZykJ6Rr5K8b4RN8caKc= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 253EF6A4DD for ; Thu, 16 Apr 2026 10:59:08 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358745; bh=s6ypKC9OGgqynUGlnezGktE76QjQsfa1bcpPGk0p0JA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=fMC2WPaRLQo+5M5jeUwti3QwkSY+yfXFycz4hIhBp9yZWaTOtOUcqvFn8loO1oqwW B+97ClFzbAur9Rr5zr62HHEpP6ihAexcpcWX3/zhlbdfmi12RuRCZbi2BJCVKNwUle tU5C8rx8tKDRMi/nIjivmRytHlwtqOEDnF7Hb2Xg= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 73BE56A4E6; Thu, 16 Apr 2026 10:59:05 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id YR9xBUAoE-7R; Thu, 16 Apr 2026 10:59:05 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358743; bh=XScyo0OJrJPmPe45s0cwmidd2w8Rwn/Un2opZBECL+g=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=e29lqeEorT3toWM1vbfdl0to4LISOg+FSwO64dDC7dq5ehYGkjvxR6U/wR4AriIso 6Az9pepRxVxmPp0tJvFhdgrwmlRlSuj0yCo3hGdtCUyXs1YZl5NhpSBAs7EWhEeY65 JmzGdl8kFXH/934zcFUOYJINl8ysbPPOm+qtplyw= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 2A6626A4A5; Thu, 16 Apr 2026 10:59:03 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:56:58 -0600 Message-ID: <20260416165733.2923423-9-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: WHUVFLJNAJZKMR2MY3IRPPYE5GFXIXPQ X-Message-ID-Hash: WHUVFLJNAJZKMR2MY3IRPPYE5GFXIXPQ X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 08/21] ext4l: Fix alloc_inode_sb() to actually allocate List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass alloc_inode_sb() returns NULL instead of calling kmem_cache_alloc(), causing any filesystem that uses kmem_cache-based inode allocation to fail. Fix it to perform the allocation. Signed-off-by: Simon Glass --- fs/ext4l/stub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext4l/stub.c b/fs/ext4l/stub.c index 3da511fa1cf..3198c472981 100644 --- a/fs/ext4l/stub.c +++ b/fs/ext4l/stub.c @@ -343,7 +343,7 @@ int inode_generic_drop(struct inode *inode) void *alloc_inode_sb(struct super_block *sb, struct kmem_cache *cache, gfp_t gfp) { - return NULL; + return kmem_cache_alloc(cache, gfp); } /* inode_set_iversion is now a macro in linux/iversion.h */ From patchwork Thu Apr 16 16:56:59 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2205 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358750; bh=cyLt2p/op1Hfm1RHFbZCzVFjyETRnmvPEL0XR4DDj1s=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=VFKRakOOXecouY3zcvPEOwxQmW6vWD52fr8CRSeIJFdqzYM/MP02hpBbb7XBKhJIq rwjmPe4tzZrUEp1vIUB64G9JjEGI4yxaBfzKU48ILJ3QgV0mmKNVOchjSuUldcIixM T3Vd1xCJKp/ykk92zKtT02/9lZ2U4X5UF0XMCgR8= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id D4BDE6A4A5 for ; Thu, 16 Apr 2026 10:59:10 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id 3Df5-TfvXS6i for ; Thu, 16 Apr 2026 10:59:10 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358749; bh=cyLt2p/op1Hfm1RHFbZCzVFjyETRnmvPEL0XR4DDj1s=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=jshpoWcch/ggF2EbtjOv9JZxYqJiES8aNsg2ulTEktHepWvY6K2DNq+UmNhB1GP2r 1bUtei1MEwXGmP41dqXGuNNn/y8JpBYu7oNjUM5y10EvPW8eJzCcIfoJbkYKr5Czdm Tq1m+8uZM3f0uqw+bMYivqGI6GrCmB+8B4E6z294= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id B7A846A4DA for ; Thu, 16 Apr 2026 10:59:09 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358748; bh=aycAC7ouP+Zh32ZQskPEjGqqGXMS9i6k4XxjkQy5G34=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nX+Y/eYsnznjSycXT3zOXXuvSbohPkXPT/5PtpX7v/CLdOoeplN6s23N/0LMs4bKx Wl8bncvFUIOwuwxvqpqmN0I9mnBoxdj/9t8o8PMH72Pvi0KF0dDBnOE5+c97q8Iyf0 mlrrlMQ/nfcmWPdzyKuSnvlpTwq326H1gY0A9wEo= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 5F4506A4E6; Thu, 16 Apr 2026 10:59:08 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id fP4XssZQIne0; Thu, 16 Apr 2026 10:59:08 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358744; bh=l+F1ZG2asv+bx3iRWggvoIP2b5Xxjxvgs3wEuXc/BIA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YaRMai4uw3tfBC040my0O/arL9aJD3Y8J1TKsXtWY4QS4a5e2Oeejff1AUAD6qyCD ZWxUmYQYz3Aug9ywK4ZW6U8J2Zf9nVMSBhRY0orQxOOxtOybnNrr/vk9tkxE4DB5+T dXe6Z2me345wrYXteNV6tkN1SWycMT2RrrcaQfYA= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 395336A4DC; Thu, 16 Apr 2026 10:59:04 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:56:59 -0600 Message-ID: <20260416165733.2923423-10-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 6U2DCM6ASNTV4EC3JTO6TRW3LK5VGMZA X-Message-ID-Hash: 6U2DCM6ASNTV4EC3JTO6TRW3LK5VGMZA X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 09/21] fs: Move common Linux VFS functions to fs/linux_fs.c List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass The ext4l driver contains buffer cache, block I/O, and VFS helper functions that are not ext4-specific. Moving them to a shared file allows other Linux-ported filesystem drivers (such as isofs) to reuse them without duplication or weak symbols. Move the following from ext4l/support.c and ext4l/stub.c to fs/linux_fs.c: - Buffer cache (bh_cache_lookup(), bh_cache_insert(), bh_cache_clear(), bh_cache_release_jbd(), bh_cache_sync()) - Buffer head allocation (alloc_buffer_head(), alloc_buffer_head_with_data(), free_buffer_head()) - Block I/O (sb_getblk(), __getblk(), sb_bread(), __bread(), bdev_getblk(), brelse(), __brelse(), submit_bh(), bh_read(), end_buffer_write_sync()) - VFS helpers (dir_emit_dot(), dir_emit_dotdot(), generic_read_dir(), generic_check_addressable(), generic_ro_fops and page_symlink_inode_operations) Block I/O now goes through bd_blk and bd_part_start fields on the block_device rather than filesystem-specific global variables. Both ext4l and isofs set these fields during probe. Add CONFIG_FS_LINUX, selected by CONFIG_FS_EXT4L and CONFIG_FS_ISOFS, to build the common file. Signed-off-by: Simon Glass --- fs/Kconfig | 9 + fs/Makefile | 2 + fs/ext4l/Kconfig | 1 + fs/ext4l/ext4_uboot.h | 12 +- fs/ext4l/stub.c | 11 +- fs/ext4l/support.c | 665 +----------------------------------------- fs/linux_fs.c | 656 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 679 insertions(+), 677 deletions(-) create mode 100644 fs/linux_fs.c diff --git a/fs/Kconfig b/fs/Kconfig index b173b5814ed..d521114bbb7 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -37,6 +37,13 @@ config VFS Provides a virtual filesystem layer with a mount table and unified path namespace. Includes a root filesystem at "/". +config FS_LINUX + bool + help + Common Linux VFS compatibility functions shared by Linux-ported + filesystem drivers (ext4l, isofs). Selected automatically when + any Linux-ported FS is enabled. + config FS_LEGACY def_bool y help @@ -64,6 +71,8 @@ source "fs/ext4l/Kconfig" source "fs/fat/Kconfig" +source "fs/isofs/Kconfig" + source "fs/jffs2/Kconfig" source "fs/sandbox/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index 345a4627241..988a3b384f7 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -5,6 +5,7 @@ # Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. obj-$(CONFIG_$(PHASE_)FS_LEGACY) += fs_legacy.o fs_internal.o +obj-$(CONFIG_FS_LINUX) += linux_fs.o obj-$(CONFIG_$(PHASE_)FS) += fs-uclass.o obj-$(CONFIG_$(PHASE_)DIR) += dir-uclass.o obj-$(CONFIG_$(PHASE_)FILE) += file-uclass.o @@ -25,6 +26,7 @@ obj-$(CONFIG_FS_EXT4) += ext4/ obj-$(CONFIG_FS_EXT4L) += ext4l/ obj-$(CONFIG_EXT4_JOURNAL) += jbd2/ obj-$(CONFIG_FS_FAT) += fat/ +obj-$(CONFIG_FS_ISOFS) += isofs/ obj-$(CONFIG_FS_JFFS2) += jffs2/ obj-$(CONFIG_SANDBOX) += sandbox/ obj-$(CONFIG_SEMIHOSTING) += semihostingfs.o diff --git a/fs/ext4l/Kconfig b/fs/ext4l/Kconfig index 4d0f6d95926..7da2e0029c6 100644 --- a/fs/ext4l/Kconfig +++ b/fs/ext4l/Kconfig @@ -6,6 +6,7 @@ config FS_EXT4L bool "Enable ext4 filesystem support (Linux port)" depends on FS + select FS_LINUX select RBTREE select CRC32C help diff --git a/fs/ext4l/ext4_uboot.h b/fs/ext4l/ext4_uboot.h index 8face975101..1d83ac43ccc 100644 --- a/fs/ext4l/ext4_uboot.h +++ b/fs/ext4l/ext4_uboot.h @@ -243,7 +243,7 @@ int sync_filesystem(void *sb); /* Trace stubs for super.c - declaration for stub.c */ void trace_ext4_error(struct super_block *sb, const char *func, unsigned int line); -/* end_buffer_write_sync - implemented in support.c */ +/* end_buffer_write_sync - implemented in fs/linux_fs.c */ void end_buffer_write_sync(struct buffer_head *bh, int uptodate); /* ext4 superblock initialisation and commit */ @@ -251,15 +251,13 @@ int ext4_commit_super(struct super_block *sb); int ext4_fill_super(struct super_block *sb, struct fs_context *fc); void ext4_unregister_li_request(struct super_block *sb); -/* ext4l support functions (support.c) */ +/* Common VFS functions (fs/linux_fs.c) */ int bh_cache_sync(void); -int ext4l_read_block(struct block_device *bdev, sector_t block, size_t size, - void *buffer); -int ext4l_write_block(struct block_device *bdev, sector_t block, size_t size, - void *buffer); -struct membuf *ext4l_get_msg_buf(void); void bh_cache_clear(struct block_device *bdev); void bh_cache_release_jbd(struct block_device *bdev); + +/* ext4l support functions (support.c) */ +struct membuf *ext4l_get_msg_buf(void); void ext4l_crc32c_init(void); void ext4l_msg_init(void); void ext4l_print_msgs(void); diff --git a/fs/ext4l/stub.c b/fs/ext4l/stub.c index 3198c472981..6fe8398623e 100644 --- a/fs/ext4l/stub.c +++ b/fs/ext4l/stub.c @@ -233,10 +233,7 @@ struct ext4_iloc; /* * Stubs for dir.c */ -ssize_t generic_read_dir(struct file *f, char *buf, size_t count, loff_t *ppos) -{ - return -EISDIR; -} +/* generic_read_dir is now in fs/linux_fs.c */ /* __ext4_error_file is now in super.c */ @@ -480,11 +477,7 @@ int sb_set_blocksize(struct super_block *sb, int size) /* strscpy_pad is now a macro in linux/string.h */ /* kmemdup_nul is now in lib/string.c */ -/* Address check */ -int generic_check_addressable(unsigned int blocksize_bits, u64 num_blocks) -{ - return 0; -} +/* generic_check_addressable is now in fs/linux_fs.c */ /* Block device blocks */ u64 sb_bdev_nr_blocks(struct super_block *sb) diff --git a/fs/ext4l/support.c b/fs/ext4l/support.c index 4fb97c6eae8..5631a8ba4df 100644 --- a/fs/ext4l/support.c +++ b/fs/ext4l/support.c @@ -6,7 +6,7 @@ * Written by Simon Glass * * This provides internal support functions for the ext4l driver, - * including buffer_head I/O and buffer cache. + * including CRC32C, message buffer, inode allocation and folio management. */ #include @@ -205,668 +205,11 @@ int bmap(struct inode *inode, sector_t *blockp) return ext4_uboot_bmap(inode, blockp); } -/* - * Buffer cache implementation - * - * Linux's sb_getblk() returns the same buffer_head for the same block number, - * allowing flags like BH_Verified, BH_Uptodate, etc. to persist across calls. - * This is critical for ext4's bitmap validation which sets buffer_verified() - * and expects it to remain set on subsequent lookups. - */ -#define BH_CACHE_BITS 8 -#define BH_CACHE_SIZE (1 << BH_CACHE_BITS) -#define BH_CACHE_MASK (BH_CACHE_SIZE - 1) - -struct bh_cache_entry { - struct buffer_head *bh; - struct bh_cache_entry *next; -}; - -static struct bh_cache_entry *bh_cache[BH_CACHE_SIZE]; - -static inline unsigned int bh_cache_hash(sector_t block) -{ - return (unsigned int)(block & BH_CACHE_MASK); -} - -/** - * bh_cache_lookup() - Look up a buffer in the cache - * @bdev: Block device to match - * @block: Block number to look up - * @size: Expected block size - * Return: Buffer head if found with matching device and size, NULL otherwise - */ -static struct buffer_head *bh_cache_lookup(struct block_device *bdev, - sector_t block, size_t size) -{ - unsigned int hash = bh_cache_hash(block); - struct bh_cache_entry *entry; - - for (entry = bh_cache[hash]; entry; entry = entry->next) { - if (entry->bh && entry->bh->b_bdev == bdev && - entry->bh->b_blocknr == block && - entry->bh->b_size == size) { - atomic_inc(&entry->bh->b_count); - return entry->bh; - } - } - return NULL; -} - -/** - * bh_cache_insert() - Insert a buffer into the cache - * @bh: Buffer head to insert - */ -static void bh_cache_insert(struct buffer_head *bh) -{ - unsigned int hash = bh_cache_hash(bh->b_blocknr); - struct bh_cache_entry *entry; - - /* Check if already in cache - must match device, block AND size */ - for (entry = bh_cache[hash]; entry; entry = entry->next) { - if (entry->bh && entry->bh->b_bdev == bh->b_bdev && - entry->bh->b_blocknr == bh->b_blocknr && - entry->bh->b_size == bh->b_size) - return; /* Already cached */ - } - - entry = malloc(sizeof(struct bh_cache_entry)); - if (!entry) - return; /* Silently fail - cache is optional */ - - entry->bh = bh; - entry->next = bh_cache[hash]; - bh_cache[hash] = entry; - - /* Mark as cached so brelse() knows not to free it */ - set_buffer_cached(bh); - - /* Add a reference to keep the buffer alive in cache */ - atomic_inc(&bh->b_count); -} - -/** - * bh_cache_clear() - Clear the entire buffer cache - * - * Called on unmount to free all cached buffers. - */ -/** - * bh_clear_stale_jbd() - Clear stale journal_head from buffer_head - * @bh: buffer_head to check - * - * Check if the buffer still has journal_head attached. This should not happen - * if the journal was properly destroyed, but warn if it does to help debugging. - * Clear the JBD flag and b_private to prevent issues with subsequent mounts. - */ -static void bh_clear_stale_jbd(struct buffer_head *bh) -{ - if (buffer_jbd(bh)) { - log_err("bh %p block %llu still has JBD (b_private %p)\n", - bh, (unsigned long long)bh->b_blocknr, bh->b_private); - /* - * Clear the JBD flag and b_private to prevent issues. - * The journal_head itself will be freed when the - * journal_head cache is destroyed. - */ - clear_buffer_jbd(bh); - bh->b_private = NULL; - } -} - -void bh_cache_clear(struct block_device *bdev) -{ - int i; - struct bh_cache_entry *entry, *next, **prev; - - for (i = 0; i < BH_CACHE_SIZE; i++) { - prev = &bh_cache[i]; - for (entry = *prev; entry; entry = next) { - next = entry->next; - if (entry->bh && entry->bh->b_bdev == bdev) { - struct buffer_head *bh = entry->bh; - - bh_clear_stale_jbd(bh); - atomic_set(&bh->b_count, 1); - if (atomic_dec_and_test(&bh->b_count)) - free_buffer_head(bh); - *prev = next; - free(entry); - } else { - prev = &entry->next; - } - } - } -} - -/** - * bh_cache_release_jbd() - Release all JBD references from buffer cache - * - * This must be called after journal destroy but before bh_cache_clear(). - * It ensures all journal_heads are properly released from buffer_heads - * even if the journal destroy didn't fully clean up (e.g., on abort). - */ -void bh_cache_release_jbd(struct block_device *bdev) -{ - int i; - struct bh_cache_entry *entry; - - for (i = 0; i < BH_CACHE_SIZE; i++) { - for (entry = bh_cache[i]; entry; entry = entry->next) { - if (entry->bh && entry->bh->b_bdev == bdev && - buffer_jbd(entry->bh)) { - struct buffer_head *bh = entry->bh; - struct journal_head *jh = bh2jh(bh); - - /* - * Forcibly release the journal_head. - * Clear b_bh to prevent use-after-free when - * the buffer_head is later freed. - */ - if (jh) { - jh->b_bh = NULL; - jh->b_transaction = NULL; - jh->b_next_transaction = NULL; - jh->b_cp_transaction = NULL; - } - clear_buffer_jbd(bh); - bh->b_private = NULL; - } - } - } -} - -/** - * bh_cache_sync() - Sync all dirty buffers to disk - * - * U-Boot doesn't have a journal thread, so we need to manually sync - * all dirty buffers after write operations. - * - * Return: 0 on success, negative on first error - */ -int bh_cache_sync(void) -{ - int i, ret = 0; - struct bh_cache_entry *entry; - - for (i = 0; i < BH_CACHE_SIZE; i++) { - for (entry = bh_cache[i]; entry; entry = entry->next) { - if (entry->bh && buffer_dirty(entry->bh)) { - int err = ext4l_write_block(entry->bh->b_bdev, - entry->bh->b_blocknr, - entry->bh->b_size, - entry->bh->b_data); - if (err && !ret) - ret = err; - clear_buffer_dirty(entry->bh); - } - } - } - return ret; -} - -/** - * alloc_buffer_head() - Allocate a buffer_head structure - * @gfp_mask: Allocation flags (ignored in U-Boot) - * Return: Pointer to buffer_head or NULL on error - */ -struct buffer_head *alloc_buffer_head(gfp_t gfp_mask) -{ - struct buffer_head *bh; - - bh = malloc(sizeof(struct buffer_head)); - if (!bh) - return NULL; - - memset(bh, 0, sizeof(struct buffer_head)); - - /* Note: b_data will be allocated when needed by read functions */ - atomic_set(&bh->b_count, 1); - - return bh; -} - -/** - * alloc_buffer_head_with_data() - Allocate a buffer_head with data buffer - * @size: Size of the data buffer to allocate - * Return: Pointer to buffer_head or NULL on error - */ -static struct buffer_head *alloc_buffer_head_with_data(size_t size) -{ - struct buffer_head *bh; - - bh = malloc(sizeof(struct buffer_head)); - if (!bh) - return NULL; - - memset(bh, 0, sizeof(struct buffer_head)); - - bh->b_data = malloc(size); - if (!bh->b_data) { - free(bh); - return NULL; - } - - bh->b_size = size; - /* Allocate a folio for kmap_local_folio() to work */ - bh->b_folio = malloc(sizeof(struct folio)); - if (bh->b_folio) { - memset(bh->b_folio, 0, sizeof(struct folio)); - bh->b_folio->data = bh->b_data; - } - atomic_set(&bh->b_count, 1); - /* Mark that this buffer owns its b_data and should free it */ - set_bit(BH_OwnsData, &bh->b_state); - - return bh; -} - -/** - * free_buffer_head() - Free a buffer_head - * @bh: Buffer head to free - * - * Only free b_data if BH_OwnsData is set. Shadow buffers created by - * jbd2_journal_write_metadata_buffer() share b_data/b_folio with the original - * buffer and should not free them. Shadow buffers are identified by having - * b_private set to point to the original buffer. - */ -void free_buffer_head(struct buffer_head *bh) -{ - if (!bh) - return; - - /* - * Never free a buffer_head that has a journal_head attached. - * This would cause use-after-free when the journal tries to access it. - * The journal owns a reference and the buffer will be cleaned up when - * the journal_head is properly released. - */ - if (buffer_jbd(bh)) - return; - - /* - * Shadow buffers (b_private != NULL) share their folio with the - * original buffer. Don't free the shared folio. - */ - if (!bh->b_private && bh->b_folio) - free(bh->b_folio); - - /* Only free b_data if this buffer owns it */ - if (bh->b_data && test_bit(BH_OwnsData, &bh->b_state)) - free(bh->b_data); - free(bh); -} - -/** - * ext4l_read_block() - Read a block from the block device - * @block: Block number (filesystem block, not sector) - * @size: Block size in bytes - * @buffer: Destination buffer - * Return: 0 on success, negative on error - */ -int ext4l_read_block(struct block_device *bdev, sector_t block, size_t size, - void *buffer) -{ - struct blk_desc *desc; - lbaint_t sector, count; - long n; - - if (!bdev || !bdev->bd_blk) - return -EIO; - - desc = dev_get_uclass_plat(bdev->bd_blk); - - /* Convert block to sector */ - sector = (block * size) / desc->blksz + bdev->bd_part_start; - count = size / desc->blksz; - - if (count == 0) - count = 1; - - n = blk_read(bdev->bd_blk, sector, count, buffer); - if (n != count) - return -EIO; - - return 0; -} - -/** - * ext4l_write_block() - Write a block to the block device - * @block: Block number (filesystem block, not sector) - * @size: Block size in bytes - * @buffer: Source buffer - * Return: 0 on success, negative on error - */ -int ext4l_write_block(struct block_device *bdev, sector_t block, size_t size, - void *buffer) -{ - struct blk_desc *desc; - lbaint_t sector, count; - long n; - - if (!bdev || !bdev->bd_blk) - return -EIO; - - desc = dev_get_uclass_plat(bdev->bd_blk); - - /* Convert block to sector */ - sector = (block * size) / desc->blksz + bdev->bd_part_start; - count = size / desc->blksz; - - if (count == 0) - count = 1; +/* Buffer cache (bh_cache_*) is now in fs/linux_fs.c */ - n = blk_write(bdev->bd_blk, sector, count, buffer); - if (n != count) - return -EIO; +/* Buffer head allocation (alloc_buffer_head, free_buffer_head) is now in fs/linux_fs.c */ - return 0; -} - -/** - * sb_getblk() - Get a buffer, using cache if available - * @sb: Super block - * @block: Block number - * Return: Buffer head or NULL on error - */ -struct buffer_head *sb_getblk(struct super_block *sb, sector_t block) -{ - struct buffer_head *bh; - - if (!sb) - return NULL; - - /* Check cache first - must match block number AND size */ - bh = bh_cache_lookup(sb->s_bdev, block, sb->s_blocksize); - if (bh) - return bh; - - /* Allocate new buffer */ - bh = alloc_buffer_head_with_data(sb->s_blocksize); - if (!bh) - return NULL; - - bh->b_blocknr = block; - bh->b_bdev = sb->s_bdev; - bh->b_size = sb->s_blocksize; - - /* Mark buffer as having a valid disk mapping */ - set_buffer_mapped(bh); - - /* Don't read - just allocate with zeroed data */ - memset(bh->b_data, '\0', bh->b_size); - - /* Add to cache */ - bh_cache_insert(bh); - - return bh; -} - -/** - * __getblk() - Get a buffer for a given block device - * @bdev: Block device - * @block: Block number - * @size: Block size - * Return: Buffer head or NULL on error - * - * Similar to sb_getblk but takes a block device instead of superblock. - * Used by the journal to allocate descriptor buffers. - */ -struct buffer_head *__getblk(struct block_device *bdev, sector_t block, - unsigned int size) -{ - struct buffer_head *bh; - - if (!bdev || !size) - return NULL; - - /* Check cache first - must match block number AND size */ - bh = bh_cache_lookup(bdev, block, size); - if (bh) - return bh; - - /* Allocate new buffer */ - bh = alloc_buffer_head_with_data(size); - if (!bh) - return NULL; - - bh->b_blocknr = block; - bh->b_bdev = bdev; - bh->b_size = size; - - /* Mark buffer as having a valid disk mapping */ - set_buffer_mapped(bh); - - /* Don't read - just allocate with zeroed data */ - memset(bh->b_data, '\0', bh->b_size); - - /* Add to cache */ - bh_cache_insert(bh); - - return bh; -} - -/** - * sb_bread() - Read a block via super_block - * @sb: Super block - * @block: Block number to read - * Return: Buffer head or NULL on error - */ -struct buffer_head *sb_bread(struct super_block *sb, sector_t block) -{ - struct buffer_head *bh; - int ret; - - if (!sb) - return NULL; - - bh = sb_getblk(sb, block); - if (!bh) - return NULL; - - /* If buffer is already up-to-date, return it without re-reading */ - if (buffer_uptodate(bh)) - return bh; - - bh->b_blocknr = block; - bh->b_bdev = sb->s_bdev; - bh->b_size = sb->s_blocksize; - - ret = ext4l_read_block(sb->s_bdev, block, sb->s_blocksize, bh->b_data); - if (ret) { - brelse(bh); - return NULL; - } - - /* Mark buffer as up-to-date */ - set_buffer_uptodate(bh); - - return bh; -} - -/** - * brelse() - Release a buffer_head - * @bh: Buffer head to release - * - * Decrements the reference count on the buffer. Cached buffer heads are - * freed by bh_cache_clear() on unmount, so this just decrements the count. - * Non-cached buffers are freed when the count reaches zero. - */ -void brelse(struct buffer_head *bh) -{ - if (!bh) - return; - - /* - * If buffer has JBD attached, don't let ref count go to zero. - * The journal owns a reference and will clean up properly. - */ - if (buffer_jbd(bh) && atomic_read(&bh->b_count) <= 1) - return; - - if (atomic_dec_and_test(&bh->b_count) && !buffer_cached(bh)) - free_buffer_head(bh); -} - -/** - * __brelse() - Release a buffer_head reference without freeing - * @bh: Buffer head to release - * - * Unlike brelse(), this only decrements the reference count without - * freeing the buffer when count reaches zero. Used when caller will - * explicitly free with free_buffer_head() afterward. - */ -void __brelse(struct buffer_head *bh) -{ - if (bh) - atomic_dec(&bh->b_count); -} - -/** - * bdev_getblk() - Get buffer via block_device - * @bdev: Block device - * @block: Block number - * @size: Block size - * @gfp: Allocation flags - * Return: Buffer head or NULL - */ -struct buffer_head *bdev_getblk(struct block_device *bdev, sector_t block, - unsigned size, gfp_t gfp) -{ - struct buffer_head *bh; - - /* Check cache first - must match block number AND size */ - bh = bh_cache_lookup(bdev, block, size); - if (bh) - return bh; - - bh = alloc_buffer_head_with_data(size); - if (!bh) - return NULL; - - bh->b_blocknr = block; - bh->b_bdev = bdev; - bh->b_size = size; - - /* Mark buffer as having a valid disk mapping */ - set_buffer_mapped(bh); - - /* Don't read - just allocate with zeroed data */ - memset(bh->b_data, 0, bh->b_size); - - /* Add to cache */ - bh_cache_insert(bh); - - return bh; -} - -/** - * __bread() - Read a block via block_device - * @bdev: Block device - * @block: Block number to read - * @size: Block size - * Return: Buffer head or NULL on error - */ -struct buffer_head *__bread(struct block_device *bdev, sector_t block, - unsigned size) -{ - struct buffer_head *bh; - int ret; - - bh = alloc_buffer_head_with_data(size); - if (!bh) - return NULL; - - bh->b_blocknr = block; - bh->b_bdev = bdev; - bh->b_size = size; - - ret = ext4l_read_block(bdev, block, size, bh->b_data); - if (ret) { - free_buffer_head(bh); - return NULL; - } - - /* Mark buffer as up-to-date */ - set_bit(BH_Uptodate, &bh->b_state); - - return bh; -} - -/** - * end_buffer_write_sync() - Completion handler for synchronous buffer writes - * @bh: Buffer head that completed I/O - * @uptodate: 1 if I/O succeeded, 0 if failed - * - * This callback is invoked after a buffer write completes. It sets the - * buffer's uptodate state based on the result and unlocks the buffer. - */ -void end_buffer_write_sync(struct buffer_head *bh, int uptodate) -{ - if (uptodate) - set_buffer_uptodate(bh); - else - clear_buffer_uptodate(bh); - unlock_buffer(bh); -} - -/** - * submit_bh() - Submit a buffer_head for I/O - * @op: Operation (REQ_OP_READ, REQ_OP_WRITE, etc.) - * @bh: Buffer head to submit - * Return: 0 on success, negative on error - */ -int submit_bh(int op, struct buffer_head *bh) -{ - int ret = 0; - int op_type = op & REQ_OP_MASK; /* Mask out flags, keep operation type */ - int uptodate; - - if (op_type == REQ_OP_READ) { - ret = ext4l_read_block(bh->b_bdev, bh->b_blocknr, bh->b_size, - bh->b_data); - if (ret) { - clear_buffer_uptodate(bh); - uptodate = 0; - } else { - set_buffer_uptodate(bh); - uptodate = 1; - } - } else if (op_type == REQ_OP_WRITE) { - ret = ext4l_write_block(bh->b_bdev, bh->b_blocknr, bh->b_size, - bh->b_data); - if (ret) { - clear_buffer_uptodate(bh); - set_buffer_write_io_error(bh); - uptodate = 0; - } else { - clear_buffer_write_io_error(bh); - uptodate = 1; - } - } else { - uptodate = 0; - } - - /* Call b_end_io callback if set - U-Boot does sync I/O */ - if (bh->b_end_io) - bh->b_end_io(bh, uptodate); - - return ret; -} - -/** - * bh_read() - Read a buffer_head from disk - * @bh: Buffer head to read - * @flags: Read flags - * Return: 0 on success, negative on error - */ -int bh_read(struct buffer_head *bh, int flags) -{ - if (!bh || !bh->b_data) - return -EINVAL; - - submit_bh(REQ_OP_READ | flags, bh); - return buffer_uptodate(bh) ? 0 : -EIO; -} +/* Block I/O (ext4l_read/write_block, sb_getblk, sb_bread, etc.) is now in fs/linux_fs.c */ /** * __filemap_get_folio() - Get or create a folio for a mapping diff --git a/fs/linux_fs.c b/fs/linux_fs.c new file mode 100644 index 00000000000..2237c567a9f --- /dev/null +++ b/fs/linux_fs.c @@ -0,0 +1,656 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Common Linux VFS compatibility functions for U-Boot + * + * Copyright 2026 Simon Glass + * + * Shared functions used by Linux-ported filesystem drivers (ext4l, isofs). + * These implement parts of the Linux VFS layer needed by multiple drivers, + * including the buffer cache and block I/O. + */ + +/* + * Suppress warnings for unused static functions from included Linux headers + */ +#pragma GCC diagnostic ignored "-Wunused-function" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if IS_ENABLED(CONFIG_FS_EXT4L) +#include "../fs/ext4l/ext4_uboot.h" +#include "../fs/ext4l/ext4.h" +#else +/* + * Standalone definitions for when ext4l is not compiled. + * These provide the buffer head private bits and JBD stubs. + */ +#define BH_OwnsData (BH_PrivateStart + 1) +BUFFER_FNS(OwnsData, ownsdata) +#define BH_Cached (BH_PrivateStart + 2) +BUFFER_FNS(Cached, cached) + +/* JBD stubs when ext4l is not compiled */ +#define buffer_jbd(bh) (0) +#define clear_buffer_jbd(bh) do { } while (0) + +struct journal_head; +static inline struct journal_head *bh2jh(struct buffer_head *bh) +{ + return NULL; +} +#endif /* CONFIG_FS_EXT4L */ + +/** + * linux_fs_read_block() - Read a block from the block device + * @sb: Super block (provides block device and partition info) + * @block: Block number (filesystem block, not sector) + * @size: Block size in bytes + * @buffer: Destination buffer + * Return: 0 on success, negative on error + */ +static int linux_fs_read_block(struct super_block *sb, sector_t block, + size_t size, void *buffer) +{ + struct udevice *blk_dev = sb->s_bdev->bd_blk; + unsigned long part_start = sb->s_bdev->bd_part_start; + lbaint_t sector, sector_count; + long n; + + if (!blk_dev) + return -EIO; + + sector = (block * size) / SECTOR_SIZE + part_start; + sector_count = size / SECTOR_SIZE; + if (sector_count == 0) + sector_count = 1; + + n = blk_read(blk_dev, sector, sector_count, buffer); + if (n != sector_count) + return -EIO; + + return 0; +} + +/** + * linux_fs_write_block() - Write a block to the block device + * @sb: Super block (provides block device and partition info) + * @block: Block number (filesystem block, not sector) + * @size: Block size in bytes + * @buffer: Source buffer + * Return: 0 on success, negative on error + */ +static int linux_fs_write_block(struct super_block *sb, sector_t block, + size_t size, void *buffer) +{ + struct udevice *blk_dev = sb->s_bdev->bd_blk; + unsigned long part_start = sb->s_bdev->bd_part_start; + lbaint_t sector, sector_count; + long n; + + if (!blk_dev) + return -EIO; + + sector = (block * size) / SECTOR_SIZE + part_start; + sector_count = size / SECTOR_SIZE; + if (sector_count == 0) + sector_count = 1; + + n = blk_write(blk_dev, sector, sector_count, buffer); + if (n != sector_count) + return -EIO; + + return 0; +} + +/* + * Buffer cache + * + * Linux's sb_getblk() returns the same buffer_head for the same block number, + * allowing flags like BH_Verified, BH_Uptodate, etc. to persist across calls. + * This is critical for ext4's bitmap validation which sets buffer_verified() + * and expects it to remain set on subsequent lookups. + */ +#define BH_CACHE_BITS 8 +#define BH_CACHE_SIZE (1 << BH_CACHE_BITS) +#define BH_CACHE_MASK (BH_CACHE_SIZE - 1) + +struct bh_cache_entry { + struct buffer_head *bh; + struct bh_cache_entry *next; +}; + +static struct bh_cache_entry *bh_cache[BH_CACHE_SIZE]; + +static inline unsigned int bh_cache_hash(sector_t block) +{ + return (unsigned int)(block & BH_CACHE_MASK); +} + +/** + * bh_cache_lookup() - Look up a buffer in the cache + * @bdev: Block device to match + * @block: Block number to look up + * @size: Expected block size + * Return: Buffer head if found with matching device and size, NULL otherwise + */ +static struct buffer_head *bh_cache_lookup(struct block_device *bdev, + sector_t block, size_t size) +{ + unsigned int hash = bh_cache_hash(block); + struct bh_cache_entry *entry; + + for (entry = bh_cache[hash]; entry; entry = entry->next) { + if (entry->bh && entry->bh->b_bdev == bdev && + entry->bh->b_blocknr == block && + entry->bh->b_size == size) { + atomic_inc(&entry->bh->b_count); + return entry->bh; + } + } + return NULL; +} + +/** + * bh_cache_insert() - Insert a buffer into the cache + * @bh: Buffer head to insert + */ +static void bh_cache_insert(struct buffer_head *bh) +{ + unsigned int hash = bh_cache_hash(bh->b_blocknr); + struct bh_cache_entry *entry; + + /* Check if already in cache - must match device, block AND size */ + for (entry = bh_cache[hash]; entry; entry = entry->next) { + if (entry->bh && entry->bh->b_bdev == bh->b_bdev && + entry->bh->b_blocknr == bh->b_blocknr && + entry->bh->b_size == bh->b_size) + return; + } + + entry = malloc(sizeof(struct bh_cache_entry)); + if (!entry) + return; + + entry->bh = bh; + entry->next = bh_cache[hash]; + bh_cache[hash] = entry; + + set_buffer_cached(bh); + atomic_inc(&bh->b_count); +} + +/** + * bh_clear_stale_jbd() - Clear stale journal_head from a buffer_head + * @bh: buffer_head to check + */ +static void bh_clear_stale_jbd(struct buffer_head *bh) +{ + if (buffer_jbd(bh)) { + clear_buffer_jbd(bh); + bh->b_private = NULL; + } +} + +/** + * bh_cache_clear() - Clear cached buffers for a block device + * @bdev: Block device whose buffers should be freed + * + * Called on unmount to free buffers belonging to @bdev only, leaving + * buffers from other mounted filesystems intact. + */ +void bh_cache_clear(struct block_device *bdev) +{ + int i; + struct bh_cache_entry *entry, *next, **prev; + + for (i = 0; i < BH_CACHE_SIZE; i++) { + prev = &bh_cache[i]; + for (entry = *prev; entry; entry = next) { + next = entry->next; + if (entry->bh && entry->bh->b_bdev == bdev) { + struct buffer_head *bh = entry->bh; + + bh_clear_stale_jbd(bh); + atomic_set(&bh->b_count, 1); + if (atomic_dec_and_test(&bh->b_count)) + free_buffer_head(bh); + *prev = next; + free(entry); + } else { + prev = &entry->next; + } + } + } +} + +/** + * bh_cache_release_jbd() - Release JBD references from cached buffers + * @bdev: Block device whose buffers should be processed + * + * Must be called after journal destroy but before bh_cache_clear(). + */ +void bh_cache_release_jbd(struct block_device *bdev) +{ + int i; + struct bh_cache_entry *entry; + + for (i = 0; i < BH_CACHE_SIZE; i++) { + for (entry = bh_cache[i]; entry; entry = entry->next) { + if (entry->bh && entry->bh->b_bdev == bdev && + buffer_jbd(entry->bh)) { + struct buffer_head *bh = entry->bh; + struct journal_head *jh = bh2jh(bh); + + if (jh) { + jh->b_bh = NULL; + jh->b_transaction = NULL; + jh->b_next_transaction = NULL; + jh->b_cp_transaction = NULL; + } + clear_buffer_jbd(bh); + bh->b_private = NULL; + } + } + } +} + +/** + * bh_cache_sync() - Sync all dirty buffers to disk + * + * Return: 0 on success, negative on first error + */ +int bh_cache_sync(void) +{ + int i, ret = 0; + struct bh_cache_entry *entry; + + for (i = 0; i < BH_CACHE_SIZE; i++) { + for (entry = bh_cache[i]; entry; entry = entry->next) { + if (entry->bh && buffer_dirty(entry->bh)) { + struct buffer_head *bh = entry->bh; + int err; + + err = linux_fs_write_block(bh->b_bdev->bd_super, + bh->b_blocknr, + bh->b_size, + bh->b_data); + if (err && !ret) + ret = err; + clear_buffer_dirty(bh); + } + } + } + return ret; +} + +/* Buffer head allocation */ + +/** + * alloc_buffer_head() - Allocate a buffer_head structure + * @gfp_mask: Allocation flags (ignored in U-Boot) + * Return: Pointer to buffer_head or NULL on error + */ +struct buffer_head *alloc_buffer_head(gfp_t gfp_mask) +{ + struct buffer_head *bh; + + bh = kzalloc(sizeof(*bh), GFP_KERNEL); + if (!bh) + return NULL; + + atomic_set(&bh->b_count, 1); + return bh; +} + +/** + * alloc_buffer_head_with_data() - Allocate a buffer_head with data buffer + * @size: Size of the data buffer to allocate + * Return: Pointer to buffer_head or NULL on error + */ +static struct buffer_head *alloc_buffer_head_with_data(size_t size) +{ + struct buffer_head *bh; + + bh = kzalloc(sizeof(*bh), GFP_KERNEL); + if (!bh) + return NULL; + + bh->b_data = malloc(size); + if (!bh->b_data) { + free(bh); + return NULL; + } + + bh->b_size = size; + bh->b_folio = kzalloc(sizeof(struct folio), GFP_KERNEL); + if (bh->b_folio) + bh->b_folio->data = bh->b_data; + atomic_set(&bh->b_count, 1); + set_bit(BH_OwnsData, &bh->b_state); + + return bh; +} + +/** + * free_buffer_head() - Free a buffer_head + * @bh: Buffer head to free + * + * Only free b_data if BH_OwnsData is set. Shadow buffers created by + * jbd2_journal_write_metadata_buffer() share b_data with the original. + */ +void free_buffer_head(struct buffer_head *bh) +{ + if (!bh) + return; + + /* Don't free if journal still holds a reference */ + if (buffer_jbd(bh)) + return; + + /* Shadow buffers share their folio - don't free it */ + if (!bh->b_private && bh->b_folio) + free(bh->b_folio); + + if (bh->b_data && test_bit(BH_OwnsData, &bh->b_state)) + free(bh->b_data); + free(bh); +} + +/* Buffer head I/O */ + +/** + * sb_getblk() - Get a buffer, using cache if available + * @sb: Super block + * @block: Block number + * Return: Buffer head or NULL on error + */ +struct buffer_head *sb_getblk(struct super_block *sb, sector_t block) +{ + struct buffer_head *bh; + + if (!sb) + return NULL; + + bh = bh_cache_lookup(sb->s_bdev, block, sb->s_blocksize); + if (bh) + return bh; + + bh = alloc_buffer_head_with_data(sb->s_blocksize); + if (!bh) + return NULL; + + bh->b_blocknr = block; + bh->b_bdev = sb->s_bdev; + bh->b_size = sb->s_blocksize; + set_buffer_mapped(bh); + memset(bh->b_data, '\0', bh->b_size); + + bh_cache_insert(bh); + + return bh; +} + +/** + * __getblk() - Get a buffer for a given block device + * @bdev: Block device + * @block: Block number + * @size: Block size + * Return: Buffer head or NULL on error + */ +struct buffer_head *__getblk(struct block_device *bdev, sector_t block, + unsigned int size) +{ + struct buffer_head *bh; + + bh = bh_cache_lookup(bdev, block, size); + if (bh) + return bh; + + bh = alloc_buffer_head_with_data(size); + if (!bh) + return NULL; + + bh->b_blocknr = block; + bh->b_bdev = bdev; + bh->b_size = size; + set_buffer_mapped(bh); + memset(bh->b_data, '\0', bh->b_size); + + bh_cache_insert(bh); + + return bh; +} + +/** + * sb_bread() - Read a block via super_block + * @sb: Super block + * @block: Block number to read + * Return: Buffer head with data, or NULL on error + */ +struct buffer_head *sb_bread(struct super_block *sb, sector_t block) +{ + struct buffer_head *bh; + int ret; + + if (!sb) + return NULL; + + bh = sb_getblk(sb, block); + if (!bh) + return NULL; + + if (buffer_uptodate(bh)) + return bh; + + bh->b_blocknr = block; + bh->b_bdev = sb->s_bdev; + bh->b_size = sb->s_blocksize; + + ret = linux_fs_read_block(sb, block, sb->s_blocksize, bh->b_data); + if (ret) { + brelse(bh); + return NULL; + } + + set_buffer_uptodate(bh); + + return bh; +} + +/** + * bdev_getblk() - Get buffer via block_device + * @bdev: Block device + * @block: Block number + * @size: Block size + * @gfp: GFP flags (ignored) + * Return: Buffer head or NULL on error + */ +struct buffer_head *bdev_getblk(struct block_device *bdev, sector_t block, + unsigned int size, gfp_t gfp) +{ + return __getblk(bdev, block, size); +} + +/** + * __bread() - Read a block via block device + * @bdev: Block device + * @block: Block number to read + * @size: Block size + * Return: Buffer head or NULL on error + */ +struct buffer_head *__bread(struct block_device *bdev, sector_t block, + unsigned int size) +{ + struct buffer_head *bh; + int ret; + + bh = __getblk(bdev, block, size); + if (!bh) + return NULL; + + if (buffer_uptodate(bh)) + return bh; + + ret = linux_fs_read_block(bdev->bd_super, block, size, bh->b_data); + if (ret) { + brelse(bh); + return NULL; + } + set_buffer_uptodate(bh); + + return bh; +} + +/** + * brelse() - Release a buffer_head + * @bh: Buffer head to release + */ +void brelse(struct buffer_head *bh) +{ + if (!bh) + return; + + /* If JBD owns this buffer, don't let ref count reach zero */ + if (buffer_jbd(bh) && atomic_read(&bh->b_count) <= 1) + return; + + if (atomic_dec_and_test(&bh->b_count) && !buffer_cached(bh)) + free_buffer_head(bh); +} + +/** + * __brelse() - Release a buffer_head without freeing + * @bh: Buffer head to release + */ +void __brelse(struct buffer_head *bh) +{ + if (bh) + atomic_dec(&bh->b_count); +} + +/** + * submit_bh() - Submit a buffer_head for I/O + * @op: Operation (REQ_OP_READ, REQ_OP_WRITE, etc.) + * @bh: Buffer head to submit + * Return: 0 on success, negative on error + */ +int submit_bh(int op, struct buffer_head *bh) +{ + struct super_block *sb; + int ret = 0; + int op_type = op & REQ_OP_MASK; + int uptodate; + + sb = bh->b_bdev ? bh->b_bdev->bd_super : NULL; + if (!sb) + return -EIO; + + if (op_type == REQ_OP_READ) { + ret = linux_fs_read_block(sb, bh->b_blocknr, bh->b_size, + bh->b_data); + if (ret) { + clear_buffer_uptodate(bh); + uptodate = 0; + } else { + set_buffer_uptodate(bh); + uptodate = 1; + } + } else if (op_type == REQ_OP_WRITE) { + ret = linux_fs_write_block(sb, bh->b_blocknr, bh->b_size, + bh->b_data); + if (ret) { + clear_buffer_uptodate(bh); + set_buffer_write_io_error(bh); + uptodate = 0; + } else { + clear_buffer_write_io_error(bh); + uptodate = 1; + } + } else { + uptodate = 0; + } + + if (bh->b_end_io) + bh->b_end_io(bh, uptodate); + + return ret; +} + +/** + * bh_read() - Read a buffer_head from disk + * @bh: Buffer head to read + * @flags: Read flags + * Return: 0 on success, negative on error + */ +int bh_read(struct buffer_head *bh, int flags) +{ + if (!bh || !bh->b_data) + return -EINVAL; + + submit_bh(REQ_OP_READ | flags, bh); + return buffer_uptodate(bh) ? 0 : -EIO; +} + +/* end_buffer_write_sync - completion for sync writes */ +void end_buffer_write_sync(struct buffer_head *bh, int uptodate) +{ + if (uptodate) + set_buffer_uptodate(bh); + else + clear_buffer_uptodate(bh); + unlock_buffer(bh); +} + +/* VFS helpers */ + +/** + * dir_emit_dot() - Emit "." directory entry + * @file: File pointer for the directory + * @ctx: Directory context + * Return: true if iteration should continue + */ +bool dir_emit_dot(struct file *file, struct dir_context *ctx) +{ + return dir_emit(ctx, ".", 1, file_inode(file)->i_ino, DT_DIR); +} + +/** + * dir_emit_dotdot() - Emit ".." directory entry + * @file: File pointer for the directory + * @ctx: Directory context + * Return: true if iteration should continue + */ +bool dir_emit_dotdot(struct file *file, struct dir_context *ctx) +{ + return dir_emit(ctx, "..", 2, 0, DT_DIR); +} + +ssize_t generic_read_dir(struct file *f, char __user *buf, size_t count, + loff_t *ppos) +{ + return -EISDIR; +} + +int generic_check_addressable(unsigned int blocksize_bits, u64 num_blocks) +{ + if (num_blocks > (1ULL << (sizeof(sector_t) * 8 - blocksize_bits))) + return -EFBIG; + return 0; +} + +const struct file_operations generic_ro_fops = { +}; + +const struct inode_operations page_symlink_inode_operations = { +}; From patchwork Thu Apr 16 16:57:00 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2217 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776360043; bh=OqWt+dSk6jf1ZAb7PM1dYWVnMT8i3gQeoBiqaM2QJiM=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=ghqRTMPWXYvkol/gdlnLYkD0DBEo0LvayRDXis/iRlOizmeU1qSss4K+c3IDzVh13 GG2NeI4t70ftD8CmuITYENW6vGOHZ/HaFNoxLDybhYY9l4bMdwfSRkXDQydl5d9XOf xQhs6HuDyJE9CUHn3GwbQisTAc32dDIh10EJqVs0= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 36BD36A4E6 for ; Thu, 16 Apr 2026 11:20:43 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id pguRgBHZIDT0 for ; Thu, 16 Apr 2026 11:20:43 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776360043; bh=OqWt+dSk6jf1ZAb7PM1dYWVnMT8i3gQeoBiqaM2QJiM=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=ghqRTMPWXYvkol/gdlnLYkD0DBEo0LvayRDXis/iRlOizmeU1qSss4K+c3IDzVh13 GG2NeI4t70ftD8CmuITYENW6vGOHZ/HaFNoxLDybhYY9l4bMdwfSRkXDQydl5d9XOf xQhs6HuDyJE9CUHn3GwbQisTAc32dDIh10EJqVs0= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 15DAD6A497 for ; Thu, 16 Apr 2026 11:20:43 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358750; bh=5SV7sSPWDy0iZDpNv7OWb1OjL5qcKrLPffLHCjUATh4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=eqnTIdH8rLUj085OhFcNYVaHGqTf01ysXYSG/8u3tUCeBbntDRcBDW1Fi7BbZg87c YcHzFPk4EFi5FMCTH8fDSAg75Zd9N5EXhYunxuooHGedf42ChimXHZ62ELwqAnV2AN zQ/0+9BvxxV1QRowOdMFixAVK5wMRFg0GKwuEzHs= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id BCC876A4EA; Thu, 16 Apr 2026 10:59:10 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id eNduEWnRNNs5; Thu, 16 Apr 2026 10:59:10 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358747; bh=ZvXwmp4f+fvIDcFJExs2qy1Z0A0+QdjBxLEh+uE+C8A=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IQIK59UkJgDj/sdaQO29poD+TjfCpDuiF5OYuOCeSl2tq35x+LqnLDqenzEaR+uHa 5Iboo/4yQ4SigNaugPwqFT7ovwq9cs9H6YLBVocPNKFb8jSdxlf8YSLpIG4qL2I+eo WbYQPQegFTN1SiHK3cbnD+PXIYL0XL9qsl+4Ig2c= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 259566A4D8; Thu, 16 Apr 2026 10:59:07 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:57:00 -0600 Message-ID: <20260416165733.2923423-11-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Hits: max-size X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; news-moderation; no-subject; digests; suspicious-header Message-ID-Hash: MK2CYTAD6PEXLDPMQFIOYEOVG5IZXO5G X-Message-ID-Hash: MK2CYTAD6PEXLDPMQFIOYEOVG5IZXO5G X-Mailman-Approved-At: Thu, 16 Apr 2026 17:20:41 -0600 CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 10/21] fs: Add ISO 9660 filesystem source from Linux List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Copy the Linux kernel's isofs driver source files, using version 7.0 Leave out compress.c as it we don't need to support compression. Signed-off-by: Simon Glass --- fs/isofs/dir.c | 286 ++++++++ fs/isofs/export.c | 193 ++++++ fs/isofs/inode.c | 1636 +++++++++++++++++++++++++++++++++++++++++++++ fs/isofs/isofs.h | 203 ++++++ fs/isofs/joliet.c | 68 ++ fs/isofs/namei.c | 174 +++++ fs/isofs/rock.c | 810 ++++++++++++++++++++++ fs/isofs/rock.h | 119 ++++ fs/isofs/util.c | 86 +++ fs/isofs/zisofs.h | 16 + 10 files changed, 3591 insertions(+) create mode 100644 fs/isofs/dir.c create mode 100644 fs/isofs/export.c create mode 100644 fs/isofs/inode.c create mode 100644 fs/isofs/isofs.h create mode 100644 fs/isofs/joliet.c create mode 100644 fs/isofs/namei.c create mode 100644 fs/isofs/rock.c create mode 100644 fs/isofs/rock.h create mode 100644 fs/isofs/util.c create mode 100644 fs/isofs/zisofs.h -- 2.43.0 diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c new file mode 100644 index 00000000000..2ca16c3fe5e --- /dev/null +++ b/fs/isofs/dir.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/isofs/dir.c + * + * (C) 1992, 1993, 1994 Eric Youngdale Modified for ISO 9660 filesystem. + * + * (C) 1991 Linus Torvalds - minix filesystem + * + * Steve Beynon : Missing last directory entries fixed + * (stephen@askone.demon.co.uk) : 21st June 1996 + * + * isofs directory handling functions + */ +#include +#include +#include "isofs.h" + +int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode) +{ + char * old = de->name; + int len = de->name_len[0]; + int i; + + for (i = 0; i < len; i++) { + unsigned char c = old[i]; + if (!c) + break; + + if (c >= 'A' && c <= 'Z') + c |= 0x20; /* lower case */ + + /* Drop trailing '.;1' (ISO 9660:1988 7.5.1 requires period) */ + if (c == '.' && i == len - 3 && old[i + 1] == ';' && old[i + 2] == '1') + break; + + /* Drop trailing ';1' */ + if (c == ';' && i == len - 2 && old[i + 1] == '1') + break; + + /* Convert remaining ';' to '.' */ + /* Also '/' to '.' (broken Acorn-generated ISO9660 images) */ + if (c == ';' || c == '/') + c = '.'; + + new[i] = c; + } + return i; +} + +/* Acorn extensions written by Matthew Wilcox 1998 */ +int get_acorn_filename(struct iso_directory_record *de, + char *retname, struct inode *inode) +{ + int std; + unsigned char *chr; + int retnamlen = isofs_name_translate(de, retname, inode); + + if (retnamlen == 0) + return 0; + std = sizeof(struct iso_directory_record) + de->name_len[0]; + if (std & 1) + std++; + if (de->length[0] - std != 32) + return retnamlen; + chr = ((unsigned char *) de) + std; + if (strncmp(chr, "ARCHIMEDES", 10)) + return retnamlen; + if ((*retname == '_') && ((chr[19] & 1) == 1)) + *retname = '!'; + if (((de->flags[0] & 2) == 0) && (chr[13] == 0xff) + && ((chr[12] & 0xf0) == 0xf0)) { + retname[retnamlen] = ','; + sprintf(retname+retnamlen+1, "%3.3x", + ((chr[12] & 0xf) << 8) | chr[11]); + retnamlen += 4; + } + return retnamlen; +} + +/* + * This should _really_ be cleaned up some day.. + */ +static int do_isofs_readdir(struct inode *inode, struct file *file, + struct dir_context *ctx, + char *tmpname, struct iso_directory_record *tmpde) +{ + unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); + unsigned char bufbits = ISOFS_BUFFER_BITS(inode); + unsigned long block, offset, block_saved, offset_saved; + unsigned long inode_number = 0; /* Quiet GCC */ + struct buffer_head *bh = NULL; + int len; + int map; + int first_de = 1; + char *p = NULL; /* Quiet GCC */ + struct iso_directory_record *de; + struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb); + + offset = ctx->pos & (bufsize - 1); + block = ctx->pos >> bufbits; + + while (ctx->pos < inode->i_size) { + int de_len; + + if (!bh) { + bh = isofs_bread(inode, block); + if (!bh) + return 0; + } + + de = (struct iso_directory_record *) (bh->b_data + offset); + + de_len = *(unsigned char *)de; + + /* + * If the length byte is zero, we should move on to the next + * CDROM sector. If we are at the end of the directory, we + * kick out of the while loop. + */ + + if (de_len == 0) { + brelse(bh); + bh = NULL; + ctx->pos = (ctx->pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1); + block = ctx->pos >> bufbits; + offset = 0; + continue; + } + + block_saved = block; + offset_saved = offset; + offset += de_len; + + /* Make sure we have a full directory entry */ + if (offset >= bufsize) { + int slop = bufsize - offset + de_len; + memcpy(tmpde, de, slop); + offset &= bufsize - 1; + block++; + brelse(bh); + bh = NULL; + if (offset) { + bh = isofs_bread(inode, block); + if (!bh) + return 0; + memcpy((void *) tmpde + slop, bh->b_data, offset); + } + de = tmpde; + } + /* Basic sanity check, whether name doesn't exceed dir entry */ + if (de_len < sizeof(struct iso_directory_record) || + de_len < de->name_len[0] + + sizeof(struct iso_directory_record)) { + printk(KERN_NOTICE "iso9660: Corrupted directory entry" + " in block %lu of inode %lu\n", block, + inode->i_ino); + brelse(bh); + return -EIO; + } + + if (first_de) { + isofs_normalize_block_and_offset(de, + &block_saved, + &offset_saved); + inode_number = isofs_get_ino(block_saved, + offset_saved, bufbits); + } + + if (de->flags[-sbi->s_high_sierra] & 0x80) { + first_de = 0; + ctx->pos += de_len; + continue; + } + first_de = 1; + + /* Handle the case of the '.' directory */ + if (de->name_len[0] == 1 && de->name[0] == 0) { + if (!dir_emit_dot(file, ctx)) + break; + ctx->pos += de_len; + continue; + } + + len = 0; + + /* Handle the case of the '..' directory */ + if (de->name_len[0] == 1 && de->name[0] == 1) { + if (!dir_emit_dotdot(file, ctx)) + break; + ctx->pos += de_len; + continue; + } + + /* Handle everything else. Do name translation if there + is no Rock Ridge NM field. */ + + /* + * Do not report hidden files if so instructed, or associated + * files unless instructed to do so + */ + if ((sbi->s_hide && (de->flags[-sbi->s_high_sierra] & 1)) || + (!sbi->s_showassoc && + (de->flags[-sbi->s_high_sierra] & 4))) { + ctx->pos += de_len; + continue; + } + + map = 1; + if (sbi->s_rock) { + len = get_rock_ridge_filename(de, tmpname, inode); + if (len != 0) { /* may be -1 */ + p = tmpname; + map = 0; + } + } + if (map) { +#ifdef CONFIG_JOLIET + if (sbi->s_joliet_level) { + len = get_joliet_filename(de, tmpname, inode); + p = tmpname; + } else +#endif + if (sbi->s_mapping == 'a') { + len = get_acorn_filename(de, tmpname, inode); + p = tmpname; + } else + if (sbi->s_mapping == 'n') { + len = isofs_name_translate(de, tmpname, inode); + p = tmpname; + } else { + p = de->name; + len = de->name_len[0]; + } + } + if (len > 0) { + if (!dir_emit(ctx, p, len, inode_number, DT_UNKNOWN)) + break; + } + ctx->pos += de_len; + } + if (bh) + brelse(bh); + return 0; +} + +/* + * Handle allocation of temporary space for name translation and + * handling split directory entries.. The real work is done by + * "do_isofs_readdir()". + */ +static int isofs_readdir(struct file *file, struct dir_context *ctx) +{ + int result; + char *tmpname; + struct iso_directory_record *tmpde; + struct inode *inode = file_inode(file); + + tmpname = (char *)__get_free_page(GFP_KERNEL); + if (tmpname == NULL) + return -ENOMEM; + + tmpde = (struct iso_directory_record *) (tmpname+1024); + + result = do_isofs_readdir(inode, file, ctx, tmpname, tmpde); + + free_page((unsigned long) tmpname); + return result; +} + +const struct file_operations isofs_dir_operations = +{ + .llseek = generic_file_llseek, + .read = generic_read_dir, + .iterate_shared = isofs_readdir, + .setlease = generic_setlease, +}; + +/* + * directories can handle most operations... + */ +const struct inode_operations isofs_dir_inode_operations = +{ + .lookup = isofs_lookup, +}; + + diff --git a/fs/isofs/export.c b/fs/isofs/export.c new file mode 100644 index 00000000000..421d247fae5 --- /dev/null +++ b/fs/isofs/export.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * fs/isofs/export.c + * + * (C) 2004 Paul Serice - The new inode scheme requires switching + * from iget() to iget5_locked() which means + * the NFS export operations have to be hand + * coded because the default routines rely on + * iget(). + * + * The following files are helpful: + * + * Documentation/filesystems/nfs/exporting.rst + * fs/exportfs/expfs.c. + */ + +#include "isofs.h" + +static struct dentry * +isofs_export_iget(struct super_block *sb, + unsigned long block, + unsigned long offset, + __u32 generation) +{ + struct inode *inode; + + if (block == 0) + return ERR_PTR(-ESTALE); + inode = isofs_iget(sb, block, offset); + if (IS_ERR(inode)) + return ERR_CAST(inode); + if (generation && inode->i_generation != generation) { + iput(inode); + return ERR_PTR(-ESTALE); + } + return d_obtain_alias(inode); +} + +/* This function is surprisingly simple. The trick is understanding + * that "child" is always a directory. So, to find its parent, you + * simply need to find its ".." entry, normalize its block and offset, + * and return the underlying inode. See the comments for + * isofs_normalize_block_and_offset(). */ +static struct dentry *isofs_export_get_parent(struct dentry *child) +{ + unsigned long parent_block = 0; + unsigned long parent_offset = 0; + struct inode *child_inode = d_inode(child); + struct iso_inode_info *e_child_inode = ISOFS_I(child_inode); + struct iso_directory_record *de = NULL; + struct buffer_head * bh = NULL; + struct dentry *rv = NULL; + + /* "child" must always be a directory. */ + if (!S_ISDIR(child_inode->i_mode)) { + printk(KERN_ERR "isofs: isofs_export_get_parent(): " + "child is not a directory!\n"); + rv = ERR_PTR(-EACCES); + goto out; + } + + /* It is an invariant that the directory offset is zero. If + * it is not zero, it means the directory failed to be + * normalized for some reason. */ + if (e_child_inode->i_iget5_offset != 0) { + printk(KERN_ERR "isofs: isofs_export_get_parent(): " + "child directory not normalized!\n"); + rv = ERR_PTR(-EACCES); + goto out; + } + + /* The child inode has been normalized such that its + * i_iget5_block value points to the "." entry. Fortunately, + * the ".." entry is located in the same block. */ + parent_block = e_child_inode->i_iget5_block; + + /* Get the block in question. */ + bh = sb_bread(child_inode->i_sb, parent_block); + if (bh == NULL) { + rv = ERR_PTR(-EACCES); + goto out; + } + + /* This is the "." entry. */ + de = (struct iso_directory_record*)bh->b_data; + + /* The ".." entry is always the second entry. */ + parent_offset = (unsigned long)isonum_711(de->length); + de = (struct iso_directory_record*)(bh->b_data + parent_offset); + + /* Verify it is in fact the ".." entry. */ + if ((isonum_711(de->name_len) != 1) || (de->name[0] != 1)) { + printk(KERN_ERR "isofs: Unable to find the \"..\" " + "directory for NFS.\n"); + rv = ERR_PTR(-EACCES); + goto out; + } + + /* Normalize */ + isofs_normalize_block_and_offset(de, &parent_block, &parent_offset); + + rv = d_obtain_alias(isofs_iget(child_inode->i_sb, parent_block, + parent_offset)); + out: + if (bh) + brelse(bh); + return rv; +} + +static int +isofs_export_encode_fh(struct inode *inode, + __u32 *fh32, + int *max_len, + struct inode *parent) +{ + struct iso_inode_info * ei = ISOFS_I(inode); + int len = *max_len; + int type = 1; + __u16 *fh16 = (__u16*)fh32; + + /* + * WARNING: max_len is 5 for NFSv2. Because of this + * limitation, we use the lower 16 bits of fh32[1] to hold the + * offset of the inode and the upper 16 bits of fh32[1] to + * hold the offset of the parent. + */ + if (parent && (len < 5)) { + *max_len = 5; + return FILEID_INVALID; + } else if (len < 3) { + *max_len = 3; + return FILEID_INVALID; + } + + len = 3; + fh32[0] = ei->i_iget5_block; + fh16[2] = (__u16)ei->i_iget5_offset; /* fh16 [sic] */ + fh16[3] = 0; /* avoid leaking uninitialized data */ + fh32[2] = inode->i_generation; + if (parent) { + struct iso_inode_info *eparent; + eparent = ISOFS_I(parent); + fh32[3] = eparent->i_iget5_block; + fh16[3] = (__u16)eparent->i_iget5_offset; /* fh16 [sic] */ + fh32[4] = parent->i_generation; + len = 5; + type = 2; + } + *max_len = len; + return type; +} + +struct isofs_fid { + u32 block; + u16 offset; + u16 parent_offset; + u32 generation; + u32 parent_block; + u32 parent_generation; +}; + +static struct dentry *isofs_fh_to_dentry(struct super_block *sb, + struct fid *fid, int fh_len, int fh_type) +{ + struct isofs_fid *ifid = (struct isofs_fid *)fid; + + if (fh_len < 3 || fh_type > 2) + return NULL; + + return isofs_export_iget(sb, ifid->block, ifid->offset, + ifid->generation); +} + +static struct dentry *isofs_fh_to_parent(struct super_block *sb, + struct fid *fid, int fh_len, int fh_type) +{ + struct isofs_fid *ifid = (struct isofs_fid *)fid; + + if (fh_len < 2 || fh_type != 2) + return NULL; + + return isofs_export_iget(sb, + fh_len > 3 ? ifid->parent_block : 0, + ifid->parent_offset, + fh_len > 4 ? ifid->parent_generation : 0); +} + +const struct export_operations isofs_export_ops = { + .encode_fh = isofs_export_encode_fh, + .fh_to_dentry = isofs_fh_to_dentry, + .fh_to_parent = isofs_fh_to_parent, + .get_parent = isofs_export_get_parent, +}; diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c new file mode 100644 index 00000000000..5c01536c5e8 --- /dev/null +++ b/fs/isofs/inode.c @@ -0,0 +1,1636 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/fs/isofs/inode.c + * + * (C) 1991 Linus Torvalds - minix filesystem + * 1992, 1993, 1994 Eric Youngdale Modified for ISO 9660 filesystem. + * 1994 Eberhard Mönkeberg - multi session handling. + * 1995 Mark Dobie - allow mounting of some weird VideoCDs and PhotoCDs. + * 1997 Gordon Chaffee - Joliet CDs + * 1998 Eric Lammerts - ISO 9660 Level 3 + * 2004 Paul Serice - Inode Support pushed out from 4GB to 128GB + * 2004 Paul Serice - NFS Export Operations + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "isofs.h" +#include "zisofs.h" + +/* max tz offset is 13 hours */ +#define MAX_TZ_OFFSET (52*15*60) + +#define BEQUIET + +static int isofs_hashi(const struct dentry *parent, struct qstr *qstr); +static int isofs_dentry_cmpi(const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name); + +#ifdef CONFIG_JOLIET +static int isofs_hashi_ms(const struct dentry *parent, struct qstr *qstr); +static int isofs_hash_ms(const struct dentry *parent, struct qstr *qstr); +static int isofs_dentry_cmpi_ms(const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name); +static int isofs_dentry_cmp_ms(const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name); +#endif + +static void isofs_put_super(struct super_block *sb) +{ + struct isofs_sb_info *sbi = ISOFS_SB(sb); + +#ifdef CONFIG_JOLIET + unload_nls(sbi->s_nls_iocharset); +#endif + + kfree(sbi); + sb->s_fs_info = NULL; + return; +} + +static int isofs_read_inode(struct inode *, int relocated); +static int isofs_statfs (struct dentry *, struct kstatfs *); +static int isofs_show_options(struct seq_file *, struct dentry *); + +static struct kmem_cache *isofs_inode_cachep; + +static struct inode *isofs_alloc_inode(struct super_block *sb) +{ + struct iso_inode_info *ei; + ei = alloc_inode_sb(sb, isofs_inode_cachep, GFP_KERNEL); + if (!ei) + return NULL; + return &ei->vfs_inode; +} + +static void isofs_free_inode(struct inode *inode) +{ + kmem_cache_free(isofs_inode_cachep, ISOFS_I(inode)); +} + +static void init_once(void *foo) +{ + struct iso_inode_info *ei = foo; + + inode_init_once(&ei->vfs_inode); +} + +static int __init init_inodecache(void) +{ + isofs_inode_cachep = kmem_cache_create("isofs_inode_cache", + sizeof(struct iso_inode_info), + 0, (SLAB_RECLAIM_ACCOUNT| + SLAB_ACCOUNT), + init_once); + if (!isofs_inode_cachep) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + /* + * Make sure all delayed rcu free inodes are flushed before we + * destroy cache. + */ + rcu_barrier(); + kmem_cache_destroy(isofs_inode_cachep); +} + +static int isofs_reconfigure(struct fs_context *fc) +{ + sync_filesystem(fc->root->d_sb); + if (!(fc->sb_flags & SB_RDONLY)) + return -EROFS; + return 0; +} + +static const struct super_operations isofs_sops = { + .alloc_inode = isofs_alloc_inode, + .free_inode = isofs_free_inode, + .put_super = isofs_put_super, + .statfs = isofs_statfs, + .show_options = isofs_show_options, +}; + + +static const struct dentry_operations isofs_dentry_ops[] = { + { + .d_hash = isofs_hashi, + .d_compare = isofs_dentry_cmpi, + }, +#ifdef CONFIG_JOLIET + { + .d_hash = isofs_hash_ms, + .d_compare = isofs_dentry_cmp_ms, + }, + { + .d_hash = isofs_hashi_ms, + .d_compare = isofs_dentry_cmpi_ms, + }, +#endif +}; + +struct isofs_options{ + unsigned int rock:1; + unsigned int joliet:1; + unsigned int cruft:1; + unsigned int hide:1; + unsigned int showassoc:1; + unsigned int nocompress:1; + unsigned int overriderockperm:1; + unsigned int uid_set:1; + unsigned int gid_set:1; + unsigned char map; + unsigned char check; + unsigned int blocksize; + umode_t fmode; + umode_t dmode; + kgid_t gid; + kuid_t uid; + char *iocharset; + /* LVE */ + s32 session; + s32 sbsector; +}; + +/* + * Compute the hash for the isofs name corresponding to the dentry. + */ +static int +isofs_hashi_common(const struct dentry *dentry, struct qstr *qstr, int ms) +{ + const char *name; + int len; + char c; + unsigned long hash; + + len = qstr->len; + name = qstr->name; + if (ms) { + while (len && name[len-1] == '.') + len--; + } + + hash = init_name_hash(dentry); + while (len--) { + c = tolower(*name++); + hash = partial_name_hash(c, hash); + } + qstr->hash = end_name_hash(hash); + + return 0; +} + +/* + * Compare of two isofs names. + */ +static int isofs_dentry_cmp_common( + unsigned int len, const char *str, + const struct qstr *name, int ms, int ci) +{ + int alen, blen; + + /* A filename cannot end in '.' or we treat it like it has none */ + alen = name->len; + blen = len; + if (ms) { + while (alen && name->name[alen-1] == '.') + alen--; + while (blen && str[blen-1] == '.') + blen--; + } + if (alen == blen) { + if (ci) { + if (strncasecmp(name->name, str, alen) == 0) + return 0; + } else { + if (strncmp(name->name, str, alen) == 0) + return 0; + } + } + return 1; +} + +static int +isofs_hashi(const struct dentry *dentry, struct qstr *qstr) +{ + return isofs_hashi_common(dentry, qstr, 0); +} + +static int +isofs_dentry_cmpi(const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) +{ + return isofs_dentry_cmp_common(len, str, name, 0, 1); +} + +#ifdef CONFIG_JOLIET +/* + * Compute the hash for the isofs name corresponding to the dentry. + */ +static int +isofs_hash_common(const struct dentry *dentry, struct qstr *qstr, int ms) +{ + const char *name; + int len; + + len = qstr->len; + name = qstr->name; + if (ms) { + while (len && name[len-1] == '.') + len--; + } + + qstr->hash = full_name_hash(dentry, name, len); + + return 0; +} + +static int +isofs_hash_ms(const struct dentry *dentry, struct qstr *qstr) +{ + return isofs_hash_common(dentry, qstr, 1); +} + +static int +isofs_hashi_ms(const struct dentry *dentry, struct qstr *qstr) +{ + return isofs_hashi_common(dentry, qstr, 1); +} + +static int +isofs_dentry_cmp_ms(const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) +{ + return isofs_dentry_cmp_common(len, str, name, 1, 0); +} + +static int +isofs_dentry_cmpi_ms(const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) +{ + return isofs_dentry_cmp_common(len, str, name, 1, 1); +} +#endif + +enum { + Opt_block, Opt_check, Opt_cruft, Opt_gid, Opt_ignore, Opt_iocharset, + Opt_map, Opt_mode, Opt_nojoliet, Opt_norock, Opt_sb, Opt_session, + Opt_uid, Opt_unhide, Opt_utf8, Opt_err, Opt_nocompress, Opt_hide, + Opt_showassoc, Opt_dmode, Opt_overriderockperm, +}; + +static const struct constant_table isofs_param_map[] = { + {"acorn", 'a'}, + {"a", 'a'}, + {"normal", 'n'}, + {"n", 'n'}, + {"off", 'o'}, + {"o", 'o'}, + {} +}; + +static const struct constant_table isofs_param_check[] = { + {"relaxed", 'r'}, + {"r", 'r'}, + {"strict", 's'}, + {"s", 's'}, + {} +}; + +static const struct fs_parameter_spec isofs_param_spec[] = { + fsparam_flag ("norock", Opt_norock), + fsparam_flag ("nojoliet", Opt_nojoliet), + fsparam_flag ("unhide", Opt_unhide), + fsparam_flag ("hide", Opt_hide), + fsparam_flag ("showassoc", Opt_showassoc), + fsparam_flag ("cruft", Opt_cruft), + fsparam_flag ("utf8", Opt_utf8), + fsparam_string ("iocharset", Opt_iocharset), + fsparam_enum ("map", Opt_map, isofs_param_map), + fsparam_u32 ("session", Opt_session), + fsparam_u32 ("sbsector", Opt_sb), + fsparam_enum ("check", Opt_check, isofs_param_check), + fsparam_uid ("uid", Opt_uid), + fsparam_gid ("gid", Opt_gid), + /* Note: mode/dmode historically accepted %u not strictly %o */ + fsparam_u32 ("mode", Opt_mode), + fsparam_u32 ("dmode", Opt_dmode), + fsparam_flag ("overriderockperm", Opt_overriderockperm), + fsparam_u32 ("block", Opt_block), + fsparam_string ("conv", Opt_ignore), + fsparam_flag ("nocompress", Opt_nocompress), + {} +}; + +static int isofs_parse_param(struct fs_context *fc, + struct fs_parameter *param) +{ + struct isofs_options *popt = fc->fs_private; + struct fs_parse_result result; + int opt; + unsigned int n; + + /* There are no remountable options */ + if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) + return 0; + + opt = fs_parse(fc, isofs_param_spec, param, &result); + if (opt < 0) + return opt; + + switch (opt) { + case Opt_norock: + popt->rock = 0; + break; + case Opt_nojoliet: + popt->joliet = 0; + break; + case Opt_hide: + popt->hide = 1; + break; + case Opt_unhide: + case Opt_showassoc: + popt->showassoc = 1; + break; + case Opt_cruft: + popt->cruft = 1; + break; +#ifdef CONFIG_JOLIET + case Opt_utf8: + kfree(popt->iocharset); + popt->iocharset = kstrdup("utf8", GFP_KERNEL); + if (!popt->iocharset) + return -ENOMEM; + break; + case Opt_iocharset: + kfree(popt->iocharset); + popt->iocharset = kstrdup(param->string, GFP_KERNEL); + if (!popt->iocharset) + return -ENOMEM; + break; +#endif + case Opt_map: + popt->map = result.uint_32; + break; + case Opt_session: + n = result.uint_32; + /* + * Track numbers are supposed to be in range 1-99, the + * mount option starts indexing at 0. + */ + if (n >= 99) + return -EINVAL; + popt->session = n + 1; + break; + case Opt_sb: + popt->sbsector = result.uint_32; + break; + case Opt_check: + popt->check = result.uint_32; + break; + case Opt_ignore: + break; + case Opt_uid: + popt->uid = result.uid; + popt->uid_set = 1; + break; + case Opt_gid: + popt->gid = result.gid; + popt->gid_set = 1; + break; + case Opt_mode: + popt->fmode = result.uint_32; + break; + case Opt_dmode: + popt->dmode = result.uint_32; + break; + case Opt_overriderockperm: + popt->overriderockperm = 1; + break; + case Opt_block: + n = result.uint_32; + if (n != 512 && n != 1024 && n != 2048) + return -EINVAL; + popt->blocksize = n; + break; + case Opt_nocompress: + popt->nocompress = 1; + break; + default: + return -EINVAL; + } + return 0; +} + +/* + * Display the mount options in /proc/mounts. + */ +static int isofs_show_options(struct seq_file *m, struct dentry *root) +{ + struct isofs_sb_info *sbi = ISOFS_SB(root->d_sb); + + if (!sbi->s_rock) seq_puts(m, ",norock"); + else if (!sbi->s_joliet_level) seq_puts(m, ",nojoliet"); + if (sbi->s_cruft) seq_puts(m, ",cruft"); + if (sbi->s_hide) seq_puts(m, ",hide"); + if (sbi->s_nocompress) seq_puts(m, ",nocompress"); + if (sbi->s_overriderockperm) seq_puts(m, ",overriderockperm"); + if (sbi->s_showassoc) seq_puts(m, ",showassoc"); + + if (sbi->s_check) seq_printf(m, ",check=%c", sbi->s_check); + if (sbi->s_mapping) seq_printf(m, ",map=%c", sbi->s_mapping); + if (sbi->s_session != 255) seq_printf(m, ",session=%u", sbi->s_session - 1); + if (sbi->s_sbsector != -1) seq_printf(m, ",sbsector=%u", sbi->s_sbsector); + + if (root->d_sb->s_blocksize != 1024) + seq_printf(m, ",blocksize=%lu", root->d_sb->s_blocksize); + + if (sbi->s_uid_set) + seq_printf(m, ",uid=%u", + from_kuid_munged(&init_user_ns, sbi->s_uid)); + if (sbi->s_gid_set) + seq_printf(m, ",gid=%u", + from_kgid_munged(&init_user_ns, sbi->s_gid)); + + if (sbi->s_dmode != ISOFS_INVALID_MODE) + seq_printf(m, ",dmode=%o", sbi->s_dmode); + if (sbi->s_fmode != ISOFS_INVALID_MODE) + seq_printf(m, ",fmode=%o", sbi->s_fmode); + +#ifdef CONFIG_JOLIET + if (sbi->s_nls_iocharset) + seq_printf(m, ",iocharset=%s", sbi->s_nls_iocharset->charset); + else + seq_puts(m, ",iocharset=utf8"); +#endif + return 0; +} + +/* + * look if the driver can tell the multi session redirection value + * + * don't change this if you don't know what you do, please! + * Multisession is legal only with XA disks. + * A non-XA disk with more than one volume descriptor may do it right, but + * usually is written in a nowhere standardized "multi-partition" manner. + * Multisession uses absolute addressing (solely the first frame of the whole + * track is #0), multi-partition uses relative addressing (each first frame of + * each track is #0), and a track is not a session. + * + * A broken CDwriter software or drive firmware does not set new standards, + * at least not if conflicting with the existing ones. + * + * emoenke@gwdg.de + */ +#define WE_OBEY_THE_WRITTEN_STANDARDS 1 + +static unsigned int isofs_get_last_session(struct super_block *sb, s32 session) +{ + struct cdrom_device_info *cdi = disk_to_cdi(sb->s_bdev->bd_disk); + unsigned int vol_desc_start = 0; + + if (session > 0) { + struct cdrom_tocentry te; + + if (!cdi) + return 0; + + te.cdte_track = session; + te.cdte_format = CDROM_LBA; + if (cdrom_read_tocentry(cdi, &te) == 0) { + printk(KERN_DEBUG "ISOFS: Session %d start %d type %d\n", + session, te.cdte_addr.lba, + te.cdte_ctrl & CDROM_DATA_TRACK); + if ((te.cdte_ctrl & CDROM_DATA_TRACK) == 4) + return te.cdte_addr.lba; + } + + printk(KERN_ERR "ISOFS: Invalid session number or type of track\n"); + } + + if (cdi) { + struct cdrom_multisession ms_info; + + ms_info.addr_format = CDROM_LBA; + if (cdrom_multisession(cdi, &ms_info) == 0) { +#if WE_OBEY_THE_WRITTEN_STANDARDS + /* necessary for a valid ms_info.addr */ + if (ms_info.xa_flag) +#endif + vol_desc_start = ms_info.addr.lba; + } + } + + return vol_desc_start; +} + +/* + * Check if root directory is empty (has less than 3 files). + * + * Used to detect broken CDs where ISO root directory is empty but Joliet root + * directory is OK. If such CD has Rock Ridge extensions, they will be disabled + * (and Joliet used instead) or else no files would be visible. + */ +static bool rootdir_empty(struct super_block *sb, unsigned long block) +{ + int offset = 0, files = 0, de_len; + struct iso_directory_record *de; + struct buffer_head *bh; + + bh = sb_bread(sb, block); + if (!bh) + return true; + while (files < 3) { + de = (struct iso_directory_record *) (bh->b_data + offset); + de_len = *(unsigned char *) de; + if (de_len == 0) + break; + files++; + offset += de_len; + } + brelse(bh); + return files < 3; +} + +/* + * Initialize the superblock and read the root inode. + */ +static int isofs_fill_super(struct super_block *s, struct fs_context *fc) +{ + struct buffer_head *bh = NULL, *pri_bh = NULL; + struct hs_primary_descriptor *h_pri = NULL; + struct iso_primary_descriptor *pri = NULL; + struct iso_supplementary_descriptor *sec = NULL; + struct iso_directory_record *rootp; + struct inode *inode; + struct isofs_options *opt = fc->fs_private; + struct isofs_sb_info *sbi; + unsigned long first_data_zone; + int joliet_level = 0; + int iso_blknum, block; + int orig_zonesize; + int table, error = -EINVAL; + unsigned int vol_desc_start; + int silent = fc->sb_flags & SB_SILENT; + + sbi = kzalloc_obj(*sbi); + if (!sbi) + return -ENOMEM; + s->s_fs_info = sbi; + + /* + * First of all, get the hardware blocksize for this device. + * If we don't know what it is, or the hardware blocksize is + * larger than the blocksize the user specified, then use + * that value. + */ + /* + * What if bugger tells us to go beyond page size? + */ + if (bdev_logical_block_size(s->s_bdev) > 2048) { + printk(KERN_WARNING + "ISOFS: unsupported/invalid hardware sector size %d\n", + bdev_logical_block_size(s->s_bdev)); + goto out_freesbi; + } + opt->blocksize = sb_min_blocksize(s, opt->blocksize); + if (!opt->blocksize) { + printk(KERN_ERR + "ISOFS: unable to set blocksize\n"); + goto out_freesbi; + } + + sbi->s_high_sierra = 0; /* default is iso9660 */ + sbi->s_session = opt->session; + sbi->s_sbsector = opt->sbsector; + + vol_desc_start = (opt->sbsector != -1) ? + opt->sbsector : isofs_get_last_session(s, opt->session); + + for (iso_blknum = vol_desc_start+16; + iso_blknum < vol_desc_start+100; iso_blknum++) { + struct hs_volume_descriptor *hdp; + struct iso_volume_descriptor *vdp; + + block = iso_blknum << (ISOFS_BLOCK_BITS - s->s_blocksize_bits); + if (!(bh = sb_bread(s, block))) + goto out_no_read; + + vdp = (struct iso_volume_descriptor *)bh->b_data; + hdp = (struct hs_volume_descriptor *)bh->b_data; + + /* + * Due to the overlapping physical location of the descriptors, + * ISO CDs can match hdp->id==HS_STANDARD_ID as well. To ensure + * proper identification in this case, we first check for ISO. + */ + if (strncmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) == 0) { + if (isonum_711(vdp->type) == ISO_VD_END) + break; + if (isonum_711(vdp->type) == ISO_VD_PRIMARY) { + if (!pri) { + pri = (struct iso_primary_descriptor *)vdp; + /* Save the buffer in case we need it ... */ + pri_bh = bh; + bh = NULL; + } + } +#ifdef CONFIG_JOLIET + else if (isonum_711(vdp->type) == ISO_VD_SUPPLEMENTARY) { + sec = (struct iso_supplementary_descriptor *)vdp; + if (sec->escape[0] == 0x25 && sec->escape[1] == 0x2f) { + if (opt->joliet) { + if (sec->escape[2] == 0x40) + joliet_level = 1; + else if (sec->escape[2] == 0x43) + joliet_level = 2; + else if (sec->escape[2] == 0x45) + joliet_level = 3; + + printk(KERN_DEBUG "ISO 9660 Extensions: " + "Microsoft Joliet Level %d\n", + joliet_level); + } + goto root_found; + } else { + /* Unknown supplementary volume descriptor */ + sec = NULL; + } + } +#endif + } else { + if (strncmp (hdp->id, HS_STANDARD_ID, sizeof hdp->id) == 0) { + if (isonum_711(hdp->type) != ISO_VD_PRIMARY) + goto out_freebh; + + sbi->s_high_sierra = 1; + opt->rock = 0; + h_pri = (struct hs_primary_descriptor *)vdp; + goto root_found; + } + } + + /* Just skip any volume descriptors we don't recognize */ + + brelse(bh); + bh = NULL; + } + /* + * If we fall through, either no volume descriptor was found, + * or else we passed a primary descriptor looking for others. + */ + if (!pri) + goto out_unknown_format; + brelse(bh); + bh = pri_bh; + pri_bh = NULL; + +root_found: + /* We don't support read-write mounts */ + if (!sb_rdonly(s)) { + error = -EACCES; + goto out_freebh; + } + + if (joliet_level && (!pri || !opt->rock)) { + /* This is the case of Joliet with the norock mount flag. + * A disc with both Joliet and Rock Ridge is handled later + */ + pri = (struct iso_primary_descriptor *) sec; + } + + if(sbi->s_high_sierra){ + rootp = (struct iso_directory_record *) h_pri->root_directory_record; + sbi->s_nzones = isonum_733(h_pri->volume_space_size); + sbi->s_log_zone_size = isonum_723(h_pri->logical_block_size); + sbi->s_max_size = isonum_733(h_pri->volume_space_size); + } else { + if (!pri) + goto out_freebh; + rootp = (struct iso_directory_record *) pri->root_directory_record; + sbi->s_nzones = isonum_733(pri->volume_space_size); + sbi->s_log_zone_size = isonum_723(pri->logical_block_size); + sbi->s_max_size = isonum_733(pri->volume_space_size); + } + + sbi->s_ninodes = 0; /* No way to figure this out easily */ + + orig_zonesize = sbi->s_log_zone_size; + /* + * If the zone size is smaller than the hardware sector size, + * this is a fatal error. This would occur if the disc drive + * had sectors that were 2048 bytes, but the filesystem had + * blocks that were 512 bytes (which should only very rarely + * happen.) + */ + if (orig_zonesize < opt->blocksize) + goto out_bad_size; + + /* RDE: convert log zone size to bit shift */ + switch (sbi->s_log_zone_size) { + case 512: sbi->s_log_zone_size = 9; break; + case 1024: sbi->s_log_zone_size = 10; break; + case 2048: sbi->s_log_zone_size = 11; break; + + default: + goto out_bad_zone_size; + } + + s->s_magic = ISOFS_SUPER_MAGIC; + + /* + * With multi-extent files, file size is only limited by the maximum + * size of a file system, which is 8 TB. + */ + s->s_maxbytes = 0x80000000000LL; + + /* ECMA-119 timestamp from 1900/1/1 with tz offset */ + s->s_time_min = mktime64(1900, 1, 1, 0, 0, 0) - MAX_TZ_OFFSET; + s->s_time_max = mktime64(U8_MAX+1900, 12, 31, 23, 59, 59) + MAX_TZ_OFFSET; + + /* Set this for reference. Its not currently used except on write + which we don't have .. */ + + first_data_zone = isonum_733(rootp->extent) + + isonum_711(rootp->ext_attr_length); + sbi->s_firstdatazone = first_data_zone; +#ifndef BEQUIET + printk(KERN_DEBUG "ISOFS: Max size:%ld Log zone size:%ld\n", + sbi->s_max_size, 1UL << sbi->s_log_zone_size); + printk(KERN_DEBUG "ISOFS: First datazone:%ld\n", sbi->s_firstdatazone); + if(sbi->s_high_sierra) + printk(KERN_DEBUG "ISOFS: Disc in High Sierra format.\n"); +#endif + + /* + * If the Joliet level is set, we _may_ decide to use the + * secondary descriptor, but can't be sure until after we + * read the root inode. But before reading the root inode + * we may need to change the device blocksize, and would + * rather release the old buffer first. So, we cache the + * first_data_zone value from the secondary descriptor. + */ + if (joliet_level) { + pri = (struct iso_primary_descriptor *) sec; + rootp = (struct iso_directory_record *) + pri->root_directory_record; + first_data_zone = isonum_733(rootp->extent) + + isonum_711(rootp->ext_attr_length); + } + + /* + * We're all done using the volume descriptor, and may need + * to change the device blocksize, so release the buffer now. + */ + brelse(pri_bh); + brelse(bh); + + /* + * Force the blocksize to 512 for 512 byte sectors. The file + * read primitives really get it wrong in a bad way if we don't + * do this. + * + * Note - we should never be setting the blocksize to something + * less than the hardware sector size for the device. If we + * do, we would end up having to read larger buffers and split + * out portions to satisfy requests. + * + * Note2- the idea here is that we want to deal with the optimal + * zonesize in the filesystem. If we have it set to something less, + * then we have horrible problems with trying to piece together + * bits of adjacent blocks in order to properly read directory + * entries. By forcing the blocksize in this way, we ensure + * that we will never be required to do this. + */ + sb_set_blocksize(s, orig_zonesize); + + sbi->s_nls_iocharset = NULL; + +#ifdef CONFIG_JOLIET + if (joliet_level) { + char *p = opt->iocharset ? opt->iocharset : CONFIG_NLS_DEFAULT; + if (strcmp(p, "utf8") != 0) { + sbi->s_nls_iocharset = opt->iocharset ? + load_nls(opt->iocharset) : load_nls_default(); + if (!sbi->s_nls_iocharset) + goto out_freesbi; + } + } +#endif + s->s_op = &isofs_sops; + s->s_export_op = &isofs_export_ops; + sbi->s_mapping = opt->map; + sbi->s_rock = (opt->rock ? 2 : 0); + sbi->s_rock_offset = -1; /* initial offset, will guess until SP is found*/ + sbi->s_cruft = opt->cruft; + sbi->s_hide = opt->hide; + sbi->s_showassoc = opt->showassoc; + sbi->s_uid = opt->uid; + sbi->s_gid = opt->gid; + sbi->s_uid_set = opt->uid_set; + sbi->s_gid_set = opt->gid_set; + sbi->s_nocompress = opt->nocompress; + sbi->s_overriderockperm = opt->overriderockperm; + /* + * It would be incredibly stupid to allow people to mark every file + * on the disk as suid, so we merely allow them to set the default + * permissions. + */ + if (opt->fmode != ISOFS_INVALID_MODE) + sbi->s_fmode = opt->fmode & 0777; + else + sbi->s_fmode = ISOFS_INVALID_MODE; + if (opt->dmode != ISOFS_INVALID_MODE) + sbi->s_dmode = opt->dmode & 0777; + else + sbi->s_dmode = ISOFS_INVALID_MODE; + + /* + * Read the root inode, which _may_ result in changing + * the s_rock flag. Once we have the final s_rock value, + * we then decide whether to use the Joliet descriptor. + */ + inode = isofs_iget(s, sbi->s_firstdatazone, 0); + + /* + * Fix for broken CDs with a corrupt root inode but a correct Joliet + * root directory. + */ + if (IS_ERR(inode)) { + if (joliet_level && sbi->s_firstdatazone != first_data_zone) { + printk(KERN_NOTICE + "ISOFS: root inode is unusable. " + "Disabling Rock Ridge and switching to Joliet."); + sbi->s_rock = 0; + inode = NULL; + } else { + goto out_no_root; + } + } + + /* + * Fix for broken CDs with Rock Ridge and empty ISO root directory but + * correct Joliet root directory. + */ + if (sbi->s_rock == 1 && joliet_level && + rootdir_empty(s, sbi->s_firstdatazone)) { + printk(KERN_NOTICE + "ISOFS: primary root directory is empty. " + "Disabling Rock Ridge and switching to Joliet."); + sbi->s_rock = 0; + } + + /* + * If this disk has both Rock Ridge and Joliet on it, then we + * want to use Rock Ridge by default. This can be overridden + * by using the norock mount option. There is still one other + * possibility that is not taken into account: a Rock Ridge + * CD with Unicode names. Until someone sees such a beast, it + * will not be supported. + */ + if (sbi->s_rock == 1) { + joliet_level = 0; + } else if (joliet_level) { + sbi->s_rock = 0; + if (sbi->s_firstdatazone != first_data_zone) { + sbi->s_firstdatazone = first_data_zone; + printk(KERN_DEBUG + "ISOFS: changing to secondary root\n"); + iput(inode); + inode = isofs_iget(s, sbi->s_firstdatazone, 0); + if (IS_ERR(inode)) + goto out_no_root; + } + } + + if (opt->check == 'u') { + /* Only Joliet is case insensitive by default */ + if (joliet_level) + opt->check = 'r'; + else + opt->check = 's'; + } + sbi->s_joliet_level = joliet_level; + + /* Make sure the root inode is a directory */ + if (!S_ISDIR(inode->i_mode)) { + printk(KERN_WARNING + "isofs_fill_super: root inode is not a directory. " + "Corrupted media?\n"); + goto out_iput; + } + + table = 0; + if (joliet_level) + table += 2; + if (opt->check == 'r') + table++; + sbi->s_check = opt->check; + + if (table) + set_default_d_op(s, &isofs_dentry_ops[table - 1]); + + /* get the root dentry */ + s->s_root = d_make_root(inode); + if (!(s->s_root)) { + error = -ENOMEM; + goto out_no_inode; + } + + return 0; + + /* + * Display error messages and free resources. + */ +out_iput: + iput(inode); + goto out_no_inode; +out_no_root: + error = PTR_ERR(inode); + if (error != -ENOMEM) + printk(KERN_WARNING "%s: get root inode failed\n", __func__); +out_no_inode: +#ifdef CONFIG_JOLIET + unload_nls(sbi->s_nls_iocharset); +#endif + goto out_freesbi; +out_no_read: + printk(KERN_WARNING "%s: bread failed, dev=%s, iso_blknum=%d, block=%d\n", + __func__, s->s_id, iso_blknum, block); + goto out_freebh; +out_bad_zone_size: + printk(KERN_WARNING "ISOFS: Bad logical zone size %ld\n", + sbi->s_log_zone_size); + goto out_freebh; +out_bad_size: + printk(KERN_WARNING "ISOFS: Logical zone size(%d) < hardware blocksize(%u)\n", + orig_zonesize, opt->blocksize); + goto out_freebh; +out_unknown_format: + if (!silent) + printk(KERN_WARNING "ISOFS: Unable to identify CD-ROM format.\n"); + +out_freebh: + brelse(bh); + brelse(pri_bh); +out_freesbi: + kfree(sbi); + s->s_fs_info = NULL; + return error; +} + +static int isofs_statfs (struct dentry *dentry, struct kstatfs *buf) +{ + struct super_block *sb = dentry->d_sb; + u64 id = huge_encode_dev(sb->s_bdev->bd_dev); + + buf->f_type = ISOFS_SUPER_MAGIC; + buf->f_bsize = sb->s_blocksize; + buf->f_blocks = (ISOFS_SB(sb)->s_nzones + << (ISOFS_SB(sb)->s_log_zone_size - sb->s_blocksize_bits)); + buf->f_bfree = 0; + buf->f_bavail = 0; + buf->f_files = ISOFS_SB(sb)->s_ninodes; + buf->f_ffree = 0; + buf->f_fsid = u64_to_fsid(id); + buf->f_namelen = NAME_MAX; + return 0; +} + +/* + * Get a set of blocks; filling in buffer_heads if already allocated + * or getblk() if they are not. Returns the number of blocks inserted + * (-ve == error.) + */ +int isofs_get_blocks(struct inode *inode, sector_t iblock, + struct buffer_head **bh, unsigned long nblocks) +{ + unsigned long b_off = iblock; + unsigned offset, sect_size; + unsigned int firstext; + unsigned long nextblk, nextoff; + int section, rv, error; + struct iso_inode_info *ei = ISOFS_I(inode); + + error = -EIO; + rv = 0; + if (iblock != b_off) { + printk(KERN_DEBUG "%s: block number too large\n", __func__); + goto abort; + } + + + offset = 0; + firstext = ei->i_first_extent; + sect_size = ei->i_section_size >> ISOFS_BUFFER_BITS(inode); + nextblk = ei->i_next_section_block; + nextoff = ei->i_next_section_offset; + section = 0; + + while (nblocks) { + /* If we are *way* beyond the end of the file, print a message. + * Access beyond the end of the file up to the next page boundary + * is normal, however because of the way the page cache works. + * In this case, we just return 0 so that we can properly fill + * the page with useless information without generating any + * I/O errors. + */ + if (b_off > ((inode->i_size + PAGE_SIZE - 1) >> ISOFS_BUFFER_BITS(inode))) { + printk(KERN_DEBUG "%s: block >= EOF (%lu, %llu)\n", + __func__, b_off, + (unsigned long long)inode->i_size); + goto abort; + } + + /* On the last section, nextblk == 0, section size is likely to + * exceed sect_size by a partial block, and access beyond the + * end of the file will reach beyond the section size, too. + */ + while (nextblk && (b_off >= (offset + sect_size))) { + struct inode *ninode; + + offset += sect_size; + ninode = isofs_iget(inode->i_sb, nextblk, nextoff); + if (IS_ERR(ninode)) { + error = PTR_ERR(ninode); + goto abort; + } + firstext = ISOFS_I(ninode)->i_first_extent; + sect_size = ISOFS_I(ninode)->i_section_size >> ISOFS_BUFFER_BITS(ninode); + nextblk = ISOFS_I(ninode)->i_next_section_block; + nextoff = ISOFS_I(ninode)->i_next_section_offset; + iput(ninode); + + if (++section > 100) { + printk(KERN_DEBUG "%s: More than 100 file sections ?!?" + " aborting...\n", __func__); + printk(KERN_DEBUG "%s: block=%lu firstext=%u sect_size=%u " + "nextblk=%lu nextoff=%lu\n", __func__, + b_off, firstext, (unsigned) sect_size, + nextblk, nextoff); + goto abort; + } + } + + if (*bh) { + map_bh(*bh, inode->i_sb, firstext + b_off - offset); + } else { + *bh = sb_getblk(inode->i_sb, firstext+b_off-offset); + if (!*bh) + goto abort; + } + bh++; /* Next buffer head */ + b_off++; /* Next buffer offset */ + nblocks--; + rv++; + } + + error = 0; +abort: + return rv != 0 ? rv : error; +} + +/* + * Used by the standard interfaces. + */ +static int isofs_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create) +{ + int ret; + + if (create) { + printk(KERN_DEBUG "%s: Kernel tries to allocate a block\n", __func__); + return -EROFS; + } + + ret = isofs_get_blocks(inode, iblock, &bh_result, 1); + return ret < 0 ? ret : 0; +} + +static int isofs_bmap(struct inode *inode, sector_t block) +{ + struct buffer_head dummy; + int error; + + dummy.b_state = 0; + dummy.b_blocknr = -1000; + error = isofs_get_block(inode, block, &dummy, 0); + if (!error) + return dummy.b_blocknr; + return 0; +} + +struct buffer_head *isofs_bread(struct inode *inode, sector_t block) +{ + sector_t blknr = isofs_bmap(inode, block); + if (!blknr) + return NULL; + return sb_bread(inode->i_sb, blknr); +} + +static int isofs_read_folio(struct file *file, struct folio *folio) +{ + return mpage_read_folio(folio, isofs_get_block); +} + +static void isofs_readahead(struct readahead_control *rac) +{ + mpage_readahead(rac, isofs_get_block); +} + +static sector_t _isofs_bmap(struct address_space *mapping, sector_t block) +{ + return generic_block_bmap(mapping,block,isofs_get_block); +} + +static const struct address_space_operations isofs_aops = { + .read_folio = isofs_read_folio, + .readahead = isofs_readahead, + .bmap = _isofs_bmap +}; + +static int isofs_read_level3_size(struct inode *inode) +{ + unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); + int high_sierra = ISOFS_SB(inode->i_sb)->s_high_sierra; + struct buffer_head *bh = NULL; + unsigned long block, offset, block_saved, offset_saved; + int i = 0; + int more_entries = 0; + struct iso_directory_record *tmpde = NULL; + struct iso_inode_info *ei = ISOFS_I(inode); + + inode->i_size = 0; + + /* The first 16 blocks are reserved as the System Area. Thus, + * no inodes can appear in block 0. We use this to flag that + * this is the last section. */ + ei->i_next_section_block = 0; + ei->i_next_section_offset = 0; + + block = ei->i_iget5_block; + offset = ei->i_iget5_offset; + + do { + struct iso_directory_record *de; + unsigned int de_len; + + if (!bh) { + bh = sb_bread(inode->i_sb, block); + if (!bh) + goto out_noread; + } + de = (struct iso_directory_record *) (bh->b_data + offset); + de_len = *(unsigned char *) de; + + if (de_len == 0) { + brelse(bh); + bh = NULL; + ++block; + offset = 0; + continue; + } + + block_saved = block; + offset_saved = offset; + offset += de_len; + + /* Make sure we have a full directory entry */ + if (offset >= bufsize) { + int slop = bufsize - offset + de_len; + if (!tmpde) { + tmpde = kmalloc(256, GFP_KERNEL); + if (!tmpde) + goto out_nomem; + } + memcpy(tmpde, de, slop); + offset &= bufsize - 1; + block++; + brelse(bh); + bh = NULL; + if (offset) { + bh = sb_bread(inode->i_sb, block); + if (!bh) + goto out_noread; + memcpy((void *)tmpde+slop, bh->b_data, offset); + } + de = tmpde; + } + + inode->i_size += isonum_733(de->size); + if (i == 1) { + ei->i_next_section_block = block_saved; + ei->i_next_section_offset = offset_saved; + } + + more_entries = de->flags[-high_sierra] & 0x80; + + i++; + if (i > 100) + goto out_toomany; + } while (more_entries); +out: + kfree(tmpde); + brelse(bh); + return 0; + +out_nomem: + brelse(bh); + return -ENOMEM; + +out_noread: + printk(KERN_INFO "ISOFS: unable to read i-node block %lu\n", block); + kfree(tmpde); + return -EIO; + +out_toomany: + printk(KERN_INFO "%s: More than 100 file sections ?!?, aborting...\n" + "isofs_read_level3_size: inode=%lu\n", + __func__, inode->i_ino); + goto out; +} + +static int isofs_read_inode(struct inode *inode, int relocated) +{ + struct super_block *sb = inode->i_sb; + struct isofs_sb_info *sbi = ISOFS_SB(sb); + unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); + unsigned long block; + int high_sierra = sbi->s_high_sierra; + struct buffer_head *bh; + struct iso_directory_record *de; + struct iso_directory_record *tmpde = NULL; + unsigned int de_len; + unsigned long offset; + struct iso_inode_info *ei = ISOFS_I(inode); + int ret = -EIO; + struct timespec64 ts; + + block = ei->i_iget5_block; + bh = sb_bread(inode->i_sb, block); + if (!bh) + goto out_badread; + + offset = ei->i_iget5_offset; + + de = (struct iso_directory_record *) (bh->b_data + offset); + de_len = *(unsigned char *) de; + if (de_len < sizeof(struct iso_directory_record)) + goto fail; + + if (offset + de_len > bufsize) { + int frag1 = bufsize - offset; + + tmpde = kmalloc(de_len, GFP_KERNEL); + if (!tmpde) { + ret = -ENOMEM; + goto fail; + } + memcpy(tmpde, bh->b_data + offset, frag1); + brelse(bh); + bh = sb_bread(inode->i_sb, ++block); + if (!bh) + goto out_badread; + memcpy((char *)tmpde+frag1, bh->b_data, de_len - frag1); + de = tmpde; + } + + inode->i_ino = isofs_get_ino(ei->i_iget5_block, + ei->i_iget5_offset, + ISOFS_BUFFER_BITS(inode)); + + /* Assume it is a normal-format file unless told otherwise */ + ei->i_file_format = isofs_file_normal; + + if (de->flags[-high_sierra] & 2) { + if (sbi->s_dmode != ISOFS_INVALID_MODE) + inode->i_mode = S_IFDIR | sbi->s_dmode; + else + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; + set_nlink(inode, 1); /* + * Set to 1. We know there are 2, but + * the find utility tries to optimize + * if it is 2, and it screws up. It is + * easier to give 1 which tells find to + * do it the hard way. + */ + } else { + if (sbi->s_fmode != ISOFS_INVALID_MODE) { + inode->i_mode = S_IFREG | sbi->s_fmode; + } else { + /* + * Set default permissions: r-x for all. The disc + * could be shared with DOS machines so virtually + * anything could be a valid executable. + */ + inode->i_mode = S_IFREG | S_IRUGO | S_IXUGO; + } + set_nlink(inode, 1); + } + inode->i_uid = sbi->s_uid; + inode->i_gid = sbi->s_gid; + inode->i_blocks = 0; + + ei->i_format_parm[0] = 0; + ei->i_format_parm[1] = 0; + ei->i_format_parm[2] = 0; + + ei->i_section_size = isonum_733(de->size); + if (de->flags[-high_sierra] & 0x80) { + ret = isofs_read_level3_size(inode); + if (ret < 0) + goto fail; + ret = -EIO; + } else { + ei->i_next_section_block = 0; + ei->i_next_section_offset = 0; + inode->i_size = isonum_733(de->size); + } + + /* + * Some dipshit decided to store some other bit of information + * in the high byte of the file length. Truncate size in case + * this CDROM was mounted with the cruft option. + */ + + if (sbi->s_cruft) + inode->i_size &= 0x00ffffff; + + if (de->interleave[0]) { + printk(KERN_DEBUG "ISOFS: Interleaved files not (yet) supported.\n"); + inode->i_size = 0; + } + + /* I have no idea what file_unit_size is used for, so + we will flag it for now */ + if (de->file_unit_size[0] != 0) { + printk(KERN_DEBUG "ISOFS: File unit size != 0 for ISO file (%ld).\n", + inode->i_ino); + } + + /* I have no idea what other flag bits are used for, so + we will flag it for now */ +#ifdef DEBUG + if((de->flags[-high_sierra] & ~2)!= 0){ + printk(KERN_DEBUG "ISOFS: Unusual flag settings for ISO file " + "(%ld %x).\n", + inode->i_ino, de->flags[-high_sierra]); + } +#endif + ts = iso_date(de->date, high_sierra ? ISO_DATE_HIGH_SIERRA : 0); + inode_set_ctime_to_ts(inode, ts); + inode_set_atime_to_ts(inode, ts); + inode_set_mtime_to_ts(inode, ts); + + ei->i_first_extent = (isonum_733(de->extent) + + isonum_711(de->ext_attr_length)); + + /* Set the number of blocks for stat() - should be done before RR */ + inode->i_blocks = (inode->i_size + 511) >> 9; + + /* + * Now test for possible Rock Ridge extensions which will override + * some of these numbers in the inode structure. + */ + + if (!high_sierra) { + parse_rock_ridge_inode(de, inode, relocated); + /* if we want uid/gid set, override the rock ridge setting */ + if (sbi->s_uid_set) + inode->i_uid = sbi->s_uid; + if (sbi->s_gid_set) + inode->i_gid = sbi->s_gid; + } + /* Now set final access rights if overriding rock ridge setting */ + if (S_ISDIR(inode->i_mode) && sbi->s_overriderockperm && + sbi->s_dmode != ISOFS_INVALID_MODE) + inode->i_mode = S_IFDIR | sbi->s_dmode; + if (S_ISREG(inode->i_mode) && sbi->s_overriderockperm && + sbi->s_fmode != ISOFS_INVALID_MODE) + inode->i_mode = S_IFREG | sbi->s_fmode; + + /* Install the inode operations vector */ + if (S_ISREG(inode->i_mode)) { + inode->i_fop = &generic_ro_fops; + switch (ei->i_file_format) { +#ifdef CONFIG_ZISOFS + case isofs_file_compressed: + inode->i_data.a_ops = &zisofs_aops; + break; +#endif + default: + inode->i_data.a_ops = &isofs_aops; + break; + } + } else if (S_ISDIR(inode->i_mode)) { + inode->i_op = &isofs_dir_inode_operations; + inode->i_fop = &isofs_dir_operations; + } else if (S_ISLNK(inode->i_mode)) { + inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); + inode->i_data.a_ops = &isofs_symlink_aops; + } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || + S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { + /* XXX - parse_rock_ridge_inode() had already set i_rdev. */ + init_special_inode(inode, inode->i_mode, inode->i_rdev); + } else { + printk(KERN_DEBUG "ISOFS: Invalid file type 0%04o for inode %lu.\n", + inode->i_mode, inode->i_ino); + ret = -EIO; + goto fail; + } + + ret = 0; +out: + kfree(tmpde); + brelse(bh); + return ret; + +out_badread: + printk(KERN_WARNING "ISOFS: unable to read i-node block\n"); +fail: + goto out; +} + +struct isofs_iget5_callback_data { + unsigned long block; + unsigned long offset; +}; + +static int isofs_iget5_test(struct inode *ino, void *data) +{ + struct iso_inode_info *i = ISOFS_I(ino); + struct isofs_iget5_callback_data *d = + (struct isofs_iget5_callback_data*)data; + return (i->i_iget5_block == d->block) + && (i->i_iget5_offset == d->offset); +} + +static int isofs_iget5_set(struct inode *ino, void *data) +{ + struct iso_inode_info *i = ISOFS_I(ino); + struct isofs_iget5_callback_data *d = + (struct isofs_iget5_callback_data*)data; + i->i_iget5_block = d->block; + i->i_iget5_offset = d->offset; + return 0; +} + +/* Store, in the inode's containing structure, the block and block + * offset that point to the underlying meta-data for the inode. The + * code below is otherwise similar to the iget() code in + * include/linux/fs.h */ +struct inode *__isofs_iget(struct super_block *sb, + unsigned long block, + unsigned long offset, + int relocated) +{ + unsigned long hashval; + struct inode *inode; + struct isofs_iget5_callback_data data; + long ret; + + if (offset >= 1ul << sb->s_blocksize_bits) + return ERR_PTR(-EINVAL); + + data.block = block; + data.offset = offset; + + hashval = (block << sb->s_blocksize_bits) | offset; + + inode = iget5_locked(sb, hashval, &isofs_iget5_test, + &isofs_iget5_set, &data); + + if (!inode) + return ERR_PTR(-ENOMEM); + + if (inode_state_read_once(inode) & I_NEW) { + ret = isofs_read_inode(inode, relocated); + if (ret < 0) { + iget_failed(inode); + inode = ERR_PTR(ret); + } else { + unlock_new_inode(inode); + } + } + + return inode; +} + +static int isofs_get_tree(struct fs_context *fc) +{ + return get_tree_bdev(fc, isofs_fill_super); +} + +static void isofs_free_fc(struct fs_context *fc) +{ + struct isofs_options *opt = fc->fs_private; + + kfree(opt->iocharset); + kfree(opt); +} + +static const struct fs_context_operations isofs_context_ops = { + .parse_param = isofs_parse_param, + .get_tree = isofs_get_tree, + .reconfigure = isofs_reconfigure, + .free = isofs_free_fc, +}; + +static int isofs_init_fs_context(struct fs_context *fc) +{ + struct isofs_options *opt; + + opt = kzalloc_obj(*opt); + if (!opt) + return -ENOMEM; + + opt->map = 'n'; + opt->rock = 1; + opt->joliet = 1; + opt->cruft = 0; + opt->hide = 0; + opt->showassoc = 0; + opt->check = 'u'; /* unset */ + opt->nocompress = 0; + opt->blocksize = 1024; + opt->fmode = opt->dmode = ISOFS_INVALID_MODE; + opt->uid_set = 0; + opt->gid_set = 0; + opt->gid = GLOBAL_ROOT_GID; + opt->uid = GLOBAL_ROOT_UID; + opt->iocharset = NULL; + opt->overriderockperm = 0; + opt->session = -1; + opt->sbsector = -1; + + fc->fs_private = opt; + fc->ops = &isofs_context_ops; + + return 0; +} + +static struct file_system_type iso9660_fs_type = { + .owner = THIS_MODULE, + .name = "iso9660", + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, + .init_fs_context = isofs_init_fs_context, + .parameters = isofs_param_spec, +}; +MODULE_ALIAS_FS("iso9660"); +MODULE_ALIAS("iso9660"); + +static int __init init_iso9660_fs(void) +{ + int err = init_inodecache(); + if (err) + goto out; +#ifdef CONFIG_ZISOFS + err = zisofs_init(); + if (err) + goto out1; +#endif + err = register_filesystem(&iso9660_fs_type); + if (err) + goto out2; + return 0; +out2: +#ifdef CONFIG_ZISOFS + zisofs_cleanup(); +out1: +#endif + destroy_inodecache(); +out: + return err; +} + +static void __exit exit_iso9660_fs(void) +{ + unregister_filesystem(&iso9660_fs_type); +#ifdef CONFIG_ZISOFS + zisofs_cleanup(); +#endif + destroy_inodecache(); +} + +module_init(init_iso9660_fs) +module_exit(exit_iso9660_fs) +MODULE_DESCRIPTION("ISO 9660 CDROM file system support"); +MODULE_LICENSE("GPL"); diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h new file mode 100644 index 00000000000..50655583753 --- /dev/null +++ b/fs/isofs/isofs.h @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include +#include +#include +#include + +enum isofs_file_format { + isofs_file_normal = 0, + isofs_file_sparse = 1, + isofs_file_compressed = 2, +}; + +/* + * iso fs inode data in memory + */ +struct iso_inode_info { + unsigned long i_iget5_block; + unsigned long i_iget5_offset; + unsigned int i_first_extent; + unsigned char i_file_format; + unsigned char i_format_parm[3]; + unsigned long i_next_section_block; + unsigned long i_next_section_offset; + off_t i_section_size; + struct inode vfs_inode; +}; + +/* + * iso9660 super-block data in memory + */ +struct isofs_sb_info { + unsigned long s_ninodes; + unsigned long s_nzones; + unsigned long s_firstdatazone; + unsigned long s_log_zone_size; + unsigned long s_max_size; + + int s_rock_offset; /* offset of SUSP fields within SU area */ + s32 s_sbsector; + unsigned char s_joliet_level; + unsigned char s_mapping; + unsigned char s_check; + unsigned char s_session; + unsigned int s_high_sierra:1; + unsigned int s_rock:2; + unsigned int s_cruft:1; /* Broken disks with high byte of length + * containing junk */ + unsigned int s_nocompress:1; + unsigned int s_hide:1; + unsigned int s_showassoc:1; + unsigned int s_overriderockperm:1; + unsigned int s_uid_set:1; + unsigned int s_gid_set:1; + + umode_t s_fmode; + umode_t s_dmode; + kgid_t s_gid; + kuid_t s_uid; + struct nls_table *s_nls_iocharset; /* Native language support table */ +}; + +#define ISOFS_INVALID_MODE ((umode_t) -1) + +static inline struct isofs_sb_info *ISOFS_SB(struct super_block *sb) +{ + return sb->s_fs_info; +} + +static inline struct iso_inode_info *ISOFS_I(struct inode *inode) +{ + return container_of(inode, struct iso_inode_info, vfs_inode); +} + +static inline int isonum_711(u8 *p) +{ + return *p; +} +static inline int isonum_712(s8 *p) +{ + return *p; +} +static inline unsigned int isonum_721(u8 *p) +{ + return get_unaligned_le16(p); +} +static inline unsigned int isonum_722(u8 *p) +{ + return get_unaligned_be16(p); +} +static inline unsigned int isonum_723(u8 *p) +{ + /* Ignore bigendian datum due to broken mastering programs */ + return get_unaligned_le16(p); +} +static inline unsigned int isonum_731(u8 *p) +{ + return get_unaligned_le32(p); +} +static inline unsigned int isonum_732(u8 *p) +{ + return get_unaligned_be32(p); +} +static inline unsigned int isonum_733(u8 *p) +{ + /* Ignore bigendian datum due to broken mastering programs */ + return get_unaligned_le32(p); +} +#define ISO_DATE_HIGH_SIERRA (1 << 0) +#define ISO_DATE_LONG_FORM (1 << 1) +struct timespec64 iso_date(u8 *p, int flags); + +struct inode; /* To make gcc happy */ + +extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *, int relocated); +extern int get_rock_ridge_filename(struct iso_directory_record *, char *, struct inode *); +extern int isofs_name_translate(struct iso_directory_record *, char *, struct inode *); + +int get_joliet_filename(struct iso_directory_record *, unsigned char *, struct inode *); +int get_acorn_filename(struct iso_directory_record *, char *, struct inode *); + +extern struct dentry *isofs_lookup(struct inode *, struct dentry *, unsigned int flags); +extern struct buffer_head *isofs_bread(struct inode *, sector_t); +extern int isofs_get_blocks(struct inode *, sector_t, struct buffer_head **, unsigned long); + +struct inode *__isofs_iget(struct super_block *sb, + unsigned long block, + unsigned long offset, + int relocated); + +static inline struct inode *isofs_iget(struct super_block *sb, + unsigned long block, + unsigned long offset) +{ + return __isofs_iget(sb, block, offset, 0); +} + +static inline struct inode *isofs_iget_reloc(struct super_block *sb, + unsigned long block, + unsigned long offset) +{ + return __isofs_iget(sb, block, offset, 1); +} + +/* Because the inode number is no longer relevant to finding the + * underlying meta-data for an inode, we are free to choose a more + * convenient 32-bit number as the inode number. The inode numbering + * scheme was recommended by Sergey Vlasov and Eric Lammerts. */ +static inline unsigned long isofs_get_ino(unsigned long block, + unsigned long offset, + unsigned long bufbits) +{ + return (block << (bufbits - 5)) | (offset >> 5); +} + +/* Every directory can have many redundant directory entries scattered + * throughout the directory tree. First there is the directory entry + * with the name of the directory stored in the parent directory. + * Then, there is the "." directory entry stored in the directory + * itself. Finally, there are possibly many ".." directory entries + * stored in all the subdirectories. + * + * In order for the NFS get_parent() method to work and for the + * general consistency of the dcache, we need to make sure the + * "i_iget5_block" and "i_iget5_offset" all point to exactly one of + * the many redundant entries for each directory. We normalize the + * block and offset by always making them point to the "." directory. + * + * Notice that we do not use the entry for the directory with the name + * that is located in the parent directory. Even though choosing this + * first directory is more natural, it is much easier to find the "." + * entry in the NFS get_parent() method because it is implicitly + * encoded in the "extent + ext_attr_length" fields of _all_ the + * redundant entries for the directory. Thus, it can always be + * reached regardless of which directory entry you have in hand. + * + * This works because the "." entry is simply the first directory + * record when you start reading the file that holds all the directory + * records, and this file starts at "extent + ext_attr_length" blocks. + * Because the "." entry is always the first entry listed in the + * directories file, the normalized "offset" value is always 0. + * + * You should pass the directory entry in "de". On return, "block" + * and "offset" will hold normalized values. Only directories are + * affected making it safe to call even for non-directory file + * types. */ +static inline void +isofs_normalize_block_and_offset(struct iso_directory_record* de, + unsigned long *block, + unsigned long *offset) +{ + /* Only directories are normalized. */ + if (de->flags[0] & 2) { + *offset = 0; + *block = (unsigned long)isonum_733(de->extent) + + (unsigned long)isonum_711(de->ext_attr_length); + } +} + +extern const struct inode_operations isofs_dir_inode_operations; +extern const struct file_operations isofs_dir_operations; +extern const struct address_space_operations isofs_symlink_aops; +extern const struct export_operations isofs_export_ops; diff --git a/fs/isofs/joliet.c b/fs/isofs/joliet.c new file mode 100644 index 00000000000..c0f04a1e7f6 --- /dev/null +++ b/fs/isofs/joliet.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/isofs/joliet.c + * + * (C) 1996 Gordon Chaffee + * + * Joliet: Microsoft's Unicode extensions to iso9660 + */ + +#include +#include +#include "isofs.h" + +/* + * Convert Unicode 16 to UTF-8 or ASCII. + */ +static int +uni16_to_x8(unsigned char *ascii, __be16 *uni, int len, struct nls_table *nls) +{ + __be16 *ip, ch; + unsigned char *op; + + ip = uni; + op = ascii; + + while ((ch = get_unaligned(ip)) && len) { + int llen; + llen = nls->uni2char(be16_to_cpu(ch), op, NLS_MAX_CHARSET_SIZE); + if (llen > 0) + op += llen; + else + *op++ = '?'; + ip++; + + len--; + } + *op = 0; + return (op - ascii); +} + +int +get_joliet_filename(struct iso_directory_record * de, unsigned char *outname, struct inode * inode) +{ + struct nls_table *nls; + unsigned char len = 0; + + nls = ISOFS_SB(inode->i_sb)->s_nls_iocharset; + + if (!nls) { + len = utf16s_to_utf8s((const wchar_t *) de->name, + de->name_len[0] >> 1, UTF16_BIG_ENDIAN, + outname, PAGE_SIZE); + } else { + len = uni16_to_x8(outname, (__be16 *) de->name, + de->name_len[0] >> 1, nls); + } + if ((len > 2) && (outname[len-2] == ';') && (outname[len-1] == '1')) + len -= 2; + + /* + * Windows doesn't like periods at the end of a name, + * so neither do we + */ + while (len >= 2 && (outname[len-1] == '.')) + len--; + + return len; +} diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c new file mode 100644 index 00000000000..58f80e1b3ac --- /dev/null +++ b/fs/isofs/namei.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/isofs/namei.c + * + * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem. + * + * (C) 1991 Linus Torvalds - minix filesystem + */ + +#include +#include "isofs.h" + +/* + * ok, we cannot use strncmp, as the name is not in our data space. + * Thus we'll have to use isofs_match. No big problem. Match also makes + * some sanity tests. + */ +static int +isofs_cmp(struct dentry *dentry, const char *compare, int dlen) +{ + struct qstr qstr; + qstr.name = compare; + qstr.len = dlen; + if (likely(!dentry->d_op)) + return dentry->d_name.len != dlen || memcmp(dentry->d_name.name, compare, dlen); + return dentry->d_op->d_compare(NULL, dentry->d_name.len, dentry->d_name.name, &qstr); +} + +/* + * isofs_find_entry() + * + * finds an entry in the specified directory with the wanted name. It + * returns the inode number of the found entry, or 0 on error. + */ +static unsigned long +isofs_find_entry(struct inode *dir, struct dentry *dentry, + unsigned long *block_rv, unsigned long *offset_rv, + char *tmpname, struct iso_directory_record *tmpde) +{ + unsigned long bufsize = ISOFS_BUFFER_SIZE(dir); + unsigned char bufbits = ISOFS_BUFFER_BITS(dir); + unsigned long block, f_pos, offset, block_saved, offset_saved; + struct buffer_head *bh = NULL; + struct isofs_sb_info *sbi = ISOFS_SB(dir->i_sb); + + if (!ISOFS_I(dir)->i_first_extent) + return 0; + + f_pos = 0; + offset = 0; + block = 0; + + while (f_pos < dir->i_size) { + struct iso_directory_record *de; + int de_len, match, i, dlen; + char *dpnt; + + if (!bh) { + bh = isofs_bread(dir, block); + if (!bh) + return 0; + } + + de = (struct iso_directory_record *) (bh->b_data + offset); + + de_len = *(unsigned char *) de; + if (!de_len) { + brelse(bh); + bh = NULL; + f_pos = (f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1); + block = f_pos >> bufbits; + offset = 0; + continue; + } + + block_saved = bh->b_blocknr; + offset_saved = offset; + offset += de_len; + f_pos += de_len; + + /* Make sure we have a full directory entry */ + if (offset >= bufsize) { + int slop = bufsize - offset + de_len; + memcpy(tmpde, de, slop); + offset &= bufsize - 1; + block++; + brelse(bh); + bh = NULL; + if (offset) { + bh = isofs_bread(dir, block); + if (!bh) + return 0; + memcpy((void *) tmpde + slop, bh->b_data, offset); + } + de = tmpde; + } + + dlen = de->name_len[0]; + dpnt = de->name; + /* Basic sanity check, whether name doesn't exceed dir entry */ + if (de_len < dlen + sizeof(struct iso_directory_record)) { + printk(KERN_NOTICE "iso9660: Corrupted directory entry" + " in block %lu of inode %lu\n", block, + dir->i_ino); + brelse(bh); + return 0; + } + + if (sbi->s_rock && + ((i = get_rock_ridge_filename(de, tmpname, dir)))) { + dlen = i; /* possibly -1 */ + dpnt = tmpname; +#ifdef CONFIG_JOLIET + } else if (sbi->s_joliet_level) { + dlen = get_joliet_filename(de, tmpname, dir); + dpnt = tmpname; +#endif + } else if (sbi->s_mapping == 'a') { + dlen = get_acorn_filename(de, tmpname, dir); + dpnt = tmpname; + } else if (sbi->s_mapping == 'n') { + dlen = isofs_name_translate(de, tmpname, dir); + dpnt = tmpname; + } + + /* + * Skip hidden or associated files unless hide or showassoc, + * respectively, is set + */ + match = 0; + if (dlen > 0 && + (!sbi->s_hide || + (!(de->flags[-sbi->s_high_sierra] & 1))) && + (sbi->s_showassoc || + (!(de->flags[-sbi->s_high_sierra] & 4)))) { + if (dpnt && (dlen > 1 || dpnt[0] > 1)) + match = (isofs_cmp(dentry, dpnt, dlen) == 0); + } + if (match) { + isofs_normalize_block_and_offset(de, + &block_saved, + &offset_saved); + *block_rv = block_saved; + *offset_rv = offset_saved; + brelse(bh); + return 1; + } + } + brelse(bh); + return 0; +} + +struct dentry *isofs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) +{ + int found; + unsigned long block; + unsigned long offset; + struct inode *inode; + struct page *page; + + page = alloc_page(GFP_USER); + if (!page) + return ERR_PTR(-ENOMEM); + + found = isofs_find_entry(dir, dentry, + &block, &offset, + page_address(page), + 1024 + page_address(page)); + __free_page(page); + + inode = found ? isofs_iget(dir->i_sb, block, offset) : NULL; + + return d_splice_alias(inode, dentry); +} diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c new file mode 100644 index 00000000000..6fe6dbd0c74 --- /dev/null +++ b/fs/isofs/rock.c @@ -0,0 +1,810 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/isofs/rock.c + * + * (C) 1992, 1993 Eric Youngdale + * + * Rock Ridge Extensions to iso9660 + */ + +#include +#include + +#include "isofs.h" +#include "rock.h" + +/* + * These functions are designed to read the system areas of a directory record + * and extract relevant information. There are different functions provided + * depending upon what information we need at the time. One function fills + * out an inode structure, a second one extracts a filename, a third one + * returns a symbolic link name, and a fourth one returns the extent number + * for the file. + */ + +#define SIG(A,B) ((A) | ((B) << 8)) /* isonum_721() */ + +struct rock_state { + void *buffer; + unsigned char *chr; + int len; + int cont_size; + int cont_extent; + int cont_offset; + int cont_loops; + struct inode *inode; +}; + +/* + * This is a way of ensuring that we have something in the system + * use fields that is compatible with Rock Ridge. Return zero on success. + */ + +static int check_sp(struct rock_ridge *rr, struct inode *inode) +{ + if (rr->u.SP.magic[0] != 0xbe) + return -1; + if (rr->u.SP.magic[1] != 0xef) + return -1; + ISOFS_SB(inode->i_sb)->s_rock_offset = rr->u.SP.skip; + return 0; +} + +static void setup_rock_ridge(struct iso_directory_record *de, + struct inode *inode, struct rock_state *rs) +{ + rs->len = sizeof(struct iso_directory_record) + de->name_len[0]; + if (rs->len & 1) + (rs->len)++; + rs->chr = (unsigned char *)de + rs->len; + rs->len = *((unsigned char *)de) - rs->len; + if (rs->len < 0) + rs->len = 0; + + if (ISOFS_SB(inode->i_sb)->s_rock_offset != -1) { + rs->len -= ISOFS_SB(inode->i_sb)->s_rock_offset; + rs->chr += ISOFS_SB(inode->i_sb)->s_rock_offset; + if (rs->len < 0) + rs->len = 0; + } +} + +static void init_rock_state(struct rock_state *rs, struct inode *inode) +{ + memset(rs, 0, sizeof(*rs)); + rs->inode = inode; +} + +/* Maximum number of Rock Ridge continuation entries */ +#define RR_MAX_CE_ENTRIES 32 + +/* + * Returns 0 if the caller should continue scanning, 1 if the scan must end + * and -ve on error. + */ +static int rock_continue(struct rock_state *rs) +{ + int ret = 1; + int blocksize = 1 << rs->inode->i_blkbits; + const int min_de_size = offsetof(struct rock_ridge, u); + + kfree(rs->buffer); + rs->buffer = NULL; + + if ((unsigned)rs->cont_offset > blocksize - min_de_size || + (unsigned)rs->cont_size > blocksize || + (unsigned)(rs->cont_offset + rs->cont_size) > blocksize) { + printk(KERN_NOTICE "rock: corrupted directory entry. " + "extent=%d, offset=%d, size=%d\n", + rs->cont_extent, rs->cont_offset, rs->cont_size); + ret = -EIO; + goto out; + } + + if (rs->cont_extent) { + struct buffer_head *bh; + + rs->buffer = kmalloc(rs->cont_size, GFP_KERNEL); + if (!rs->buffer) { + ret = -ENOMEM; + goto out; + } + ret = -EIO; + if (++rs->cont_loops >= RR_MAX_CE_ENTRIES) + goto out; + bh = sb_bread(rs->inode->i_sb, rs->cont_extent); + if (bh) { + memcpy(rs->buffer, bh->b_data + rs->cont_offset, + rs->cont_size); + put_bh(bh); + rs->chr = rs->buffer; + rs->len = rs->cont_size; + rs->cont_extent = 0; + rs->cont_size = 0; + rs->cont_offset = 0; + return 0; + } + printk("Unable to read rock-ridge attributes\n"); + } +out: + kfree(rs->buffer); + rs->buffer = NULL; + return ret; +} + +/* + * We think there's a record of type `sig' at rs->chr. Parse the signature + * and make sure that there's really room for a record of that type. + */ +static int rock_check_overflow(struct rock_state *rs, int sig) +{ + int len; + + switch (sig) { + case SIG('S', 'P'): + len = sizeof(struct SU_SP_s); + break; + case SIG('C', 'E'): + len = sizeof(struct SU_CE_s); + break; + case SIG('E', 'R'): + len = sizeof(struct SU_ER_s); + break; + case SIG('R', 'R'): + len = sizeof(struct RR_RR_s); + break; + case SIG('P', 'X'): + len = sizeof(struct RR_PX_s); + break; + case SIG('P', 'N'): + len = sizeof(struct RR_PN_s); + break; + case SIG('S', 'L'): + len = sizeof(struct RR_SL_s); + break; + case SIG('N', 'M'): + len = sizeof(struct RR_NM_s); + break; + case SIG('C', 'L'): + len = sizeof(struct RR_CL_s); + break; + case SIG('P', 'L'): + len = sizeof(struct RR_PL_s); + break; + case SIG('T', 'F'): + len = sizeof(struct RR_TF_s); + break; + case SIG('Z', 'F'): + len = sizeof(struct RR_ZF_s); + break; + default: + len = 0; + break; + } + len += offsetof(struct rock_ridge, u); + if (len > rs->len) { + printk(KERN_NOTICE "rock: directory entry would overflow " + "storage\n"); + printk(KERN_NOTICE "rock: sig=0x%02x, size=%d, remaining=%d\n", + sig, len, rs->len); + return -EIO; + } + return 0; +} + +/* + * return length of name field; 0: not found, -1: to be ignored + */ +int get_rock_ridge_filename(struct iso_directory_record *de, + char *retname, struct inode *inode) +{ + struct rock_state rs; + struct rock_ridge *rr; + int sig; + int retnamlen = 0; + int truncate = 0; + int ret = 0; + char *p; + int len; + + if (!ISOFS_SB(inode->i_sb)->s_rock) + return 0; + *retname = 0; + + init_rock_state(&rs, inode); + setup_rock_ridge(de, inode, &rs); +repeat: + + while (rs.len > 2) { /* There may be one byte for padding somewhere */ + rr = (struct rock_ridge *)rs.chr; + /* + * Ignore rock ridge info if rr->len is out of range, but + * don't return -EIO because that would make the file + * invisible. + */ + if (rr->len < 3) + goto out; /* Something got screwed up here */ + sig = isonum_721(rs.chr); + if (rock_check_overflow(&rs, sig)) + goto eio; + rs.chr += rr->len; + rs.len -= rr->len; + /* + * As above, just ignore the rock ridge info if rr->len + * is bogus. + */ + if (rs.len < 0) + goto out; /* Something got screwed up here */ + + switch (sig) { + case SIG('R', 'R'): + if ((rr->u.RR.flags[0] & RR_NM) == 0) + goto out; + break; + case SIG('S', 'P'): + if (check_sp(rr, inode)) + goto out; + break; + case SIG('C', 'E'): + rs.cont_extent = isonum_733(rr->u.CE.extent); + rs.cont_offset = isonum_733(rr->u.CE.offset); + rs.cont_size = isonum_733(rr->u.CE.size); + break; + case SIG('N', 'M'): + if (truncate) + break; + if (rr->len < 5) + break; + /* + * If the flags are 2 or 4, this indicates '.' or '..'. + * We don't want to do anything with this, because it + * screws up the code that calls us. We don't really + * care anyways, since we can just use the non-RR + * name. + */ + if (rr->u.NM.flags & 6) + break; + + if (rr->u.NM.flags & ~1) { + printk("Unsupported NM flag settings (%d)\n", + rr->u.NM.flags); + break; + } + len = rr->len - 5; + if (retnamlen + len > NAME_MAX) { + truncate = 1; + break; + } + p = memchr(rr->u.NM.name, '\0', len); + if (unlikely(p)) + len = p - rr->u.NM.name; + memcpy(retname + retnamlen, rr->u.NM.name, len); + retnamlen += len; + retname[retnamlen] = '\0'; + break; + case SIG('R', 'E'): + kfree(rs.buffer); + return -1; + default: + break; + } + } + ret = rock_continue(&rs); + if (ret == 0) + goto repeat; + if (ret == 1) + return retnamlen; /* If 0, this file did not have a NM field */ +out: + kfree(rs.buffer); + return ret; +eio: + ret = -EIO; + goto out; +} + +#define RR_REGARD_XA 1 +#define RR_RELOC_DE 2 + +static int +parse_rock_ridge_inode_internal(struct iso_directory_record *de, + struct inode *inode, int flags) +{ + int symlink_len = 0; + int cnt, sig; + unsigned int reloc_block; + struct inode *reloc; + struct rock_ridge *rr; + int rootflag; + struct rock_state rs; + int ret = 0; + + if (!ISOFS_SB(inode->i_sb)->s_rock) + return 0; + + init_rock_state(&rs, inode); + setup_rock_ridge(de, inode, &rs); + if (flags & RR_REGARD_XA) { + rs.chr += 14; + rs.len -= 14; + if (rs.len < 0) + rs.len = 0; + } + +repeat: + while (rs.len > 2) { /* There may be one byte for padding somewhere */ + rr = (struct rock_ridge *)rs.chr; + /* + * Ignore rock ridge info if rr->len is out of range, but + * don't return -EIO because that would make the file + * invisible. + */ + if (rr->len < 3) + goto out; /* Something got screwed up here */ + sig = isonum_721(rs.chr); + if (rock_check_overflow(&rs, sig)) + goto eio; + rs.chr += rr->len; + rs.len -= rr->len; + /* + * As above, just ignore the rock ridge info if rr->len + * is bogus. + */ + if (rs.len < 0) + goto out; /* Something got screwed up here */ + + switch (sig) { +#ifndef CONFIG_ZISOFS /* No flag for SF or ZF */ + case SIG('R', 'R'): + if ((rr->u.RR.flags[0] & + (RR_PX | RR_TF | RR_SL | RR_CL)) == 0) + goto out; + break; +#endif + case SIG('S', 'P'): + if (check_sp(rr, inode)) + goto out; + break; + case SIG('C', 'E'): + rs.cont_extent = isonum_733(rr->u.CE.extent); + rs.cont_offset = isonum_733(rr->u.CE.offset); + rs.cont_size = isonum_733(rr->u.CE.size); + break; + case SIG('E', 'R'): + /* Invalid length of ER tag id? */ + if (rr->u.ER.len_id + offsetof(struct rock_ridge, u.ER.data) > rr->len) + goto out; + ISOFS_SB(inode->i_sb)->s_rock = 1; + printk(KERN_DEBUG "ISO 9660 Extensions: "); + { + int p; + for (p = 0; p < rr->u.ER.len_id; p++) + printk(KERN_CONT "%c", rr->u.ER.data[p]); + } + printk(KERN_CONT "\n"); + break; + case SIG('P', 'X'): + inode->i_mode = isonum_733(rr->u.PX.mode); + set_nlink(inode, isonum_733(rr->u.PX.n_links)); + i_uid_write(inode, isonum_733(rr->u.PX.uid)); + i_gid_write(inode, isonum_733(rr->u.PX.gid)); + break; + case SIG('P', 'N'): + { + int high, low; + high = isonum_733(rr->u.PN.dev_high); + low = isonum_733(rr->u.PN.dev_low); + /* + * The Rock Ridge standard specifies that if + * sizeof(dev_t) <= 4, then the high field is + * unused, and the device number is completely + * stored in the low field. Some writers may + * ignore this subtlety, + * and as a result we test to see if the entire + * device number is + * stored in the low field, and use that. + */ + if ((low & ~0xff) && high == 0) { + inode->i_rdev = + MKDEV(low >> 8, low & 0xff); + } else { + inode->i_rdev = + MKDEV(high, low); + } + } + break; + case SIG('T', 'F'): { + int flags, size, slen; + + flags = rr->u.TF.flags & TF_LONG_FORM ? ISO_DATE_LONG_FORM : 0; + size = rr->u.TF.flags & TF_LONG_FORM ? 17 : 7; + slen = rr->len - 5; + /* + * Some RRIP writers incorrectly place ctime in the + * TF_CREATE field. Try to handle this correctly for + * either case. + */ + /* Rock ridge never appears on a High Sierra disk */ + cnt = 0; + if ((rr->u.TF.flags & TF_CREATE) && size <= slen) { + inode_set_ctime_to_ts(inode, + iso_date(rr->u.TF.data + size * cnt++, flags)); + slen -= size; + } + if ((rr->u.TF.flags & TF_MODIFY) && size <= slen) { + inode_set_mtime_to_ts(inode, + iso_date(rr->u.TF.data + size * cnt++, flags)); + slen -= size; + } + if ((rr->u.TF.flags & TF_ACCESS) && size <= slen) { + inode_set_atime_to_ts(inode, + iso_date(rr->u.TF.data + size * cnt++, flags)); + slen -= size; + } + if ((rr->u.TF.flags & TF_ATTRIBUTES) && size <= slen) { + inode_set_ctime_to_ts(inode, + iso_date(rr->u.TF.data + size * cnt++, flags)); + slen -= size; + } + break; + } + case SIG('S', 'L'): + { + int slen; + struct SL_component *slp; + struct SL_component *oldslp; + slen = rr->len - 5; + slp = &rr->u.SL.link; + inode->i_size = symlink_len; + while (slen > 1) { + rootflag = 0; + switch (slp->flags & ~1) { + case 0: + inode->i_size += + slp->len; + break; + case 2: + inode->i_size += 1; + break; + case 4: + inode->i_size += 2; + break; + case 8: + rootflag = 1; + inode->i_size += 1; + break; + default: + printk("Symlink component flag " + "not implemented\n"); + } + slen -= slp->len + 2; + oldslp = slp; + slp = (struct SL_component *) + (((char *)slp) + slp->len + 2); + + if (slen < 2) { + if (((rr->u.SL. + flags & 1) != 0) + && + ((oldslp-> + flags & 1) == 0)) + inode->i_size += + 1; + break; + } + + /* + * If this component record isn't + * continued, then append a '/'. + */ + if (!rootflag + && (oldslp->flags & 1) == 0) + inode->i_size += 1; + } + } + symlink_len = inode->i_size; + break; + case SIG('R', 'E'): + printk(KERN_WARNING "Attempt to read inode for " + "relocated directory\n"); + goto out; + case SIG('C', 'L'): + if (flags & RR_RELOC_DE) { + printk(KERN_ERR + "ISOFS: Recursive directory relocation " + "is not supported\n"); + goto eio; + } + reloc_block = isonum_733(rr->u.CL.location); + if (reloc_block == ISOFS_I(inode)->i_iget5_block && + ISOFS_I(inode)->i_iget5_offset == 0) { + printk(KERN_ERR + "ISOFS: Directory relocation points to " + "itself\n"); + goto eio; + } + ISOFS_I(inode)->i_first_extent = reloc_block; + reloc = isofs_iget_reloc(inode->i_sb, reloc_block, 0); + if (IS_ERR(reloc)) { + ret = PTR_ERR(reloc); + goto out; + } + inode->i_mode = reloc->i_mode; + set_nlink(inode, reloc->i_nlink); + inode->i_uid = reloc->i_uid; + inode->i_gid = reloc->i_gid; + inode->i_rdev = reloc->i_rdev; + inode->i_size = reloc->i_size; + inode->i_blocks = reloc->i_blocks; + inode_set_atime_to_ts(inode, inode_get_atime(reloc)); + inode_set_ctime_to_ts(inode, inode_get_ctime(reloc)); + inode_set_mtime_to_ts(inode, inode_get_mtime(reloc)); + iput(reloc); + break; +#ifdef CONFIG_ZISOFS + case SIG('Z', 'F'): { + int algo; + + if (ISOFS_SB(inode->i_sb)->s_nocompress) + break; + algo = isonum_721(rr->u.ZF.algorithm); + if (algo == SIG('p', 'z')) { + int block_shift = + isonum_711(&rr->u.ZF.parms[1]); + if (block_shift > 17) { + printk(KERN_WARNING "isofs: " + "Can't handle ZF block " + "size of 2^%d\n", + block_shift); + } else { + /* + * Note: we don't change + * i_blocks here + */ + ISOFS_I(inode)->i_file_format = + isofs_file_compressed; + /* + * Parameters to compression + * algorithm (header size, + * block size) + */ + ISOFS_I(inode)->i_format_parm[0] = + isonum_711(&rr->u.ZF.parms[0]); + ISOFS_I(inode)->i_format_parm[1] = + isonum_711(&rr->u.ZF.parms[1]); + inode->i_size = + isonum_733(rr->u.ZF. + real_size); + } + } else { + printk(KERN_WARNING + "isofs: Unknown ZF compression " + "algorithm: %c%c\n", + rr->u.ZF.algorithm[0], + rr->u.ZF.algorithm[1]); + } + break; + } +#endif + default: + break; + } + } + ret = rock_continue(&rs); + if (ret == 0) + goto repeat; + if (ret == 1) + ret = 0; +out: + kfree(rs.buffer); + return ret; +eio: + ret = -EIO; + goto out; +} + +static char *get_symlink_chunk(char *rpnt, struct rock_ridge *rr, char *plimit) +{ + int slen; + int rootflag; + struct SL_component *oldslp; + struct SL_component *slp; + slen = rr->len - 5; + slp = &rr->u.SL.link; + while (slen > 1) { + rootflag = 0; + switch (slp->flags & ~1) { + case 0: + if (slp->len > plimit - rpnt) + return NULL; + memcpy(rpnt, slp->text, slp->len); + rpnt += slp->len; + break; + case 2: + if (rpnt >= plimit) + return NULL; + *rpnt++ = '.'; + break; + case 4: + if (2 > plimit - rpnt) + return NULL; + *rpnt++ = '.'; + *rpnt++ = '.'; + break; + case 8: + if (rpnt >= plimit) + return NULL; + rootflag = 1; + *rpnt++ = '/'; + break; + default: + printk("Symlink component flag not implemented (%d)\n", + slp->flags); + } + slen -= slp->len + 2; + oldslp = slp; + slp = (struct SL_component *)((char *)slp + slp->len + 2); + + if (slen < 2) { + /* + * If there is another SL record, and this component + * record isn't continued, then add a slash. + */ + if ((!rootflag) && (rr->u.SL.flags & 1) && + !(oldslp->flags & 1)) { + if (rpnt >= plimit) + return NULL; + *rpnt++ = '/'; + } + break; + } + + /* + * If this component record isn't continued, then append a '/'. + */ + if (!rootflag && !(oldslp->flags & 1)) { + if (rpnt >= plimit) + return NULL; + *rpnt++ = '/'; + } + } + return rpnt; +} + +int parse_rock_ridge_inode(struct iso_directory_record *de, struct inode *inode, + int relocated) +{ + int flags = relocated ? RR_RELOC_DE : 0; + int result = parse_rock_ridge_inode_internal(de, inode, flags); + + /* + * if rockridge flag was reset and we didn't look for attributes + * behind eventual XA attributes, have a look there + */ + if ((ISOFS_SB(inode->i_sb)->s_rock_offset == -1) + && (ISOFS_SB(inode->i_sb)->s_rock == 2)) { + result = parse_rock_ridge_inode_internal(de, inode, + flags | RR_REGARD_XA); + } + return result; +} + +/* + * read_folio() for symlinks: reads symlink contents into the folio and either + * makes it uptodate and returns 0 or returns error (-EIO) + */ +static int rock_ridge_symlink_read_folio(struct file *file, struct folio *folio) +{ + struct inode *inode = folio->mapping->host; + struct iso_inode_info *ei = ISOFS_I(inode); + struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb); + char *link = folio_address(folio); + unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); + struct buffer_head *bh; + char *rpnt = link; + unsigned char *pnt; + struct iso_directory_record *raw_de; + unsigned long block, offset; + int sig; + struct rock_ridge *rr; + struct rock_state rs; + int ret; + + if (!sbi->s_rock) + goto error; + + init_rock_state(&rs, inode); + block = ei->i_iget5_block; + bh = sb_bread(inode->i_sb, block); + if (!bh) + goto out_noread; + + offset = ei->i_iget5_offset; + pnt = (unsigned char *)bh->b_data + offset; + + raw_de = (struct iso_directory_record *)pnt; + + /* + * If we go past the end of the buffer, there is some sort of error. + */ + if (offset + *pnt > bufsize) + goto out_bad_span; + + /* + * Now test for possible Rock Ridge extensions which will override + * some of these numbers in the inode structure. + */ + + setup_rock_ridge(raw_de, inode, &rs); + +repeat: + while (rs.len > 2) { /* There may be one byte for padding somewhere */ + rr = (struct rock_ridge *)rs.chr; + if (rr->len < 3) + goto out; /* Something got screwed up here */ + sig = isonum_721(rs.chr); + if (rock_check_overflow(&rs, sig)) + goto out; + rs.chr += rr->len; + rs.len -= rr->len; + if (rs.len < 0) + goto out; /* corrupted isofs */ + + switch (sig) { + case SIG('R', 'R'): + if ((rr->u.RR.flags[0] & RR_SL) == 0) + goto out; + break; + case SIG('S', 'P'): + if (check_sp(rr, inode)) + goto out; + break; + case SIG('S', 'L'): + rpnt = get_symlink_chunk(rpnt, rr, + link + (PAGE_SIZE - 1)); + if (rpnt == NULL) + goto out; + break; + case SIG('C', 'E'): + /* This tells is if there is a continuation record */ + rs.cont_extent = isonum_733(rr->u.CE.extent); + rs.cont_offset = isonum_733(rr->u.CE.offset); + rs.cont_size = isonum_733(rr->u.CE.size); + break; + default: + break; + } + } + ret = rock_continue(&rs); + if (ret == 0) + goto repeat; + if (ret < 0) + goto fail; + + if (rpnt == link) + goto fail; + brelse(bh); + *rpnt = '\0'; + ret = 0; +end: + folio_end_read(folio, ret == 0); + return ret; + + /* error exit from macro */ +out: + kfree(rs.buffer); + goto fail; +out_noread: + printk("unable to read i-node block"); + goto fail; +out_bad_span: + printk("symlink spans iso9660 blocks\n"); +fail: + brelse(bh); +error: + ret = -EIO; + goto end; +} + +const struct address_space_operations isofs_symlink_aops = { + .read_folio = rock_ridge_symlink_read_folio +}; diff --git a/fs/isofs/rock.h b/fs/isofs/rock.h new file mode 100644 index 00000000000..c0856fa9bb6 --- /dev/null +++ b/fs/isofs/rock.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * These structs are used by the system-use-sharing protocol, in which the + * Rock Ridge extensions are embedded. It is quite possible that other + * extensions are present on the disk, and this is fine as long as they + * all use SUSP + */ + +struct SU_SP_s { + __u8 magic[2]; + __u8 skip; +} __attribute__ ((packed)); + +struct SU_CE_s { + __u8 extent[8]; + __u8 offset[8]; + __u8 size[8]; +}; + +struct SU_ER_s { + __u8 len_id; + __u8 len_des; + __u8 len_src; + __u8 ext_ver; + __u8 data[]; +} __attribute__ ((packed)); + +struct RR_RR_s { + __u8 flags[1]; +} __attribute__ ((packed)); + +struct RR_PX_s { + __u8 mode[8]; + __u8 n_links[8]; + __u8 uid[8]; + __u8 gid[8]; +}; + +struct RR_PN_s { + __u8 dev_high[8]; + __u8 dev_low[8]; +}; + +struct SL_component { + __u8 flags; + __u8 len; + __u8 text[] __counted_by(len); +} __attribute__ ((packed)); + +struct RR_SL_s { + __u8 flags; + struct SL_component link; +} __attribute__ ((packed)); + +struct RR_NM_s { + __u8 flags; + char name[]; +} __attribute__ ((packed)); + +struct RR_CL_s { + __u8 location[8]; +}; + +struct RR_PL_s { + __u8 location[8]; +}; + +struct RR_TF_s { + __u8 flags; + __u8 data[]; +} __attribute__ ((packed)); + +/* Linux-specific extension for transparent decompression */ +struct RR_ZF_s { + __u8 algorithm[2]; + __u8 parms[2]; + __u8 real_size[8]; +}; + +/* + * These are the bits and their meanings for flags in the TF structure. + */ +#define TF_CREATE 1 +#define TF_MODIFY 2 +#define TF_ACCESS 4 +#define TF_ATTRIBUTES 8 +#define TF_BACKUP 16 +#define TF_EXPIRATION 32 +#define TF_EFFECTIVE 64 +#define TF_LONG_FORM 128 + +struct rock_ridge { + __u8 signature[2]; + __u8 len; + __u8 version; + union { + struct SU_SP_s SP; + struct SU_CE_s CE; + struct SU_ER_s ER; + struct RR_RR_s RR; + struct RR_PX_s PX; + struct RR_PN_s PN; + struct RR_SL_s SL; + struct RR_NM_s NM; + struct RR_CL_s CL; + struct RR_PL_s PL; + struct RR_TF_s TF; + struct RR_ZF_s ZF; + } u; +}; + +#define RR_PX 1 /* POSIX attributes */ +#define RR_PN 2 /* POSIX devices */ +#define RR_SL 4 /* Symbolic link */ +#define RR_NM 8 /* Alternate Name */ +#define RR_CL 16 /* Child link */ +#define RR_PL 32 /* Parent link */ +#define RR_RE 64 /* Relocation directory */ +#define RR_TF 128 /* Timestamps */ diff --git a/fs/isofs/util.c b/fs/isofs/util.c new file mode 100644 index 00000000000..42f479da0b2 --- /dev/null +++ b/fs/isofs/util.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/isofs/util.c + */ + +#include +#include "isofs.h" + +/* + * We have to convert from a MM/DD/YY format to the Unix ctime format. + * We have to take into account leap years and all of that good stuff. + * Unfortunately, the kernel does not have the information on hand to + * take into account daylight savings time, but it shouldn't matter. + * The time stored should be localtime (with or without DST in effect), + * and the timezone offset should hold the offset required to get back + * to GMT. Thus we should always be correct. + */ + +struct timespec64 iso_date(u8 *p, int flags) +{ + int year, month, day, hour, minute, second, tz; + struct timespec64 ts; + + if (flags & ISO_DATE_LONG_FORM) { + year = (p[0] - '0') * 1000 + + (p[1] - '0') * 100 + + (p[2] - '0') * 10 + + (p[3] - '0') - 1900; + month = ((p[4] - '0') * 10 + (p[5] - '0')); + day = ((p[6] - '0') * 10 + (p[7] - '0')); + hour = ((p[8] - '0') * 10 + (p[9] - '0')); + minute = ((p[10] - '0') * 10 + (p[11] - '0')); + second = ((p[12] - '0') * 10 + (p[13] - '0')); + ts.tv_nsec = ((p[14] - '0') * 10 + (p[15] - '0')) * 10000000; + tz = p[16]; + } else { + year = p[0]; + month = p[1]; + day = p[2]; + hour = p[3]; + minute = p[4]; + second = p[5]; + ts.tv_nsec = 0; + /* High sierra has no time zone */ + tz = flags & ISO_DATE_HIGH_SIERRA ? 0 : p[6]; + } + + if (year < 0) { + ts.tv_sec = 0; + } else { + ts.tv_sec = mktime64(year+1900, month, day, hour, minute, second); + + /* sign extend */ + if (tz & 0x80) + tz |= (-1 << 8); + + /* + * The timezone offset is unreliable on some disks, + * so we make a sanity check. In no case is it ever + * more than 13 hours from GMT, which is 52*15min. + * The time is always stored in localtime with the + * timezone offset being what get added to GMT to + * get to localtime. Thus we need to subtract the offset + * to get to true GMT, which is what we store the time + * as internally. On the local system, the user may set + * their timezone any way they wish, of course, so GMT + * gets converted back to localtime on the receiving + * system. + * + * NOTE: mkisofs in versions prior to mkisofs-1.10 had + * the sign wrong on the timezone offset. This has now + * been corrected there too, but if you are getting screwy + * results this may be the explanation. If enough people + * complain, a user configuration option could be added + * to add the timezone offset in with the wrong sign + * for 'compatibility' with older discs, but I cannot see how + * it will matter that much. + * + * Thanks to kuhlmav@elec.canterbury.ac.nz (Volker Kuhlmann) + * for pointing out the sign error. + */ + if (-52 <= tz && tz <= 52) + ts.tv_sec -= tz * 15 * 60; + } + return ts; +} diff --git a/fs/isofs/zisofs.h b/fs/isofs/zisofs.h new file mode 100644 index 00000000000..70f1a218fc9 --- /dev/null +++ b/fs/isofs/zisofs.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved + * + * ----------------------------------------------------------------------- */ + +/* + * Prototypes for functions exported from the compressed isofs subsystem + */ + +#ifdef CONFIG_ZISOFS +extern const struct address_space_operations zisofs_aops; +extern int __init zisofs_init(void); +extern void zisofs_cleanup(void); +#endif From patchwork Thu Apr 16 16:57:01 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2206 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358756; bh=adqCI4Ehy0d6VxKCcaYc4WO0FygcUyF6X209ESJ9KCA=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=UGt+kKgVFI+Wu7DekS+NeTc/GrW+m5ekvTrH6vwqDILDBMA2zvp6tioObufx/FJtI ctITqQxJd9rgOTUeHio4V2JJmvbh0qcAvcmB8M247UASwX8UniWMZ+9sdRa+vlCm2E 6mx9En2pAJp8HjDdxYsa31vEUnTD1j7dHoD7xQcE= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 5F5E46A4E6 for ; Thu, 16 Apr 2026 10:59:16 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id mPSbk9SJiaoU for ; Thu, 16 Apr 2026 10:59:16 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358756; bh=adqCI4Ehy0d6VxKCcaYc4WO0FygcUyF6X209ESJ9KCA=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=UGt+kKgVFI+Wu7DekS+NeTc/GrW+m5ekvTrH6vwqDILDBMA2zvp6tioObufx/FJtI ctITqQxJd9rgOTUeHio4V2JJmvbh0qcAvcmB8M247UASwX8UniWMZ+9sdRa+vlCm2E 6mx9En2pAJp8HjDdxYsa31vEUnTD1j7dHoD7xQcE= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 4B6756A4DC for ; Thu, 16 Apr 2026 10:59:16 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358754; bh=bmqCRDRW+v6+InnQ57NTidhqn3UdhR6X6ujHrLxvYtY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ihRyaQvpqkSs1crOvyora1ZDDbYr172BWbVKBlbgOrIpvZ+59QD3GNIWvfZtz9iKa CfzLAJyIItTqD4q60I5JjoMTlJ4tql/NYrfWb2Hi2ed/zddAxDXafNMtEbwzUzzvf9 NLTT6NsCLUCqLvOaciPY9WWMnff2ImmLpknuI7Ag= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 91B876A4A5; Thu, 16 Apr 2026 10:59:14 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id mESNg1HNSsNi; Thu, 16 Apr 2026 10:59:14 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358750; bh=k0MCXZKjanhaoY+mLv9p1qRMNHa/5TQs8GNLfwDiNBQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=QZD0mWGAr5qTWZBC75V18fVMlisDre1kf+df/xZAcvVwj8molcKUImB7par1TUD1V f/MZSizzYqNnGaN7axPrvuh7TN6bWFPOGDrbkz8Pei6+JhAmJkdreJxm7pL0eaZc/r 6IY5olbkGbIONsUOclG7R7F5Nm9/eQdFz1UfxwHo= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 3743D6A4DD; Thu, 16 Apr 2026 10:59:10 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:57:01 -0600 Message-ID: <20260416165733.2923423-12-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: GE4GR6RFDYRJ5SUN7USEQL7TGBANCQYM X-Message-ID-Hash: GE4GR6RFDYRJ5SUN7USEQL7TGBANCQYM X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 11/21] isofs: Add include guards and U-Boot compat to headers List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass The isofs internal headers lack include guards, which can cause issues with multiple inclusion. Also, isofs.h references Linux kernel headers that are not available in U-Boot. Add include guards to isofs.h, rock.h, and zisofs.h. Replace the Linux kernel includes in isofs.h with the U-Boot compatibility shim (isofs_uboot.h). Replace zisofs.h with a stub since U-Boot does not support transparent decompression. Fix minor whitespace and formatting issues in isofs.h (trailing whitespace on blank lines, pointer declaration style). Signed-off-by: Simon Glass --- fs/isofs/isofs.h | 21 ++++++++++++++------- fs/isofs/rock.h | 4 ++++ fs/isofs/zisofs.h | 20 ++++++++------------ 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h index 50655583753..66a21648623 100644 --- a/fs/isofs/isofs.h +++ b/fs/isofs/isofs.h @@ -1,16 +1,21 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#include -#include -#include +#ifndef _ISOFS_ISOFS_H +#define _ISOFS_ISOFS_H +/* + * isofs internal header - based on Linux 6.18 + * + * Modifications for U-Boot: + * - Replace includes with isofs_uboot.h + */ +#include "isofs_uboot.h" #include -#include enum isofs_file_format { isofs_file_normal = 0, isofs_file_sparse = 1, isofs_file_compressed = 2, }; - + /* * iso fs inode data in memory */ @@ -35,7 +40,7 @@ struct isofs_sb_info { unsigned long s_firstdatazone; unsigned long s_log_zone_size; unsigned long s_max_size; - + int s_rock_offset; /* offset of SUSP fields within SU area */ s32 s_sbsector; unsigned char s_joliet_level; @@ -185,7 +190,7 @@ static inline unsigned long isofs_get_ino(unsigned long block, * affected making it safe to call even for non-directory file * types. */ static inline void -isofs_normalize_block_and_offset(struct iso_directory_record* de, +isofs_normalize_block_and_offset(struct iso_directory_record *de, unsigned long *block, unsigned long *offset) { @@ -201,3 +206,5 @@ extern const struct inode_operations isofs_dir_inode_operations; extern const struct file_operations isofs_dir_operations; extern const struct address_space_operations isofs_symlink_aops; extern const struct export_operations isofs_export_ops; + +#endif /* _ISOFS_ISOFS_H */ diff --git a/fs/isofs/rock.h b/fs/isofs/rock.h index c0856fa9bb6..18e84bdf081 100644 --- a/fs/isofs/rock.h +++ b/fs/isofs/rock.h @@ -1,4 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ISOFS_ROCK_H +#define _ISOFS_ROCK_H /* * These structs are used by the system-use-sharing protocol, in which the * Rock Ridge extensions are embedded. It is quite possible that other @@ -117,3 +119,5 @@ struct rock_ridge { #define RR_PL 32 /* Parent link */ #define RR_RE 64 /* Relocation directory */ #define RR_TF 128 /* Timestamps */ + +#endif /* _ISOFS_ROCK_H */ diff --git a/fs/isofs/zisofs.h b/fs/isofs/zisofs.h index 70f1a218fc9..65c3c39fa6d 100644 --- a/fs/isofs/zisofs.h +++ b/fs/isofs/zisofs.h @@ -1,16 +1,12 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* ----------------------------------------------------------------------- * - * - * Copyright 2001 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ - +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Prototypes for functions exported from the compressed isofs subsystem + * ZISOFS stub header for U-Boot + * Transparent decompression not supported in U-Boot. */ +#ifndef _ISOFS_ZISOFS_H +#define _ISOFS_ZISOFS_H + +static inline int zisofs_init(void) { return 0; } +static inline void zisofs_cleanup(void) { } -#ifdef CONFIG_ZISOFS -extern const struct address_space_operations zisofs_aops; -extern int __init zisofs_init(void); -extern void zisofs_cleanup(void); #endif From patchwork Thu Apr 16 16:57:02 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2207 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358759; bh=qO9hNyvdMdDXTVRUlFZipk6alZW9VVSA4BXEpJ90O8s=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=OX+NLnIv9dXNIpoa3o8v86IqJuapyNzVtBGTWNylMzTurOTkePqYxL/Desuh4g/0V 3EQsU1UHxb0Et6+R17KhySYD2L0jOxj6i4Ba0CTO1wE533hy9HjtM+1emqmrMbGUDp Y/PYWIifj5gikdIDpDIV/EtUgLGePeBg4zHspaoc= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 239656A4F8 for ; Thu, 16 Apr 2026 10:59:19 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id s3P-hNnEeh3O for ; Thu, 16 Apr 2026 10:59:19 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358757; bh=qO9hNyvdMdDXTVRUlFZipk6alZW9VVSA4BXEpJ90O8s=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=bnEnUAACHRjLZbuctRyxzj/UNXHw80MIM19DN3p2GAvXh0D1vKg6rqP3dLcppfzuF 49RkH6bgB0goDKOLjJ6dcTy3rfVBJ4ZX+ddkXmFj8fpHlDeSQG7u5jjdbjdLljHng6 rwXtJ7y7UBygeuHOzq4wNaojrbB72O7SmFxsATLc= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 2068A6A4ED for ; Thu, 16 Apr 2026 10:59:17 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358755; bh=KwwPOtjDlDPGp95fX4IrMnAMQ15/nVWWf/byaEoDZDI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=MB1gNArLmhBe5BntKvsejn4YPiKXmK9XScDbbwQMdnZkkg+jXAznbt1Y1W1GuYDV7 Tw+OKr8EUhcmmM3O0gbhdjMcVwyhnxrN5s19dM16m+Bp+ifQvED8TPhft25SfUoeIp wfOXjFkApFpGvGYyEh5BE53MN41HhBNhXiryv56A= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 9E4AC6A4DC; Thu, 16 Apr 2026 10:59:15 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id BJVUf8apvDU0; Thu, 16 Apr 2026 10:59:15 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358755; bh=4PDiLGN2HCg4RsUjSquMoIhwhsi2j7di4XTkR/T+TKo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=bAjNZB9r6vxqrPjIzuq7Cso8jn15ZRzNWfPqUxBWEfTqMsNf2IhbZFVeuZQUSYZhr qZCaQrgaAQWuwvs/G2CscQNidUBbRCMQzw53u8m4vhhWsFNkyBCwJk9tfa2+hRKnBP JACZCCY9hmFVUEFtpduJtQbQPuRbLQap4tYyS+lc= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 296C66A4D8; Thu, 16 Apr 2026 10:59:15 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:57:02 -0600 Message-ID: <20260416165733.2923423-13-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: TTAOKLV5LQVHI55NK5KGPTFGDHDGXDRU X-Message-ID-Hash: TTAOKLV5LQVHI55NK5KGPTFGDHDGXDRU X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 12/21] isofs: Replace Linux includes in source files List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass The Linux kernel isofs source files include kernel-specific headers (, , , etc.) that are not available in U-Boot. Replace these with the single internal isofs.h header, which pulls in the U-Boot compatibility layer. The only modification to each file is replacing the Linux includes with #include "isofs.h" and adding a "Ported to U-Boot" comment. Signed-off-by: Simon Glass --- fs/isofs/dir.c | 4 ++-- fs/isofs/inode.c | 18 ++---------------- fs/isofs/joliet.c | 4 ++-- fs/isofs/namei.c | 3 ++- fs/isofs/rock.c | 5 ++--- fs/isofs/util.c | 3 +-- 6 files changed, 11 insertions(+), 26 deletions(-) diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index 2ca16c3fe5e..e40cdb68ff5 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -10,9 +10,9 @@ * (stephen@askone.demon.co.uk) : 21st June 1996 * * isofs directory handling functions + * + * Ported to U-Boot with minimal modifications. */ -#include -#include #include "isofs.h" int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode) diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 5c01536c5e8..4d0c83eb5df 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -10,24 +10,10 @@ * 1998 Eric Lammerts - ISO 9660 Level 3 * 2004 Paul Serice - Inode Support pushed out from 4GB to 128GB * 2004 Paul Serice - NFS Export Operations + * + * Ported to U-Boot with minimal modifications. */ -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include "isofs.h" #include "zisofs.h" diff --git a/fs/isofs/joliet.c b/fs/isofs/joliet.c index c0f04a1e7f6..793621fd97f 100644 --- a/fs/isofs/joliet.c +++ b/fs/isofs/joliet.c @@ -5,10 +5,10 @@ * (C) 1996 Gordon Chaffee * * Joliet: Microsoft's Unicode extensions to iso9660 + * + * Ported to U-Boot with minimal modifications. */ -#include -#include #include "isofs.h" /* diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c index 58f80e1b3ac..b44cd0ea73c 100644 --- a/fs/isofs/namei.c +++ b/fs/isofs/namei.c @@ -5,9 +5,10 @@ * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem. * * (C) 1991 Linus Torvalds - minix filesystem + * + * Ported to U-Boot with minimal modifications. */ -#include #include "isofs.h" /* diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index 6fe6dbd0c74..7232f3ec124 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -5,11 +5,10 @@ * (C) 1992, 1993 Eric Youngdale * * Rock Ridge Extensions to iso9660 + * + * Ported to U-Boot with minimal modifications. */ -#include -#include - #include "isofs.h" #include "rock.h" diff --git a/fs/isofs/util.c b/fs/isofs/util.c index 42f479da0b2..2e77932664a 100644 --- a/fs/isofs/util.c +++ b/fs/isofs/util.c @@ -3,7 +3,6 @@ * linux/fs/isofs/util.c */ -#include #include "isofs.h" /* @@ -52,7 +51,7 @@ struct timespec64 iso_date(u8 *p, int flags) /* sign extend */ if (tz & 0x80) - tz |= (-1 << 8); + tz |= ~0U << 8; /* * The timezone offset is unreliable on some disks, From patchwork Thu Apr 16 16:57:03 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2208 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358759; bh=yUJGVsXvkiftBq/55gAlpsiBen7B03A6lWs+Okn+2/k=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=cHPeXy1RunaktdMtKVL3gCxV7hhPVECuc7xE45Jld4JHS7NSBkPT/36rQLxLFTdHJ j2x+H/BaOuHcxCTAlqdMq7nxMv9WToSYOJa1kVxJs4KfcHEaukAo2/UWGKPV9RzHTs gWqSafkn3eh5ISeAdj9DM1s8uZyPLDqVHd/K+yMk= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 6A4B66A4DA for ; Thu, 16 Apr 2026 10:59:19 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id 7AqZzDBLru1U for ; Thu, 16 Apr 2026 10:59:19 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358758; bh=yUJGVsXvkiftBq/55gAlpsiBen7B03A6lWs+Okn+2/k=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=gPmGc+ROY+qy5HSfgeI4QEBewWAKz9Eg6ES6E72HAE5yCltZ0CaRXsevAkTiwBrSq bSXtMWpJ2w3an+keMyhrQTVr6L7I/4QK+7hufC0CbxjQcKllYG+/NrsjC/gKntLdac C/4xKM051TIO/KgBtD6DQUggXcxW8tb+3l/G8vn4= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 83D386A4DC for ; Thu, 16 Apr 2026 10:59:18 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358756; bh=ElU5bZcwmgJqNNEqvvuRqlYNg95gke+BH4FlUdjFxJU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FPtdU4Xcfhg/B5LqCp1AEIdDR/ZcS2+XSFqKCVXjBzWVnvLq+8snIo5cFBspr24vd G+o3OUm1AWgw0lQw3jFr1mlg25fcP2hwG8VaZYKCBMTwe83t7Ry1z7BGMpq9k9d0h8 O7Nj7JljCVR6YjxdiimTFtsZkVGt9EMvWF/Z8eko= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id C392A6A4DF; Thu, 16 Apr 2026 10:59:16 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id E0AjLJsUG4WC; Thu, 16 Apr 2026 10:59:16 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358756; bh=+gytbkSolbLWhAxFc+rW6JZenNvq+Wd2SrEjwS++cos=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=bsNFYbKtvKEXNgUH2V9tgi2IUaxIv1JzskANMsSlkgnpZEOi9i+ZCAjm5FXlz74Nm +CkgPJFuDzdwd4umZ8jULldleYg3hKY1MXgLHnB4EpVqU8a9dFIzsxh3ABFyEA1eDB r/i6SRZJjf1bb9UX82TkflfQIPWIblJNcyFpogt8= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 365AD6A4A5; Thu, 16 Apr 2026 10:59:16 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:57:03 -0600 Message-ID: <20260416165733.2923423-14-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 56PHLPGE426BR3KTZAWRKAEXN3TVFW5S X-Message-ID-Hash: 56PHLPGE426BR3KTZAWRKAEXN3TVFW5S X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 13/21] isofs: Move struct isofs_options to isofs.h List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass The struct isofs_options is defined locally in inode.c, but the U-Boot integration layer (interface.c) also needs access to set up mount options when probing an ISO 9660 filesystem. Move the struct definition to isofs.h so it is visible to both inode.c and interface.c. Signed-off-by: Simon Glass --- fs/isofs/inode.c | 23 +---------------------- fs/isofs/isofs.h | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 4d0c83eb5df..e50664b29e9 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -131,28 +131,7 @@ static const struct dentry_operations isofs_dentry_ops[] = { #endif }; -struct isofs_options{ - unsigned int rock:1; - unsigned int joliet:1; - unsigned int cruft:1; - unsigned int hide:1; - unsigned int showassoc:1; - unsigned int nocompress:1; - unsigned int overriderockperm:1; - unsigned int uid_set:1; - unsigned int gid_set:1; - unsigned char map; - unsigned char check; - unsigned int blocksize; - umode_t fmode; - umode_t dmode; - kgid_t gid; - kuid_t uid; - char *iocharset; - /* LVE */ - s32 session; - s32 sbsector; -}; +/* struct isofs_options is defined in isofs.h */ /* * Compute the hash for the isofs name corresponding to the dentry. diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h index 66a21648623..95d85f58733 100644 --- a/fs/isofs/isofs.h +++ b/fs/isofs/isofs.h @@ -202,6 +202,32 @@ isofs_normalize_block_and_offset(struct iso_directory_record *de, } } +/* + * Mount options - defined here so interface.c can set them up. + */ +struct isofs_options{ + unsigned int rock:1; + unsigned int joliet:1; + unsigned int cruft:1; + unsigned int hide:1; + unsigned int showassoc:1; + unsigned int nocompress:1; + unsigned int overriderockperm:1; + unsigned int uid_set:1; + unsigned int gid_set:1; + unsigned char map; + unsigned char check; + unsigned int blocksize; + umode_t fmode; + umode_t dmode; + kgid_t gid; + kuid_t uid; + char *iocharset; + /* LVE */ + s32 session; + s32 sbsector; +}; + extern const struct inode_operations isofs_dir_inode_operations; extern const struct file_operations isofs_dir_operations; extern const struct address_space_operations isofs_symlink_aops; From patchwork Thu Apr 16 16:57:04 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2209 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358764; bh=SEu5c+IMN3MUFHwC8ihPZKuWqIgy2aWbNH1YS8MOmtg=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=HUQdYp4MwyEBk9TJ6+jvbrP8/g0q5u4hyrepSKf4U3An9p9sJOTG59GsD6xPvkXT1 jtmue+GzrVEvLmsNMT086gW643OqP4wvaMpuAPfoYWtX74uZ7vCmKEyHvU7wo2bt6q sSgzieZ4Eeh4icNSSSkJ9EPrM0vTBAFAFOkC2jyM= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id BD66F6A4B0 for ; Thu, 16 Apr 2026 10:59:24 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id NZfzLhPn27N2 for ; Thu, 16 Apr 2026 10:59:24 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358764; bh=SEu5c+IMN3MUFHwC8ihPZKuWqIgy2aWbNH1YS8MOmtg=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=HUQdYp4MwyEBk9TJ6+jvbrP8/g0q5u4hyrepSKf4U3An9p9sJOTG59GsD6xPvkXT1 jtmue+GzrVEvLmsNMT086gW643OqP4wvaMpuAPfoYWtX74uZ7vCmKEyHvU7wo2bt6q sSgzieZ4Eeh4icNSSSkJ9EPrM0vTBAFAFOkC2jyM= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id ACFEF6A4D8 for ; Thu, 16 Apr 2026 10:59:24 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358761; bh=0gzpPxkT9uXrgPw2rdI5bco7OChlNcjU+KJZaQZhkRQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ZEsqjOpfNeb6sv35n2rZX6mEBBy/dr2AI2J4sr8l1F2KMCFqQx10WfWiqXXOfNk0w GK+R7lZsq1Yir71AX8HGRWH5tmAkQq+OOP/bZvUVVlNvr1erD1HcaDUdnqp0mp5QyS iNDcI6JkGHe35Psc7n2p3P3g+pbhzlTeypsbpqEQ= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id B41AF6A4A5; Thu, 16 Apr 2026 10:59:21 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id PycsXtnk9FKu; Thu, 16 Apr 2026 10:59:21 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358757; bh=KglMb5bYka8QYUVnP9cgfRDJiRRj+rSvXHSdBvuOhHw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=oM6KuMoH+hYfFvN2gjud3rFo7q+ZfOja8hd3hSw8zULTpwiMWxKjbdekiPkR3XnAR zOtpyLS9+kPOGXg9myJ/AgTMbllXs7ZiKn2PFnrU6bc8622um1QQd29p96//7G6Hbl sGlGeCgz/KbsmQA7QwdwWWn0WfEk1XE0kHwZjwP8= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 54B6A6A4F3; Thu, 16 Apr 2026 10:59:17 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:57:04 -0600 Message-ID: <20260416165733.2923423-15-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: V7TZKEH3NKXSWXWZCQF2FIHD52YA3V52 X-Message-ID-Hash: V7TZKEH3NKXSWXWZCQF2FIHD52YA3V52 X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 14/21] inode.c: Remove Linux module and fs_context code List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass U-Boot does not use the Linux kernel's module or fs_context infrastructure. Filesystem mounting is handled by the U-Boot integration layer (interface.c) instead. Remove the mount option parsing (isofs_parse_param), /proc/mounts display (isofs_show_options), reconfigure support, fs_context operations, filesystem type registration, and module init/exit code. Also remove the forward declaration and sops entry for isofs_show_options. Signed-off-by: Simon Glass --- fs/isofs/inode.c | 308 +---------------------------------------------- 1 file changed, 1 insertion(+), 307 deletions(-) diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index e50664b29e9..fdb5702120e 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -49,8 +49,7 @@ static void isofs_put_super(struct super_block *sb) } static int isofs_read_inode(struct inode *, int relocated); -static int isofs_statfs (struct dentry *, struct kstatfs *); -static int isofs_show_options(struct seq_file *, struct dentry *); +static int isofs_statfs(struct dentry *, struct kstatfs *); static struct kmem_cache *isofs_inode_cachep; @@ -97,20 +96,11 @@ static void destroy_inodecache(void) kmem_cache_destroy(isofs_inode_cachep); } -static int isofs_reconfigure(struct fs_context *fc) -{ - sync_filesystem(fc->root->d_sb); - if (!(fc->sb_flags & SB_RDONLY)) - return -EROFS; - return 0; -} - static const struct super_operations isofs_sops = { .alloc_inode = isofs_alloc_inode, .free_inode = isofs_free_inode, .put_super = isofs_put_super, .statfs = isofs_statfs, - .show_options = isofs_show_options, }; @@ -253,200 +243,6 @@ isofs_dentry_cmpi_ms(const struct dentry *dentry, } #endif -enum { - Opt_block, Opt_check, Opt_cruft, Opt_gid, Opt_ignore, Opt_iocharset, - Opt_map, Opt_mode, Opt_nojoliet, Opt_norock, Opt_sb, Opt_session, - Opt_uid, Opt_unhide, Opt_utf8, Opt_err, Opt_nocompress, Opt_hide, - Opt_showassoc, Opt_dmode, Opt_overriderockperm, -}; - -static const struct constant_table isofs_param_map[] = { - {"acorn", 'a'}, - {"a", 'a'}, - {"normal", 'n'}, - {"n", 'n'}, - {"off", 'o'}, - {"o", 'o'}, - {} -}; - -static const struct constant_table isofs_param_check[] = { - {"relaxed", 'r'}, - {"r", 'r'}, - {"strict", 's'}, - {"s", 's'}, - {} -}; - -static const struct fs_parameter_spec isofs_param_spec[] = { - fsparam_flag ("norock", Opt_norock), - fsparam_flag ("nojoliet", Opt_nojoliet), - fsparam_flag ("unhide", Opt_unhide), - fsparam_flag ("hide", Opt_hide), - fsparam_flag ("showassoc", Opt_showassoc), - fsparam_flag ("cruft", Opt_cruft), - fsparam_flag ("utf8", Opt_utf8), - fsparam_string ("iocharset", Opt_iocharset), - fsparam_enum ("map", Opt_map, isofs_param_map), - fsparam_u32 ("session", Opt_session), - fsparam_u32 ("sbsector", Opt_sb), - fsparam_enum ("check", Opt_check, isofs_param_check), - fsparam_uid ("uid", Opt_uid), - fsparam_gid ("gid", Opt_gid), - /* Note: mode/dmode historically accepted %u not strictly %o */ - fsparam_u32 ("mode", Opt_mode), - fsparam_u32 ("dmode", Opt_dmode), - fsparam_flag ("overriderockperm", Opt_overriderockperm), - fsparam_u32 ("block", Opt_block), - fsparam_string ("conv", Opt_ignore), - fsparam_flag ("nocompress", Opt_nocompress), - {} -}; - -static int isofs_parse_param(struct fs_context *fc, - struct fs_parameter *param) -{ - struct isofs_options *popt = fc->fs_private; - struct fs_parse_result result; - int opt; - unsigned int n; - - /* There are no remountable options */ - if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) - return 0; - - opt = fs_parse(fc, isofs_param_spec, param, &result); - if (opt < 0) - return opt; - - switch (opt) { - case Opt_norock: - popt->rock = 0; - break; - case Opt_nojoliet: - popt->joliet = 0; - break; - case Opt_hide: - popt->hide = 1; - break; - case Opt_unhide: - case Opt_showassoc: - popt->showassoc = 1; - break; - case Opt_cruft: - popt->cruft = 1; - break; -#ifdef CONFIG_JOLIET - case Opt_utf8: - kfree(popt->iocharset); - popt->iocharset = kstrdup("utf8", GFP_KERNEL); - if (!popt->iocharset) - return -ENOMEM; - break; - case Opt_iocharset: - kfree(popt->iocharset); - popt->iocharset = kstrdup(param->string, GFP_KERNEL); - if (!popt->iocharset) - return -ENOMEM; - break; -#endif - case Opt_map: - popt->map = result.uint_32; - break; - case Opt_session: - n = result.uint_32; - /* - * Track numbers are supposed to be in range 1-99, the - * mount option starts indexing at 0. - */ - if (n >= 99) - return -EINVAL; - popt->session = n + 1; - break; - case Opt_sb: - popt->sbsector = result.uint_32; - break; - case Opt_check: - popt->check = result.uint_32; - break; - case Opt_ignore: - break; - case Opt_uid: - popt->uid = result.uid; - popt->uid_set = 1; - break; - case Opt_gid: - popt->gid = result.gid; - popt->gid_set = 1; - break; - case Opt_mode: - popt->fmode = result.uint_32; - break; - case Opt_dmode: - popt->dmode = result.uint_32; - break; - case Opt_overriderockperm: - popt->overriderockperm = 1; - break; - case Opt_block: - n = result.uint_32; - if (n != 512 && n != 1024 && n != 2048) - return -EINVAL; - popt->blocksize = n; - break; - case Opt_nocompress: - popt->nocompress = 1; - break; - default: - return -EINVAL; - } - return 0; -} - -/* - * Display the mount options in /proc/mounts. - */ -static int isofs_show_options(struct seq_file *m, struct dentry *root) -{ - struct isofs_sb_info *sbi = ISOFS_SB(root->d_sb); - - if (!sbi->s_rock) seq_puts(m, ",norock"); - else if (!sbi->s_joliet_level) seq_puts(m, ",nojoliet"); - if (sbi->s_cruft) seq_puts(m, ",cruft"); - if (sbi->s_hide) seq_puts(m, ",hide"); - if (sbi->s_nocompress) seq_puts(m, ",nocompress"); - if (sbi->s_overriderockperm) seq_puts(m, ",overriderockperm"); - if (sbi->s_showassoc) seq_puts(m, ",showassoc"); - - if (sbi->s_check) seq_printf(m, ",check=%c", sbi->s_check); - if (sbi->s_mapping) seq_printf(m, ",map=%c", sbi->s_mapping); - if (sbi->s_session != 255) seq_printf(m, ",session=%u", sbi->s_session - 1); - if (sbi->s_sbsector != -1) seq_printf(m, ",sbsector=%u", sbi->s_sbsector); - - if (root->d_sb->s_blocksize != 1024) - seq_printf(m, ",blocksize=%lu", root->d_sb->s_blocksize); - - if (sbi->s_uid_set) - seq_printf(m, ",uid=%u", - from_kuid_munged(&init_user_ns, sbi->s_uid)); - if (sbi->s_gid_set) - seq_printf(m, ",gid=%u", - from_kgid_munged(&init_user_ns, sbi->s_gid)); - - if (sbi->s_dmode != ISOFS_INVALID_MODE) - seq_printf(m, ",dmode=%o", sbi->s_dmode); - if (sbi->s_fmode != ISOFS_INVALID_MODE) - seq_printf(m, ",fmode=%o", sbi->s_fmode); - -#ifdef CONFIG_JOLIET - if (sbi->s_nls_iocharset) - seq_printf(m, ",iocharset=%s", sbi->s_nls_iocharset->charset); - else - seq_puts(m, ",iocharset=utf8"); -#endif - return 0; -} - /* * look if the driver can tell the multi session redirection value * @@ -1497,105 +1293,3 @@ struct inode *__isofs_iget(struct super_block *sb, return inode; } - -static int isofs_get_tree(struct fs_context *fc) -{ - return get_tree_bdev(fc, isofs_fill_super); -} - -static void isofs_free_fc(struct fs_context *fc) -{ - struct isofs_options *opt = fc->fs_private; - - kfree(opt->iocharset); - kfree(opt); -} - -static const struct fs_context_operations isofs_context_ops = { - .parse_param = isofs_parse_param, - .get_tree = isofs_get_tree, - .reconfigure = isofs_reconfigure, - .free = isofs_free_fc, -}; - -static int isofs_init_fs_context(struct fs_context *fc) -{ - struct isofs_options *opt; - - opt = kzalloc_obj(*opt); - if (!opt) - return -ENOMEM; - - opt->map = 'n'; - opt->rock = 1; - opt->joliet = 1; - opt->cruft = 0; - opt->hide = 0; - opt->showassoc = 0; - opt->check = 'u'; /* unset */ - opt->nocompress = 0; - opt->blocksize = 1024; - opt->fmode = opt->dmode = ISOFS_INVALID_MODE; - opt->uid_set = 0; - opt->gid_set = 0; - opt->gid = GLOBAL_ROOT_GID; - opt->uid = GLOBAL_ROOT_UID; - opt->iocharset = NULL; - opt->overriderockperm = 0; - opt->session = -1; - opt->sbsector = -1; - - fc->fs_private = opt; - fc->ops = &isofs_context_ops; - - return 0; -} - -static struct file_system_type iso9660_fs_type = { - .owner = THIS_MODULE, - .name = "iso9660", - .kill_sb = kill_block_super, - .fs_flags = FS_REQUIRES_DEV, - .init_fs_context = isofs_init_fs_context, - .parameters = isofs_param_spec, -}; -MODULE_ALIAS_FS("iso9660"); -MODULE_ALIAS("iso9660"); - -static int __init init_iso9660_fs(void) -{ - int err = init_inodecache(); - if (err) - goto out; -#ifdef CONFIG_ZISOFS - err = zisofs_init(); - if (err) - goto out1; -#endif - err = register_filesystem(&iso9660_fs_type); - if (err) - goto out2; - return 0; -out2: -#ifdef CONFIG_ZISOFS - zisofs_cleanup(); -out1: -#endif - destroy_inodecache(); -out: - return err; -} - -static void __exit exit_iso9660_fs(void) -{ - unregister_filesystem(&iso9660_fs_type); -#ifdef CONFIG_ZISOFS - zisofs_cleanup(); -#endif - destroy_inodecache(); -} - -module_init(init_iso9660_fs) -module_exit(exit_iso9660_fs) -MODULE_DESCRIPTION("ISO 9660 CDROM file system support"); -MODULE_LICENSE("GPL"); From patchwork Thu Apr 16 16:57:05 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2210 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358769; bh=22k7EsYSqA22hctYoqY1ZRjymoc9Hq4NwBrbCUEd1ig=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=aMV7OucVGmbfZz13jtq4PxCq8gKWf+a4sdfbco8RRELMZNQwRKitHUCn4lSAMAqRO jqLorTZw3xjg0OnMspnVuJKr1x7aPYWpsAejRD1odFgnM0hsrHvG6rExDRQVHcyVrf HMTORsyQqzYPY29B+9Nuax9aYASkM5dEAjHCUKoM= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 31CA26A4DC for ; Thu, 16 Apr 2026 10:59:29 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id UUoSmHuuqbXj for ; Thu, 16 Apr 2026 10:59:29 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358769; bh=22k7EsYSqA22hctYoqY1ZRjymoc9Hq4NwBrbCUEd1ig=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=aMV7OucVGmbfZz13jtq4PxCq8gKWf+a4sdfbco8RRELMZNQwRKitHUCn4lSAMAqRO jqLorTZw3xjg0OnMspnVuJKr1x7aPYWpsAejRD1odFgnM0hsrHvG6rExDRQVHcyVrf HMTORsyQqzYPY29B+9Nuax9aYASkM5dEAjHCUKoM= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 2046B6A4D8 for ; Thu, 16 Apr 2026 10:59:29 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358767; bh=2Hx0HKOV05SsUKvkBIHS2KIj0J8JvNVMqB41EydIcww=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=F3rqVsEUzh4ymurlEDX/K2ViXU26nhBrPj8cF7gcYeNl9MeQcnRa9+VYZdp8waaHS gH0Ur4hk7cijaFTS+RxrKF/QTbyv08iLzeZBZ2TEENkDvqUgbz/Swh7MIRedLaIhj2 L8tqqIJzjFkENUm7dFJoOF85qChZU5B/52hQdzAA= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 23BD26A4B0; Thu, 16 Apr 2026 10:59:27 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id SM8vMOjDonyY; Thu, 16 Apr 2026 10:59:27 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358762; bh=Gu7XMil3LqmAloaBIzlFWh5T/pM1UoO6Q19J1Y7nOGI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=UZT/XPO7ZLpx8VrlxCYz+2K5geUNKoVSpfusLMQtgVLLZE7R9Tj13b2F6IXraG45T T47tjVQeCmJeinln1Vx87WGO3pq+NzpYx+Z2WANFyyrx3KlwUxfmFWMviftUPoVRBJ iYWm1pGdsoiYme4hmsCbK1FwJ2SHJEJQMEyWB9Qw= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 4ED806A4A5; Thu, 16 Apr 2026 10:59:22 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:57:05 -0600 Message-ID: <20260416165733.2923423-16-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: RDCFR3GPCNNOA5YU2CC6DTGE4D36GIM6 X-Message-ID-Hash: RDCFR3GPCNNOA5YU2CC6DTGE4D36GIM6 X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 15/21] inode.c: Adapt for U-Boot environment List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Several functions and code paths in inode.c depend on Linux kernel infrastructure that is not available in U-Boot. Rename init_inodecache()/destroy_inodecache() to have an isofs_ prefix, avoiding symbol clashes with ext4l which has identically named functions. Make isofs_fill_super() non-static so the U-Boot integration layer (interface.c) can call it directly. Stub isofs_get_last_session() since U-Boot has no CDROM multi-session support. Replace bdev blocksize queries with direct blocksize setting. Remove the read-write mount check (U-Boot mounts are always read-only), the zisofs a_ops assignment, the s_id reference in the bread failure message, the silent flag (always print diagnostics), and several obsolete comments. Signed-off-by: Simon Glass --- fs/isofs/inode.c | 123 +++++------------------------------------------ 1 file changed, 13 insertions(+), 110 deletions(-) diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index fdb5702120e..6d629252575 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -74,7 +74,7 @@ static void init_once(void *foo) inode_init_once(&ei->vfs_inode); } -static int __init init_inodecache(void) +int isofs_init_inodecache(void) { isofs_inode_cachep = kmem_cache_create("isofs_inode_cache", sizeof(struct iso_inode_info), @@ -86,12 +86,8 @@ static int __init init_inodecache(void) return 0; } -static void destroy_inodecache(void) +void isofs_destroy_inodecache(void) { - /* - * Make sure all delayed rcu free inodes are flushed before we - * destroy cache. - */ rcu_barrier(); kmem_cache_destroy(isofs_inode_cachep); } @@ -244,61 +240,11 @@ isofs_dentry_cmpi_ms(const struct dentry *dentry, #endif /* - * look if the driver can tell the multi session redirection value - * - * don't change this if you don't know what you do, please! - * Multisession is legal only with XA disks. - * A non-XA disk with more than one volume descriptor may do it right, but - * usually is written in a nowhere standardized "multi-partition" manner. - * Multisession uses absolute addressing (solely the first frame of the whole - * track is #0), multi-partition uses relative addressing (each first frame of - * each track is #0), and a track is not a session. - * - * A broken CDwriter software or drive firmware does not set new standards, - * at least not if conflicting with the existing ones. - * - * emoenke@gwdg.de + * U-Boot: Multi-session support is not available. */ -#define WE_OBEY_THE_WRITTEN_STANDARDS 1 - static unsigned int isofs_get_last_session(struct super_block *sb, s32 session) { - struct cdrom_device_info *cdi = disk_to_cdi(sb->s_bdev->bd_disk); - unsigned int vol_desc_start = 0; - - if (session > 0) { - struct cdrom_tocentry te; - - if (!cdi) - return 0; - - te.cdte_track = session; - te.cdte_format = CDROM_LBA; - if (cdrom_read_tocentry(cdi, &te) == 0) { - printk(KERN_DEBUG "ISOFS: Session %d start %d type %d\n", - session, te.cdte_addr.lba, - te.cdte_ctrl & CDROM_DATA_TRACK); - if ((te.cdte_ctrl & CDROM_DATA_TRACK) == 4) - return te.cdte_addr.lba; - } - - printk(KERN_ERR "ISOFS: Invalid session number or type of track\n"); - } - - if (cdi) { - struct cdrom_multisession ms_info; - - ms_info.addr_format = CDROM_LBA; - if (cdrom_multisession(cdi, &ms_info) == 0) { -#if WE_OBEY_THE_WRITTEN_STANDARDS - /* necessary for a valid ms_info.addr */ - if (ms_info.xa_flag) -#endif - vol_desc_start = ms_info.addr.lba; - } - } - - return vol_desc_start; + return 0; } /* @@ -332,7 +278,7 @@ static bool rootdir_empty(struct super_block *sb, unsigned long block) /* * Initialize the superblock and read the root inode. */ -static int isofs_fill_super(struct super_block *s, struct fs_context *fc) +int isofs_fill_super(struct super_block *s, struct fs_context *fc) { struct buffer_head *bh = NULL, *pri_bh = NULL; struct hs_primary_descriptor *h_pri = NULL; @@ -348,7 +294,6 @@ static int isofs_fill_super(struct super_block *s, struct fs_context *fc) int orig_zonesize; int table, error = -EINVAL; unsigned int vol_desc_start; - int silent = fc->sb_flags & SB_SILENT; sbi = kzalloc_obj(*sbi); if (!sbi) @@ -356,26 +301,11 @@ static int isofs_fill_super(struct super_block *s, struct fs_context *fc) s->s_fs_info = sbi; /* - * First of all, get the hardware blocksize for this device. - * If we don't know what it is, or the hardware blocksize is - * larger than the blocksize the user specified, then use - * that value. + * U-Boot: Use the configured blocksize directly (no bdev query). */ - /* - * What if bugger tells us to go beyond page size? - */ - if (bdev_logical_block_size(s->s_bdev) > 2048) { - printk(KERN_WARNING - "ISOFS: unsupported/invalid hardware sector size %d\n", - bdev_logical_block_size(s->s_bdev)); - goto out_freesbi; - } - opt->blocksize = sb_min_blocksize(s, opt->blocksize); - if (!opt->blocksize) { - printk(KERN_ERR - "ISOFS: unable to set blocksize\n"); - goto out_freesbi; - } + if (!opt->blocksize) + opt->blocksize = ISOFS_BLOCK_SIZE; + sb_set_blocksize(s, opt->blocksize); sbi->s_high_sierra = 0; /* default is iso9660 */ sbi->s_session = opt->session; @@ -463,12 +393,6 @@ static int isofs_fill_super(struct super_block *s, struct fs_context *fc) pri_bh = NULL; root_found: - /* We don't support read-write mounts */ - if (!sb_rdonly(s)) { - error = -EACCES; - goto out_freebh; - } - if (joliet_level && (!pri || !opt->rock)) { /* This is the case of Joliet with the norock mount flag. * A disc with both Joliet and Rock Ridge is handled later @@ -525,9 +449,6 @@ root_found: s->s_time_min = mktime64(1900, 1, 1, 0, 0, 0) - MAX_TZ_OFFSET; s->s_time_max = mktime64(U8_MAX+1900, 12, 31, 23, 59, 59) + MAX_TZ_OFFSET; - /* Set this for reference. Its not currently used except on write - which we don't have .. */ - first_data_zone = isonum_733(rootp->extent) + isonum_711(rootp->ext_attr_length); sbi->s_firstdatazone = first_data_zone; @@ -563,21 +484,7 @@ root_found: brelse(bh); /* - * Force the blocksize to 512 for 512 byte sectors. The file - * read primitives really get it wrong in a bad way if we don't - * do this. - * - * Note - we should never be setting the blocksize to something - * less than the hardware sector size for the device. If we - * do, we would end up having to read larger buffers and split - * out portions to satisfy requests. - * - * Note2- the idea here is that we want to deal with the optimal - * zonesize in the filesystem. If we have it set to something less, - * then we have horrible problems with trying to piece together - * bits of adjacent blocks in order to properly read directory - * entries. By forcing the blocksize in this way, we ensure - * that we will never be required to do this. + * Force the blocksize to the zone size. */ sb_set_blocksize(s, orig_zonesize); @@ -732,8 +639,8 @@ out_no_inode: #endif goto out_freesbi; out_no_read: - printk(KERN_WARNING "%s: bread failed, dev=%s, iso_blknum=%d, block=%d\n", - __func__, s->s_id, iso_blknum, block); + printk(KERN_WARNING "%s: bread failed, iso_blknum=%d, block=%d\n", + __func__, iso_blknum, block); goto out_freebh; out_bad_zone_size: printk(KERN_WARNING "ISOFS: Bad logical zone size %ld\n", @@ -744,8 +651,7 @@ out_bad_size: orig_zonesize, opt->blocksize); goto out_freebh; out_unknown_format: - if (!silent) - printk(KERN_WARNING "ISOFS: Unable to identify CD-ROM format.\n"); + printk(KERN_WARNING "ISOFS: Unable to identify CD-ROM format.\n"); out_freebh: brelse(bh); @@ -1145,8 +1051,6 @@ static int isofs_read_inode(struct inode *inode, int relocated) inode->i_ino); } - /* I have no idea what other flag bits are used for, so - we will flag it for now */ #ifdef DEBUG if((de->flags[-high_sierra] & ~2)!= 0){ printk(KERN_DEBUG "ISOFS: Unusual flag settings for ISO file " @@ -1192,7 +1096,6 @@ static int isofs_read_inode(struct inode *inode, int relocated) switch (ei->i_file_format) { #ifdef CONFIG_ZISOFS case isofs_file_compressed: - inode->i_data.a_ops = &zisofs_aops; break; #endif default: From patchwork Thu Apr 16 16:57:06 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2211 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358771; bh=LT6bEMluro+rJM8ZX3j/rKm04q6tYHs2gzhV1vWnL5g=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=eXqoHFPw1/CZgWDSXcgZy8NpKd3EjHE5PrtkyG0HNOHTR9ZQ4vDaepZVVOiL58MIZ ynfZFb1hqiHlIKwYNBZXqanAONOjtsbl6RKHCrpOTuDms2IegbD7v+cfmkjF623p/I ef4g3WZ8yUAD1wgwNl5v2HbBrtKf9bvosxg9C0/c= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 97E0C6A4D8 for ; Thu, 16 Apr 2026 10:59:31 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id Ll0QaTkr_8nH for ; Thu, 16 Apr 2026 10:59:31 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358769; bh=LT6bEMluro+rJM8ZX3j/rKm04q6tYHs2gzhV1vWnL5g=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=X261gGiJ17S+3G9BkhqjpPuN3D30Pggu7k2bd20RftIVDKxMjfYACzFMPH89wfBGf Vxv23YDtFWX0FvVOrWBLmilnETDx4OcdZn/GzuJII6dKTlT5tGQiVRxS3NI+Z7OOUA RfgrEM50OjsIcAy2pvgf4ew8edFfa5JCZM9MU7iI= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id A12776A4DA for ; Thu, 16 Apr 2026 10:59:29 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358767; bh=AGMrAVOHsHjyJR5MchbWST35W4+tQrvIZnH+yslmkB8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=BoqmURZG6a/2s/0MscG80xAYAoZaX4XdD3X8CtrDaJE0LWsnhUNQflI/58Wab5azg vaFT00sOlPkZyrUb1//El+lq4lYyBs5M7Cm0xJ2NwHg9IIx3n9lBngvr9rSBjkF5Gk O+i0dNA+mluvY8YexlcHQk/TQir1NfiHyYsiKgP0= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id B37626A4D8; Thu, 16 Apr 2026 10:59:27 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id 7xZjzL0jL53R; Thu, 16 Apr 2026 10:59:27 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358767; bh=MNv0Na3L+BVfhwlzG12GS1gT/2YLUlA3g+cPCy1YNIQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ksFedu4it5+Ti26fGWIqpUG7/pXysiZ+hJPMnODAFPFn7bkVHXBEyH1ZCpwZ23LvN tgFT7GCxYFMZ6hrKgYcAksBQQJawKgqNEpieCmySSjWzydgFvlJ0adhPKOWDTWVddo f613mz3Z3nQ1HoKg1suX5kAMQ7vr3KTnNzLK5pYE= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 4AD426A4A5; Thu, 16 Apr 2026 10:59:27 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:57:06 -0600 Message-ID: <20260416165733.2923423-17-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: OGG6AYXRWAABFHNHBELMO3J5NVCWTLRF X-Message-ID-Hash: OGG6AYXRWAABFHNHBELMO3J5NVCWTLRF X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 16/21] dir.c: Advance ctx->pos before dir_emit() calls List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass U-Boot's VFS layer iterates directories one entry at a time, calling iterate_shared() repeatedly. The Linux implementation advances ctx->pos after a successful dir_emit(), but this causes the U-Boot single-entry iterator to see the same entry twice since it re-enters with the old position. Move ctx->pos advancement before the dir_emit() call for '.', '..', and regular entries so that when dir_emit() signals "buffer full" and iteration resumes, the position already points past the current entry. Signed-off-by: Simon Glass --- fs/isofs/dir.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index e40cdb68ff5..6065f46f762 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -175,9 +175,9 @@ static int do_isofs_readdir(struct inode *inode, struct file *file, /* Handle the case of the '.' directory */ if (de->name_len[0] == 1 && de->name[0] == 0) { + ctx->pos += de_len; if (!dir_emit_dot(file, ctx)) break; - ctx->pos += de_len; continue; } @@ -185,9 +185,9 @@ static int do_isofs_readdir(struct inode *inode, struct file *file, /* Handle the case of the '..' directory */ if (de->name_len[0] == 1 && de->name[0] == 1) { + ctx->pos += de_len; if (!dir_emit_dotdot(file, ctx)) break; - ctx->pos += de_len; continue; } @@ -232,11 +232,11 @@ static int do_isofs_readdir(struct inode *inode, struct file *file, len = de->name_len[0]; } } + ctx->pos += de_len; if (len > 0) { if (!dir_emit(ctx, p, len, inode_number, DT_UNKNOWN)) break; } - ctx->pos += de_len; } if (bh) brelse(bh); From patchwork Thu Apr 16 16:57:07 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2212 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358771; bh=5fu9JJ88Ntx21wku4Kp6eVEHjqdeZRqC2KeQYvhVdas=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=eeDZ2eloPc5OKEhjC49csmSB0KtVFDxCAwYOu1+W9xbGPN993IsbvPJOtLiCboRxW dU+O5Zwe0Y6nEI24zDAa5quEp3w78RYwb6GuuUNG3N47pSsxXmDs2ozZ7ocxVXLz7W Ms0DnqSB5vqBRBAUsW1wC4FD4HuH+plXUJM3Yi8U= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id D21C76A4F1 for ; Thu, 16 Apr 2026 10:59:31 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id qJ3aUur4iaUA for ; Thu, 16 Apr 2026 10:59:31 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358770; bh=5fu9JJ88Ntx21wku4Kp6eVEHjqdeZRqC2KeQYvhVdas=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=GMmLdVJ0MSXEpoWODz8bYpbU+1L662aGutMuYXkLbCfZ29BXx7AeAP4kWjwm7+q5J rFyZRxUQllvc1e71gccqHSLQSf6aIHneDnv4w91+fttcBE9F6oLBsG8IFsIuxuMYoR v1VN0FSgrT6CT9mLZYZD//OoRVXX5OZ3lHmoeDm0= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 0BA606A4F5 for ; Thu, 16 Apr 2026 10:59:30 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358768; bh=ibSfGMciHAECQURtkXdKJuritaSGMmAjALy7vO8MRxE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pQbyuKWqyEpbOpCepjXcf44jYzPVO6++jGwr36K5d3vuus04KTtAb/jKss6soPuhV HD0qwEDL0COW5oIaN+VZkU4BJuHcHXcLtg+vkTukt5V3eT6GKXFvL0z3QCgCOj0BEV eSdbZwH+Sj+DvozxeOaeSaOGM29J3mGg5UPJxIK4= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id BFC5A6A4B0; Thu, 16 Apr 2026 10:59:28 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id Puj_q7zwsVa9; Thu, 16 Apr 2026 10:59:28 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358768; bh=l64W8q07/iJjv+foynuoxNkFEDekarpPntIDcktUf7k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=KxYXCrWxB431YI/Yy8SXGmHit75OC7irG3EBSDhJBYEVB7uG5uLSs207Zba43HIhF JvLYxNfFrKVFMNl8nU2elfG/bsZYPPMYFX433Loni5kZpoHuuTBD8V5No49W/R6k6Q Q99IQemIqDrBY+/TUMhhgzvB6XR8pX/E7rYnV/fs= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 4CFA76A4A5; Thu, 16 Apr 2026 10:59:28 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:57:07 -0600 Message-ID: <20260416165733.2923423-18-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: KGQVTRVKB7WDYEVJEUJ66BNABADB4OIH X-Message-ID-Hash: KGQVTRVKB7WDYEVJEUJ66BNABADB4OIH X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 17/21] dir.c: Remove setlease from file_operations List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass U-Boot does not support file leases. Remove the setlease callback from isofs_dir_operations since generic_setlease() is not available in the U-Boot compatibility layer. Signed-off-by: Simon Glass --- fs/isofs/dir.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index 6065f46f762..2ff7fd22375 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -272,7 +272,6 @@ const struct file_operations isofs_dir_operations = .llseek = generic_file_llseek, .read = generic_read_dir, .iterate_shared = isofs_readdir, - .setlease = generic_setlease, }; /* From patchwork Thu Apr 16 16:57:08 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2213 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358775; bh=hNXmrnHqaopWwCOv1pjNuufgaoc6CKBR05YFptsyuuE=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=C3E3V8Hb5BYIJ/sTFpah0NoZfqAC00/LgkXUEzeHsDHgBB1HRMRSw765TjHd55HDt D1ibhN9/3M/Qc7UZzvfdZFG8kw90Ab5R6awfJ9OmGjc83hlcgt4rdyNLAewIxXSR2i TsO+YRCh5L8jYYK+fumCpxaHS4MkCPVMGYsZYuHM= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id C263A6A4E6 for ; Thu, 16 Apr 2026 10:59:35 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id Bmy8-iAjv0C6 for ; Thu, 16 Apr 2026 10:59:35 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358775; bh=hNXmrnHqaopWwCOv1pjNuufgaoc6CKBR05YFptsyuuE=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=C3E3V8Hb5BYIJ/sTFpah0NoZfqAC00/LgkXUEzeHsDHgBB1HRMRSw765TjHd55HDt D1ibhN9/3M/Qc7UZzvfdZFG8kw90Ab5R6awfJ9OmGjc83hlcgt4rdyNLAewIxXSR2i TsO+YRCh5L8jYYK+fumCpxaHS4MkCPVMGYsZYuHM= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id B1AB26A4D8 for ; Thu, 16 Apr 2026 10:59:35 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358774; bh=SZ9YpN3NZ7NKiBa1PBrj6jUbZ5aY53HL92sWz0O6riY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=p1B8dWnsi+4CYyozFbYoIMSioPNMILgrn56L+NUkr5L7jfNGM4fwC1T4UWO6tRZ3u rH6CAJJVu5+dQ4434Paiz2Fgi2cnyoqTRR/DdgZshZTUBziQNi6d0i7VzTCt8snBf+ c1ntQtpkwOZQvOtBJOUn9cC2l3wfr3XyfLq6bjXo= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 26B8D6A4A5; Thu, 16 Apr 2026 10:59:34 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id O4HW-5dvv-Ku; Thu, 16 Apr 2026 10:59:34 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358769; bh=LpZZYF66jkEpii0wBnmNZYGqKbYFJ9+gfoCH62wUoXo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=eVBGqI/gVGM5M3AAF4F94FchW8wn9bCDRhMlZ77IF9uZdh0WYOBbMhoV4/bRRuC/K meyIUE6PfUEDmfs2izL5NV4HIlAHrF1XOh4kiVArAHSDA7HMPGC48QFIFUhOqv0Hgz dObvZezfA+76gNQ2laDHf34nGj10ZvYLb6Od/O0U= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 58C656A4E6; Thu, 16 Apr 2026 10:59:29 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:57:08 -0600 Message-ID: <20260416165733.2923423-19-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: FPULIWKBAIR34MVUYKPOFZYYBPOJOQQY X-Message-ID-Hash: FPULIWKBAIR34MVUYKPOFZYYBPOJOQQY X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 18/21] fs: Add ISO 9660 U-Boot integration layer List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Provide the U-Boot-specific integration that makes the Linux isofs source files work within U-Boot's filesystem framework. The compatibility header (isofs_uboot.h) follows the same approach as ext4l: it stubs out Linux kernel infrastructure that is not available in U-Boot, such as the filesystem-context mount API, CDROM multi-session support and the mpage readahead layer. All printk() calls are silenced since U-Boot does not filter by log level. The isofs driver uses iget5_locked() with custom test/set callbacks in support.c because ISO 9660 identifies inodes by their on-disk block and offset rather than a simple inode number. The interface layer (interface.c) bridges U-Boot's filesystem conventions and the Linux VFS entry points. It handles mounting by setting up the superblock and calling isofs_fill_super(), resolves paths component by component using isofs_lookup(), and uses isofs_bread() to read file data block by block. Directory listing uses the Linux iterate_shared() callback with an actor that captures one entry per call, matching the U-Boot opendir/readdir/closedir model. The VFS driver (fs.c) exposes the filesystem through UCLASS_FS, UCLASS_DIR and UCLASS_FILE so that it works with the mount/ls/cat commands. The driver is also registered in the legacy filesystem table for use with the load command. Signed-off-by: Simon Glass --- fs/fs_legacy.c | 24 ++ fs/isofs/Kconfig | 19 ++ fs/isofs/Makefile | 13 + fs/isofs/fs.c | 228 +++++++++++++++ fs/isofs/inode.c | 2 +- fs/isofs/interface.c | 623 +++++++++++++++++++++++++++++++++++++++++ fs/isofs/isofs_uboot.h | 308 ++++++++++++++++++++ fs/isofs/support.c | 85 ++++++ include/fs_common.h | 1 + include/isofs.h | 93 ++++++ include/linux/slab.h | 2 + 11 files changed, 1397 insertions(+), 1 deletion(-) create mode 100644 fs/isofs/Kconfig create mode 100644 fs/isofs/Makefile create mode 100644 fs/isofs/fs.c create mode 100644 fs/isofs/interface.c create mode 100644 fs/isofs/isofs_uboot.h create mode 100644 fs/isofs/support.c create mode 100644 include/isofs.h diff --git a/fs/fs_legacy.c b/fs/fs_legacy.c index 5286f07c659..5f7fd8e7ec5 100644 --- a/fs/fs_legacy.c +++ b/fs/fs_legacy.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -295,6 +296,29 @@ static struct fstype_info fstypes[] = { .statfs = ext4l_statfs_legacy, }, #endif +#if CONFIG_IS_ENABLED(FS_ISOFS) + { + .fstype = FS_TYPE_ISO, + .name = "iso9660", + .null_dev_desc_ok = false, + .probe = isofs_probe, + .close = isofs_close, + .ls = isofs_ls, + .exists = isofs_exists, + .size = isofs_size, + .read = isofs_read, + .write = fs_write_unsupported, + .uuid = fs_uuid_unsupported, + .opendir = isofs_opendir, + .readdir = isofs_readdir, + .closedir = isofs_closedir, + .unlink = fs_unlink_unsupported, + .mkdir = fs_mkdir_unsupported, + .ln = fs_ln_unsupported, + .rename = fs_rename_unsupported, + .statfs = fs_statfs_unsupported, + }, +#endif #if IS_ENABLED(CONFIG_SANDBOX) && !IS_ENABLED(CONFIG_XPL_BUILD) { .fstype = FS_TYPE_SANDBOX, diff --git a/fs/isofs/Kconfig b/fs/isofs/Kconfig new file mode 100644 index 00000000000..4eda062796c --- /dev/null +++ b/fs/isofs/Kconfig @@ -0,0 +1,19 @@ +config FS_ISOFS + bool "ISO 9660 filesystem support (Linux port)" + depends on FS + select FS_LINUX + help + Support for reading ISO 9660 CD-ROM/DVD filesystems. This is a port + of the Linux kernel isofs driver, providing full read-only access + including Rock Ridge extensions for POSIX filenames and permissions. + + This driver uses the same porting approach as ext4l, keeping the + Linux source files nearly identical. + +config JOLIET + bool "Microsoft Joliet CD-ROM extensions" + depends on FS_ISOFS + help + Support for Microsoft Joliet CD-ROM extensions which provide + Unicode filenames on ISO 9660 filesystems. This requires NLS + (National Language Support) for character set conversion. diff --git a/fs/isofs/Makefile b/fs/isofs/Makefile new file mode 100644 index 00000000000..34b0ed75a83 --- /dev/null +++ b/fs/isofs/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 + +# U-Boot interface and support +obj-y := interface.o support.o + +# VFS layer integration +obj-$(CONFIG_$(PHASE_)VFS) += fs.o + +# Core Linux isofs files (kept close to upstream) +obj-y += namei.o inode.o dir.o util.o rock.o export.o + +# Optional extensions +obj-$(CONFIG_JOLIET) += joliet.o diff --git a/fs/isofs/fs.c b/fs/isofs/fs.c new file mode 100644 index 00000000000..835b8a9e5a7 --- /dev/null +++ b/fs/isofs/fs.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * isofs filesystem driver for the VFS layer + * + * Wraps the Linux-ported isofs implementation to provide UCLASS_FS + * and UCLASS_DIR devices, following the same pattern as ext4l/fs.c. + * + * Copyright 2026 Simon Glass + */ + +#define LOG_CATEGORY UCLASS_FS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct isofs_dir_priv - Private info for isofs directory devices + * + * @strm: Directory stream from isofs_opendir(), or NULL + */ +struct isofs_dir_priv { + struct fs_dir_stream *strm; +}; + +static int isofs_vfs_mount(struct udevice *dev) +{ + struct fs_priv *uc_priv = dev_get_uclass_priv(dev); + struct fs_plat *plat = dev_get_uclass_plat(dev); + int ret; + + if (uc_priv->mounted) + return log_msg_ret("emm", -EISCONN); + + if (!plat->desc) + return log_msg_ret("emd", -ENODEV); + + ret = isofs_probe(plat->desc, &plat->part); + if (ret) + return log_msg_ret("emp", ret); + + uc_priv->mounted = true; + + return 0; +} + +static int isofs_vfs_unmount(struct udevice *dev) +{ + struct fs_priv *uc_priv = dev_get_uclass_priv(dev); + + if (!uc_priv->mounted) + return log_msg_ret("euu", -ENOTCONN); + + isofs_close(); + uc_priv->mounted = false; + + return 0; +} + +static int isofs_vfs_lookup_dir(struct udevice *dev, const char *path, + struct udevice **dirp) +{ + struct udevice *dir; + int ret; + + ret = dir_add_probe(dev, DM_DRIVER_GET(isofs_vfs_dir), path, &dir); + if (ret) + return log_msg_ret("eld", ret); + + *dirp = dir; + + return 0; +} + +static const struct fs_ops isofs_vfs_ops = { + .mount = isofs_vfs_mount, + .unmount = isofs_vfs_unmount, + .lookup_dir = isofs_vfs_lookup_dir, +}; + +U_BOOT_DRIVER(isofs_fs) = { + .name = "isofs_fs", + .id = UCLASS_FS, + .ops = &isofs_vfs_ops, +}; + +/* isofs directory driver */ + +static int isofs_dir_open(struct udevice *dev, struct fs_dir_stream *strm) +{ + struct dir_uc_priv *uc_priv = dev_get_uclass_priv(dev); + struct isofs_dir_priv *priv = dev_get_priv(dev); + struct fs_dir_stream *iso_strm; + const char *path; + int ret; + + path = *uc_priv->path ? uc_priv->path : "/"; + ret = isofs_opendir(path, &iso_strm); + if (ret) + return log_msg_ret("edo", ret); + + priv->strm = iso_strm; + + return 0; +} + +static int isofs_dir_read(struct udevice *dev, struct fs_dir_stream *strm, + struct fs_dirent *dent) +{ + struct isofs_dir_priv *priv = dev_get_priv(dev); + struct fs_dirent *iso_dent; + int ret; + + ret = isofs_readdir(priv->strm, &iso_dent); + if (ret) + return ret; + + *dent = *iso_dent; + + return 0; +} + +static int isofs_dir_close(struct udevice *dev, struct fs_dir_stream *strm) +{ + struct isofs_dir_priv *priv = dev_get_priv(dev); + + isofs_closedir(priv->strm); + priv->strm = NULL; + + return 0; +} + +/* isofs file driver */ + +/** + * struct isofs_file_priv - Private info for isofs file devices + * + * @path: Full path within the isofs filesystem + */ +struct isofs_file_priv { + char path[FILE_MAX_PATH_LEN]; +}; + +static ssize_t isofs_vfs_read_iter(struct udevice *dev, struct iov_iter *iter, + loff_t pos) +{ + struct isofs_file_priv *priv = dev_get_priv(dev); + loff_t actual; + int ret; + + ret = isofs_read(priv->path, iter_iov_ptr(iter), pos, + iter_iov_avail(iter), &actual); + if (ret) + return log_msg_ret("efr", ret); + iter_advance(iter, actual); + + return actual; +} + +static struct file_ops isofs_file_ops = { + .read_iter = isofs_vfs_read_iter, +}; + +U_BOOT_DRIVER(isofs_vfs_file) = { + .name = "isofs_vfs_file", + .id = UCLASS_FILE, + .ops = &isofs_file_ops, + .priv_auto = sizeof(struct isofs_file_priv), +}; + +static int isofs_dir_open_file(struct udevice *dir, const char *leaf, + enum dir_open_flags_t oflags, + struct udevice **filp) +{ + struct dir_uc_priv *uc_priv = dev_get_uclass_priv(dir); + char path[FILE_MAX_PATH_LEN]; + struct isofs_file_priv *priv; + struct udevice *dev; + loff_t size = 0; + int ret; + + /* ISO 9660 is read-only */ + if (oflags != DIR_O_RDONLY) + return log_msg_ret("eow", -EROFS); + + if (*uc_priv->path) + snprintf(path, sizeof(path), "%s/%s", uc_priv->path, leaf); + else + snprintf(path, sizeof(path), "/%s", leaf); + + if (!isofs_exists(path)) + return log_msg_ret("eoe", -ENOENT); + ret = isofs_size(path, &size); + if (ret) + return log_msg_ret("eos", ret); + + ret = file_add_probe(dir, DM_DRIVER_REF(isofs_vfs_file), leaf, + size, oflags, &dev); + if (ret) + return log_msg_ret("eop", ret); + + priv = dev_get_priv(dev); + strlcpy(priv->path, path, sizeof(priv->path)); + *filp = dev; + + return 0; +} + +static struct dir_ops isofs_dir_vfs_ops = { + .open = isofs_dir_open, + .read = isofs_dir_read, + .close = isofs_dir_close, + .open_file = isofs_dir_open_file, +}; + +U_BOOT_DRIVER(isofs_vfs_dir) = { + .name = "isofs_vfs_dir", + .id = UCLASS_DIR, + .ops = &isofs_dir_vfs_ops, + .priv_auto = sizeof(struct isofs_dir_priv), +}; diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 6d629252575..3d9e9301a9f 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -492,7 +492,7 @@ root_found: #ifdef CONFIG_JOLIET if (joliet_level) { - char *p = opt->iocharset ? opt->iocharset : CONFIG_NLS_DEFAULT; + char *p = opt->iocharset ? opt->iocharset : CFG_NLS_DEFAULT; if (strcmp(p, "utf8") != 0) { sbi->s_nls_iocharset = opt->iocharset ? load_nls(opt->iocharset) : load_nls_default(); diff --git a/fs/isofs/interface.c b/fs/isofs/interface.c new file mode 100644 index 00000000000..6f07f9951f1 --- /dev/null +++ b/fs/isofs/interface.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * U-Boot interface for isofs filesystem (Linux port) + * + * Copyright 2026 Simon Glass + * + * This provides the interface between U-Boot's filesystem layer and + * the isofs driver (ISO 9660 CD-ROM filesystem). + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "isofs.h" + +/** + * struct isofs_state - global mount state for the isofs driver + * + * @blk_dev: Block device descriptor + * @partition: Partition info + * @sb: Superblock pointer + * @mounted: Whether a filesystem is currently mounted + */ +static struct isofs_state { + struct blk_desc *blk_dev; + struct disk_partition partition; + struct super_block *sb; + bool mounted; +} ifs; + +/** + * struct isofs_dir_actor_ctx - Context for directory listing callback + * @ctx: Base dir_context (must be first) + * @print: Whether to print entries + */ +struct isofs_dir_actor_ctx { + struct dir_context ctx; + bool print; +}; + +/** + * struct isofs_dir - isofs directory stream state + * @parent: base fs_dir_stream structure + * @dirent: directory entry to return to caller + * @dir_inode: pointer to directory inode + * @file: file structure for readdir + * @entry_found: flag set by actor when entry is captured + */ +struct isofs_dir { + struct fs_dir_stream parent; + struct fs_dirent dirent; + struct inode *dir_inode; + struct file file; + bool entry_found; +}; + +struct isofs_readdir_ctx { + struct dir_context ctx; + struct isofs_dir *dir; +}; + +/** + * isofs_probe() - Mount an ISO 9660 filesystem + * @fs_dev_desc: Block device descriptor + * @fs_partition: Partition info + * Return: 0 on success, negative on error + */ +int isofs_probe(struct blk_desc *fs_dev_desc, + struct disk_partition *fs_partition) +{ + struct isofs_options *opt; + struct super_block *sb; + struct fs_context fc; + loff_t part_offset; + u8 *buf; + int ret; + + if (!fs_dev_desc) + return -EINVAL; + + /* Close any previous mount */ + if (ifs.sb) + isofs_close(); + + /* Initialise inode cache */ + ret = isofs_init_inodecache(); + if (ret) + return ret; + + /* Allocate super_block */ + sb = kzalloc(sizeof(*sb), GFP_KERNEL); + if (!sb) { + ret = -ENOMEM; + goto err_destroy_cache; + } + INIT_LIST_HEAD(&sb->s_inodes); + + /* Allocate block_device */ + sb->s_bdev = kzalloc(sizeof(*sb->s_bdev), GFP_KERNEL); + if (!sb->s_bdev) { + ret = -ENOMEM; + goto err_free_sb; + } + + /* Initialise super_block fields */ + sb->s_bdev->bd_super = sb; + sb->s_bdev->bd_blk = fs_dev_desc->bdev; + sb->s_bdev->bd_part_start = fs_partition ? fs_partition->start : 0; + sb->s_blocksize = ISOFS_BLOCK_SIZE; + sb->s_blocksize_bits = ISOFS_BLOCK_BITS; + sb->s_flags = SB_RDONLY; /* ISO 9660 is always read-only */ + + /* Save device info */ + ifs.blk_dev = fs_dev_desc; + if (fs_partition) + ifs.partition = *fs_partition; + else + memset(&ifs.partition, '\0', sizeof(ifs.partition)); + ifs.mounted = true; + + /* Read first sector to verify it's an ISO filesystem */ + part_offset = fs_partition ? + (loff_t)fs_partition->start * fs_dev_desc->blksz : 0; + + buf = malloc(ISOFS_BLOCK_SIZE); + if (!buf) { + ret = -ENOMEM; + goto err_free_bdev; + } + + /* + * Read system area block 16 where the primary volume descriptor + * should be located. + */ + if (blk_read(fs_dev_desc->bdev, + (part_offset + 16 * ISOFS_BLOCK_SIZE) / fs_dev_desc->blksz, + ISOFS_BLOCK_SIZE / fs_dev_desc->blksz, buf) != + ISOFS_BLOCK_SIZE / fs_dev_desc->blksz) { + ret = -EIO; + goto err_free_buf; + } + + /* Quick check for ISO standard ID "CD001" at offset 1 */ + if (strncmp((char *)buf + 1, ISO_STANDARD_ID, 5)) { + ret = -EINVAL; + goto err_free_buf; + } + free(buf); + + /* Set up mount options */ + opt = kzalloc(sizeof(*opt), GFP_KERNEL); + if (!opt) { + ret = -ENOMEM; + goto err_free_bdev; + } + + opt->map = 'n'; + opt->rock = 1; + opt->joliet = 1; + opt->cruft = 0; + opt->hide = 0; + opt->showassoc = 0; + opt->check = 'u'; + opt->nocompress = 0; + opt->blocksize = ISOFS_BLOCK_SIZE; + opt->fmode = ISOFS_INVALID_MODE; + opt->dmode = ISOFS_INVALID_MODE; + opt->uid_set = 0; + opt->gid_set = 0; + opt->gid = GLOBAL_ROOT_GID; + opt->uid = GLOBAL_ROOT_UID; + opt->iocharset = NULL; + opt->overriderockperm = 0; + opt->session = -1; + opt->sbsector = -1; + + /* Set up fs_context */ + memset(&fc, '\0', sizeof(fc)); + fc.fs_private = opt; + fc.sb_flags = SB_RDONLY; + + /* Mount the filesystem */ + ret = isofs_fill_super(sb, &fc); + kfree(opt); + if (ret) { + printf("isofs: mount failed: %d\n", ret); + goto err_free_bdev; + } + + ifs.sb = sb; + return 0; + +err_free_buf: + free(buf); +err_free_bdev: + kfree(sb->s_bdev); +err_free_sb: + kfree(sb); +err_destroy_cache: + isofs_destroy_inodecache(); + ifs.mounted = false; + + return ret; +} + +/** + * isofs_close() - Unmount the ISO 9660 filesystem + */ +void isofs_close(void) +{ + struct super_block *sb = ifs.sb; + + if (!sb) + return; + + /* Free all inodes */ + isofs_free_inodes(sb); + + /* Free root dentry */ + kfree(sb->s_root); + sb->s_root = NULL; + + /* Free sb_info */ + kfree(sb->s_fs_info); + sb->s_fs_info = NULL; + + /* Clear cached buffers for this device before freeing it */ + bh_cache_clear(sb->s_bdev); + + /* Free block device */ + kfree(sb->s_bdev); + kfree(sb); + + ifs.sb = NULL; + + /* Destroy inode cache */ + isofs_destroy_inodecache(); + + ifs.blk_dev = NULL; + ifs.mounted = false; +} + +/** + * isofs_resolve_path() - Resolve a path to an inode + * + * This duplicates ext4l_resolve_path_internal(). Consider refactoring + * into a shared vfs_resolve_path() in linux_fs.c with a lookup callback. + * + * @path: Path to resolve + * @inodep: Output inode pointer + * Return: 0 on success, negative on error + */ +static int isofs_resolve_path(const char *path, struct inode **inodep) +{ + char *path_copy, *component, *next_component; + struct dentry *dentry, *result; + struct inode *dir; + int ret; + + if (!ifs.mounted) + return -ENODEV; + + dir = ifs.sb->s_root->d_inode; + + if (!path || !*path || !strcmp(path, "/")) { + *inodep = dir; + return 0; + } + + path_copy = strdup(path); + if (!path_copy) + return -ENOMEM; + + component = path_copy; + if (*component == '/') + component++; + + while (component && *component) { + next_component = strchr(component, '/'); + if (next_component) { + *next_component = '\0'; + next_component++; + } + + if (!*component || !strcmp(component, ".")) { + component = next_component; + continue; + } + + dentry = kzalloc(sizeof(*dentry), GFP_KERNEL); + if (!dentry) { + ret = -ENOMEM; + goto out_free; + } + + dentry->d_name.name = component; + dentry->d_name.len = strlen(component); + dentry->d_sb = ifs.sb; + dentry->d_parent = NULL; + + result = isofs_lookup(dir, dentry, 0); + + if (IS_ERR(result)) { + ret = PTR_ERR(result); + kfree(dentry); + goto out_free; + } + + if (result) { + if (!result->d_inode) { + if (result != dentry) + kfree(dentry); + kfree(result); + ret = -ENOENT; + goto out_free; + } + dir = result->d_inode; + if (result != dentry) + kfree(dentry); + kfree(result); + } else { + if (!dentry->d_inode) { + kfree(dentry); + ret = -ENOENT; + goto out_free; + } + dir = dentry->d_inode; + kfree(dentry); + } + + if (!dir) { + ret = -ENOENT; + goto out_free; + } + + /* + * Follow symlinks (Rock Ridge). Read the link target and + * resolve recursively. Limit depth to prevent loops. + */ + if (S_ISLNK(dir->i_mode)) { + /* Symlinks not yet implemented for isofs */ + ret = -ENOENT; + goto out_free; + } + + component = next_component; + } + + *inodep = dir; + ret = 0; + +out_free: + free(path_copy); + + return ret; +} + +static int isofs_dir_actor(struct dir_context *ctx, const char *name, + int namelen, loff_t offset, u64 ino, + unsigned int d_type) +{ + char namebuf[256]; + + if (namelen >= sizeof(namebuf)) + namelen = sizeof(namebuf) - 1; + memcpy(namebuf, name, namelen); + namebuf[namelen] = '\0'; + + if (d_type == DT_DIR) + printf(" %s/\n", namebuf); + else if (d_type == DT_LNK) + printf(" %s\n", namebuf); + else + printf(" %s\n", namebuf); + + return 0; +} + +int isofs_ls(const char *dirname) +{ + struct dir_context ctx; + struct inode *dir; + struct file file; + int ret; + + ret = isofs_resolve_path(dirname, &dir); + if (ret) + return ret; + + if (!S_ISDIR(dir->i_mode)) + return -ENOTDIR; + + memset(&file, '\0', sizeof(file)); + file.f_inode = dir; + file.f_mapping = dir->i_mapping; + + memset(&ctx, '\0', sizeof(ctx)); + ctx.actor = isofs_dir_actor; + + /* Call isofs_readdir via the file operations */ + if (dir->i_fop && dir->i_fop->iterate_shared) + ret = dir->i_fop->iterate_shared(&file, &ctx); + else + ret = -ENOTDIR; + + return ret; +} + +int isofs_exists(const char *filename) +{ + struct inode *inode; + + if (!filename) + return 0; + + if (isofs_resolve_path(filename, &inode)) + return 0; + + return 1; +} + +int isofs_size(const char *filename, loff_t *sizep) +{ + struct inode *inode; + int ret; + + ret = isofs_resolve_path(filename, &inode); + if (ret) + return ret; + + *sizep = inode->i_size; + + return 0; +} + +int isofs_read(const char *filename, void *buf, loff_t offset, loff_t len, + loff_t *actread) +{ + uint copy_len, blk_off, blksize; + loff_t bytes_left, file_size; + struct buffer_head *bh; + struct inode *inode; + sector_t block; + char *dst; + int ret; + + *actread = 0; + + ret = isofs_resolve_path(filename, &inode); + if (ret) { + printf("** File not found %s **\n", filename); + return ret; + } + + file_size = inode->i_size; + if (offset >= file_size) + return 0; + + /* If len is 0, read the whole file from offset */ + if (!len) + len = file_size - offset; + + /* Clamp to file size */ + if (offset + len > file_size) + len = file_size - offset; + + blksize = inode->i_sb->s_blocksize; + bytes_left = len; + dst = buf; + + while (bytes_left > 0) { + /* Calculate logical block number and offset within block */ + block = offset / blksize; + blk_off = offset % blksize; + + /* Read the block using isofs_bread (handles block mapping) */ + bh = isofs_bread(inode, block); + if (!bh) + return -EIO; + + /* Calculate how much to copy from this block */ + copy_len = blksize - blk_off; + if (copy_len > bytes_left) + copy_len = bytes_left; + + memcpy(dst, bh->b_data + blk_off, copy_len); + brelse(bh); + + dst += copy_len; + offset += copy_len; + bytes_left -= copy_len; + *actread += copy_len; + } + + return 0; +} + +static int isofs_opendir_actor(struct dir_context *ctx, const char *name, + int namelen, loff_t offset, u64 ino, + unsigned int d_type) +{ + struct isofs_readdir_ctx *rctx; + struct dentry de, *result; + struct fs_dirent *dent; + struct isofs_dir *dir; + + rctx = container_of(ctx, struct isofs_readdir_ctx, ctx); + dir = rctx->dir; + dent = &dir->dirent; + + namelen = min(namelen, (int)FS_DIRENT_NAME_LEN - 1); + memcpy(dent->name, name, namelen); + dent->name[namelen] = '\0'; + + dent->size = 0; + dent->type = FS_DT_REG; + + /* "." and ".." are always directories */ + if (namelen <= 2 && name[0] == '.') { + dent->type = FS_DT_DIR; + } else { + /* Look up the entry to get its inode for size and type */ + memset(&de, '\0', sizeof(de)); + de.d_name.name = (const unsigned char *)dent->name; + de.d_name.len = namelen; + de.d_sb = dir->dir_inode->i_sb; + + result = isofs_lookup(dir->dir_inode, &de, 0); + if (!IS_ERR_OR_NULL(result) && result->d_inode) { + dent->size = result->d_inode->i_size; + if (S_ISDIR(result->d_inode->i_mode)) + dent->type = FS_DT_DIR; + else if (S_ISLNK(result->d_inode->i_mode)) + dent->type = FS_DT_LNK; + } else if (de.d_inode) { + dent->size = de.d_inode->i_size; + if (S_ISDIR(de.d_inode->i_mode)) + dent->type = FS_DT_DIR; + else if (S_ISLNK(de.d_inode->i_mode)) + dent->type = FS_DT_LNK; + } + } + + dir->entry_found = true; + + /* Return non-zero to stop after one entry */ + return 1; +} + +int isofs_opendir(const char *filename, struct fs_dir_stream **dirsp) +{ + struct isofs_dir *dir; + struct inode *inode; + int ret; + + if (!ifs.mounted) + return -ENODEV; + + ret = isofs_resolve_path(filename, &inode); + if (ret) + return ret; + + if (!S_ISDIR(inode->i_mode)) + return -ENOTDIR; + + dir = calloc(1, sizeof(*dir)); + if (!dir) + return -ENOMEM; + + dir->dir_inode = inode; + dir->file.f_inode = inode; + dir->file.f_mapping = inode->i_mapping; + + *dirsp = (struct fs_dir_stream *)dir; + + return 0; +} + +int isofs_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp) +{ + struct isofs_dir *dir = container_of(dirs, struct isofs_dir, parent); + struct isofs_readdir_ctx rctx; + int ret; + + if (!ifs.mounted) + return -ENODEV; + + memset(&dir->dirent, '\0', sizeof(dir->dirent)); + dir->entry_found = false; + + memset(&rctx, '\0', sizeof(rctx)); + rctx.ctx.actor = isofs_opendir_actor; + rctx.ctx.pos = dir->file.f_pos; + rctx.dir = dir; + + if (dir->dir_inode->i_fop && dir->dir_inode->i_fop->iterate_shared) + ret = dir->dir_inode->i_fop->iterate_shared(&dir->file, + &rctx.ctx); + else + ret = -ENOTDIR; + + dir->file.f_pos = rctx.ctx.pos; + + if (ret < 0) + return ret; + + if (!dir->entry_found) + return -ENOENT; + + *dentp = &dir->dirent; + + return 0; +} + +void isofs_closedir(struct fs_dir_stream *dirs) +{ + free(dirs); +} diff --git a/fs/isofs/isofs_uboot.h b/fs/isofs/isofs_uboot.h new file mode 100644 index 00000000000..b543ecb8999 --- /dev/null +++ b/fs/isofs/isofs_uboot.h @@ -0,0 +1,308 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * U-Boot compatibility header for isofs filesystem + * + * Copyright 2026 Simon Glass + * + * This provides minimal definitions to allow Linux isofs code to compile + * in U-Boot. The isofs driver is read-only and much simpler than ext4, + * so the compatibility layer is lighter. + */ + +#ifndef __ISOFS_UBOOT_H__ +#define __ISOFS_UBOOT_H__ + +/* + * Suppress warnings for unused static functions and variables in Linux isofs + * source files. + */ +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wunused-variable" + +/* U-Boot headers */ +#include +#include + +/* Linux types - must come first */ +#include + +/* Linux headers */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Suppress printk messages from Linux isofs code. In Linux these are + * filtered by log level; in U-Boot printk maps to printf for all levels. + * The isofs code uses printk only for debug and error messages that are + * not useful in U-Boot's context. + */ +#undef printk +#define printk(fmt, ...) ({ if (0) printf(fmt, ##__VA_ARGS__); 0; }) + +/* Page allocation wrappers */ +#ifndef __get_free_page +#define __get_free_page(gfp) \ + ((unsigned long)memalign(PAGE_SIZE, PAGE_SIZE)) +#endif +void free_page(unsigned long addr); + +#define alloc_page(gfp) \ + ((struct page *)memalign(PAGE_SIZE, PAGE_SIZE)) +#define __free_page(page) free(page) + +/* Joliet support */ +#ifndef CFG_NLS_DEFAULT +#define CFG_NLS_DEFAULT "utf8" +#endif + +/* CDROM support stubs - not available in U-Boot */ +struct cdrom_device_info; + +static inline struct cdrom_device_info *disk_to_cdi(void *disk) +{ + return NULL; +} + +#define CDROM_LBA 0 +#define CDROM_DATA_TRACK 4 + +struct cdrom_tocentry { + int cdte_track; + int cdte_format; + struct { int lba; } cdte_addr; + int cdte_ctrl; +}; + +struct cdrom_multisession { + int addr_format; + int xa_flag; + struct { int lba; } addr; +}; + +static inline int cdrom_read_tocentry(struct cdrom_device_info *cdi, + struct cdrom_tocentry *te) +{ + return -ENODEV; +} + +static inline int cdrom_multisession(struct cdrom_device_info *cdi, + struct cdrom_multisession *ms) +{ + return -ENODEV; +} + +/* set_default_d_op - no dentry ops needed in U-Boot */ +static inline void set_default_d_op(struct super_block *sb, + const struct dentry_operations *ops) { } + +/* inode_nohighmem stub */ +static inline void inode_nohighmem(struct inode *inode) { } + +/* generic_ro_fops, page_symlink_inode_operations - defined in linux/fs.h */ + +/* generic_setlease - lease management stub */ +struct file_lease; +static inline int generic_setlease(struct file *fp, int arg, + struct file_lease **flp, void **priv) +{ + return -EINVAL; +} + +/* init_special_inode - override the macro from fs.h */ +#undef init_special_inode +static inline void init_special_inode(struct inode *inode, umode_t mode, + dev_t rdev) +{ + inode->i_mode = mode; + inode->i_rdev = rdev; +} + +/* User namespace helpers for mount options */ +static inline uid_t from_kuid_munged(void *ns, kuid_t uid) +{ + return uid.val; +} + +static inline gid_t from_kgid_munged(void *ns, kgid_t gid) +{ + return gid.val; +} + +/* gendisk is forward-declared in linux/blkdev.h */ + +/* + * mpage stubs - isofs uses mpage_read_folio/mpage_readahead for + * regular file I/O. We stub these since file reading goes through + * the interface layer directly. + */ +static inline int mpage_read_folio(struct folio *folio, + get_block_t *get_block) +{ + return -EIO; +} + +static inline void mpage_readahead(struct readahead_control *rac, + get_block_t *get_block) { } + +/* generic_block_bmap stub */ +static inline sector_t generic_block_bmap(struct address_space *mapping, + sector_t block, + get_block_t *get_block) +{ + return 0; +} + +/* huge_encode_dev - encode device for statfs */ +static inline u64 huge_encode_dev(dev_t dev) +{ + return dev; +} + +/* u64_to_fsid - convert u64 to kernel_fsid_t */ +static inline __kernel_fsid_t u64_to_fsid(u64 v) +{ + __kernel_fsid_t fsid; + + fsid.val[0] = v; + fsid.val[1] = v >> 32; + return fsid; +} + +/* NAME_MAX */ +#ifndef NAME_MAX +#define NAME_MAX 255 +#endif + +/* S_IXUGO */ +#ifndef S_IXUGO +#define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH) +#endif + +/* FILEID_INVALID for export.c */ +#ifndef FILEID_INVALID +#define FILEID_INVALID 0xff +#endif + +/* page_address - for page used as buffer */ +static inline void *page_address(void *page) +{ + return page; +} + +/* folio_address is defined in linux/pagemap.h */ +/* folio_end_read is defined as a macro in linux/pagemap.h */ + +/* Dentry operations - needed by isofs for case-insensitive lookup */ +struct dentry_operations { + int (*d_hash)(const struct dentry *dentry, struct qstr *qstr); + int (*d_compare)(const struct dentry *dentry, unsigned int len, + const char *str, const struct qstr *name); +}; + +/* Name hash functions for dentry operations */ +static inline unsigned long init_name_hash(const struct dentry *dentry) +{ + return 0; +} + +static inline unsigned long partial_name_hash(unsigned long c, + unsigned long prevhash) +{ + return (prevhash + (c << 4) + (c >> 4)) * 11; +} + +static inline unsigned long end_name_hash(unsigned long hash) +{ + return (unsigned int)hash; +} + +static inline unsigned int full_name_hash(const struct dentry *dentry, + const char *name, unsigned int len) +{ + unsigned long hash = init_name_hash(dentry); + + while (len--) + hash = partial_name_hash((unsigned char)*name++, hash); + return end_name_hash(hash); +} + +/* generic_file_llseek - stub for directory operations */ +static inline loff_t generic_file_llseek(struct file *file, loff_t offset, + int whence) +{ + return offset; +} + +/* isofs_close - forward declaration for interface.c */ +void isofs_close(void); + +/* + * iget5_locked - isofs uses custom test/set callbacks for inode lookup. + * Implemented in support.c. + */ +struct inode *iget5_locked(struct super_block *sb, unsigned long hashval, + int (*test)(struct inode *, void *), + int (*set)(struct inode *, void *), void *data); + +/* iget_failed - override the macro from fs.h */ +#undef iget_failed +void iget_failed(struct inode *inode); + +/* + * d_obtain_alias - override the stub macro from dcache.h. + * Needed by export.c for NFS file handle lookup. + */ +#undef d_obtain_alias +static inline struct dentry *d_obtain_alias(struct inode *inode) +{ + struct dentry *d; + + if (IS_ERR(inode)) + return ERR_CAST(inode); + d = kzalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + iput(inode); + return ERR_PTR(-ENOMEM); + } + d->d_inode = inode; + return d; +} + +/* dir_emit, dir_emit_dot, dir_emit_dotdot - defined in linux/fs.h */ + +/* sb_bread, brelse, etc. are now in fs/linux_fs.c */ +struct buffer_head *sb_bread(struct super_block *sb, sector_t block); +void bh_cache_clear(struct block_device *bdev); +void isofs_free_inodes(struct super_block *sb); + +/* isofs inode.c exports */ +int isofs_fill_super(struct super_block *s, struct fs_context *fc); +int isofs_init_inodecache(void); +void isofs_destroy_inodecache(void); + +#endif /* __ISOFS_UBOOT_H__ */ diff --git a/fs/isofs/support.c b/fs/isofs/support.c new file mode 100644 index 00000000000..1ac986d882e --- /dev/null +++ b/fs/isofs/support.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Internal support functions for isofs filesystem + * + * Copyright 2026 Simon Glass + * + * This provides isofs-specific support functions: iget5_locked() with + * custom test/set callbacks, and inode management. + * + * Common VFS functions (buffer cache, block I/O, brelse, dir_emit, etc.) + * are provided by fs/linux_fs.c. + */ + +#include +#include +#include + +#include "isofs.h" + +/** + * iget5_locked() - Get an inode with custom test/set callbacks + * @sb: Superblock + * @hashval: Hash value (unused in U-Boot) + * @test: Test function to check if inode matches + * @set: Set function to initialise inode data + * @data: Opaque data passed to test/set + * + * In U-Boot we always allocate a new inode since we don't cache them. + */ +struct inode *iget5_locked(struct super_block *sb, unsigned long hashval, + int (*test)(struct inode *, void *), + int (*set)(struct inode *, void *), void *data) +{ + struct iso_inode_info *ei; + struct inode *inode; + + ei = kzalloc(sizeof(*ei), GFP_KERNEL); + if (!ei) + return NULL; + + inode = &ei->vfs_inode; + memset(inode, '\0', sizeof(*inode)); + inode->i_sb = sb; + inode->i_blkbits = sb->s_blocksize_bits; + inode->i_state = I_NEW; + inode->i_count.counter = 1; + inode->i_mapping = &inode->i_data; + inode->i_data.host = inode; + INIT_LIST_HEAD(&inode->i_sb_list); + + if (set) + set(inode, data); + + list_add(&inode->i_sb_list, &sb->s_inodes); + + return inode; +} + +/** + * iget_failed() - Mark inode as failed and release + * @inode: Inode that failed to initialise + */ +void iget_failed(struct inode *inode) +{ + if (!inode) + return; + list_del_init(&inode->i_sb_list); + kfree(ISOFS_I(inode)); +} + +/** + * isofs_free_inodes() - Free all inodes on the superblock list + * @sb: Superblock whose inodes to free + */ +void isofs_free_inodes(struct super_block *sb) +{ + while (!list_empty(&sb->s_inodes)) { + struct inode *inode; + + inode = list_first_entry(&sb->s_inodes, + struct inode, i_sb_list); + list_del_init(&inode->i_sb_list); + kfree(ISOFS_I(inode)); + } +} diff --git a/include/fs_common.h b/include/fs_common.h index 396e36fa8ee..68c4f5c9a8f 100644 --- a/include/fs_common.h +++ b/include/fs_common.h @@ -22,6 +22,7 @@ enum fs_type_t { FS_TYPE_SEMIHOSTING, FS_TYPE_EXFAT, FS_TYPE_VIRTIO, + FS_TYPE_ISO, }; /* diff --git a/include/isofs.h b/include/isofs.h new file mode 100644 index 00000000000..e9e02011bc0 --- /dev/null +++ b/include/isofs.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Public interface for the isofs (ISO 9660) filesystem driver + * + * Copyright 2026 Simon Glass + */ + +#ifndef __ISOFS_H__ +#define __ISOFS_H__ + +#include +#include +#include + +/** + * isofs_probe() - Mount an ISO 9660 filesystem + * + * @fs_dev_desc: Block device descriptor + * @fs_partition: Partition info (can be NULL for whole disk) + * Return: 0 on success, negative on error + */ +int isofs_probe(struct blk_desc *fs_dev_desc, + struct disk_partition *fs_partition); + +/** + * isofs_close() - Unmount the ISO 9660 filesystem + */ +void isofs_close(void); + +/** + * isofs_ls() - List files in a directory + * + * @dirname: Directory path + * Return: 0 on success, negative on error + */ +int isofs_ls(const char *dirname); + +/** + * isofs_exists() - Check if a file exists + * + * @filename: File path + * Return: 1 if file exists, 0 if not + */ +int isofs_exists(const char *filename); + +/** + * isofs_size() - Get the size of a file + * + * @filename: File path + * @sizep: Output file size + * Return: 0 on success, negative on error + */ +int isofs_size(const char *filename, loff_t *sizep); + +/** + * isofs_read() - Read data from a file + * + * @filename: File path + * @buf: Output buffer + * @offset: Byte offset to read from + * @len: Number of bytes to read (0 = entire file) + * @actread: Output actual bytes read + * Return: 0 on success, negative on error + */ +int isofs_read(const char *filename, void *buf, loff_t offset, loff_t len, + loff_t *actread); + +/** + * isofs_opendir() - Open a directory for iteration + * + * @filename: Directory path + * @dirsp: Output directory stream pointer + * Return: 0 on success, negative on error + */ +int isofs_opendir(const char *filename, struct fs_dir_stream **dirsp); + +/** + * isofs_readdir() - Read next directory entry + * + * @dirs: Directory stream from isofs_opendir() + * @dentp: Output directory entry pointer + * Return: 0 on success, -ENOENT at end, negative on error + */ +int isofs_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp); + +/** + * isofs_closedir() - Close a directory stream + * + * @dirs: Directory stream to close + */ +void isofs_closedir(struct fs_dir_stream *dirs); + +#endif /* __ISOFS_H__ */ diff --git a/include/linux/slab.h b/include/linux/slab.h index 6722450a5cc..866db0230eb 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -72,6 +72,8 @@ static inline void *kzalloc(size_t size, gfp_t flags) return kmalloc(size, flags | __GFP_ZERO); } +#define kzalloc_obj(obj, ...) kzalloc(sizeof(obj), GFP_KERNEL) + static inline void *kmalloc_array(size_t n, size_t size, gfp_t flags) { if (size != 0 && n > SIZE_MAX / size) From patchwork Thu Apr 16 16:57:09 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2214 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358780; bh=1EQslYpPqGf7HQkqU78OcyTZA/rqe5RYXFDrl0gL4VY=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=Y/HJidc++DoaSVB3HHfnuRZrL4iOvi0EoLymrR04exrREVucgoZcl6gMm8raNDoMn MdaETF4l0nQpIcZ0d1gssNVg8O5jk/8lsZ2sS7bU8EUSgw2mLdjOA1UZ+B3vnY8u84 Ab0Aan7QX2yB8OOpq0vGI77GAZP+LuK//kPzv11g= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 42E326A4DC for ; Thu, 16 Apr 2026 10:59:40 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id n0Kg8sS4eG0w for ; Thu, 16 Apr 2026 10:59:40 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358780; bh=1EQslYpPqGf7HQkqU78OcyTZA/rqe5RYXFDrl0gL4VY=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=Y/HJidc++DoaSVB3HHfnuRZrL4iOvi0EoLymrR04exrREVucgoZcl6gMm8raNDoMn MdaETF4l0nQpIcZ0d1gssNVg8O5jk/8lsZ2sS7bU8EUSgw2mLdjOA1UZ+B3vnY8u84 Ab0Aan7QX2yB8OOpq0vGI77GAZP+LuK//kPzv11g= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 300256A4D8 for ; Thu, 16 Apr 2026 10:59:40 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358778; bh=jnYfBoQNaPUkydJ0nCfuPvEewmSbxEcqU8NdyKjMPoM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HcX3r3BS0LaBBRU5FXm5iXHv8Mh7E8ifZuT62JXzW9mNd5z63I0DvXeQwD5KR5VYo oL7g+50MPweh4AdBA45pz4rjqJYY46WME6sWd3Ltw7PSAs9zYnWI6L4YlJodoZ6A2l 2mNKF2ZWLvJQGmZpYoye5PsjinQVyfKQWtt25MBE= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id BF1986A4A5; Thu, 16 Apr 2026 10:59:38 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id 2yHTacnlHXJh; Thu, 16 Apr 2026 10:59:38 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358774; bh=zS0u7naAX4ypEfwk1Nr4L5vcFVkn41gANWEHbbjz+C4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nu1yXT+hUs1K/hdECLevS+UlinahHQDlC/UU4QD5sdp6fVGVdRkd3Pqv++EhrGzqu jYO49GhYdy3F2KawndpG80LnRkMdTsocUCop5EAz8DVOeHT/am3Egr51w2jHuCsjMy CzbpvHp1KxBopEIQYXibw1yP964iGt+6UYQqNeN4= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 5182D6A4B0; Thu, 16 Apr 2026 10:59:34 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:57:09 -0600 Message-ID: <20260416165733.2923423-20-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: UGAGUEBOFUOWE6TCFY2LXBZZLQQKY7HQ X-Message-ID-Hash: UGAGUEBOFUOWE6TCFY2LXBZZLQQKY7HQ X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 19/21] test: Add VFS tests for ISO 9660 filesystems List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Create a small ISO 9660 image in the pytest fixture using genisoimage with Rock Ridge extensions, containing the same test files used by the ext4 and FAT VFS tests (testfile.txt and subdir/nested.txt). Exercise read-only VFS operations on the mounted image: ls, cat, load, size, cd/pwd, and nested path access. Write operations (save, mkdir, rm, mv, ln) are not tested since ISO 9660 is a read-only filesystem. The genisoimage tool is needed in the test environment to create the ISO image. Signed-off-by: Simon Glass --- configs/sandbox_defconfig | 1 + doc/build/gcc.rst | 3 +- test/fs/Makefile | 1 + test/fs/vfs_iso.c | 219 ++++++++++++++++++++++++++++++ test/py/tests/fs_helper.py | 16 ++- test/py/tests/test_fs/test_vfs.py | 34 ++++- 6 files changed, 269 insertions(+), 5 deletions(-) create mode 100644 test/fs/vfs_iso.c diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 4307cb8acdc..3db6ba6c219 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -381,6 +381,7 @@ CONFIG_FS_CBFS=y CONFIG_FS_EXFAT=y CONFIG_EXT4_WRITE=y CONFIG_FS_EXT4L=y +CONFIG_FS_ISOFS=y CONFIG_FS_CRAMFS=y CONFIG_ADDR_MAP=y CONFIG_BACKTRACE=y diff --git a/doc/build/gcc.rst b/doc/build/gcc.rst index 847b39b0d7b..0b6db0b2317 100644 --- a/doc/build/gcc.rst +++ b/doc/build/gcc.rst @@ -24,7 +24,8 @@ Depending on the build targets further packages maybe needed .. code-block:: bash sudo apt-get install acpica-tools bc bison build-essential coccinelle \ - device-tree-compiler dfu-util efitools flex gdisk graphviz imagemagick \ + device-tree-compiler dfu-util efitools flex gdisk genisoimage \ + graphviz imagemagick \ libgnutls28-dev libguestfs-tools libncurses-dev \ libpython3-dev libsdl2-dev libssl-dev lz4 lzma lzma-alone openssl \ pkg-config python3 python3-asteval python3-coverage python3-filelock \ diff --git a/test/fs/Makefile b/test/fs/Makefile index 0e7d167a345..e07b9dc1367 100644 --- a/test/fs/Makefile +++ b/test/fs/Makefile @@ -3,3 +3,4 @@ obj-y += fs_basic.o obj-$(CONFIG_FS_EXT4L) += ext4l.o obj-$(CONFIG_$(PHASE_)VFS) += vfs.o +obj-$(CONFIG_FS_ISOFS) += vfs_iso.o diff --git a/test/fs/vfs_iso.c b/test/fs/vfs_iso.c new file mode 100644 index 00000000000..79fab28956f --- /dev/null +++ b/test/fs/vfs_iso.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * VFS tests for ISO 9660 filesystems + * + * These tests are marked UTF_MANUAL and are called from test_vfs.py + * which creates the ISO filesystem image using genisoimage. + * + * Copyright 2026 Simon Glass + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VFS_ARG_IMAGE 0 /* fs_image: path to filesystem image */ +#define BUF_ADDR 0x10000 + +/** + * iso_mount_image() - Bind a host image and mount it via VFS + * + * @uts: Unit test state + * @image: Path to filesystem image + * @devp: Returns the host device + * Return: 0 on success + */ +static int iso_mount_image(struct unit_test_state *uts, const char *image, + struct udevice **devp) +{ + struct udevice *blk; + struct blk_desc *desc; + + ut_assertok(vfs_init()); + ut_assertok(host_create_device("isotest", true, DEFAULT_BLKSZ, devp)); + ut_assertok(host_attach_file(*devp, image)); + ut_assertok(blk_get_from_parent(*devp, &blk)); + ut_assertok(device_probe(blk)); + desc = dev_get_uclass_plat(blk); + + ut_assertok(run_commandf("mount host %x:0 /mnt", desc->devnum)); + ut_assert_console_end(); + + return 0; +} + +/** + * iso_umount_image() - Unmount and clean up a host image + * + * @uts: Unit test state + * @dev: The host device to detach + * Return: 0 on success + */ +static int iso_umount_image(struct unit_test_state *uts, struct udevice *dev) +{ + ut_assertok(run_command("umount /mnt", 0)); + ut_assert_console_end(); + ut_assertok(host_detach_file(dev)); + ut_assertok(device_unbind(dev)); + + return 0; +} + +/* Test ls on VFS-mounted ISO filesystem */ +static int fs_test_vfs_iso_ls_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(VFS_ARG_IMAGE); + struct udevice *dev; + + ut_assertok(iso_mount_image(uts, fs_image, &dev)); + + ut_assertok(run_command("ls /mnt", 0)); + ut_assert_nextline("DIR 0 ."); + ut_assert_nextline("DIR 0 .."); + ut_assert_nextline("DIR 0 subdir"); + ut_assert_nextline(" 12 testfile.txt"); + ut_assert_console_end(); + + ut_assertok(iso_umount_image(uts, dev)); + + return 0; +} +FS_TEST_ARGS(fs_test_vfs_iso_ls_norun, + UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); + +/* Test cat on VFS-mounted ISO filesystem */ +static int fs_test_vfs_iso_cat_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(VFS_ARG_IMAGE); + struct udevice *dev; + + ut_assertok(iso_mount_image(uts, fs_image, &dev)); + + ut_assertok(run_command("cat /mnt/testfile.txt", 0)); + ut_assert_nextline("hello world"); + ut_assert_console_end(); + + ut_assertok(iso_umount_image(uts, dev)); + + return 0; +} +FS_TEST_ARGS(fs_test_vfs_iso_cat_norun, + UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); + +/* Test load on VFS-mounted ISO filesystem */ +static int fs_test_vfs_iso_load_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(VFS_ARG_IMAGE); + struct udevice *dev; + char *buf; + + ut_assertok(iso_mount_image(uts, fs_image, &dev)); + + buf = map_sysmem(BUF_ADDR, 0x100); + memset(buf, '\0', 0x100); + ut_assertok(run_commandf("load %x /mnt/testfile.txt", BUF_ADDR)); + ut_assert_nextline("12 bytes read"); + ut_assert_console_end(); + ut_asserteq_str("hello world\n", buf); + unmap_sysmem(buf); + + ut_assertok(iso_umount_image(uts, dev)); + + return 0; +} +FS_TEST_ARGS(fs_test_vfs_iso_load_norun, + UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); + +/* Test size on VFS-mounted ISO filesystem */ +static int fs_test_vfs_iso_size_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(VFS_ARG_IMAGE); + struct udevice *dev; + + ut_assertok(iso_mount_image(uts, fs_image, &dev)); + + env_set("filesize", NULL); + ut_assertok(run_command("size /mnt/testfile.txt", 0)); + ut_assert_console_end(); + ut_asserteq(12, env_get_hex("filesize", 0)); + + ut_assertok(iso_umount_image(uts, dev)); + + return 0; +} +FS_TEST_ARGS(fs_test_vfs_iso_size_norun, + UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); + +/* Test nested path access on VFS-mounted ISO filesystem */ +static int fs_test_vfs_iso_nested_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(VFS_ARG_IMAGE); + struct udevice *dev; + char *buf; + + ut_assertok(iso_mount_image(uts, fs_image, &dev)); + + /* Load file from subdirectory */ + buf = map_sysmem(BUF_ADDR, 0x100); + memset(buf, '\0', 0x100); + ut_assertok(run_commandf("load %x /mnt/subdir/nested.txt", BUF_ADDR)); + ut_assert_nextline("12 bytes read"); + ut_assert_console_end(); + ut_asserteq_str("hello world\n", buf); + unmap_sysmem(buf); + + ut_assertok(iso_umount_image(uts, dev)); + + return 0; +} +FS_TEST_ARGS(fs_test_vfs_iso_nested_norun, + UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); + +/* Test cd and pwd on VFS-mounted ISO filesystem */ +static int fs_test_vfs_iso_cd_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(VFS_ARG_IMAGE); + struct udevice *dev; + + ut_assertok(iso_mount_image(uts, fs_image, &dev)); + + ut_assertok(run_command("cd /mnt", 0)); + ut_assert_console_end(); + + ut_assertok(run_command("pwd", 0)); + ut_assert_nextline("/mnt"); + ut_assert_console_end(); + + /* ls without path should list cwd */ + ut_assertok(run_command("ls", 0)); + ut_assert_nextline("DIR 0 ."); + ut_assert_nextline("DIR 0 .."); + ut_assert_nextline("DIR 0 subdir"); + ut_assert_nextline(" 12 testfile.txt"); + ut_assert_console_end(); + + /* cd back to root */ + ut_assertok(run_command("cd /", 0)); + ut_assert_console_end(); + + ut_assertok(iso_umount_image(uts, dev)); + + return 0; +} +FS_TEST_ARGS(fs_test_vfs_iso_cd_norun, + UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); diff --git a/test/py/tests/fs_helper.py b/test/py/tests/fs_helper.py index 269b1c2d10f..d762f975582 100644 --- a/test/py/tests/fs_helper.py +++ b/test/py/tests/fs_helper.py @@ -101,7 +101,7 @@ class FsHelper: master key (via --master-key-file), enabling pre_derived unlock. """ if ('fat' not in fs_type and 'ext' not in fs_type and - fs_type not in ['exfat', 'fs_generic']): + fs_type not in ['exfat', 'fs_generic', 'iso']): raise ValueError(f"Unsupported filesystem type '{fs_type}'") self.config = config @@ -164,6 +164,11 @@ class FsHelper: def mk_fs(self): """Make a new filesystem and copy in the files""" self.setup() + + if self.fs_type == 'iso': + self._mk_iso() + return + mkfs_opt, fs_lnxtype = self._get_fs_args() fs_img = self.fs_img with open(fs_img, 'wb') as fsi: @@ -195,6 +200,15 @@ class FsHelper: if self.passphrase or self.encrypt_keyfile: self.encrypt_luks() + def _mk_iso(self): + """Make an ISO 9660 filesystem image with Rock Ridge extensions""" + fs_img = self.fs_img + self._do_cleanup = True + quiet = '-quiet' if self.quiet else '' + check_call(f'genisoimage -R {quiet} -o {fs_img} {self.srcdir}', + shell=True, stdout=DEVNULL if self.quiet else None, + stderr=DEVNULL if self.quiet else None) + def setup(self): """Set up the srcdir ready to receive files""" if not self.srcdir: diff --git a/test/py/tests/test_fs/test_vfs.py b/test/py/tests/test_fs/test_vfs.py index 7f0fbdc802e..f4c6108efd0 100644 --- a/test/py/tests/test_fs/test_vfs.py +++ b/test/py/tests/test_fs/test_vfs.py @@ -4,9 +4,9 @@ # Test VFS operations on real filesystem images """ -Test VFS mount, ls, stat, load, save, cat, mkdir, rm, mv on ext4 and -FAT images. Python creates the filesystem image; C tests mount it via -VFS and exercise the operations. +Test VFS mount, ls, stat, load, save, cat, mkdir, rm, mv on ext4, +FAT and ISO 9660 images. Python creates the filesystem image; C tests +mount it via VFS and exercise the operations. """ import os @@ -96,3 +96,31 @@ class TestVfsFat: # Attach shared test methods to TestVfsFat for _name in VFS_TESTS: setattr(TestVfsFat, f'test_{_name}', _define_test(_name)) + + +# Read-only tests applicable to ISO 9660 +ISO_TESTS = ['cat', 'cd', 'load', 'ls', 'nested', 'size'] + + +@pytest.mark.buildconfigspec('cmd_vfs') +@pytest.mark.buildconfigspec('fs_isofs') +class TestVfsIso: + """Test VFS operations on an ISO 9660 filesystem.""" + + @pytest.fixture(scope='class') + def fs_image(self, u_boot_config): + """Create an ISO 9660 filesystem image with test files.""" + fsh = FsHelper(u_boot_config, 'iso', 0, 'vfs') + fsh.setup() + _populate_srcdir(fsh.srcdir) + fsh.mk_fs() + + yield fsh.fs_img + + fsh.cleanup() + + +# Attach read-only test methods to TestVfsIso (using vfs_iso prefix) +for _name in ISO_TESTS: + setattr(TestVfsIso, f'test_{_name}', + _define_test(_name, 'vfs_iso')) From patchwork Thu Apr 16 16:57:10 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2215 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358782; bh=nw1syzYZa26bBv5lYfwMOXjJJDIGjI8FO8BTDo+THk4=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=Q66fSpNpTdRK7+qiiO6axbjgA4UT1sqJTnYrupHI5RfwE148dIAKdfXbAG8CWxU9T /hWXPEpSvsjNtkIlrFb+TCFtPIRXdZ9gMIlq0C4fxY/lif+Qt8c7X3kSENm7WttVoe 77JozO01y9Nf2Kui2HGKDfOCP3OUt6GS7LjMiTAQ= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id B2ED36A4A5 for ; Thu, 16 Apr 2026 10:59:42 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id inAzb7KO5sXg for ; Thu, 16 Apr 2026 10:59:42 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358781; bh=nw1syzYZa26bBv5lYfwMOXjJJDIGjI8FO8BTDo+THk4=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=hUpWtew5CUtyCEbb/HhtlIhyvqkAcdtCHbYAYZOIsueAuvDynCdZpTy6Pt9rnRBoG KBm1xSee6FyN4xtYhuhqCNwc15NbDWiNxDzjoenGlpbLDcHKW4iDYHfIEZXcZDwmO5 5HjaTVg6r2sIAXVQPrIrlcaSI7sUJz74Vfgrc85c= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 9D9AC6A4B0 for ; Thu, 16 Apr 2026 10:59:41 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358779; bh=CL5OPl9ZyzLlwf55oXH5vcOsgbjslRh3+Kts01oNfZg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pgVV9RN4sP2LdnC2kbghnDTCchk9avq+uKRVScE76AvytjuSnka1RnTuiWdcakhVJ JMfhAahHMLRL7iw0cnTVa+g0rV079kKyupC9PqaESHiVSyY9jYihOvoSgaADeW2vbG Y/uoAMKUznPiT/plY8TBte0jdk3D2ZHrl9YZvTzU= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id C99D16A4B0; Thu, 16 Apr 2026 10:59:39 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id b8UBdxTcQpuO; Thu, 16 Apr 2026 10:59:39 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358779; bh=TxdHKTS7JKP02SygIjJ1CsIanIsxv1Fgjsj/QxkRZ3s=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=cPR2/1vxvBwPVKaYLx6CJ1t+bw16XlvY+GiHHElOqIdx31uKcZpkb13de/Gf5agFI qz26EHENrQnMcYeZXHD/fHokDEZHOjQY01fGvAhL1aD4nVi2zKbnwILesrjjObl8wX nOdSt5BDFDLf1kd55rSJ8XHlxe6/5q0QiBabTP2c= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 54C9D6A4A5; Thu, 16 Apr 2026 10:59:39 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:57:10 -0600 Message-ID: <20260416165733.2923423-21-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 3FL5QPC3KGATIPWR5NHRSLMHE5MDC2TC X-Message-ID-Hash: 3FL5QPC3KGATIPWR5NHRSLMHE5MDC2TC X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 20/21] docker: Add genisoimage for ISO 9660 tests List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass The VFS ISO 9660 tests create a test image using genisoimage with Rock Ridge extensions. Add it to the CI Docker image. Signed-off-by: Simon Glass --- tools/docker/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile index 02b9875be96..36318217b77 100644 --- a/tools/docker/Dockerfile +++ b/tools/docker/Dockerfile @@ -84,6 +84,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ flex \ gawk \ gdisk \ + genisoimage \ gettext \ git \ gnu-efi \ From patchwork Thu Apr 16 16:57:11 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2216 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358785; bh=OLntQ3kDpmEOuN/jTCxYXdwJCZO6zV2TsYVsKjwVOac=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=RVgQM6v00x9M5OigZOVTzQIdUrndheTtMOr4HTyfNT4kKGN+i2nM3S6Q7pjBldW6g 2BdyHRUpbSygd0+fOov2d2ujKtav+uzECG1aGpPj5dd1+V4Ue/r+llmip4iZLfZUns t7MpGBBt4leb21NoO62CkJdc2ag089CoLObweg0s= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 1F3D96A4DA for ; Thu, 16 Apr 2026 10:59:45 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id GznLeJa7Y1lj for ; Thu, 16 Apr 2026 10:59:45 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358785; bh=OLntQ3kDpmEOuN/jTCxYXdwJCZO6zV2TsYVsKjwVOac=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=RVgQM6v00x9M5OigZOVTzQIdUrndheTtMOr4HTyfNT4kKGN+i2nM3S6Q7pjBldW6g 2BdyHRUpbSygd0+fOov2d2ujKtav+uzECG1aGpPj5dd1+V4Ue/r+llmip4iZLfZUns t7MpGBBt4leb21NoO62CkJdc2ag089CoLObweg0s= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 0A6086A4B0 for ; Thu, 16 Apr 2026 10:59:45 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358782; bh=RxbbPybM9hQxQ4+Ra9yfScUAksDRnw30CWAm6H0c8ig=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NBGJiTeBspYqc6puIW9AfneDwF7zVveGmIpPXGPcZKmeaK0NjrwA0xj6iJm/ZT2TT N8BSElGe3Dq2F0Mizf+hQWBkNKX5pj6MhWULWGVIaetHNQYPyRqGi8oiPbFQt0eS3Q 3me4Z7HLkiY+rNHl2k9D1n7+FFw4WZlBIc3hcuug= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id A1A386A4D8; Thu, 16 Apr 2026 10:59:42 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id CijeJz8ZoPl6; Thu, 16 Apr 2026 10:59:42 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776358780; bh=C2fOxDhSefSCEOasWyPA5CzatOSjXmFuK+J8YBZtqyU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=QtsC6p1joAEeWTYKcMOi1ibGa5ui787bba6RsAeYotuN4Qt5WDtAZNCFLb7sih6ob 2ckzGxLKiKbbM+ZRDh76HDJZP4uI2auvWuwkcVI+nBdC/aJ6byZbUAWMw3JDXcZPkv NCmQbWHhLGHX6rGSm330CEn3Mhl7lko9c96i2nR4= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 6262F6A4E8; Thu, 16 Apr 2026 10:59:40 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 16 Apr 2026 10:57:11 -0600 Message-ID: <20260416165733.2923423-22-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416165733.2923423-1-sjg@u-boot.org> References: <20260416165733.2923423-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: UPXL5BBAZ2XC22UP35TPVF4QP6QQAULD X-Message-ID-Hash: UPXL5BBAZ2XC22UP35TPVF4QP6QQAULD X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 21/21] CI: Update container to noble-20251001-13Apr2026 List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Rebuild the CI runner image with genisoimage included for the ISO 9660 tests. Update .gitlab-ci.yml and .azure-pipelines.yml to refer to the new tag. Signed-off-by: Simon Glass --- .azure-pipelines.yml | 2 +- .gitlab-ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index b7a685a7961..11393997933 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -2,7 +2,7 @@ variables: windows_vm: windows-2022 ubuntu_vm: ubuntu-24.04 macos_vm: macOS-14 - ci_runner_image: sjg20/u-boot-gitlab-ci-runner:noble-20251001-10Apr2026 + ci_runner_image: sjg20/u-boot-gitlab-ci-runner:noble-20251001-13Apr2026 # Ensure we do a shallow clone Agent.Source.Git.ShallowFetchDepth: 1 # Add '-u 0' options for Azure pipelines, otherwise we get "permission diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5c2cf647408..7b9bfb7c04e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -27,7 +27,7 @@ default: - ${DEFAULT_TAG} # Grab our configured image. The source for this is found # in the u-boot tree at tools/docker/Dockerfile - image: ${MIRROR_DOCKER}/sjg20/u-boot-gitlab-ci-runner:noble-20251001-10Apr2026 + image: ${MIRROR_DOCKER}/sjg20/u-boot-gitlab-ci-runner:noble-20251001-13Apr2026 services: - name: container-$(CI_JOB_ID) command: ["--rm"]