From nobody Fri Nov 29 20:56:13 2024 Received: from mail.tlmp.cc (unknown [148.135.17.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5350215AD96; Mon, 16 Sep 2024 13:56:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.135.17.20 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726495011; cv=none; b=oBKRJPFMkCd9djlkKKx/g5hQ/QOywSAyROIk4HE6zemSAZsEoB062OFhTruQcRXIvNTjINMzo2BEBjWZeTs+urrDjBuuVCZqfD4LiQ0QN8dy4lhHtoDH6dv+/kkydsflfycJb4c0Ad1PS28wajBBZj8kZl7Gd7GWq+uTU85csBY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726495011; c=relaxed/simple; bh=+GE0m90lwhlb9OljwVtEQsDCa+zy3VtJ1xGQMp1f3Pc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LiC7jHtUnRY+vqz9NuQlC/4NSiswY6CTVdp1yQDDfucpN3CD0++AGQIuJXHh87A7/kNlw8Ptin6gsZqqdTbMA9QHfXDqWji0Y2SKGONV6iSaqW17n7JeAFdfOB25p0N9XZkf8OlCcF6eGVR5FNer/KIAl7hxFxwq9IThO6yPX40= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=tlmp.cc; spf=pass smtp.mailfrom=tlmp.cc; dkim=pass (2048-bit key) header.d=tlmp.cc header.i=@tlmp.cc header.b=h2GVWAST; arc=none smtp.client-ip=148.135.17.20 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=tlmp.cc Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=tlmp.cc Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=tlmp.cc header.i=@tlmp.cc header.b="h2GVWAST" Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 142B66984E; Mon, 16 Sep 2024 09:56:46 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tlmp.cc; s=dkim; t=1726495008; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=3lB7vMUDAoeBGlHx0db3VH42iw8zhtpQjBkF7LrP3EU=; b=h2GVWASTJojpiXbZ3KpUm3pheSuuyx/JanOK1t7Ybvtvqmp3+KVE1DHEHRd0TFyN9fhgMp eUGeq008fBE+5C6wnxcZwwHN4ZBKplzgWbF95ymWTg3duKL92DzWAWc9UQqApkWQvuIDeG gg2jXnIPZf3GVARjUk/HXee5uTZ+pwWgFF0XvcGOhX+a3z8grxTrGDfo1IlBv4JhszOb+L baOaTpLxv6kdiDcpeAXPDCvVeXliIEnvtZ8y43PCjjHeEu5g4qhZa8mM4IpiAr0KMLndQg Mj5u6MYFpRjxuY0Cd6hoVsshWsKiTe+Hb18JdsoOV0p+Lwa5JErTZ2bUO883uA== From: Yiyang Wu To: linux-erofs@lists.ozlabs.org Cc: rust-for-linux@vger.kernel.org, linux-fsdevel@vger.kernel.org, LKML Subject: [RFC PATCH 05/24] erofs: add inode data structure in Rust Date: Mon, 16 Sep 2024 21:56:15 +0800 Message-ID: <20240916135634.98554-6-toolmanp@tlmp.cc> X-Mailer: git-send-email 2.46.0 In-Reply-To: <20240916135634.98554-1-toolmanp@tlmp.cc> References: <20240916135634.98554-1-toolmanp@tlmp.cc> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" This patch introduces the same on-disk erofs data structure in rust and also introduces multiple helpers for inode i_format and chunk_indexing and later can be used to implement map_blocks. Signed-off-by: Yiyang Wu --- fs/erofs/rust/erofs_sys.rs | 1 + fs/erofs/rust/erofs_sys/inode.rs | 291 +++++++++++++++++++++++++++++++ 2 files changed, 292 insertions(+) create mode 100644 fs/erofs/rust/erofs_sys/inode.rs diff --git a/fs/erofs/rust/erofs_sys.rs b/fs/erofs/rust/erofs_sys.rs index 6f3c12665ed6..34267ec7772d 100644 --- a/fs/erofs/rust/erofs_sys.rs +++ b/fs/erofs/rust/erofs_sys.rs @@ -24,6 +24,7 @@ pub(crate) type PosixResult =3D Result; =20 pub(crate) mod errnos; +pub(crate) mod inode; pub(crate) mod superblock; pub(crate) mod xattrs; pub(crate) use errnos::Errno; diff --git a/fs/erofs/rust/erofs_sys/inode.rs b/fs/erofs/rust/erofs_sys/ino= de.rs new file mode 100644 index 000000000000..1762023e97f8 --- /dev/null +++ b/fs/erofs/rust/erofs_sys/inode.rs @@ -0,0 +1,291 @@ +use super::xattrs::*; +use super::*; +use core::ffi::*; +use core::mem::size_of; + +/// Represents the compact bitfield of the Erofs Inode format. +#[repr(transparent)] +#[derive(Clone, Copy)] +pub(crate) struct Format(u16); + +pub(crate) const INODE_VERSION_MASK: u16 =3D 0x1; +pub(crate) const INODE_VERSION_BIT: u16 =3D 0; + +pub(crate) const INODE_LAYOUT_BIT: u16 =3D 1; +pub(crate) const INODE_LAYOUT_MASK: u16 =3D 0x7; + +/// Helper macro to extract property from the bitfield. +macro_rules! extract { + ($name: expr, $bit: expr, $mask: expr) =3D> { + ($name >> $bit) & ($mask) + }; +} + +/// The Version of the Inode which represents whether this inode is extend= ed or compact. +/// Extended inodes have more infos about nlinks + mtime. +/// This is documented in https://erofs.docs.kernel.org/en/latest/core_ond= isk.html#inodes +#[repr(C)] +#[derive(Clone, Copy)] +pub(crate) enum Version { + Compat, + Extended, + Unknown, +} + +/// Represents the data layout backed by the Inode. +/// As Documented in https://erofs.docs.kernel.org/en/latest/core_ondisk.h= tml#inode-data-layouts +#[repr(C)] +#[derive(Clone, Copy, PartialEq)] +pub(crate) enum Layout { + FlatPlain, + CompressedFull, + FlatInline, + CompressedCompact, + Chunk, + Unknown, +} + +#[repr(C)] +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug, PartialEq)] +pub(crate) enum Type { + Regular, + Directory, + Link, + Character, + Block, + Fifo, + Socket, + Unknown, +} + +/// This is format extracted from i_format bit representation. +/// This includes various infos and specs about the inode. +impl Format { + pub(crate) fn version(&self) -> Version { + match extract!(self.0, INODE_VERSION_BIT, INODE_VERSION_MASK) { + 0 =3D> Version::Compat, + 1 =3D> Version::Extended, + _ =3D> Version::Unknown, + } + } + + pub(crate) fn layout(&self) -> Layout { + match extract!(self.0, INODE_LAYOUT_BIT, INODE_LAYOUT_MASK) { + 0 =3D> Layout::FlatPlain, + 1 =3D> Layout::CompressedFull, + 2 =3D> Layout::FlatInline, + 3 =3D> Layout::CompressedCompact, + 4 =3D> Layout::Chunk, + _ =3D> Layout::Unknown, + } + } +} + +/// Represents the compact inode which resides on-disk. +/// This is documented in https://erofs.docs.kernel.org/en/latest/core_ond= isk.html#inodes +#[repr(C)] +#[derive(Clone, Copy)] +pub(crate) struct CompactInodeInfo { + pub(crate) i_format: Format, + pub(crate) i_xattr_icount: u16, + pub(crate) i_mode: u16, + pub(crate) i_nlink: u16, + pub(crate) i_size: u32, + pub(crate) i_reserved: [u8; 4], + pub(crate) i_u: [u8; 4], + pub(crate) i_ino: u32, + pub(crate) i_uid: u16, + pub(crate) i_gid: u16, + pub(crate) i_reserved2: [u8; 4], +} + +/// Represents the extended inode which resides on-disk. +/// This is documented in https://erofs.docs.kernel.org/en/latest/core_ond= isk.html#inodes +#[repr(C)] +#[derive(Clone, Copy)] +pub(crate) struct ExtendedInodeInfo { + pub(crate) i_format: Format, + pub(crate) i_xattr_icount: u16, + pub(crate) i_mode: u16, + pub(crate) i_reserved: [u8; 2], + pub(crate) i_size: u64, + pub(crate) i_u: [u8; 4], + pub(crate) i_ino: u32, + pub(crate) i_uid: u32, + pub(crate) i_gid: u32, + pub(crate) i_mtime: u64, + pub(crate) i_mtime_nsec: u32, + pub(crate) i_nlink: u32, + pub(crate) i_reserved2: [u8; 16], +} + +/// Represents the inode info which is either compact or extended. +#[derive(Clone, Copy)] +pub(crate) enum InodeInfo { + Extended(ExtendedInodeInfo), + Compact(CompactInodeInfo), +} + +pub(crate) const CHUNK_BLKBITS_MASK: u16 =3D 0x1f; +pub(crate) const CHUNK_FORMAT_INDEX_BIT: u16 =3D 0x20; + +/// Represents on-disk chunk index of the file backing inode. +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub(crate) struct ChunkIndex { + pub(crate) advise: u16, + pub(crate) device_id: u16, + pub(crate) blkaddr: u32, +} + +impl From<[u8; 8]> for ChunkIndex { + fn from(u: [u8; 8]) -> Self { + let advise =3D u16::from_le_bytes([u[0], u[1]]); + let device_id =3D u16::from_le_bytes([u[2], u[3]]); + let blkaddr =3D u32::from_le_bytes([u[4], u[5], u[6], u[7]]); + ChunkIndex { + advise, + device_id, + blkaddr, + } + } +} + +/// Chunk format used for indicating the chunkbits and chunkindex. +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub(crate) struct ChunkFormat(pub(crate) u16); + +impl ChunkFormat { + pub(crate) fn is_chunkindex(&self) -> bool { + self.0 & CHUNK_FORMAT_INDEX_BIT !=3D 0 + } + pub(crate) fn chunkbits(&self) -> u16 { + self.0 & CHUNK_BLKBITS_MASK + } +} + +/// Represents the inode spec which is either data or device. +#[derive(Clone, Copy, Debug)] +#[repr(u32)] +pub(crate) enum Spec { + Chunk(ChunkFormat), + RawBlk(u32), + Device(u32), + CompressedBlocks(u32), + Unknown, +} + +/// Convert the spec from the format of the inode based on the layout. +impl From<(&[u8; 4], Layout)> for Spec { + fn from(value: (&[u8; 4], Layout)) -> Self { + match value.1 { + Layout::FlatInline | Layout::FlatPlain =3D> Spec::RawBlk(u32::= from_le_bytes(*value.0)), + Layout::CompressedFull | Layout::CompressedCompact =3D> { + Spec::CompressedBlocks(u32::from_le_bytes(*value.0)) + } + Layout::Chunk =3D> Self::Chunk(ChunkFormat(u16::from_le_bytes(= [value.0[0], value.0[1]]))), + // We don't support compressed inlines or compressed chunks cu= rrently. + _ =3D> Spec::Unknown, + } + } +} + +/// Helper functions for Inode Info. +impl InodeInfo { + const S_IFMT: u16 =3D 0o170000; + const S_IFSOCK: u16 =3D 0o140000; + const S_IFLNK: u16 =3D 0o120000; + const S_IFREG: u16 =3D 0o100000; + const S_IFBLK: u16 =3D 0o60000; + const S_IFDIR: u16 =3D 0o40000; + const S_IFCHR: u16 =3D 0o20000; + const S_IFIFO: u16 =3D 0o10000; + const S_ISUID: u16 =3D 0o4000; + const S_ISGID: u16 =3D 0o2000; + const S_ISVTX: u16 =3D 0o1000; + pub(crate) fn ino(&self) -> u32 { + match self { + Self::Extended(extended) =3D> extended.i_ino, + Self::Compact(compact) =3D> compact.i_ino, + } + } + + pub(crate) fn format(&self) -> Format { + match self { + Self::Extended(extended) =3D> extended.i_format, + Self::Compact(compact) =3D> compact.i_format, + } + } + + pub(crate) fn file_size(&self) -> Off { + match self { + Self::Extended(extended) =3D> extended.i_size, + Self::Compact(compact) =3D> compact.i_size as u64, + } + } + + pub(crate) fn inode_size(&self) -> Off { + match self { + Self::Extended(_) =3D> 64, + Self::Compact(_) =3D> 32, + } + } + + pub(crate) fn spec(&self) -> Spec { + let mode =3D match self { + Self::Extended(extended) =3D> extended.i_mode, + Self::Compact(compact) =3D> compact.i_mode, + }; + + let u =3D match self { + Self::Extended(extended) =3D> &extended.i_u, + Self::Compact(compact) =3D> &compact.i_u, + }; + + match mode & 0o170000 { + 0o40000 | 0o100000 | 0o120000 =3D> Spec::from((u, self.format(= ).layout())), + // We don't support device inodes currently. + _ =3D> Spec::Unknown, + } + } + + pub(crate) fn inode_type(&self) -> Type { + let mode =3D match self { + Self::Extended(extended) =3D> extended.i_mode, + Self::Compact(compact) =3D> compact.i_mode, + }; + match mode & Self::S_IFMT { + Self::S_IFDIR =3D> Type::Directory, // Directory + Self::S_IFREG =3D> Type::Regular, // Regular File + Self::S_IFLNK =3D> Type::Link, // Symbolic Link + Self::S_IFIFO =3D> Type::Fifo, // FIFO + Self::S_IFSOCK =3D> Type::Socket, // Socket + Self::S_IFBLK =3D> Type::Block, // Block + Self::S_IFCHR =3D> Type::Character, // Character + _ =3D> Type::Unknown, + } + } + + pub(crate) fn xattr_size(&self) -> Off { + match self { + Self::Extended(extended) =3D> { + size_of::() as Off + + (size_of::() as Off) * (extended.i_xattr_icou= nt as Off - 1) + } + Self::Compact(_) =3D> 0, + } + } + + pub(crate) fn xattr_count(&self) -> u16 { + match self { + Self::Extended(extended) =3D> extended.i_xattr_icount, + Self::Compact(compact) =3D> compact.i_xattr_icount, + } + } +} + +pub(crate) type CompactInodeInfoBuf =3D [u8; size_of::()= ]; +pub(crate) type ExtendedInodeInfoBuf =3D [u8; size_of::= ()]; +pub(crate) const DEFAULT_INODE_BUF: ExtendedInodeInfoBuf =3D [0; size_of::= ()]; --=20 2.46.0