* [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
@ 2023-06-09 6:29 Ariel Miculas
2023-06-09 6:29 ` [PATCH 01/80] rust: add definitions for ref-counted inodes and dentries Ariel Miculas
` (77 more replies)
0 siblings, 78 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:29 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Hi all!
This is a proof of concept driver written for the PuzzleFS
next-generation container filesystem [1]. I've included a short abstract
about puzzlefs further below. This driver is based on the rust-next
branch, on top of which I've backported the filesystem abstractions from
Wedson Almeida Filho [2][3] and Miguel Ojeda's third-party crates
support: proc-macro2, quote, syn, serde and serde_derive [4]. I've added
the additional third-party crates serde_cbor[5] and hex [6]. Then I've
adapted the user space puzzlefs code [1] so that the puzzlefs kernel
module could present the directory hierarchy and implement the basic
read functionality.
For some additional context, puzzlefs was started by Tycho Andersen and
it's the successor of atomfs. This FOSDEM presentation from 2019 [12]
covers the rationale for a new oci image format and presents a higher
level overview of our goals with puzzlefs.
I've split the rest of the cover letter in following sections (using a
markdown style):
* Example: it describes a practical example of what was achieved
* Limitations: it presents the existing limitations of this POC
* Upstreaming steps: it describes the steps needed for upstreaming this
driver
* Setup: it shows how to setup the necessary environment for testing the
puzzlefs driver
* Puzzlefs abstract: it provides a short overview of puzzlefs
# Example
An example is provided below:
```
~ # cat /proc/filesystems | grep puzzlefs
nodev puzzlefs
~ # mount -t puzzlefs -o oci_root_dir="/home/puzzlefs_oci" -o image_manifest="2d
6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b" none /mnt
~ # ls -lR /mnt/
/mnt/:
total 0
drwxr-xr-x 2 0 0 0 Jun 8 12:26 dir-1
drwxr-xr-x 2 0 0 0 Jun 8 12:26 dir-2
drwxr-xr-x 2 0 0 0 Jun 8 12:26 dir-3
drwxr-xr-x 2 0 0 0 Jun 8 12:26 dir-4
-rw-r--r-- 1 0 0 0 Jun 8 12:26 file1
-rw-r--r-- 1 0 0 0 Jun 8 12:26 file2
/mnt/dir-1:
total 0
/mnt/dir-2:
total 0
/mnt/dir-3:
total 0
/mnt/dir-4:
total 0
~ # cat /mnt/file2
ana are mere bla bla bla
~ # wc /mnt/file1
202 202 5454 /mnt/file1
```
In this example, /home/puzzlefs_oci is the puzzlefs oci directory:
```
~ # ls -lR /home/puzzlefs_oci/
/home/puzzlefs_oci/:
total 8
drwxr-xr-x 3 1000 1000 0 Jun 8 14:33 blobs
-rw-r--r-- 1 1000 1000 266 Jun 8 14:33 index.json
-rw-r--r-- 1 1000 1000 37 Jun 8 14:33 oci-layout
/home/puzzlefs_oci/blobs:
total 0
drwxr-xr-x 2 1000 1000 0 Jun 8 14:33 sha256
/home/puzzlefs_oci/blobs/sha256:
total 16
-rw------- 1 1000 1000 89 Jun 8 14:33 2d6602d678140540dc7e96de652a76a8b16eb
-rw------- 1 1000 1000 925 Jun 8 14:33 4df03518eea406343dbb55046720f6a478881
-rw------- 1 1000 1000 5479 Jun 8 14:33 d86a87b19bd9a2fec0d31687c1d669cdb59eb
```
`2d6602d678140540dc7e96de652a76a8b16eb` is the puzzlefs image manifest
hash for the first_try tag:
```
$ cat /tmp/oci-simple/index.json | jq .
{
"schemaVersion": -1,
"manifests": [
{
"digest": "sha256:2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b",
"size": 89,
"media_type": "application/vnd.puzzlefs.image.rootfs.v1",
"annotations": {
"org.opencontainers.image.ref.name": "first_try"
}
}
],
"annotations": {}
}
```
I will describe how to build a puzzlefs image in the `Setup` section, at
step 5.
# Limitations
One limitation is that the puzzlefs driver doesn't implement any lookup
functionality and instead it inserts every directory entry into the
dcache during init (see the `DCACHE_BASED` constant). This is similar to
how the sample `rust_fs` driver works, but the goal is to implement
proper lookup functions. However, more filesystem abstractions need to
be implemented before this can be achieved.
# Upstreaming steps
Before the puzzlefs driver can be upstreamed, the following need to be
merged:
* Wedson's filesystem abstractions [3]
* the necessary third-party crates [4] (with the preliminary discussion
about whether this is desirable)
# Setup
My setup is based on Wedson's tutorial [8]. Next, I will describe the
necessary steps to build an initrd and run a custom kernel under qemu.
1. Get the linux rust-next branch [9] and apply this patchset
2. Follow the rust quickstart guide [10]
3. configure and build the kernel
```
$ make LLVM=1 allnoconfig qemu-busybox-min.config rust.config
$ make LLVM=1 -j$(nproc)
```
4. setup busybox
```
git clone git://git.busybox.net/busybox
cd busybox
make menuconfig # enable 'Build static binary' config
make
make install
```
This will create the `_install` directory with the rootfs inside it.
5. create a home directory in the rootfs and copy a puzzlefs oci
directory in home/puzzlefs_oci
To create a puzzlefs oci directory:
* download this custom puzzlefs repository [11] (it's custom because we
want to build an image without verity data)
* run `make release`
* create a simple filesystem structure with a few directories and files
(I've created one at ../test-puzzlefs/simple_rootfs)
* build a puzzlefs oci image at
`~/work/busybox/_install/home/puzzlefs_oci` (replace this path with
your busybox path) with the tag `first_try`:
```
$ target/release/puzzlefs build --omit-verity \
../test-puzzlefs/simple_rootfs ~/work/busybox/_install/home/puzzlefs_oci \
first_try
```
* get first_try's image manifest from index.json (inside `puzzlefs_oci`)
```
$ cat index.json | jq . | grep digest
"digest": "sha256:2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b",
```
6. add the following 'init' script in the busybox rootfs (rootfs path
defaults to `./_install'):
```
#!/bin/sh
mount -t devtmpfs none /dev
mkdir -p /proc
mount -t proc none /proc
ifconfig lo up
udhcpc -i eth0
mkdir /mnt
mount -t puzzlefs -o oci_root_dir="/home/puzzlefs_oci" -o \
image_manifest="2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b" \
none /mnt
setsid sh -c 'exec sh -l </dev/ttyS0 >/dev/ttyS0 2>&1'
```
Make sure to replace the `image_manifest` with your own digest. This
init script will be passed to rdinit in the kernel command line.
7. generate the initramfs
Assuming busybox is in `~/work/busybox`:
```
cd ~/work/busybox/_install && find . | cpio -H newc -o | gzip > ../ramdisk.img
```
This will generate a compressed ramdisk image in
`~/work/busybox/ramdisk.img`.
8. run with qemu (this assumes the linux tree is at '../linux' and busybox
is at '../busybox'):
```
qemu-system-x86_64 \
-accel kvm \
-cpu host \
-m 4G \
-initrd ../busybox/ramdisk.img \
-kernel ../linux/arch/x86/boot/bzImage \
-nographic \
-append 'console=ttyS0 nokaslr debug rdinit=/init' \
-nic user,model=rtl8139 \
-no-reboot
```
9. Check whether puzzlefs has been successfully mounted
```
~ # mount | grep puzzlefs
none on /mnt type puzzlefs (rw,relatime)
~ # ls /mnt/
dir-1 dir-2 dir-3 dir-4 file1 file2
```
# Puzzlefs abstract
Puzzlefs [1] is a container filesystem designed to address the
limitations of the existing OCI format. The main goals of the project
are reduced duplication, reproducible image builds, direct mounting
support and memory safety guarantees, some inspired by the OCIv2 design
document [7].
Reduced duplication is achieved using the content defined chunking
algorithm FastCDC. This implementation allows chunks to be shared among
layers. Building a new layer starting from an existing one allows
reusing most of the chunks.
Another goal of the project is reproducible image builds, which is
achieved by defining a canonical representation of the image format.
Direct mounting support is a key feature of puzzlefs and, together with
fs-verity, it provides data integrity. Currently, puzzlefs is
implemented as a userspace filesystem (FUSE). A read-only kernel
filesystem driver is underway.
Lastly, memory safety is critical to puzzlefs, leading to the decision
to implement it in Rust. Another goal is to share the same code between
user space and kernel space in order to provide one secure
implementation.
[1] https://github.com/anuvu/puzzlefs
[2] https://github.com/wedsonaf/linux/tree/fs
[3] https://github.com/Rust-for-Linux/linux/issues/1004
[4] https://github.com/Rust-for-Linux/linux/pull/1007
[5] https://docs.rs/serde_cbor/latest/serde_cbor/
[6] https://docs.rs/hex/0.4.3/hex/
[7] https://hackmd.io/@cyphar/ociv2-brainstorm
[8] https://www.youtube.com/watch?v=tPs1uRqOnlk
[9] https://github.com/Rust-for-Linux/linux/tree/rust-next
[10] https://docs.kernel.org/rust/quick-start.html
[11] https://github.com/ariel-miculas/puzzlefs/tree/support-no-verity-data
[12] https://archive.fosdem.org/2019/schedule/event/containers_atomfs/
Ariel Miculas (58):
rust: kernel: add libraries required by the filesystem abstractions
rust: kernel: backport the delay module from the rust branch
rust: kernel: add container_of macro
rust: kernel: add offset_of macro
drop: Add crate::pr_warn declaration
rust: kernel: rename from_kernel_errno to from_errno
rust: kernel: Rename from_pointer to from_foreing and into_pointer to
into_foreign
rust: kernel: add count_paren_items macro, needed by define_fs_params
macro
rust: helpers: add missing rust helper 'alloc_pages'
kernel: configs: add qemu-busybox-min.config
rust: kernel: format the rust code
samples: puzzlefs: add initial puzzlefs sample, copied from rust_fs.rs
kernel: configs: enable rust samples in rust.config
Add SAMPLE_RUST_SERDE in rust.config
rust: kernel: fix compile errors after rebase to rust-next
rust: serde_cbor: import crate
rust: serde_cbor: add SPDX License Identifiers
rust: serde_cbor: add no_fp_fmt_parse support
rust: Kbuild: enable serde_cbor
samples: rust: add cbor serialize/deserialize example
rust: serde_cbor: add support for serde_cbor's from_slice method by
using a custom alloc_kernel feature
rust: serde: add support for deserializing Vec with kernel_alloc
feature
rust: file: Replace UnsafeCell with Opaque for File
rust: kernel: implement fmt::Debug for CString
samples: puzzlefs: rename RustFs to PuzzleFs
samples: puzzlefs: add basic deserializing support for the puzzlefs
metadata
rust: file: present the filesystem context to the open function
rust: kernel: add an abstraction over vfsmount to allow cloning a new
private mount
rust: file: add from_path, from_path_in_root_mnt and read_with_offset
methods to File
samples: puzzlefs: pass the Vfsmount structure from open to read and
return the contents of the data file inside /home/puzzlefs_oci
rust: file: move from_path, from_path_in_root_mnt and read_with_offset
methods to a RegularFile newtype
rust: file: ensure RegularFile can only create regular files
rust: file: add get_pos method to RegularFile
rust: file: add methods read_to_end, get_file_size and update_pos to
RegularFile
rust: file: define a minimal Read trait and implement it for
RegularFile
samples: puzzlefs: add cbor_get_array_size method
samples: puzzlefs: add KernelError to WireFormatError and implement
From conversion
samples: puzzlefs: implement new for MetadataBlob
samples: puzzlefs: build puzzlefs into the kernel, thus avoiding the
need to export rust symbols
rust: alloc: add try_clone for Vec<T>
rust: alloc: add from_iter_fallible for Vec<T>
samples: puzzlefs: implement to_errno and from_errno for
WireFormatError
samples: puzzlefs: add TryReserveError (and from conversion) to
WireFormatError
samples: puzzlefs: add higher level inode related functionality
samples: puzzlefs: populate the directory entries with the inodes from
the puzzlefs metadata file
rust: hex: import crate
rust: hex: add SPDX license identifiers
rust: Kbuild: enable `hex`
rust: hex: implement FromHex trait and hex::decode using a custom
kernel_alloc feature
rust: hex: add encode_hex_iter and encode_hex_upper_iter methods
rust: puzzlefs: add HexError to WireFormatError and implement the From
conversion
rust: puzzlefs: display the error value for
WireFormatError::KernelError
samples: puzzlefs: add Rootfs and Digest structs to types.rs
samples: puzzlefs: implement the conversion from WireFormatError to
kernel::error::Error
rust: puzzlefs: read the puzzlefs image manifest instead of an
individual metadata layer
rust: puzzlefs: rename PuzzleFs to PuzzleFsModule to avoid confusion
with the PuzzleFS struct
rust: puzzlefs: add support for reading files
rust: puzzlefs: add oci_root_dir and image_manifest filesystem
parameters
Miguel Ojeda (15):
rust: proc-macro2: import crate
rust: proc-macro2: add SPDX License Identifiers
rust: proc-macro2: remove `unicode_ident` dependency
rust: quote: import crate
rust: quote: add SPDX License Identifiers
rust: syn: import crate
rust: syn: add SPDX License Identifiers
rust: syn: remove `unicode-ident` dependency
rust: serde: import crate
rust: serde: add `no_fp_fmt_parse` support
rust: serde: add SPDX License Identifiers
rust: serde_derive: import crate
rust: serde_derive: add SPDX License Identifiers
rust: Kbuild: enable `proc-macro2`, `quote`, `syn`, `serde` and
`serde_derive`
rust: test `serde` support
Wedson Almeida Filho (7):
rust: add definitions for ref-counted inodes and dentries
rust: add ability to register a file system
rust: define fs context
rust: add support for file system parameters
rust: allow fs driver to initialise new superblocks
rust: add `module_fs` macro
WIP: rust: allow fs to be populated
Makefile | 14 +-
arch/x86/configs/qemu-busybox-min.config | 11 +
kernel/configs/qemu-busybox-min.config | 56 +
kernel/configs/rust.config | 11 +
rust/.gitignore | 1 +
rust/Makefile | 232 +-
rust/alloc/vec/mod.rs | 48 +
rust/bindings/bindings_helper.h | 14 +
rust/bindings/lib.rs | 5 +
rust/helpers.c | 76 +
rust/hex/error.rs | 78 +
rust/hex/lib.rs | 506 +++
rust/hex/serde.rs | 104 +
rust/kernel/cred.rs | 46 +
rust/kernel/delay.rs | 104 +
rust/kernel/driver.rs | 28 +
rust/kernel/error.rs | 52 +-
rust/kernel/file.rs | 1117 ++++++
rust/kernel/fs.rs | 1478 ++++++++
rust/kernel/fs/param.rs | 558 +++
rust/kernel/io_buffer.rs | 153 +
rust/kernel/iov_iter.rs | 81 +
rust/kernel/lib.rs | 83 +
rust/kernel/mm.rs | 149 +
rust/kernel/mount.rs | 66 +
rust/kernel/pages.rs | 144 +
rust/kernel/str.rs | 6 +
rust/kernel/test_serde.rs | 26 +
rust/kernel/test_serde/de.rs | 439 +++
rust/kernel/test_serde/error.rs | 73 +
rust/kernel/test_serde/ser.rs | 466 +++
rust/kernel/user_ptr.rs | 175 +
rust/proc-macro2/detection.rs | 77 +
rust/proc-macro2/fallback.rs | 1004 ++++++
rust/proc-macro2/lib.rs | 1341 ++++++++
rust/proc-macro2/marker.rs | 20 +
rust/proc-macro2/parse.rs | 874 +++++
rust/proc-macro2/rcvec.rs | 144 +
rust/proc-macro2/wrapper.rs | 996 ++++++
rust/quote/ext.rs | 112 +
rust/quote/format.rs | 170 +
rust/quote/ident_fragment.rs | 88 +
rust/quote/lib.rs | 1436 ++++++++
rust/quote/runtime.rs | 440 +++
rust/quote/spanned.rs | 45 +
rust/quote/to_tokens.rs | 211 ++
rust/serde/de/format.rs | 32 +
rust/serde/de/ignored_any.rs | 246 ++
rust/serde/de/impls.rs | 2755 +++++++++++++++
rust/serde/de/mod.rs | 2313 +++++++++++++
rust/serde/de/seed.rs | 21 +
rust/serde/de/utf8.rs | 48 +
rust/serde/de/value.rs | 1718 ++++++++++
rust/serde/integer128.rs | 84 +
rust/serde/lib.rs | 351 ++
rust/serde/macros.rs | 238 ++
rust/serde/private/de.rs | 2997 ++++++++++++++++
rust/serde/private/doc.rs | 161 +
rust/serde/private/mod.rs | 52 +
rust/serde/private/ser.rs | 1316 +++++++
rust/serde/private/size_hint.rs | 23 +
rust/serde/ser/fmt.rs | 180 +
rust/serde/ser/impls.rs | 987 ++++++
rust/serde/ser/impossible.rs | 218 ++
rust/serde/ser/mod.rs | 1992 +++++++++++
rust/serde/std_error.rs | 50 +
rust/serde_cbor/de.rs | 1370 ++++++++
rust/serde_cbor/error.rs | 320 ++
rust/serde_cbor/lib.rs | 371 ++
rust/serde_cbor/read.rs | 647 ++++
rust/serde_cbor/ser.rs | 748 ++++
rust/serde_cbor/tags.rs | 224 ++
rust/serde_cbor/value/de.rs | 168 +
rust/serde_cbor/value/mod.rs | 158 +
rust/serde_cbor/value/ser.rs | 447 +++
rust/serde_cbor/write.rs | 177 +
rust/serde_derive/bound.rs | 408 +++
rust/serde_derive/de.rs | 3148 +++++++++++++++++
rust/serde_derive/dummy.rs | 46 +
rust/serde_derive/fragment.rs | 76 +
rust/serde_derive/internals/ast.rs | 204 ++
rust/serde_derive/internals/attr.rs | 1908 +++++++++++
rust/serde_derive/internals/case.rs | 199 ++
rust/serde_derive/internals/check.rs | 445 +++
rust/serde_derive/internals/ctxt.rs | 64 +
rust/serde_derive/internals/mod.rs | 30 +
rust/serde_derive/internals/receiver.rs | 287 ++
rust/serde_derive/internals/respan.rs | 18 +
rust/serde_derive/internals/symbol.rs | 71 +
rust/serde_derive/lib.rs | 112 +
rust/serde_derive/pretend.rs | 203 ++
rust/serde_derive/ser.rs | 1342 ++++++++
rust/serde_derive/this.rs | 34 +
rust/serde_derive/try.rs | 26 +
rust/syn/attr.rs | 664 ++++
rust/syn/await.rs | 4 +
rust/syn/bigint.rs | 68 +
rust/syn/buffer.rs | 400 +++
rust/syn/custom_keyword.rs | 255 ++
rust/syn/custom_punctuation.rs | 302 ++
rust/syn/data.rs | 495 +++
rust/syn/derive.rs | 276 ++
rust/syn/discouraged.rs | 196 ++
rust/syn/error.rs | 430 +++
rust/syn/export.rs | 41 +
rust/syn/expr.rs | 3560 +++++++++++++++++++
rust/syn/ext.rs | 141 +
rust/syn/file.rs | 127 +
rust/syn/gen/clone.rs | 2243 ++++++++++++
rust/syn/gen/debug.rs | 3044 +++++++++++++++++
rust/syn/gen/eq.rs | 2197 ++++++++++++
rust/syn/gen/fold.rs | 3343 ++++++++++++++++++
rust/syn/gen/hash.rs | 2871 ++++++++++++++++
rust/syn/gen/visit.rs | 3788 +++++++++++++++++++++
rust/syn/gen/visit_mut.rs | 3788 +++++++++++++++++++++
rust/syn/gen_helper.rs | 156 +
rust/syn/generics.rs | 1339 ++++++++
rust/syn/group.rs | 284 ++
rust/syn/ident.rs | 103 +
rust/syn/item.rs | 3315 ++++++++++++++++++
rust/syn/lib.rs | 985 ++++++
rust/syn/lifetime.rs | 156 +
rust/syn/lit.rs | 1602 +++++++++
rust/syn/lookahead.rs | 171 +
rust/syn/mac.rs | 221 ++
rust/syn/macros.rs | 179 +
rust/syn/op.rs | 236 ++
rust/syn/parse.rs | 1316 +++++++
rust/syn/parse_macro_input.rs | 181 +
rust/syn/parse_quote.rs | 169 +
rust/syn/pat.rs | 929 +++++
rust/syn/path.rs | 856 +++++
rust/syn/print.rs | 18 +
rust/syn/punctuated.rs | 1070 ++++++
rust/syn/reserved.rs | 46 +
rust/syn/sealed.rs | 6 +
rust/syn/span.rs | 69 +
rust/syn/spanned.rs | 116 +
rust/syn/stmt.rs | 351 ++
rust/syn/thread.rs | 43 +
rust/syn/token.rs | 1015 ++++++
rust/syn/tt.rs | 109 +
rust/syn/ty.rs | 1288 +++++++
rust/syn/verbatim.rs | 35 +
rust/syn/whitespace.rs | 67 +
samples/rust/Kconfig | 28 +
samples/rust/Makefile | 3 +
samples/rust/local_data_format/de.rs | 422 +++
samples/rust/local_data_format/error.rs | 73 +
samples/rust/local_data_format/ser.rs | 443 +++
samples/rust/puzzle.rs | 4 +
samples/rust/puzzle/error.rs | 91 +
samples/rust/puzzle/inode.rs | 150 +
samples/rust/puzzle/oci.rs | 71 +
samples/rust/puzzle/types.rs | 389 +++
samples/rust/puzzle/types/cbor_helpers.rs | 50 +
samples/rust/puzzlefs.rs | 220 ++
samples/rust/rust_fs.rs | 105 +
samples/rust/rust_serde.rs | 125 +
scripts/Makefile.build | 4 +-
160 files changed, 89204 insertions(+), 29 deletions(-)
create mode 100644 arch/x86/configs/qemu-busybox-min.config
create mode 100644 kernel/configs/qemu-busybox-min.config
create mode 100644 rust/hex/error.rs
create mode 100644 rust/hex/lib.rs
create mode 100644 rust/hex/serde.rs
create mode 100644 rust/kernel/cred.rs
create mode 100644 rust/kernel/delay.rs
create mode 100644 rust/kernel/driver.rs
create mode 100644 rust/kernel/file.rs
create mode 100644 rust/kernel/fs.rs
create mode 100644 rust/kernel/fs/param.rs
create mode 100644 rust/kernel/io_buffer.rs
create mode 100644 rust/kernel/iov_iter.rs
create mode 100644 rust/kernel/mm.rs
create mode 100644 rust/kernel/mount.rs
create mode 100644 rust/kernel/pages.rs
create mode 100644 rust/kernel/test_serde.rs
create mode 100644 rust/kernel/test_serde/de.rs
create mode 100644 rust/kernel/test_serde/error.rs
create mode 100644 rust/kernel/test_serde/ser.rs
create mode 100644 rust/kernel/user_ptr.rs
create mode 100644 rust/proc-macro2/detection.rs
create mode 100644 rust/proc-macro2/fallback.rs
create mode 100644 rust/proc-macro2/lib.rs
create mode 100644 rust/proc-macro2/marker.rs
create mode 100644 rust/proc-macro2/parse.rs
create mode 100644 rust/proc-macro2/rcvec.rs
create mode 100644 rust/proc-macro2/wrapper.rs
create mode 100644 rust/quote/ext.rs
create mode 100644 rust/quote/format.rs
create mode 100644 rust/quote/ident_fragment.rs
create mode 100644 rust/quote/lib.rs
create mode 100644 rust/quote/runtime.rs
create mode 100644 rust/quote/spanned.rs
create mode 100644 rust/quote/to_tokens.rs
create mode 100644 rust/serde/de/format.rs
create mode 100644 rust/serde/de/ignored_any.rs
create mode 100644 rust/serde/de/impls.rs
create mode 100644 rust/serde/de/mod.rs
create mode 100644 rust/serde/de/seed.rs
create mode 100644 rust/serde/de/utf8.rs
create mode 100644 rust/serde/de/value.rs
create mode 100644 rust/serde/integer128.rs
create mode 100644 rust/serde/lib.rs
create mode 100644 rust/serde/macros.rs
create mode 100644 rust/serde/private/de.rs
create mode 100644 rust/serde/private/doc.rs
create mode 100644 rust/serde/private/mod.rs
create mode 100644 rust/serde/private/ser.rs
create mode 100644 rust/serde/private/size_hint.rs
create mode 100644 rust/serde/ser/fmt.rs
create mode 100644 rust/serde/ser/impls.rs
create mode 100644 rust/serde/ser/impossible.rs
create mode 100644 rust/serde/ser/mod.rs
create mode 100644 rust/serde/std_error.rs
create mode 100644 rust/serde_cbor/de.rs
create mode 100644 rust/serde_cbor/error.rs
create mode 100644 rust/serde_cbor/lib.rs
create mode 100644 rust/serde_cbor/read.rs
create mode 100644 rust/serde_cbor/ser.rs
create mode 100644 rust/serde_cbor/tags.rs
create mode 100644 rust/serde_cbor/value/de.rs
create mode 100644 rust/serde_cbor/value/mod.rs
create mode 100644 rust/serde_cbor/value/ser.rs
create mode 100644 rust/serde_cbor/write.rs
create mode 100644 rust/serde_derive/bound.rs
create mode 100644 rust/serde_derive/de.rs
create mode 100644 rust/serde_derive/dummy.rs
create mode 100644 rust/serde_derive/fragment.rs
create mode 100644 rust/serde_derive/internals/ast.rs
create mode 100644 rust/serde_derive/internals/attr.rs
create mode 100644 rust/serde_derive/internals/case.rs
create mode 100644 rust/serde_derive/internals/check.rs
create mode 100644 rust/serde_derive/internals/ctxt.rs
create mode 100644 rust/serde_derive/internals/mod.rs
create mode 100644 rust/serde_derive/internals/receiver.rs
create mode 100644 rust/serde_derive/internals/respan.rs
create mode 100644 rust/serde_derive/internals/symbol.rs
create mode 100644 rust/serde_derive/lib.rs
create mode 100644 rust/serde_derive/pretend.rs
create mode 100644 rust/serde_derive/ser.rs
create mode 100644 rust/serde_derive/this.rs
create mode 100644 rust/serde_derive/try.rs
create mode 100644 rust/syn/attr.rs
create mode 100644 rust/syn/await.rs
create mode 100644 rust/syn/bigint.rs
create mode 100644 rust/syn/buffer.rs
create mode 100644 rust/syn/custom_keyword.rs
create mode 100644 rust/syn/custom_punctuation.rs
create mode 100644 rust/syn/data.rs
create mode 100644 rust/syn/derive.rs
create mode 100644 rust/syn/discouraged.rs
create mode 100644 rust/syn/error.rs
create mode 100644 rust/syn/export.rs
create mode 100644 rust/syn/expr.rs
create mode 100644 rust/syn/ext.rs
create mode 100644 rust/syn/file.rs
create mode 100644 rust/syn/gen/clone.rs
create mode 100644 rust/syn/gen/debug.rs
create mode 100644 rust/syn/gen/eq.rs
create mode 100644 rust/syn/gen/fold.rs
create mode 100644 rust/syn/gen/hash.rs
create mode 100644 rust/syn/gen/visit.rs
create mode 100644 rust/syn/gen/visit_mut.rs
create mode 100644 rust/syn/gen_helper.rs
create mode 100644 rust/syn/generics.rs
create mode 100644 rust/syn/group.rs
create mode 100644 rust/syn/ident.rs
create mode 100644 rust/syn/item.rs
create mode 100644 rust/syn/lib.rs
create mode 100644 rust/syn/lifetime.rs
create mode 100644 rust/syn/lit.rs
create mode 100644 rust/syn/lookahead.rs
create mode 100644 rust/syn/mac.rs
create mode 100644 rust/syn/macros.rs
create mode 100644 rust/syn/op.rs
create mode 100644 rust/syn/parse.rs
create mode 100644 rust/syn/parse_macro_input.rs
create mode 100644 rust/syn/parse_quote.rs
create mode 100644 rust/syn/pat.rs
create mode 100644 rust/syn/path.rs
create mode 100644 rust/syn/print.rs
create mode 100644 rust/syn/punctuated.rs
create mode 100644 rust/syn/reserved.rs
create mode 100644 rust/syn/sealed.rs
create mode 100644 rust/syn/span.rs
create mode 100644 rust/syn/spanned.rs
create mode 100644 rust/syn/stmt.rs
create mode 100644 rust/syn/thread.rs
create mode 100644 rust/syn/token.rs
create mode 100644 rust/syn/tt.rs
create mode 100644 rust/syn/ty.rs
create mode 100644 rust/syn/verbatim.rs
create mode 100644 rust/syn/whitespace.rs
create mode 100644 samples/rust/local_data_format/de.rs
create mode 100644 samples/rust/local_data_format/error.rs
create mode 100644 samples/rust/local_data_format/ser.rs
create mode 100644 samples/rust/puzzle.rs
create mode 100644 samples/rust/puzzle/error.rs
create mode 100644 samples/rust/puzzle/inode.rs
create mode 100644 samples/rust/puzzle/oci.rs
create mode 100644 samples/rust/puzzle/types.rs
create mode 100644 samples/rust/puzzle/types/cbor_helpers.rs
create mode 100644 samples/rust/puzzlefs.rs
create mode 100644 samples/rust/rust_fs.rs
create mode 100644 samples/rust/rust_serde.rs
--
2.40.1
^ permalink raw reply [flat|nested] 134+ messages in thread
* [PATCH 01/80] rust: add definitions for ref-counted inodes and dentries
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
@ 2023-06-09 6:29 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 02/80] rust: add ability to register a file system Ariel Miculas
` (76 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:29 UTC (permalink / raw)
To: rust-for-linux; +Cc: Wedson Almeida Filho
From: Wedson Almeida Filho <wedsonaf@google.com>
This is in preparation for adding support for the implementation of file
systems in Rust.
Signed-off-by: Wedson Almeida Filho <wedsonaf@google.com>
---
rust/bindings/bindings_helper.h | 1 +
rust/helpers.c | 7 +++++
rust/kernel/fs.rs | 53 +++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 1 +
4 files changed, 62 insertions(+)
create mode 100644 rust/kernel/fs.rs
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 50e7a76d5455..eeb5b7c0528a 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -10,6 +10,7 @@
#include <linux/refcount.h>
#include <linux/wait.h>
#include <linux/sched.h>
+#include <linux/fs.h>
/* `bindgen` gets confused at certain things. */
const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;
diff --git a/rust/helpers.c b/rust/helpers.c
index 81e80261d597..e00c53c94c43 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -26,6 +26,7 @@
#include <linux/spinlock.h>
#include <linux/sched/signal.h>
#include <linux/wait.h>
+#include <linux/fs_parser.h>
__noreturn void rust_helper_BUG(void)
{
@@ -128,6 +129,12 @@ void rust_helper_put_task_struct(struct task_struct *t)
}
EXPORT_SYMBOL_GPL(rust_helper_put_task_struct);
+struct dentry *rust_helper_dget(struct dentry *dentry)
+{
+ return dget(dentry);
+}
+EXPORT_SYMBOL_GPL(rust_helper_dget);
+
/*
* We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
* as the Rust `usize` type, so we can use it in contexts where Rust
diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs
new file mode 100644
index 000000000000..157fdb917ad1
--- /dev/null
+++ b/rust/kernel/fs.rs
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! File systems.
+//!
+//! C headers: [`include/linux/fs.h`](../../../../include/linux/fs.h)
+
+use crate::{bindings};
+use crate::types::AlwaysRefCounted;
+use core::{cell::UnsafeCell, ptr};
+
+/// Wraps the kernel's `struct inode`.
+///
+/// # Invariants
+///
+/// Instances of this type are always ref-counted, that is, a call to `ihold` ensures that the
+/// allocation remains valid at least until the matching call to `iput`.
+#[repr(transparent)]
+pub struct INode(pub(crate) UnsafeCell<bindings::inode>);
+
+// SAFETY: The type invariants guarantee that `INode` is always ref-counted.
+unsafe impl AlwaysRefCounted for INode {
+ fn inc_ref(&self) {
+ // SAFETY: The existence of a shared reference means that the refcount is nonzero.
+ unsafe { bindings::ihold(self.0.get()) };
+ }
+
+ unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
+ // SAFETY: The safety requirements guarantee that the refcount is nonzero.
+ unsafe { bindings::iput(obj.cast().as_ptr()) }
+ }
+}
+
+/// Wraps the kernel's `struct dentry`.
+///
+/// # Invariants
+///
+/// Instances of this type are always ref-counted, that is, a call to `dget` ensures that the
+/// allocation remains valid at least until the matching call to `dput`.
+#[repr(transparent)]
+pub struct DEntry(pub(crate) UnsafeCell<bindings::dentry>);
+
+// SAFETY: The type invariants guarantee that `DEntry` is always ref-counted.
+unsafe impl AlwaysRefCounted for DEntry {
+ fn inc_ref(&self) {
+ // SAFETY: The existence of a shared reference means that the refcount is nonzero.
+ unsafe { bindings::dget(self.0.get()) };
+ }
+
+ unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
+ // SAFETY: The safety requirements guarantee that the refcount is nonzero.
+ unsafe { bindings::dput(obj.cast().as_ptr()) }
+ }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 85b261209977..486f60912132 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -30,6 +30,7 @@
#[cfg(not(test))]
#[cfg(not(testlib))]
mod allocator;
+pub mod fs;
mod build_assert;
pub mod error;
pub mod init;
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 02/80] rust: add ability to register a file system
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
2023-06-09 6:29 ` [PATCH 01/80] rust: add definitions for ref-counted inodes and dentries Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 9:23 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 03/80] rust: define fs context Ariel Miculas
` (75 subsequent siblings)
77 siblings, 1 reply; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Wedson Almeida Filho
From: Wedson Almeida Filho <wedsonaf@google.com>
Also add sample that uses the new public API. The registered file system
cannot be mounted yet, but can be seen in /proc/filesystems.
More functionality will be added in subsequent commits.
Signed-off-by: Wedson Almeida Filho <wedsonaf@google.com>
---
rust/kernel/fs.rs | 116 +++++++++++++++++++++++++++++++++++++++-
samples/rust/Kconfig | 10 ++++
samples/rust/Makefile | 1 +
samples/rust/rust_fs.rs | 31 +++++++++++
scripts/Makefile.build | 2 +-
5 files changed, 157 insertions(+), 3 deletions(-)
create mode 100644 samples/rust/rust_fs.rs
diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs
index 157fdb917ad1..b61e6f03e871 100644
--- a/rust/kernel/fs.rs
+++ b/rust/kernel/fs.rs
@@ -4,9 +4,121 @@
//!
//! C headers: [`include/linux/fs.h`](../../../../include/linux/fs.h)
-use crate::{bindings};
+use crate::{bindings, error::code::*, str::CStr, ThisModule};
+use crate::error::{to_result, Result};
use crate::types::AlwaysRefCounted;
-use core::{cell::UnsafeCell, ptr};
+use core::{cell::UnsafeCell, marker::PhantomPinned, pin::Pin, ptr};
+
+/// A file system type.
+pub trait Type {
+ /// The name of the file system type.
+ const NAME: &'static CStr;
+
+ /// The flags of this file system type.
+ ///
+ /// It is a combination of the flags in the [`flags`] module.
+ const FLAGS: i32;
+}
+
+/// File system flags.
+pub mod flags {
+ use crate::bindings;
+
+ /// The file system requires a device.
+ pub const REQUIRES_DEV: i32 = bindings::FS_REQUIRES_DEV as _;
+
+ /// The options provided when mounting are in binary form.
+ pub const BINARY_MOUNTDATA: i32 = bindings::FS_BINARY_MOUNTDATA as _;
+
+ /// The file system has a subtype. It is extracted from the name and passed in as a parameter.
+ pub const HAS_SUBTYPE: i32 = bindings::FS_HAS_SUBTYPE as _;
+
+ /// The file system can be mounted by userns root.
+ pub const USERNS_MOUNT: i32 = bindings::FS_USERNS_MOUNT as _;
+
+ /// Disables fanotify permission events.
+ pub const DISALLOW_NOTIFY_PERM: i32 = bindings::FS_DISALLOW_NOTIFY_PERM as _;
+
+ /// The file system has been updated to handle vfs idmappings.
+ pub const ALLOW_IDMAP: i32 = bindings::FS_ALLOW_IDMAP as _;
+
+ /// The file systen will handle `d_move` during `rename` internally.
+ pub const RENAME_DOES_D_MOVE: i32 = bindings::FS_RENAME_DOES_D_MOVE as _;
+}
+
+/// A file system registration.
+#[derive(Default)]
+pub struct Registration {
+ is_registered: bool,
+ fs: UnsafeCell<bindings::file_system_type>,
+ _pin: PhantomPinned,
+}
+
+// SAFETY: `Registration` doesn't really provide any `&self` methods, so it is safe to pass
+// references to it around.
+unsafe impl Sync for Registration {}
+
+// SAFETY: Both registration and unregistration are implemented in C and safe to be performed from
+// any thread, so `Registration` is `Send`.
+unsafe impl Send for Registration {}
+
+impl Registration {
+ /// Creates a new file system registration.
+ ///
+ /// It is not visible or accessible yet. A successful call to [`Registration::register`] needs
+ /// to be made before users can mount it.
+ pub fn new() -> Self {
+ Self {
+ is_registered: false,
+ fs: UnsafeCell::new(bindings::file_system_type::default()),
+ _pin: PhantomPinned,
+ }
+ }
+
+ /// Registers a file system so that it can be mounted by users.
+ ///
+ /// The file system is described by the [`Type`] argument.
+ ///
+ /// It is automatically unregistered when the registration is dropped.
+ pub fn register<T: Type>(self: Pin<&mut Self>, module: &'static ThisModule) -> Result {
+ // SAFETY: We never move out of `this`.
+ let this = unsafe { self.get_unchecked_mut() };
+
+ if this.is_registered {
+ return Err(EINVAL);
+ }
+
+ let mut fs = this.fs.get_mut();
+ fs.owner = module.0;
+ fs.name = T::NAME.as_char_ptr();
+ fs.fs_flags = T::FLAGS;
+ fs.init_fs_context = Some(Self::init_fs_context_callback::<T>);
+ fs.kill_sb = Some(Self::kill_sb_callback::<T>);
+ // SAFETY: Pointers stored in `fs` are either static so will live for as long as the
+ // registration is active (it is undone in `drop`).
+ to_result(unsafe { bindings::register_filesystem(this.fs.get()) })?;
+ this.is_registered = true;
+ Ok(())
+ }
+
+ unsafe extern "C" fn init_fs_context_callback<T: Type>(
+ _fc_ptr: *mut bindings::fs_context,
+ ) -> core::ffi::c_int {
+ EINVAL.to_errno()
+ }
+
+ unsafe extern "C" fn kill_sb_callback<T: Type>(_sb_ptr: *mut bindings::super_block) {}
+}
+
+impl Drop for Registration {
+ fn drop(&mut self) {
+ if self.is_registered {
+ // SAFETY: When `is_registered` is `true`, a previous call to `register_filesystem` has
+ // succeeded, so it is safe to unregister here.
+ unsafe { bindings::unregister_filesystem(self.fs.get()) };
+ }
+ }
+}
/// Wraps the kernel's `struct inode`.
///
diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index b0f74a81c8f9..2bd736f99189 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -30,6 +30,16 @@ config SAMPLE_RUST_PRINT
If unsure, say N.
+config SAMPLE_RUST_FS
+ tristate "File system"
+ help
+ This option builds the Rust file system sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_fs.
+
+ If unsure, say N.
+
config SAMPLE_RUST_HOSTPROGS
bool "Host programs"
help
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index 03086dabbea4..e5941037e673 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -2,5 +2,6 @@
obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o
obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o
+obj-$(CONFIG_SAMPLE_RUST_FS) += rust_fs.o
subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs
diff --git a/samples/rust/rust_fs.rs b/samples/rust/rust_fs.rs
new file mode 100644
index 000000000000..2caacea50e73
--- /dev/null
+++ b/samples/rust/rust_fs.rs
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust file system sample.
+
+use kernel::prelude::*;
+use kernel::{c_str, fs};
+
+module! {
+ type: FsModule,
+ name: "rust_fs",
+ author: "Rust for Linux Contributors",
+ license: "GPL",
+}
+
+struct RustFs;
+impl fs::Type for RustFs {
+ const NAME: &'static CStr = c_str!("rustfs");
+ const FLAGS: i32 = fs::flags::USERNS_MOUNT;
+}
+
+struct FsModule {
+ _fs: Pin<Box<fs::Registration>>,
+}
+
+impl kernel::Module for FsModule {
+ fn init(module: &'static ThisModule) -> Result<Self> {
+ let mut reg = Pin::from(Box::try_new(fs::Registration::new())?);
+ reg.as_mut().register::<RustFs>(module)?;
+ Ok(Self { _fs: reg })
+ }
+}
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 78175231c969..1404967e908e 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -277,7 +277,7 @@ $(obj)/%.lst: $(src)/%.c FORCE
# Compile Rust sources (.rs)
# ---------------------------------------------------------------------------
-rust_allowed_features := new_uninit
+rust_allowed_features := allocator_api,new_uninit
rust_common_cmd = \
RUST_MODFILE=$(modfile) $(RUSTC_OR_CLIPPY) $(rust_flags) \
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 03/80] rust: define fs context
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
2023-06-09 6:29 ` [PATCH 01/80] rust: add definitions for ref-counted inodes and dentries Ariel Miculas
2023-06-09 6:30 ` [PATCH 02/80] rust: add ability to register a file system Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 04/80] rust: add support for file system parameters Ariel Miculas
` (74 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Wedson Almeida Filho
From: Wedson Almeida Filho <wedsonaf@google.com>
Also make fs mountable, but empty for now.
Signed-off-by: Wedson Almeida Filho <wedsonaf@google.com>
---
rust/bindings/bindings_helper.h | 3 +
rust/bindings/lib.rs | 2 +
rust/helpers.c | 12 ++
rust/kernel/error.rs | 46 +++++++
rust/kernel/fs.rs | 224 ++++++++++++++++++++++++++++++--
rust/kernel/lib.rs | 5 +
samples/rust/rust_fs.rs | 13 ++
7 files changed, 297 insertions(+), 8 deletions(-)
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index eeb5b7c0528a..556f2e7c3ddb 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -11,7 +11,10 @@
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/fs.h>
+#include <linux/fs_parser.h>
/* `bindgen` gets confused at certain things. */
const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;
const gfp_t BINDINGS___GFP_ZERO = __GFP_ZERO;
+
+const loff_t BINDINGS_MAX_LFS_FILESIZE = MAX_LFS_FILESIZE;
diff --git a/rust/bindings/lib.rs b/rust/bindings/lib.rs
index 9bcbea04dac3..cd1fceb31390 100644
--- a/rust/bindings/lib.rs
+++ b/rust/bindings/lib.rs
@@ -51,3 +51,5 @@ mod bindings_helper {
pub const GFP_KERNEL: gfp_t = BINDINGS_GFP_KERNEL;
pub const __GFP_ZERO: gfp_t = BINDINGS___GFP_ZERO;
+
+pub const MAX_LFS_FILESIZE: loff_t = BINDINGS_MAX_LFS_FILESIZE;
diff --git a/rust/helpers.c b/rust/helpers.c
index e00c53c94c43..d6f277c3b7a7 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -135,6 +135,18 @@ struct dentry *rust_helper_dget(struct dentry *dentry)
}
EXPORT_SYMBOL_GPL(rust_helper_dget);
+void rust_helper_lockdep_register_key(struct lock_class_key *key)
+{
+ lockdep_register_key(key);
+}
+EXPORT_SYMBOL_GPL(rust_helper_lockdep_register_key);
+
+void rust_helper_lockdep_unregister_key(struct lock_class_key *key)
+{
+ lockdep_unregister_key(key);
+}
+EXPORT_SYMBOL_GPL(rust_helper_lockdep_unregister_key);
+
/*
* We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
* as the Rust `usize` type, so we can use it in contexts where Rust
diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index 7c1ce2bccd08..231f7dfd8005 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -296,3 +296,49 @@ pub(crate) fn from_result<T, F>(f: F) -> T
Err(e) => T::from(e.to_errno() as i16),
}
}
+
+pub(crate) fn from_kernel_result_helper<T>(r: Result<T>) -> T
+where
+ T: From<i16>,
+{
+ match r {
+ Ok(v) => v,
+ // NO-OVERFLOW: negative `errno`s are no smaller than `-bindings::MAX_ERRNO`,
+ // `-bindings::MAX_ERRNO` fits in an `i16` as per invariant above,
+ // therefore a negative `errno` always fits in an `i16` and will not overflow.
+ Err(e) => T::from(e.to_errno() as i16),
+ }
+}
+
+/// Transforms a [`crate::error::Result<T>`] to a kernel C integer result.
+///
+/// This is useful when calling Rust functions that return [`crate::error::Result<T>`]
+/// from inside `extern "C"` functions that need to return an integer
+/// error result.
+///
+/// `T` should be convertible to an `i16` via `From<i16>`.
+///
+/// # Examples
+///
+/// ```ignore
+/// # use kernel::from_kernel_result;
+/// # use kernel::bindings;
+/// unsafe extern "C" fn probe_callback(
+/// pdev: *mut bindings::platform_device,
+/// ) -> core::ffi::c_int {
+/// from_kernel_result! {
+/// let ptr = devm_alloc(pdev)?;
+/// bindings::platform_set_drvdata(pdev, ptr);
+/// Ok(0)
+/// }
+/// }
+/// ```
+macro_rules! from_kernel_result {
+ ($($tt:tt)*) => {{
+ $crate::error::from_kernel_result_helper((|| {
+ $($tt)*
+ })())
+ }};
+}
+
+pub(crate) use from_kernel_result;
diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs
index b61e6f03e871..187f0c19dcd0 100644
--- a/rust/kernel/fs.rs
+++ b/rust/kernel/fs.rs
@@ -5,15 +5,162 @@
//! C headers: [`include/linux/fs.h`](../../../../include/linux/fs.h)
use crate::{bindings, error::code::*, str::CStr, ThisModule};
-use crate::error::{to_result, Result};
-use crate::types::AlwaysRefCounted;
+use crate::error::{to_result, from_kernel_result, Result};
+use crate::types::{AlwaysRefCounted, ForeignOwnable, ScopeGuard};
use core::{cell::UnsafeCell, marker::PhantomPinned, pin::Pin, ptr};
+use macros::vtable;
+
+/// A file system context.
+///
+/// It is used to gather configuration to then mount or reconfigure a file system.
+#[vtable]
+pub trait Context<T: Type + ?Sized> {
+ /// Type of the data associated with the context.
+ type Data: ForeignOwnable + Send + Sync + 'static;
+
+ /// Creates a new context.
+ fn try_new() -> Result<Self::Data>;
+}
+
+struct Tables<T: Type + ?Sized>(T);
+impl<T: Type + ?Sized> Tables<T> {
+ const CONTEXT: bindings::fs_context_operations = bindings::fs_context_operations {
+ free: Some(Self::free_callback),
+ parse_param: None,
+ get_tree: Some(Self::get_tree_callback),
+ reconfigure: Some(Self::reconfigure_callback),
+ parse_monolithic: None,
+ dup: None,
+ };
+
+ unsafe extern "C" fn free_callback(fc: *mut bindings::fs_context) {
+ // SAFETY: The callback contract guarantees that `fc` is valid.
+ let ptr = unsafe { (*fc).fs_private };
+ if !ptr.is_null() {
+ // SAFETY: `fs_private` was initialised with the result of a `to_pointer` call in
+ // `init_fs_context_callback`, so it's ok to call `from_foreign` here.
+ unsafe { <T::Context as Context<T>>::Data::from_foreign(ptr) };
+ }
+ }
+
+ unsafe extern "C" fn fill_super_callback(
+ sb_ptr: *mut bindings::super_block,
+ _fc: *mut bindings::fs_context,
+ ) -> core::ffi::c_int {
+ from_kernel_result! {
+ // The following is temporary code to create the root inode and dentry. It will be
+ // replaced with calls to Rust code.
+
+ // SAFETY: The callback contract guarantees that `sb_ptr` is the only pointer to a
+ // newly-allocated superblock, so it is safe to mutably reference it.
+ let sb = unsafe { &mut *sb_ptr };
+
+ sb.s_maxbytes = bindings::MAX_LFS_FILESIZE;
+ sb.s_blocksize = crate::PAGE_SIZE as _;
+ sb.s_blocksize_bits = bindings::PAGE_SHIFT as _;
+ sb.s_magic = T::MAGIC as _;
+ sb.s_op = &Tables::<T>::SUPER_BLOCK;
+ sb.s_time_gran = 1;
+
+ // Create and initialise the root inode.
+ let inode = unsafe { bindings::new_inode(sb) };
+ if inode.is_null() {
+ return Err(ENOMEM);
+ }
+
+ {
+ // SAFETY: This is a newly-created inode. No other references to it exist, so it is
+ // safe to mutably dereference it.
+ let inode = unsafe { &mut *inode };
+
+ // SAFETY: `current_time` requires that `inode.sb` be valid, which is the case here
+ // since we allocated the inode through the superblock.
+ let time = unsafe { bindings::current_time(inode) };
+ inode.i_ino = 1;
+ inode.i_mode = (bindings::S_IFDIR | 0o755) as _;
+ inode.i_mtime = time;
+ inode.i_atime = time;
+ inode.i_ctime = time;
+
+ // SAFETY: `simple_dir_operations` never changes, it's safe to reference it.
+ inode.__bindgen_anon_3.i_fop = unsafe { &bindings::simple_dir_operations };
+
+ // SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it.
+ inode.i_op = unsafe { &bindings::simple_dir_inode_operations };
+
+ // SAFETY: `inode` is valid for write.
+ unsafe { bindings::set_nlink(inode, 2) };
+ }
+
+ // SAFETY: `d_make_root` requires that `inode` be valid and referenced, which is the
+ // case for this call.
+ //
+ // It takes over the inode, even on failure, so we don't need to clean it up.
+ let dentry = unsafe { bindings::d_make_root(inode) };
+ if dentry.is_null() {
+ return Err(ENOMEM);
+ }
+
+ sb.s_root = dentry;
+ Ok(0)
+ }
+ }
+
+ unsafe extern "C" fn get_tree_callback(fc: *mut bindings::fs_context) -> core::ffi::c_int {
+ // SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has the
+ // right type and is a valid callback.
+ unsafe { bindings::get_tree_nodev(fc, Some(Self::fill_super_callback)) }
+ }
+
+ unsafe extern "C" fn reconfigure_callback(_fc: *mut bindings::fs_context) -> core::ffi::c_int {
+ EINVAL.to_errno()
+ }
+
+ const SUPER_BLOCK: bindings::super_operations = bindings::super_operations {
+ alloc_inode: None,
+ destroy_inode: None,
+ free_inode: None,
+ dirty_inode: None,
+ write_inode: None,
+ drop_inode: None,
+ evict_inode: None,
+ put_super: None,
+ sync_fs: None,
+ freeze_super: None,
+ freeze_fs: None,
+ thaw_super: None,
+ unfreeze_fs: None,
+ statfs: None,
+ remount_fs: None,
+ umount_begin: None,
+ show_options: None,
+ show_devname: None,
+ show_path: None,
+ show_stats: None,
+ #[cfg(CONFIG_QUOTA)]
+ quota_read: None,
+ #[cfg(CONFIG_QUOTA)]
+ quota_write: None,
+ #[cfg(CONFIG_QUOTA)]
+ get_dquots: None,
+ nr_cached_objects: None,
+ free_cached_objects: None,
+ };
+}
/// A file system type.
pub trait Type {
+ /// The context used to build fs configuration before it is mounted or reconfigured.
+ type Context: Context<Self> + ?Sized;
+
/// The name of the file system type.
const NAME: &'static CStr;
+ /// The magic number associated with the file system.
+ ///
+ /// This is normally one of the values in `include/uapi/linux/magic.h`.
+ const MAGIC: u32;
+
/// The flags of this file system type.
///
/// It is a combination of the flags in the [`flags`] module.
@@ -80,7 +227,7 @@ pub fn new() -> Self {
/// The file system is described by the [`Type`] argument.
///
/// It is automatically unregistered when the registration is dropped.
- pub fn register<T: Type>(self: Pin<&mut Self>, module: &'static ThisModule) -> Result {
+ pub fn register<T: Type + ?Sized>(self: Pin<&mut Self>, module: &'static ThisModule) -> Result {
// SAFETY: We never move out of `this`.
let this = unsafe { self.get_unchecked_mut() };
@@ -94,20 +241,81 @@ pub fn register<T: Type>(self: Pin<&mut Self>, module: &'static ThisModule) -> R
fs.fs_flags = T::FLAGS;
fs.init_fs_context = Some(Self::init_fs_context_callback::<T>);
fs.kill_sb = Some(Self::kill_sb_callback::<T>);
+
+ // SAFETY: This block registers all fs type keys with lockdep. We just need the memory
+ // locations to be owned by the caller, which is the case.
+ unsafe {
+ bindings::lockdep_register_key(&mut fs.s_lock_key);
+ bindings::lockdep_register_key(&mut fs.s_umount_key);
+ bindings::lockdep_register_key(&mut fs.s_vfs_rename_key);
+ bindings::lockdep_register_key(&mut fs.i_lock_key);
+ bindings::lockdep_register_key(&mut fs.i_mutex_key);
+ bindings::lockdep_register_key(&mut fs.invalidate_lock_key);
+ bindings::lockdep_register_key(&mut fs.i_mutex_dir_key);
+ for key in &mut fs.s_writers_key {
+ bindings::lockdep_register_key(key);
+ }
+ }
+
+ let ptr = this.fs.get();
+
+ // SAFETY: `ptr` as valid as it points to the `self.fs`.
+ let key_guard = ScopeGuard::new(|| unsafe { Self::unregister_keys(ptr) });
+
// SAFETY: Pointers stored in `fs` are either static so will live for as long as the
// registration is active (it is undone in `drop`).
- to_result(unsafe { bindings::register_filesystem(this.fs.get()) })?;
+ to_result(unsafe { bindings::register_filesystem(ptr) })?;
+ key_guard.dismiss();
this.is_registered = true;
Ok(())
}
- unsafe extern "C" fn init_fs_context_callback<T: Type>(
- _fc_ptr: *mut bindings::fs_context,
+ /// Unregisters the lockdep keys in the file system type.
+ ///
+ /// # Safety
+ ///
+ /// `fs` must be non-null and valid.
+ unsafe fn unregister_keys(fs: *mut bindings::file_system_type) {
+ // SAFETY: This block unregisters all fs type keys from lockdep. They must have been
+ // registered before.
+ unsafe {
+ bindings::lockdep_unregister_key(ptr::addr_of_mut!((*fs).s_lock_key));
+ bindings::lockdep_unregister_key(ptr::addr_of_mut!((*fs).s_umount_key));
+ bindings::lockdep_unregister_key(ptr::addr_of_mut!((*fs).s_vfs_rename_key));
+ bindings::lockdep_unregister_key(ptr::addr_of_mut!((*fs).i_lock_key));
+ bindings::lockdep_unregister_key(ptr::addr_of_mut!((*fs).i_mutex_key));
+ bindings::lockdep_unregister_key(ptr::addr_of_mut!((*fs).invalidate_lock_key));
+ bindings::lockdep_unregister_key(ptr::addr_of_mut!((*fs).i_mutex_dir_key));
+ for i in 0..(*fs).s_writers_key.len() {
+ bindings::lockdep_unregister_key(ptr::addr_of_mut!((*fs).s_writers_key[i]));
+ }
+ }
+ }
+
+ unsafe extern "C" fn init_fs_context_callback<T: Type + ?Sized>(
+ fc_ptr: *mut bindings::fs_context,
) -> core::ffi::c_int {
- EINVAL.to_errno()
+ from_kernel_result! {
+ let data = T::Context::try_new()?;
+ // SAFETY: The callback contract guarantees that `fc_ptr` is the only pointer to a
+ // newly-allocated fs context, so it is safe to mutably reference it.
+ let fc = unsafe { &mut *fc_ptr };
+ fc.fs_private = data.into_foreign() as _;
+ fc.ops = &Tables::<T>::CONTEXT;
+ Ok(0)
+ }
}
- unsafe extern "C" fn kill_sb_callback<T: Type>(_sb_ptr: *mut bindings::super_block) {}
+ unsafe extern "C" fn kill_sb_callback<T: Type + ?Sized>(sb_ptr: *mut bindings::super_block) {
+ // SAFETY: We always call `get_tree_nodev` from `get_tree_callback`, so we never have a
+ // device, so it is ok to call the function below. Additionally, the callback contract
+ // guarantees that `sb_ptr` is valid.
+ unsafe { bindings::kill_anon_super(sb_ptr) }
+
+ // SAFETY: The callback contract guarantees that `sb_ptr` is valid, and the `kill_sb`
+ // callback being called implies that the `s_type` is also valid.
+ unsafe { Self::unregister_keys((*sb_ptr).s_type) };
+ }
}
impl Drop for Registration {
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 486f60912132..48da79b02422 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -98,3 +98,8 @@ fn panic(info: &core::panic::PanicInfo<'_>) -> ! {
// instead of `!`. See <https://github.com/rust-lang/rust-bindgen/issues/2094>.
loop {}
}
+
+/// Page size defined in terms of the `PAGE_SHIFT` macro from C.
+///
+/// [`PAGE_SHIFT`]: ../../../include/asm-generic/page.h
+pub const PAGE_SIZE: usize = 1 << bindings::PAGE_SHIFT;
diff --git a/samples/rust/rust_fs.rs b/samples/rust/rust_fs.rs
index 2caacea50e73..2bd4e563c694 100644
--- a/samples/rust/rust_fs.rs
+++ b/samples/rust/rust_fs.rs
@@ -13,9 +13,22 @@
}
struct RustFs;
+
+#[vtable]
+impl fs::Context<Self> for RustFs {
+ type Data = ();
+
+ fn try_new() -> Result {
+ pr_info!("context created!\n");
+ Ok(())
+ }
+}
+
impl fs::Type for RustFs {
+ type Context = Self;
const NAME: &'static CStr = c_str!("rustfs");
const FLAGS: i32 = fs::flags::USERNS_MOUNT;
+ const MAGIC: u32 = 0x72757374;
}
struct FsModule {
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 04/80] rust: add support for file system parameters
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (2 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 03/80] rust: define fs context Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 05/80] rust: kernel: add libraries required by the filesystem abstractions Ariel Miculas
` (73 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Wedson Almeida Filho
From: Wedson Almeida Filho <wedsonaf@google.com>
This allows file system contexts to be further initialised with
parameters from userspace before a fs is mounted or reconfigured.
Signed-off-by: Wedson Almeida Filho <wedsonaf@google.com>
---
rust/helpers.c | 9 +
rust/kernel/fs.rs | 142 ++++++++++-
rust/kernel/fs/param.rs | 537 ++++++++++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 1 +
samples/rust/rust_fs.rs | 15 ++
5 files changed, 701 insertions(+), 3 deletions(-)
create mode 100644 rust/kernel/fs/param.rs
diff --git a/rust/helpers.c b/rust/helpers.c
index d6f277c3b7a7..b042d496649f 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -147,6 +147,15 @@ void rust_helper_lockdep_unregister_key(struct lock_class_key *key)
}
EXPORT_SYMBOL_GPL(rust_helper_lockdep_unregister_key);
+int rust_helper_fs_parse(struct fs_context *fc,
+ const struct fs_parameter_spec *desc,
+ struct fs_parameter *param,
+ struct fs_parse_result *result)
+{
+ return fs_parse(fc, desc, param, result);
+}
+EXPORT_SYMBOL_GPL(rust_helper_fs_parse);
+
/*
* We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
* as the Rust `usize` type, so we can use it in contexts where Rust
diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs
index 187f0c19dcd0..e68b67b8adb0 100644
--- a/rust/kernel/fs.rs
+++ b/rust/kernel/fs.rs
@@ -5,11 +5,13 @@
//! C headers: [`include/linux/fs.h`](../../../../include/linux/fs.h)
use crate::{bindings, error::code::*, str::CStr, ThisModule};
-use crate::error::{to_result, from_kernel_result, Result};
+use crate::error::{to_result, from_kernel_result, Error, Result};
use crate::types::{AlwaysRefCounted, ForeignOwnable, ScopeGuard};
use core::{cell::UnsafeCell, marker::PhantomPinned, pin::Pin, ptr};
use macros::vtable;
+pub mod param;
+
/// A file system context.
///
/// It is used to gather configuration to then mount or reconfigure a file system.
@@ -18,18 +20,48 @@ pub trait Context<T: Type + ?Sized> {
/// Type of the data associated with the context.
type Data: ForeignOwnable + Send + Sync + 'static;
+ /// The typed file system parameters.
+ ///
+ /// Users are encouraged to define it using the [`crate::define_fs_params`] macro.
+ const PARAMS: param::SpecTable<'static, Self::Data> = param::SpecTable::empty();
+
/// Creates a new context.
fn try_new() -> Result<Self::Data>;
+
+ /// Parses a parameter that wasn't specified in [`Self::PARAMS`].
+ fn parse_unknown_param(
+ _data: &mut Self::Data,
+ _name: &CStr,
+ _value: param::Value<'_>,
+ ) -> Result {
+ Err(ENOPARAM)
+ }
+
+ /// Parses the whole parameter block, potentially skipping regular handling for parts of it.
+ ///
+ /// The return value is the portion of the input buffer for which the regular handling
+ /// (involving [`Self::PARAMS`] and [`Self::parse_unknown_param`]) will still be carried out.
+ /// If it's `None`, the regular handling is not performed at all.
+ fn parse_monolithic<'a>(
+ _data: &mut Self::Data,
+ _buf: Option<&'a mut [u8]>,
+ ) -> Result<Option<&'a mut [u8]>> {
+ Ok(None)
+ }
}
struct Tables<T: Type + ?Sized>(T);
impl<T: Type + ?Sized> Tables<T> {
const CONTEXT: bindings::fs_context_operations = bindings::fs_context_operations {
free: Some(Self::free_callback),
- parse_param: None,
+ parse_param: Some(Self::parse_param_callback),
get_tree: Some(Self::get_tree_callback),
reconfigure: Some(Self::reconfigure_callback),
- parse_monolithic: None,
+ parse_monolithic: if <T::Context as Context<T>>::HAS_PARSE_MONOLITHIC {
+ Some(Self::parse_monolithic_callback)
+ } else {
+ None
+ },
dup: None,
};
@@ -43,6 +75,55 @@ impl<T: Type + ?Sized> Tables<T> {
}
}
+ unsafe extern "C" fn parse_param_callback(
+ fc: *mut bindings::fs_context,
+ param: *mut bindings::fs_parameter,
+ ) -> core::ffi::c_int {
+ from_kernel_result! {
+ // SAFETY: The callback contract guarantees that `fc` is valid.
+ let ptr = unsafe { (*fc).fs_private };
+
+ // SAFETY: The value of `ptr` (coming from `fs_private` was initialised in
+ // `init_fs_context_callback` to the result of an `into_pointer` call. Since the
+ // context is valid, `from_pointer` wasn't called yet, so `ptr` is valid. Additionally,
+ // the callback contract guarantees that callbacks are serialised, so it is ok to
+ // mutably reference it.
+ let mut data =
+ unsafe { <<T::Context as Context<T>>::Data as ForeignOwnable>::borrow_mut(ptr) };
+ let mut result = bindings::fs_parse_result::default();
+ // SAFETY: All parameters are valid at least for the duration of the call.
+ let opt =
+ unsafe { bindings::fs_parse(fc, T::Context::PARAMS.first, param, &mut result) };
+
+ // SAFETY: The callback contract guarantees that `param` is valid for the duration of
+ // the callback.
+ let param = unsafe { &*param };
+ if opt >= 0 {
+ let opt = opt as usize;
+ if opt >= T::Context::PARAMS.handlers.len() {
+ return Err(EINVAL);
+ }
+ T::Context::PARAMS.handlers[opt].handle_param(&mut data, param, &result)?;
+ return Ok(0);
+ }
+
+ if opt != ENOPARAM.to_errno() {
+ return Err(Error::from_errno(opt));
+ }
+
+ if !T::Context::HAS_PARSE_UNKNOWN_PARAM {
+ return Err(ENOPARAM);
+ }
+
+ let val = param::Value::from_fs_parameter(param);
+ // SAFETY: The callback contract guarantees the parameter key to be valid and last at
+ // least the duration of the callback.
+ T::Context::parse_unknown_param(
+ &mut data, unsafe { CStr::from_char_ptr(param.key) }, val)?;
+ Ok(0)
+ }
+ }
+
unsafe extern "C" fn fill_super_callback(
sb_ptr: *mut bindings::super_block,
_fc: *mut bindings::fs_context,
@@ -63,6 +144,8 @@ impl<T: Type + ?Sized> Tables<T> {
sb.s_time_gran = 1;
// Create and initialise the root inode.
+
+ // SAFETY: `sb` was just created initialised, so it is safe pass it to `new_inode`.
let inode = unsafe { bindings::new_inode(sb) };
if inode.is_null() {
return Err(ENOMEM);
@@ -116,6 +199,40 @@ impl<T: Type + ?Sized> Tables<T> {
EINVAL.to_errno()
}
+ unsafe extern "C" fn parse_monolithic_callback(
+ fc: *mut bindings::fs_context,
+ buf: *mut core::ffi::c_void,
+ ) -> core::ffi::c_int {
+ from_kernel_result! {
+ // SAFETY: The callback contract guarantees that `fc` is valid.
+ let ptr = unsafe { (*fc).fs_private };
+
+ // SAFETY: The value of `ptr` (coming from `fs_private` was initialised in
+ // `init_fs_context_callback` to the result of an `into_pointer` call. Since the
+ // context is valid, `from_pointer` wasn't called yet, so `ptr` is valid. Additionally,
+ // the callback contract guarantees that callbacks are serialised, so it is ok to
+ // mutably reference it.
+ let mut data =
+ unsafe { <<T::Context as Context<T>>::Data as ForeignOwnable>::borrow_mut(ptr) };
+ let page = if buf.is_null() {
+ None
+ } else {
+ // SAFETY: This callback is called to handle the `mount` syscall, which takes a
+ // page-sized buffer as data.
+ Some(unsafe { &mut *ptr::slice_from_raw_parts_mut(buf.cast(), crate::PAGE_SIZE) })
+ };
+ let regular = T::Context::parse_monolithic(&mut data, page)?;
+ if let Some(buf) = regular {
+ // SAFETY: Both `fc` and `buf` are guaranteed to be valid; the former because the
+ // callback is still ongoing and the latter because its lifefime is tied to that of
+ // `page`, which is also valid for the duration of the callback.
+ to_result(
+ unsafe { bindings::generic_parse_monolithic(fc, buf.as_mut_ptr().cast()) })?;
+ }
+ Ok(0)
+ }
+ }
+
const SUPER_BLOCK: bindings::super_operations = bindings::super_operations {
alloc_inode: None,
destroy_inode: None,
@@ -239,6 +356,7 @@ pub fn register<T: Type + ?Sized>(self: Pin<&mut Self>, module: &'static ThisMod
fs.owner = module.0;
fs.name = T::NAME.as_char_ptr();
fs.fs_flags = T::FLAGS;
+ fs.parameters = T::Context::PARAMS.first;
fs.init_fs_context = Some(Self::init_fs_context_callback::<T>);
fs.kill_sb = Some(Self::kill_sb_callback::<T>);
@@ -371,3 +489,21 @@ unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
unsafe { bindings::dput(obj.cast().as_ptr()) }
}
}
+
+/// Wraps the kernel's `struct filename`.
+#[repr(transparent)]
+pub struct Filename(pub(crate) UnsafeCell<bindings::filename>);
+
+impl Filename {
+ /// Creates a reference to a [`Filename`] from a valid pointer.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that `ptr` is valid and remains valid for the lifetime of the
+ /// returned [`Filename`] instance.
+ pub(crate) unsafe fn from_ptr<'a>(ptr: *const bindings::filename) -> &'a Filename {
+ // SAFETY: The safety requirements guarantee the validity of the dereference, while the
+ // `Filename` type being transparent makes the cast ok.
+ unsafe { &*ptr.cast() }
+ }
+}
diff --git a/rust/kernel/fs/param.rs b/rust/kernel/fs/param.rs
new file mode 100644
index 000000000000..f0b4393b6b67
--- /dev/null
+++ b/rust/kernel/fs/param.rs
@@ -0,0 +1,537 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! File system parameters and parsing them.
+//!
+//! C headers: [`include/linux/fs_parser.h`](../../../../../include/linux/fs_parser.h)
+
+use crate::{bindings, file, fs, str::CStr, error::Result};
+use core::{marker::PhantomData, ptr};
+
+/// The value of a file system parameter.
+pub enum Value<'a> {
+ /// The value is undefined.
+ Undefined,
+
+ /// There is no value, but parameter itself is a flag.
+ Flag,
+
+ /// The value is the given string.
+ String(&'a CStr),
+
+ /// The value is the given binary blob.
+ Blob(&'a mut [u8]),
+
+ /// The value is the given file.
+ File(&'a file::File),
+
+ /// The value is the given filename and the given directory file descriptor (which may be
+ /// `AT_FDCWD`, to indicate the current directory).
+ Filename(&'a fs::Filename, i32),
+}
+
+impl<'a> Value<'a> {
+ pub(super) fn from_fs_parameter(p: &'a bindings::fs_parameter) -> Self {
+ match p.type_() {
+ bindings::fs_value_type_fs_value_is_string => {
+ // SAFETY: `type_` is string, so it is ok to use the union field. Additionally, it
+ // is guaranteed to be valid while `p` is valid.
+ Self::String(unsafe { CStr::from_char_ptr(p.__bindgen_anon_1.string) })
+ }
+ bindings::fs_value_type_fs_value_is_flag => Self::Flag,
+ bindings::fs_value_type_fs_value_is_blob => {
+ // SAFETY: `type_` is blob, so it is ok to use the union field and size.
+ // Additionally, it is guaranteed to be valid while `p` is valid.
+ let slice = unsafe {
+ &mut *ptr::slice_from_raw_parts_mut(p.__bindgen_anon_1.blob.cast(), p.size)
+ };
+ Self::Blob(slice)
+ }
+ bindings::fs_value_type_fs_value_is_file => {
+ // SAFETY: `type_` is file, so it is ok to use the union field. Additionally, it is
+ // guaranteed to be valid while `p` is valid.
+ let file_ptr = unsafe { p.__bindgen_anon_1.file };
+ if file_ptr.is_null() {
+ Self::Undefined
+ } else {
+ // SAFETY: `file_ptr` is non-null and guaranteed to be valid while `p` is.
+ Self::File(unsafe { file::File::from_ptr(file_ptr) })
+ }
+ }
+ bindings::fs_value_type_fs_value_is_filename => {
+ // SAFETY: `type_` is filename, so it is ok to use the union field. Additionally,
+ // it is guaranteed to be valid while `p` is valid.
+ let filename_ptr = unsafe { p.__bindgen_anon_1.name };
+ if filename_ptr.is_null() {
+ Self::Undefined
+ } else {
+ // SAFETY: `filename_ptr` is non-null and guaranteed to be valid while `p` is.
+ Self::Filename(unsafe { fs::Filename::from_ptr(filename_ptr) }, p.dirfd)
+ }
+ }
+ _ => Self::Undefined,
+ }
+ }
+}
+
+/// A specification of a file system parameter.
+pub struct Spec {
+ name: &'static CStr,
+ flags: u16,
+ type_: bindings::fs_param_type,
+ extra: *const core::ffi::c_void,
+}
+
+const DEFAULT: Spec = Spec {
+ name: crate::c_str!(""),
+ flags: 0,
+ type_: None,
+ extra: core::ptr::null(),
+};
+
+macro_rules! define_param_type {
+ ($name:ident, $fntype:ty, $spec:expr, |$param:ident, $result:ident| $value:expr) => {
+ /// Module to support `$name` parameter types.
+ pub mod $name {
+ use super::*;
+
+ #[doc(hidden)]
+ pub const fn spec(name: &'static CStr) -> Spec {
+ const GIVEN: Spec = $spec;
+ Spec { name, ..GIVEN }
+ }
+
+ #[doc(hidden)]
+ pub const fn handler<S>(setfn: fn(&mut S, $fntype) -> Result) -> impl Handler<S> {
+ let c =
+ move |s: &mut S,
+ $param: &bindings::fs_parameter,
+ $result: &bindings::fs_parse_result| { setfn(s, $value) };
+ ConcreteHandler {
+ setfn: c,
+ _p: PhantomData,
+ }
+ }
+ }
+ };
+}
+
+// SAFETY: This is only called when the parse result is a boolean, so it is ok to access to union
+// field.
+define_param_type!(flag, bool, Spec { ..DEFAULT }, |_p, r| unsafe {
+ r.__bindgen_anon_1.boolean
+});
+
+define_param_type!(
+ flag_no,
+ bool,
+ Spec {
+ flags: bindings::fs_param_neg_with_no as _,
+ ..DEFAULT
+ },
+ // SAFETY: This is only called when the parse result is a boolean, so it is ok to access to
+ // union field.
+ |_p, r| unsafe { r.__bindgen_anon_1.boolean }
+);
+
+define_param_type!(
+ bool,
+ bool,
+ Spec {
+ type_: Some(bindings::fs_param_is_bool),
+ ..DEFAULT
+ },
+ // SAFETY: This is only called when the parse result is a boolean, so it is ok to access to
+ // union field.
+ |_p, r| unsafe { r.__bindgen_anon_1.boolean }
+);
+
+define_param_type!(
+ u32,
+ u32,
+ Spec {
+ type_: Some(bindings::fs_param_is_u32),
+ ..DEFAULT
+ },
+ // SAFETY: This is only called when the parse result is a u32, so it is ok to access to union
+ // field.
+ |_p, r| unsafe { r.__bindgen_anon_1.uint_32 }
+);
+
+define_param_type!(
+ u32oct,
+ u32,
+ Spec {
+ type_: Some(bindings::fs_param_is_u32),
+ extra: 8 as _,
+ ..DEFAULT
+ },
+ // SAFETY: This is only called when the parse result is a u32, so it is ok to access to union
+ // field.
+ |_p, r| unsafe { r.__bindgen_anon_1.uint_32 }
+);
+
+define_param_type!(
+ u32hex,
+ u32,
+ Spec {
+ type_: Some(bindings::fs_param_is_u32),
+ extra: 16 as _,
+ ..DEFAULT
+ },
+ // SAFETY: This is only called when the parse result is a u32, so it is ok to access to union
+ // field.
+ |_p, r| unsafe { r.__bindgen_anon_1.uint_32 }
+);
+
+define_param_type!(
+ s32,
+ i32,
+ Spec {
+ type_: Some(bindings::fs_param_is_s32),
+ ..DEFAULT
+ },
+ // SAFETY: This is only called when the parse result is an i32, so it is ok to access to union
+ // field.
+ |_p, r| unsafe { r.__bindgen_anon_1.int_32 }
+);
+
+define_param_type!(
+ u64,
+ u64,
+ Spec {
+ type_: Some(bindings::fs_param_is_u64),
+ extra: 16 as _,
+ ..DEFAULT
+ },
+ // SAFETY: This is only called when the parse result is a u32, so it is ok to access to union
+ // field.
+ |_p, r| unsafe { r.__bindgen_anon_1.uint_64 }
+);
+
+define_param_type!(
+ string,
+ &CStr,
+ Spec {
+ type_: Some(bindings::fs_param_is_string),
+ ..DEFAULT
+ },
+ // SAFETY: This is only called when the parse result is a string, so it is ok to access to
+ // union field.
+ |p, _r| unsafe { CStr::from_char_ptr(p.__bindgen_anon_1.string) }
+);
+
+/// Module to support `enum` parameter types.
+pub mod enum_ {
+ use super::*;
+
+ #[doc(hidden)]
+ pub const fn spec(name: &'static CStr, options: ConstantTable<'static>) -> Spec {
+ Spec {
+ name,
+ type_: Some(bindings::fs_param_is_enum),
+ extra: options.first as *const _ as _,
+ ..DEFAULT
+ }
+ }
+
+ #[doc(hidden)]
+ pub const fn handler<S>(setfn: fn(&mut S, u32) -> Result) -> impl Handler<S> {
+ let c = move |s: &mut S, _p: &bindings::fs_parameter, r: &bindings::fs_parse_result| {
+ // SAFETY: This is only called when the parse result is an enum, so it is ok to access
+ // to union field.
+ setfn(s, unsafe { r.__bindgen_anon_1.uint_32 })
+ };
+ ConcreteHandler {
+ setfn: c,
+ _p: PhantomData,
+ }
+ }
+}
+
+const ZERO_SPEC: bindings::fs_parameter_spec = bindings::fs_parameter_spec {
+ name: core::ptr::null(),
+ type_: None,
+ opt: 0,
+ flags: 0,
+ data: core::ptr::null(),
+};
+
+/// A zero-terminated parameter spec array, followed by handlers.
+#[repr(C)]
+pub struct SpecArray<const N: usize, S: 'static> {
+ specs: [bindings::fs_parameter_spec; N],
+ sentinel: bindings::fs_parameter_spec,
+ handlers: [&'static dyn Handler<S>; N],
+}
+
+impl<const N: usize, S: 'static> SpecArray<N, S> {
+ /// Creates a new spec array.
+ ///
+ /// Users are encouraged to use the [`define_fs_params`] macro to define the
+ /// [`super::Context::PARAMS`] constant.
+ ///
+ /// # Safety
+ ///
+ /// The type of the elements in `handlers` must be compatible with the types in specs. For
+ /// example, if `specs` declares that the i-th element is a bool then the i-th handler
+ /// should be for a bool.
+ pub const unsafe fn new(specs: [Spec; N], handlers: [&'static dyn Handler<S>; N]) -> Self {
+ let mut array = Self {
+ specs: [ZERO_SPEC; N],
+ sentinel: ZERO_SPEC,
+ handlers,
+ };
+ let mut i = 0usize;
+ while i < N {
+ array.specs[i] = bindings::fs_parameter_spec {
+ name: specs[i].name.as_char_ptr(),
+ type_: specs[i].type_,
+ opt: i as _,
+ flags: specs[i].flags,
+ data: specs[i].extra,
+ };
+ i += 1;
+ }
+ array
+ }
+
+ /// Returns a [`SpecTable`] backed by `self`.
+ ///
+ /// This is used to essentially erase the array size.
+ pub const fn as_table(&self) -> SpecTable<'_, S> {
+ SpecTable {
+ first: &self.specs[0],
+ handlers: &self.handlers,
+ _p: PhantomData,
+ }
+ }
+}
+
+/// A parameter spec table.
+///
+/// The table is guaranteed to be zero-terminated.
+///
+/// Users are encouraged to use the [`define_fs_params`] macro to define the
+/// [`super::Context::PARAMS`] constant.
+pub struct SpecTable<'a, S: 'static> {
+ pub(super) first: &'a bindings::fs_parameter_spec,
+ pub(super) handlers: &'a [&'static dyn Handler<S>],
+ _p: PhantomData<S>,
+}
+
+impl<S> SpecTable<'static, S> {
+ pub(super) const fn empty() -> Self {
+ Self {
+ first: &ZERO_SPEC,
+ handlers: &[],
+ _p: PhantomData,
+ }
+ }
+}
+
+/// A zero-terminated parameter constant array.
+#[repr(C)]
+pub struct ConstantArray<const N: usize> {
+ consts: [bindings::constant_table; N],
+ sentinel: bindings::constant_table,
+}
+
+impl<const N: usize> ConstantArray<N> {
+ /// Creates a new constant array.
+ ///
+ /// Users are encouraged to use the [`define_fs_params`] macro to define the
+ /// [`super::Context::PARAMS`] constant.
+ pub const fn new(consts: [(&'static CStr, u32); N]) -> Self {
+ const ZERO: bindings::constant_table = bindings::constant_table {
+ name: core::ptr::null(),
+ value: 0,
+ };
+ let mut array = Self {
+ consts: [ZERO; N],
+ sentinel: ZERO,
+ };
+ let mut i = 0usize;
+ while i < N {
+ array.consts[i] = bindings::constant_table {
+ name: consts[i].0.as_char_ptr(),
+ value: consts[i].1 as _,
+ };
+ i += 1;
+ }
+ array
+ }
+
+ /// Returns a [`ConstantTable`] backed by `self`.
+ ///
+ /// This is used to essentially erase the array size.
+ pub const fn as_table(&self) -> ConstantTable<'_> {
+ ConstantTable {
+ first: &self.consts[0],
+ }
+ }
+}
+
+/// A parameter constant table.
+///
+/// The table is guaranteed to be zero-terminated.
+pub struct ConstantTable<'a> {
+ pub(super) first: &'a bindings::constant_table,
+}
+
+#[doc(hidden)]
+pub trait Handler<S> {
+ fn handle_param(
+ &self,
+ state: &mut S,
+ p: &bindings::fs_parameter,
+ r: &bindings::fs_parse_result,
+ ) -> Result;
+}
+
+struct ConcreteHandler<
+ S,
+ T: Fn(&mut S, &bindings::fs_parameter, &bindings::fs_parse_result) -> Result,
+> {
+ setfn: T,
+ _p: PhantomData<S>,
+}
+
+impl<S, T: Fn(&mut S, &bindings::fs_parameter, &bindings::fs_parse_result) -> Result> Handler<S>
+ for ConcreteHandler<S, T>
+{
+ fn handle_param(
+ &self,
+ state: &mut S,
+ p: &bindings::fs_parameter,
+ r: &bindings::fs_parse_result,
+ ) -> Result {
+ (self.setfn)(state, p, r)
+ }
+}
+
+/// Counts the number of comma-separated entries surrounded by braces.
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::count_brace_items;
+///
+/// assert_eq!(0, count_brace_items!());
+/// assert_eq!(1, count_brace_items!({A}));
+/// assert_eq!(1, count_brace_items!({A},));
+/// assert_eq!(2, count_brace_items!({A}, {B}));
+/// assert_eq!(2, count_brace_items!({A}, {B},));
+/// assert_eq!(3, count_brace_items!({A}, {B}, {C}));
+/// assert_eq!(3, count_brace_items!({A}, {B}, {C},));
+/// ```
+#[macro_export]
+macro_rules! count_brace_items {
+ ({$($item:tt)*}, $($remaining:tt)*) => { 1 + $crate::count_brace_items!($($remaining)*) };
+ ({$($item:tt)*}) => { 1 };
+ () => { 0 };
+}
+
+/// Defines the file system parameters of a given file system context.
+///
+/// # Examples
+/// ```
+/// # use kernel::prelude::*;
+/// # use kernel::{c_str, fs, str::CString};
+///
+/// #[derive(Default)]
+/// struct State {
+/// flag: Option<bool>,
+/// flag_no: Option<bool>,
+/// bool_value: Option<bool>,
+/// u32_value: Option<u32>,
+/// i32_value: Option<i32>,
+/// u64_value: Option<u64>,
+/// str_value: Option<CString>,
+/// enum_value: Option<u32>,
+/// }
+///
+/// fn set_u32(s: &mut Box<State>, v: u32) -> Result {
+/// s.u32_value = Some(v);
+/// Ok(())
+/// }
+///
+/// struct Example;
+///
+/// #[vtable]
+/// impl fs::Context<Self> for Example {
+/// type Data = Box<State>;
+///
+/// kernel::define_fs_params!{Box<State>,
+/// {flag, "flag", |s, v| { s.flag = Some(v); Ok(()) } },
+/// {flag_no, "flagno", |s, v| { s.flag_no = Some(v); Ok(()) } },
+/// {bool, "bool", |s, v| { s.bool_value = Some(v); Ok(()) } },
+/// {u32, "u32", set_u32 },
+/// {u32oct, "u32oct", set_u32 },
+/// {u32hex, "u32hex", set_u32 },
+/// {s32, "s32", |s, v| { s.i32_value = Some(v); Ok(()) } },
+/// {u64, "u64", |s, v| { s.u64_value = Some(v); Ok(()) } },
+/// {string, "string", |s, v| {
+/// s.str_value = Some(CString::try_from_fmt(fmt!("{v}"))?);
+/// Ok(())
+/// }},
+/// {enum, "enum", [("first", 10), ("second", 20)], |s, v| {
+/// s.enum_value = Some(v);
+/// Ok(())
+/// }},
+/// }
+///
+/// fn try_new() -> Result<Self::Data> {
+/// Ok(Box::try_new(State::default())?)
+/// }
+/// }
+///
+/// # impl fs::Type for Example {
+/// # type Context = Self;
+/// # const NAME: &'static CStr = c_str!("example");
+/// # const FLAGS: i32 = 0;
+/// # const MAGIC: u32 = 0x6578616d;
+/// # }
+/// ```
+#[macro_export]
+macro_rules! define_fs_params {
+ ($data_type:ty, $({$($t:tt)*}),+ $(,)?) => {
+ const PARAMS: $crate::fs::param::SpecTable<'static, $data_type> =
+ {
+ use $crate::fs::param::{self, ConstantArray, Spec, SpecArray, Handler};
+ use $crate::c_str;
+ const COUNT: usize = $crate::count_brace_items!($({$($t)*},)*);
+ const SPECS: [Spec; COUNT] = $crate::define_fs_params!(@specs $({$($t)*},)*);
+ const HANDLERS: [&dyn Handler<$data_type>; COUNT] =
+ $crate::define_fs_params!(@handlers $data_type, $({$($t)*},)*);
+ // SAFETY: We defined matching specs and handlers above.
+ const ARRAY: SpecArray<COUNT, $data_type> =
+ unsafe { SpecArray::new(SPECS, HANDLERS) };
+ ARRAY.as_table()
+ };
+ };
+
+ (@handlers $data_type:ty, $({$($t:tt)*},)*) => {
+ [ $($crate::define_fs_params!(@handler $data_type, $($t)*),)* ]
+ };
+ (@handler $data_type:ty, enum, $name:expr, $opts:expr, $closure:expr) => {
+ ¶m::enum_::handler::<$data_type>($closure)
+ };
+ (@handler $data_type:ty, $type:ident, $name:expr, $closure:expr) => {
+ ¶m::$type::handler::<$data_type>($closure)
+ };
+
+ (@specs $({$($t:tt)*},)*) => {[ $($crate::define_fs_params!(@spec $($t)*),)* ]};
+ (@spec enum, $name:expr, [$($opts:tt)*], $closure:expr) => {
+ {
+ const COUNT: usize = $crate::count_paren_items!($($opts)*);
+ const OPTIONS: ConstantArray<COUNT> =
+ ConstantArray::new($crate::define_fs_params!(@c_str_first $($opts)*));
+ param::enum_::spec(c_str!($name), OPTIONS.as_table())
+ }
+ };
+ (@spec $type:ident, $name:expr, $closure:expr) => { param::$type::spec(c_str!($name)) };
+
+ (@c_str_first $(($first:expr, $second:expr)),+ $(,)?) => {
+ [$((c_str!($first), $second),)*]
+ };
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 48da79b02422..6cf267119fda 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -18,6 +18,7 @@
#![feature(new_uninit)]
#![feature(receiver_trait)]
#![feature(unsize)]
+#![feature(const_mut_refs)]
// Ensure conditional compilation based on the kernel configuration works;
// otherwise we may silently break things like initcall handling.
diff --git a/samples/rust/rust_fs.rs b/samples/rust/rust_fs.rs
index 2bd4e563c694..acaa603b0bb2 100644
--- a/samples/rust/rust_fs.rs
+++ b/samples/rust/rust_fs.rs
@@ -18,6 +18,21 @@
impl fs::Context<Self> for RustFs {
type Data = ();
+ kernel::define_fs_params! {(),
+ {flag, "flag", |_, v| { pr_info!("flag passed-in: {v}\n"); Ok(()) } },
+ {flag_no, "flagno", |_, v| { pr_info!("flagno passed-in: {v}\n"); Ok(()) } },
+ {bool, "bool", |_, v| { pr_info!("bool passed-in: {v}\n"); Ok(()) } },
+ {u32, "u32", |_, v| { pr_info!("u32 passed-in: {v}\n"); Ok(()) } },
+ {u32oct, "u32oct", |_, v| { pr_info!("u32oct passed-in: {v}\n"); Ok(()) } },
+ {u32hex, "u32hex", |_, v| { pr_info!("u32hex passed-in: {v}\n"); Ok(()) } },
+ {s32, "s32", |_, v| { pr_info!("s32 passed-in: {v}\n"); Ok(()) } },
+ {u64, "u64", |_, v| { pr_info!("u64 passed-in: {v}\n"); Ok(()) } },
+ {string, "string", |_, v| { pr_info!("string passed-in: {v}\n"); Ok(()) } },
+ {enum, "enum", [("first", 10), ("second", 20)], |_, v| {
+ pr_info!("enum passed-in: {v}\n"); Ok(()) }
+ },
+ }
+
fn try_new() -> Result {
pr_info!("context created!\n");
Ok(())
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 05/80] rust: kernel: add libraries required by the filesystem abstractions
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (3 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 04/80] rust: add support for file system parameters Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 9:46 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 06/80] rust: allow fs driver to initialise new superblocks Ariel Miculas
` (72 subsequent siblings)
77 siblings, 1 reply; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Add cred.rs, file.rs, io_buffer.rs, iov_iter.rs, mm.rs, pages.rs and
user_ptr.rs from the rust branch.
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/bindings/bindings_helper.h | 4 +
rust/helpers.c | 35 ++
rust/kernel/cred.rs | 46 ++
rust/kernel/file.rs | 913 ++++++++++++++++++++++++++++++++
rust/kernel/io_buffer.rs | 153 ++++++
rust/kernel/iov_iter.rs | 81 +++
rust/kernel/lib.rs | 11 +-
rust/kernel/mm.rs | 149 ++++++
rust/kernel/pages.rs | 144 +++++
rust/kernel/user_ptr.rs | 175 ++++++
10 files changed, 1710 insertions(+), 1 deletion(-)
create mode 100644 rust/kernel/cred.rs
create mode 100644 rust/kernel/file.rs
create mode 100644 rust/kernel/io_buffer.rs
create mode 100644 rust/kernel/iov_iter.rs
create mode 100644 rust/kernel/mm.rs
create mode 100644 rust/kernel/pages.rs
create mode 100644 rust/kernel/user_ptr.rs
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 556f2e7c3ddb..b4297f6cb99f 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -12,6 +12,10 @@
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/fs_parser.h>
+#include <linux/cred.h>
+#include <linux/poll.h>
+#include <linux/uio.h>
+#include <linux/uaccess.h>
/* `bindgen` gets confused at certain things. */
const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;
diff --git a/rust/helpers.c b/rust/helpers.c
index b042d496649f..ffe62af5ee20 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -27,6 +27,7 @@
#include <linux/sched/signal.h>
#include <linux/wait.h>
#include <linux/fs_parser.h>
+#include <linux/highmem.h>
__noreturn void rust_helper_BUG(void)
{
@@ -156,6 +157,40 @@ int rust_helper_fs_parse(struct fs_context *fc,
}
EXPORT_SYMBOL_GPL(rust_helper_fs_parse);
+const struct cred *rust_helper_get_cred(const struct cred *cred)
+{
+ return get_cred(cred);
+}
+EXPORT_SYMBOL_GPL(rust_helper_get_cred);
+
+void rust_helper_put_cred(const struct cred *cred) {
+ put_cred(cred);
+}
+EXPORT_SYMBOL_GPL(rust_helper_put_cred);
+
+struct file *rust_helper_get_file(struct file *f)
+{
+ return get_file(f);
+}
+EXPORT_SYMBOL_GPL(rust_helper_get_file);
+
+unsigned long rust_helper_clear_user(void __user *to, unsigned long n)
+{
+ return clear_user(to, n);
+}
+
+void *rust_helper_kmap(struct page *page)
+{
+ return kmap(page);
+}
+EXPORT_SYMBOL_GPL(rust_helper_kmap);
+
+void rust_helper_kunmap(struct page *page)
+{
+ return kunmap(page);
+}
+EXPORT_SYMBOL_GPL(rust_helper_kunmap);
+
/*
* We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
* as the Rust `usize` type, so we can use it in contexts where Rust
diff --git a/rust/kernel/cred.rs b/rust/kernel/cred.rs
new file mode 100644
index 000000000000..16bd883b5fb5
--- /dev/null
+++ b/rust/kernel/cred.rs
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Credentials management.
+//!
+//! C header: [`include/linux/cred.h`](../../../../include/linux/cred.h)
+//!
+//! Reference: <https://www.kernel.org/doc/html/latest/security/credentials.html>
+
+use crate::{bindings, types::AlwaysRefCounted};
+use core::cell::UnsafeCell;
+
+/// Wraps the kernel's `struct cred`.
+///
+/// # Invariants
+///
+/// Instances of this type are always ref-counted, that is, a call to `get_cred` ensures that the
+/// allocation remains valid at least until the matching call to `put_cred`.
+#[repr(transparent)]
+pub struct Credential(pub(crate) UnsafeCell<bindings::cred>);
+
+impl Credential {
+ /// Creates a reference to a [`Credential`] from a valid pointer.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that `ptr` is valid and remains valid for the lifetime of the
+ /// returned [`Credential`] reference.
+ pub(crate) unsafe fn from_ptr<'a>(ptr: *const bindings::cred) -> &'a Self {
+ // SAFETY: The safety requirements guarantee the validity of the dereference, while the
+ // `Credential` type being transparent makes the cast ok.
+ unsafe { &*ptr.cast() }
+ }
+}
+
+// SAFETY: The type invariants guarantee that `Credential` is always ref-counted.
+unsafe impl AlwaysRefCounted for Credential {
+ fn inc_ref(&self) {
+ // SAFETY: The existence of a shared reference means that the refcount is nonzero.
+ unsafe { bindings::get_cred(self.0.get()) };
+ }
+
+ unsafe fn dec_ref(obj: core::ptr::NonNull<Self>) {
+ // SAFETY: The safety requirements guarantee that the refcount is nonzero.
+ unsafe { bindings::put_cred(obj.cast().as_ptr()) };
+ }
+}
diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs
new file mode 100644
index 000000000000..1b5934838833
--- /dev/null
+++ b/rust/kernel/file.rs
@@ -0,0 +1,913 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Files and file descriptors.
+//!
+//! C headers: [`include/linux/fs.h`](../../../../include/linux/fs.h) and
+//! [`include/linux/file.h`](../../../../include/linux/file.h)
+
+use crate::{
+ bindings,
+ cred::Credential,
+ error::{code::*, from_kernel_result, Error, Result},
+ fs,
+ io_buffer::{IoBufferReader, IoBufferWriter},
+ iov_iter::IovIter,
+ mm,
+ sync::CondVar,
+ types::ForeignOwnable,
+ user_ptr::{UserSlicePtr, UserSlicePtrReader, UserSlicePtrWriter},
+ types::ARef, types::AlwaysRefCounted,
+};
+use core::convert::{TryFrom, TryInto};
+use core::{cell::UnsafeCell, marker, mem, ptr};
+use macros::vtable;
+
+/// Flags associated with a [`File`].
+pub mod flags {
+ /// File is opened in append mode.
+ pub const O_APPEND: u32 = bindings::O_APPEND;
+
+ /// Signal-driven I/O is enabled.
+ pub const O_ASYNC: u32 = bindings::FASYNC;
+
+ /// Close-on-exec flag is set.
+ pub const O_CLOEXEC: u32 = bindings::O_CLOEXEC;
+
+ /// File was created if it didn't already exist.
+ pub const O_CREAT: u32 = bindings::O_CREAT;
+
+ /// Direct I/O is enabled for this file.
+ pub const O_DIRECT: u32 = bindings::O_DIRECT;
+
+ /// File must be a directory.
+ pub const O_DIRECTORY: u32 = bindings::O_DIRECTORY;
+
+ /// Like [`O_SYNC`] except metadata is not synced.
+ pub const O_DSYNC: u32 = bindings::O_DSYNC;
+
+ /// Ensure that this file is created with the `open(2)` call.
+ pub const O_EXCL: u32 = bindings::O_EXCL;
+
+ /// Large file size enabled (`off64_t` over `off_t`)
+ pub const O_LARGEFILE: u32 = bindings::O_LARGEFILE;
+
+ /// Do not update the file last access time.
+ pub const O_NOATIME: u32 = bindings::O_NOATIME;
+
+ /// File should not be used as process's controlling terminal.
+ pub const O_NOCTTY: u32 = bindings::O_NOCTTY;
+
+ /// If basename of path is a symbolic link, fail open.
+ pub const O_NOFOLLOW: u32 = bindings::O_NOFOLLOW;
+
+ /// File is using nonblocking I/O.
+ pub const O_NONBLOCK: u32 = bindings::O_NONBLOCK;
+
+ /// Also known as `O_NDELAY`.
+ ///
+ /// This is effectively the same flag as [`O_NONBLOCK`] on all architectures
+ /// except SPARC64.
+ pub const O_NDELAY: u32 = bindings::O_NDELAY;
+
+ /// Used to obtain a path file descriptor.
+ pub const O_PATH: u32 = bindings::O_PATH;
+
+ /// Write operations on this file will flush data and metadata.
+ pub const O_SYNC: u32 = bindings::O_SYNC;
+
+ /// This file is an unnamed temporary regular file.
+ pub const O_TMPFILE: u32 = bindings::O_TMPFILE;
+
+ /// File should be truncated to length 0.
+ pub const O_TRUNC: u32 = bindings::O_TRUNC;
+
+ /// Bitmask for access mode flags.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::file;
+ /// # fn do_something() {}
+ /// # let flags = 0;
+ /// if (flags & file::flags::O_ACCMODE) == file::flags::O_RDONLY {
+ /// do_something();
+ /// }
+ /// ```
+ pub const O_ACCMODE: u32 = bindings::O_ACCMODE;
+
+ /// File is read only.
+ pub const O_RDONLY: u32 = bindings::O_RDONLY;
+
+ /// File is write only.
+ pub const O_WRONLY: u32 = bindings::O_WRONLY;
+
+ /// File can be both read and written.
+ pub const O_RDWR: u32 = bindings::O_RDWR;
+}
+
+/// Wraps the kernel's `struct file`.
+///
+/// # Invariants
+///
+/// Instances of this type are always ref-counted, that is, a call to `get_file` ensures that the
+/// allocation remains valid at least until the matching call to `fput`.
+#[repr(transparent)]
+pub struct File(pub(crate) UnsafeCell<bindings::file>);
+
+// TODO: Accessing fields of `struct file` through the pointer is UB because other threads may be
+// writing to them. However, this is how the C code currently operates: naked reads and writes to
+// fields. Even if we used relaxed atomics on the Rust side, we can't force this on the C side.
+impl File {
+ /// Constructs a new [`struct file`] wrapper from a file descriptor.
+ ///
+ /// The file descriptor belongs to the current process.
+ pub fn from_fd(fd: u32) -> Result<ARef<Self>> {
+ // SAFETY: FFI call, there are no requirements on `fd`.
+ let ptr = ptr::NonNull::new(unsafe { bindings::fget(fd) }).ok_or(EBADF)?;
+
+ // SAFETY: `fget` increments the refcount before returning.
+ Ok(unsafe { ARef::from_raw(ptr.cast()) })
+ }
+
+ /// Creates a reference to a [`File`] from a valid pointer.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that `ptr` is valid and remains valid for the lifetime of the
+ /// returned [`File`] instance.
+ pub(crate) unsafe fn from_ptr<'a>(ptr: *const bindings::file) -> &'a File {
+ // SAFETY: The safety requirements guarantee the validity of the dereference, while the
+ // `File` type being transparent makes the cast ok.
+ unsafe { &*ptr.cast() }
+ }
+
+ /// Returns the current seek/cursor/pointer position (`struct file::f_pos`).
+ pub fn pos(&self) -> u64 {
+ // SAFETY: The file is valid because the shared reference guarantees a nonzero refcount.
+ unsafe { core::ptr::addr_of!((*self.0.get()).f_pos).read() as _ }
+ }
+
+ /// Returns the credentials of the task that originally opened the file.
+ pub fn cred(&self) -> &Credential {
+ // SAFETY: The file is valid because the shared reference guarantees a nonzero refcount.
+ let ptr = unsafe { core::ptr::addr_of!((*self.0.get()).f_cred).read() };
+ // SAFETY: The lifetimes of `self` and `Credential` are tied, so it is guaranteed that
+ // the credential pointer remains valid (because the file is still alive, and it doesn't
+ // change over the lifetime of a file).
+ unsafe { Credential::from_ptr(ptr) }
+ }
+
+ /// Returns the flags associated with the file.
+ ///
+ /// The flags are a combination of the constants in [`flags`].
+ pub fn flags(&self) -> u32 {
+ // SAFETY: The file is valid because the shared reference guarantees a nonzero refcount.
+ unsafe { core::ptr::addr_of!((*self.0.get()).f_flags).read() }
+ }
+
+ /// Returns the inode associated with the file.
+ ///
+ /// It returns `None` is the type of the inode is not `T`.
+ pub fn inode<T: fs::Type + ?Sized>(&self) -> Option<&fs::INode<T>> {
+ // SAFETY: The file is valid because the shared reference guarantees a nonzero refcount.
+ let inode = unsafe { core::ptr::addr_of!((*self.0.get()).f_inode).read() };
+
+ // SAFETY: The inode and superblock are valid because the file as a reference to them.
+ let sb_ops = unsafe { (*(*inode).i_sb).s_op };
+
+ if sb_ops == &fs::Tables::<T>::SUPER_BLOCK {
+ // SAFETY: We checked that the super-block operations table is the one produced for
+ // `T`, so it's safe to cast the inode. Additionally, the lifetime of the returned
+ // inode is bound to the file object.
+ Some(unsafe { &*inode.cast() })
+ } else {
+ None
+ }
+ }
+}
+
+// SAFETY: The type invariants guarantee that `File` is always ref-counted.
+unsafe impl AlwaysRefCounted for File {
+ fn inc_ref(&self) {
+ // SAFETY: The existence of a shared reference means that the refcount is nonzero.
+ unsafe { bindings::get_file(self.0.get()) };
+ }
+
+ unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
+ // SAFETY: The safety requirements guarantee that the refcount is nonzero.
+ unsafe { bindings::fput(obj.cast().as_ptr()) }
+ }
+}
+
+/// A file descriptor reservation.
+///
+/// This allows the creation of a file descriptor in two steps: first, we reserve a slot for it,
+/// then we commit or drop the reservation. The first step may fail (e.g., the current process ran
+/// out of available slots), but commit and drop never fail (and are mutually exclusive).
+pub struct FileDescriptorReservation {
+ fd: u32,
+}
+
+impl FileDescriptorReservation {
+ /// Creates a new file descriptor reservation.
+ pub fn new(flags: u32) -> Result<Self> {
+ // SAFETY: FFI call, there are no safety requirements on `flags`.
+ let fd = unsafe { bindings::get_unused_fd_flags(flags) };
+ if fd < 0 {
+ return Err(Error::from_kernel_errno(fd));
+ }
+ Ok(Self { fd: fd as _ })
+ }
+
+ /// Returns the file descriptor number that was reserved.
+ pub fn reserved_fd(&self) -> u32 {
+ self.fd
+ }
+
+ /// Commits the reservation.
+ ///
+ /// The previously reserved file descriptor is bound to `file`.
+ pub fn commit(self, file: ARef<File>) {
+ // SAFETY: `self.fd` was previously returned by `get_unused_fd_flags`, and `file.ptr` is
+ // guaranteed to have an owned ref count by its type invariants.
+ unsafe { bindings::fd_install(self.fd, file.0.get()) };
+
+ // `fd_install` consumes both the file descriptor and the file reference, so we cannot run
+ // the destructors.
+ core::mem::forget(self);
+ core::mem::forget(file);
+ }
+}
+
+impl Drop for FileDescriptorReservation {
+ fn drop(&mut self) {
+ // SAFETY: `self.fd` was returned by a previous call to `get_unused_fd_flags`.
+ unsafe { bindings::put_unused_fd(self.fd) };
+ }
+}
+
+/// Wraps the kernel's `struct poll_table_struct`.
+///
+/// # Invariants
+///
+/// The pointer `PollTable::ptr` is null or valid.
+pub struct PollTable {
+ ptr: *mut bindings::poll_table_struct,
+}
+
+impl PollTable {
+ /// Constructors a new `struct poll_table_struct` wrapper.
+ ///
+ /// # Safety
+ ///
+ /// The pointer `ptr` must be either null or a valid pointer for the lifetime of the object.
+ unsafe fn from_ptr(ptr: *mut bindings::poll_table_struct) -> Self {
+ Self { ptr }
+ }
+
+ /// Associates the given file and condition variable to this poll table. It means notifying the
+ /// condition variable will notify the poll table as well; additionally, the association
+ /// between the condition variable and the file will automatically be undone by the kernel when
+ /// the file is destructed. To unilaterally remove the association before then, one can call
+ /// [`CondVar::free_waiters`].
+ ///
+ /// # Safety
+ ///
+ /// If the condition variable is destroyed before the file, then [`CondVar::free_waiters`] must
+ /// be called to ensure that all waiters are flushed out.
+ pub unsafe fn register_wait<'a>(&self, file: &'a File, cv: &'a CondVar) {
+ if self.ptr.is_null() {
+ return;
+ }
+
+ // SAFETY: `PollTable::ptr` is guaranteed to be valid by the type invariants and the null
+ // check above.
+ let table = unsafe { &*self.ptr };
+ if let Some(proc) = table._qproc {
+ // SAFETY: All pointers are known to be valid.
+ unsafe { proc(file.0.get() as _, cv.wait_list.get(), self.ptr) }
+ }
+ }
+}
+
+/// Equivalent to [`std::io::SeekFrom`].
+///
+/// [`std::io::SeekFrom`]: https://doc.rust-lang.org/std/io/enum.SeekFrom.html
+pub enum SeekFrom {
+ /// Equivalent to C's `SEEK_SET`.
+ Start(u64),
+
+ /// Equivalent to C's `SEEK_END`.
+ End(i64),
+
+ /// Equivalent to C's `SEEK_CUR`.
+ Current(i64),
+}
+
+pub(crate) struct OperationsVtable<A, T>(marker::PhantomData<A>, marker::PhantomData<T>);
+
+impl<A: OpenAdapter<T::OpenData>, T: Operations> OperationsVtable<A, T> {
+ /// Called by the VFS when an inode should be opened.
+ ///
+ /// Calls `T::open` on the returned value of `A::convert`.
+ ///
+ /// # Safety
+ ///
+ /// The returned value of `A::convert` must be a valid non-null pointer and
+ /// `T:open` must return a valid non-null pointer on an `Ok` result.
+ unsafe extern "C" fn open_callback(
+ inode: *mut bindings::inode,
+ file: *mut bindings::file,
+ ) -> core::ffi::c_int {
+ from_kernel_result! {
+ // SAFETY: `A::convert` must return a valid non-null pointer that
+ // should point to data in the inode or file that lives longer
+ // than the following use of `T::open`.
+ let arg = unsafe { A::convert(inode, file) };
+ // SAFETY: The C contract guarantees that `file` is valid. Additionally,
+ // `fileref` never outlives this function, so it is guaranteed to be
+ // valid.
+ let fileref = unsafe { File::from_ptr(file) };
+ // SAFETY: `arg` was previously returned by `A::convert` and must
+ // be a valid non-null pointer.
+ let ptr = T::open(unsafe { &*arg }, fileref)?.into_pointer();
+ // SAFETY: The C contract guarantees that `private_data` is available
+ // for implementers of the file operations (no other C code accesses
+ // it), so we know that there are no concurrent threads/CPUs accessing
+ // it (it's not visible to any other Rust code).
+ unsafe { (*file).private_data = ptr as *mut core::ffi::c_void };
+ Ok(0)
+ }
+ }
+
+ unsafe extern "C" fn read_callback(
+ file: *mut bindings::file,
+ buf: *mut core::ffi::c_char,
+ len: core::ffi::c_size_t,
+ offset: *mut bindings::loff_t,
+ ) -> core::ffi::c_ssize_t {
+ from_kernel_result! {
+ let mut data = unsafe { UserSlicePtr::new(buf as *mut core::ffi::c_void, len).writer() };
+ // SAFETY: `private_data` was initialised by `open_callback` with a value returned by
+ // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the
+ // `release` callback, which the C API guarantees that will be called only when all
+ // references to `file` have been released, so we know it can't be called while this
+ // function is running.
+ let f = unsafe { T::Data::borrow((*file).private_data) };
+ // No `FMODE_UNSIGNED_OFFSET` support, so `offset` must be in [0, 2^63).
+ // See discussion in https://github.com/fishinabarrel/linux-kernel-module-rust/pull/113
+ let read = T::read(
+ f,
+ unsafe { File::from_ptr(file) },
+ &mut data,
+ unsafe { *offset }.try_into()?,
+ )?;
+ unsafe { (*offset) += bindings::loff_t::try_from(read).unwrap() };
+ Ok(read as _)
+ }
+ }
+
+ unsafe extern "C" fn read_iter_callback(
+ iocb: *mut bindings::kiocb,
+ raw_iter: *mut bindings::iov_iter,
+ ) -> isize {
+ from_kernel_result! {
+ let mut iter = unsafe { IovIter::from_ptr(raw_iter) };
+ let file = unsafe { (*iocb).ki_filp };
+ let offset = unsafe { (*iocb).ki_pos };
+ // SAFETY: `private_data` was initialised by `open_callback` with a value returned by
+ // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the
+ // `release` callback, which the C API guarantees that will be called only when all
+ // references to `file` have been released, so we know it can't be called while this
+ // function is running.
+ let f = unsafe { T::Data::borrow((*file).private_data) };
+ let read =
+ T::read(f, unsafe { File::from_ptr(file) }, &mut iter, offset.try_into()?)?;
+ unsafe { (*iocb).ki_pos += bindings::loff_t::try_from(read).unwrap() };
+ Ok(read as _)
+ }
+ }
+
+ unsafe extern "C" fn write_callback(
+ file: *mut bindings::file,
+ buf: *const core::ffi::c_char,
+ len: core::ffi::c_size_t,
+ offset: *mut bindings::loff_t,
+ ) -> core::ffi::c_ssize_t {
+ from_kernel_result! {
+ let mut data = unsafe { UserSlicePtr::new(buf as *mut core::ffi::c_void, len).reader() };
+ // SAFETY: `private_data` was initialised by `open_callback` with a value returned by
+ // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the
+ // `release` callback, which the C API guarantees that will be called only when all
+ // references to `file` have been released, so we know it can't be called while this
+ // function is running.
+ let f = unsafe { T::Data::borrow((*file).private_data) };
+ // No `FMODE_UNSIGNED_OFFSET` support, so `offset` must be in [0, 2^63).
+ // See discussion in https://github.com/fishinabarrel/linux-kernel-module-rust/pull/113
+ let written = T::write(
+ f,
+ unsafe { File::from_ptr(file) },
+ &mut data,
+ unsafe { *offset }.try_into()?
+ )?;
+ unsafe { (*offset) += bindings::loff_t::try_from(written).unwrap() };
+ Ok(written as _)
+ }
+ }
+
+ unsafe extern "C" fn write_iter_callback(
+ iocb: *mut bindings::kiocb,
+ raw_iter: *mut bindings::iov_iter,
+ ) -> isize {
+ from_kernel_result! {
+ let mut iter = unsafe { IovIter::from_ptr(raw_iter) };
+ let file = unsafe { (*iocb).ki_filp };
+ let offset = unsafe { (*iocb).ki_pos };
+ // SAFETY: `private_data` was initialised by `open_callback` with a value returned by
+ // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the
+ // `release` callback, which the C API guarantees that will be called only when all
+ // references to `file` have been released, so we know it can't be called while this
+ // function is running.
+ let f = unsafe { T::Data::borrow((*file).private_data) };
+ let written =
+ T::write(f, unsafe { File::from_ptr(file) }, &mut iter, offset.try_into()?)?;
+ unsafe { (*iocb).ki_pos += bindings::loff_t::try_from(written).unwrap() };
+ Ok(written as _)
+ }
+ }
+
+ unsafe extern "C" fn release_callback(
+ _inode: *mut bindings::inode,
+ file: *mut bindings::file,
+ ) -> core::ffi::c_int {
+ let ptr = mem::replace(unsafe { &mut (*file).private_data }, ptr::null_mut());
+ T::release(unsafe { T::Data::from_pointer(ptr as _) }, unsafe {
+ File::from_ptr(file)
+ });
+ 0
+ }
+
+ unsafe extern "C" fn llseek_callback(
+ file: *mut bindings::file,
+ offset: bindings::loff_t,
+ whence: core::ffi::c_int,
+ ) -> bindings::loff_t {
+ from_kernel_result! {
+ let off = match whence as u32 {
+ bindings::SEEK_SET => SeekFrom::Start(offset.try_into()?),
+ bindings::SEEK_CUR => SeekFrom::Current(offset),
+ bindings::SEEK_END => SeekFrom::End(offset),
+ _ => return Err(EINVAL),
+ };
+ // SAFETY: `private_data` was initialised by `open_callback` with a value returned by
+ // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the
+ // `release` callback, which the C API guarantees that will be called only when all
+ // references to `file` have been released, so we know it can't be called while this
+ // function is running.
+ let f = unsafe { T::Data::borrow((*file).private_data) };
+ let off = T::seek(f, unsafe { File::from_ptr(file) }, off)?;
+ Ok(off as bindings::loff_t)
+ }
+ }
+
+ unsafe extern "C" fn unlocked_ioctl_callback(
+ file: *mut bindings::file,
+ cmd: core::ffi::c_uint,
+ arg: core::ffi::c_ulong,
+ ) -> core::ffi::c_long {
+ from_kernel_result! {
+ // SAFETY: `private_data` was initialised by `open_callback` with a value returned by
+ // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the
+ // `release` callback, which the C API guarantees that will be called only when all
+ // references to `file` have been released, so we know it can't be called while this
+ // function is running.
+ let f = unsafe { T::Data::borrow((*file).private_data) };
+ let mut cmd = IoctlCommand::new(cmd as _, arg as _);
+ let ret = T::ioctl(f, unsafe { File::from_ptr(file) }, &mut cmd)?;
+ Ok(ret as _)
+ }
+ }
+
+ unsafe extern "C" fn compat_ioctl_callback(
+ file: *mut bindings::file,
+ cmd: core::ffi::c_uint,
+ arg: core::ffi::c_ulong,
+ ) -> core::ffi::c_long {
+ from_kernel_result! {
+ // SAFETY: `private_data` was initialised by `open_callback` with a value returned by
+ // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the
+ // `release` callback, which the C API guarantees that will be called only when all
+ // references to `file` have been released, so we know it can't be called while this
+ // function is running.
+ let f = unsafe { T::Data::borrow((*file).private_data) };
+ let mut cmd = IoctlCommand::new(cmd as _, arg as _);
+ let ret = T::compat_ioctl(f, unsafe { File::from_ptr(file) }, &mut cmd)?;
+ Ok(ret as _)
+ }
+ }
+
+ unsafe extern "C" fn mmap_callback(
+ file: *mut bindings::file,
+ vma: *mut bindings::vm_area_struct,
+ ) -> core::ffi::c_int {
+ from_kernel_result! {
+ // SAFETY: `private_data` was initialised by `open_callback` with a value returned by
+ // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the
+ // `release` callback, which the C API guarantees that will be called only when all
+ // references to `file` have been released, so we know it can't be called while this
+ // function is running.
+ let f = unsafe { T::Data::borrow((*file).private_data) };
+
+ // SAFETY: The C API guarantees that `vma` is valid for the duration of this call.
+ // `area` only lives within this call, so it is guaranteed to be valid.
+ let mut area = unsafe { mm::virt::Area::from_ptr(vma) };
+
+ // SAFETY: The C API guarantees that `file` is valid for the duration of this call,
+ // which is longer than the lifetime of the file reference.
+ T::mmap(f, unsafe { File::from_ptr(file) }, &mut area)?;
+ Ok(0)
+ }
+ }
+
+ unsafe extern "C" fn fsync_callback(
+ file: *mut bindings::file,
+ start: bindings::loff_t,
+ end: bindings::loff_t,
+ datasync: core::ffi::c_int,
+ ) -> core::ffi::c_int {
+ from_kernel_result! {
+ let start = start.try_into()?;
+ let end = end.try_into()?;
+ let datasync = datasync != 0;
+ // SAFETY: `private_data` was initialised by `open_callback` with a value returned by
+ // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the
+ // `release` callback, which the C API guarantees that will be called only when all
+ // references to `file` have been released, so we know it can't be called while this
+ // function is running.
+ let f = unsafe { T::Data::borrow((*file).private_data) };
+ let res = T::fsync(f, unsafe { File::from_ptr(file) }, start, end, datasync)?;
+ Ok(res.try_into().unwrap())
+ }
+ }
+
+ unsafe extern "C" fn poll_callback(
+ file: *mut bindings::file,
+ wait: *mut bindings::poll_table_struct,
+ ) -> bindings::__poll_t {
+ // SAFETY: `private_data` was initialised by `open_callback` with a value returned by
+ // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the `release`
+ // callback, which the C API guarantees that will be called only when all references to
+ // `file` have been released, so we know it can't be called while this function is running.
+ let f = unsafe { T::Data::borrow((*file).private_data) };
+ match T::poll(f, unsafe { File::from_ptr(file) }, unsafe {
+ &PollTable::from_ptr(wait)
+ }) {
+ Ok(v) => v,
+ Err(_) => bindings::POLLERR,
+ }
+ }
+
+ const VTABLE: bindings::file_operations = bindings::file_operations {
+ open: Some(Self::open_callback),
+ release: Some(Self::release_callback),
+ read: if T::HAS_READ {
+ Some(Self::read_callback)
+ } else {
+ None
+ },
+ write: if T::HAS_WRITE {
+ Some(Self::write_callback)
+ } else {
+ None
+ },
+ llseek: if T::HAS_SEEK {
+ Some(Self::llseek_callback)
+ } else {
+ None
+ },
+
+ check_flags: None,
+ compat_ioctl: if T::HAS_COMPAT_IOCTL {
+ Some(Self::compat_ioctl_callback)
+ } else {
+ None
+ },
+ copy_file_range: None,
+ fallocate: None,
+ fadvise: None,
+ fasync: None,
+ flock: None,
+ flush: None,
+ fsync: if T::HAS_FSYNC {
+ Some(Self::fsync_callback)
+ } else {
+ None
+ },
+ get_unmapped_area: None,
+ iterate: None,
+ iterate_shared: None,
+ iopoll: None,
+ lock: None,
+ mmap: if T::HAS_MMAP {
+ Some(Self::mmap_callback)
+ } else {
+ None
+ },
+ mmap_supported_flags: 0,
+ owner: ptr::null_mut(),
+ poll: if T::HAS_POLL {
+ Some(Self::poll_callback)
+ } else {
+ None
+ },
+ read_iter: if T::HAS_READ {
+ Some(Self::read_iter_callback)
+ } else {
+ None
+ },
+ remap_file_range: None,
+ sendpage: None,
+ setlease: None,
+ show_fdinfo: None,
+ splice_read: None,
+ splice_write: None,
+ unlocked_ioctl: if T::HAS_IOCTL {
+ Some(Self::unlocked_ioctl_callback)
+ } else {
+ None
+ },
+ uring_cmd: None,
+ write_iter: if T::HAS_WRITE {
+ Some(Self::write_iter_callback)
+ } else {
+ None
+ },
+ };
+
+ /// Builds an instance of [`struct file_operations`].
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that the adapter is compatible with the way the device is registered.
+ pub(crate) const unsafe fn build() -> &'static bindings::file_operations {
+ &Self::VTABLE
+ }
+}
+
+/// Allows the handling of ioctls defined with the `_IO`, `_IOR`, `_IOW`, and `_IOWR` macros.
+///
+/// For each macro, there is a handler function that takes the appropriate types as arguments.
+pub trait IoctlHandler: Sync {
+ /// The type of the first argument to each associated function.
+ type Target<'a>;
+
+ /// Handles ioctls defined with the `_IO` macro, that is, with no buffer as argument.
+ fn pure(_this: Self::Target<'_>, _file: &File, _cmd: u32, _arg: usize) -> Result<i32> {
+ Err(EINVAL)
+ }
+
+ /// Handles ioctls defined with the `_IOR` macro, that is, with an output buffer provided as
+ /// argument.
+ fn read(
+ _this: Self::Target<'_>,
+ _file: &File,
+ _cmd: u32,
+ _writer: &mut UserSlicePtrWriter,
+ ) -> Result<i32> {
+ Err(EINVAL)
+ }
+
+ /// Handles ioctls defined with the `_IOW` macro, that is, with an input buffer provided as
+ /// argument.
+ fn write(
+ _this: Self::Target<'_>,
+ _file: &File,
+ _cmd: u32,
+ _reader: &mut UserSlicePtrReader,
+ ) -> Result<i32> {
+ Err(EINVAL)
+ }
+
+ /// Handles ioctls defined with the `_IOWR` macro, that is, with a buffer for both input and
+ /// output provided as argument.
+ fn read_write(
+ _this: Self::Target<'_>,
+ _file: &File,
+ _cmd: u32,
+ _data: UserSlicePtr,
+ ) -> Result<i32> {
+ Err(EINVAL)
+ }
+}
+
+/// Represents an ioctl command.
+///
+/// It can use the components of an ioctl command to dispatch ioctls using
+/// [`IoctlCommand::dispatch`].
+pub struct IoctlCommand {
+ cmd: u32,
+ arg: usize,
+ user_slice: Option<UserSlicePtr>,
+}
+
+impl IoctlCommand {
+ /// Constructs a new [`IoctlCommand`].
+ fn new(cmd: u32, arg: usize) -> Self {
+ let size = (cmd >> bindings::_IOC_SIZESHIFT) & bindings::_IOC_SIZEMASK;
+
+ // SAFETY: We only create one instance of the user slice per ioctl call, so TOCTOU issues
+ // are not possible.
+ let user_slice = Some(unsafe { UserSlicePtr::new(arg as _, size as _) });
+ Self {
+ cmd,
+ arg,
+ user_slice,
+ }
+ }
+
+ /// Dispatches the given ioctl to the appropriate handler based on the value of the command. It
+ /// also creates a [`UserSlicePtr`], [`UserSlicePtrReader`], or [`UserSlicePtrWriter`]
+ /// depending on the direction of the buffer of the command.
+ ///
+ /// It is meant to be used in implementations of [`Operations::ioctl`] and
+ /// [`Operations::compat_ioctl`].
+ pub fn dispatch<T: IoctlHandler>(
+ &mut self,
+ handler: T::Target<'_>,
+ file: &File,
+ ) -> Result<i32> {
+ let dir = (self.cmd >> bindings::_IOC_DIRSHIFT) & bindings::_IOC_DIRMASK;
+ if dir == bindings::_IOC_NONE {
+ return T::pure(handler, file, self.cmd, self.arg);
+ }
+
+ let data = self.user_slice.take().ok_or(EINVAL)?;
+ const READ_WRITE: u32 = bindings::_IOC_READ | bindings::_IOC_WRITE;
+ match dir {
+ bindings::_IOC_WRITE => T::write(handler, file, self.cmd, &mut data.reader()),
+ bindings::_IOC_READ => T::read(handler, file, self.cmd, &mut data.writer()),
+ READ_WRITE => T::read_write(handler, file, self.cmd, data),
+ _ => Err(EINVAL),
+ }
+ }
+
+ /// Returns the raw 32-bit value of the command and the ptr-sized argument.
+ pub fn raw(&self) -> (u32, usize) {
+ (self.cmd, self.arg)
+ }
+}
+
+/// Trait for extracting file open arguments from kernel data structures.
+///
+/// This is meant to be implemented by registration managers.
+pub trait OpenAdapter<T: Sync> {
+ /// Converts untyped data stored in [`struct inode`] and [`struct file`] (when [`struct
+ /// file_operations::open`] is called) into the given type. For example, for `miscdev`
+ /// devices, a pointer to the registered [`struct miscdev`] is stored in [`struct
+ /// file::private_data`].
+ ///
+ /// # Safety
+ ///
+ /// This function must be called only when [`struct file_operations::open`] is being called for
+ /// a file that was registered by the implementer. The returned pointer must be valid and
+ /// not-null.
+ unsafe fn convert(_inode: *mut bindings::inode, _file: *mut bindings::file) -> *const T;
+}
+
+/// Corresponds to the kernel's `struct file_operations`.
+///
+/// You implement this trait whenever you would create a `struct file_operations`.
+///
+/// File descriptors may be used from multiple threads/processes concurrently, so your type must be
+/// [`Sync`]. It must also be [`Send`] because [`Operations::release`] will be called from the
+/// thread that decrements that associated file's refcount to zero.
+#[vtable]
+pub trait Operations {
+ /// The type of the context data returned by [`Operations::open`] and made available to
+ /// other methods.
+ type Data: ForeignOwnable + Send + Sync = ();
+
+ /// The type of the context data passed to [`Operations::open`].
+ type OpenData: Sync = ();
+
+ /// Creates a new instance of this file.
+ ///
+ /// Corresponds to the `open` function pointer in `struct file_operations`.
+ fn open(context: &Self::OpenData, file: &File) -> Result<Self::Data>;
+
+ /// Cleans up after the last reference to the file goes away.
+ ///
+ /// Note that context data is moved, so it will be freed automatically unless the
+ /// implementation moves it elsewhere.
+ ///
+ /// Corresponds to the `release` function pointer in `struct file_operations`.
+ fn release(_data: Self::Data, _file: &File) {}
+
+ /// Reads data from this file to the caller's buffer.
+ ///
+ /// Corresponds to the `read` and `read_iter` function pointers in `struct file_operations`.
+ fn read(
+ _data: <Self::Data as ForeignOwnable>::Borrowed<'_>,
+ _file: &File,
+ _writer: &mut impl IoBufferWriter,
+ _offset: u64,
+ ) -> Result<usize> {
+ Err(EINVAL)
+ }
+
+ /// Writes data from the caller's buffer to this file.
+ ///
+ /// Corresponds to the `write` and `write_iter` function pointers in `struct file_operations`.
+ fn write(
+ _data: <Self::Data as ForeignOwnable>::Borrowed<'_>,
+ _file: &File,
+ _reader: &mut impl IoBufferReader,
+ _offset: u64,
+ ) -> Result<usize> {
+ Err(EINVAL)
+ }
+
+ /// Changes the position of the file.
+ ///
+ /// Corresponds to the `llseek` function pointer in `struct file_operations`.
+ fn seek(
+ _data: <Self::Data as ForeignOwnable>::Borrowed<'_>,
+ _file: &File,
+ _offset: SeekFrom,
+ ) -> Result<u64> {
+ Err(EINVAL)
+ }
+
+ /// Performs IO control operations that are specific to the file.
+ ///
+ /// Corresponds to the `unlocked_ioctl` function pointer in `struct file_operations`.
+ fn ioctl(
+ _data: <Self::Data as ForeignOwnable>::Borrowed<'_>,
+ _file: &File,
+ _cmd: &mut IoctlCommand,
+ ) -> Result<i32> {
+ Err(ENOTTY)
+ }
+
+ /// Performs 32-bit IO control operations on that are specific to the file on 64-bit kernels.
+ ///
+ /// Corresponds to the `compat_ioctl` function pointer in `struct file_operations`.
+ fn compat_ioctl(
+ _data: <Self::Data as ForeignOwnable>::Borrowed<'_>,
+ _file: &File,
+ _cmd: &mut IoctlCommand,
+ ) -> Result<i32> {
+ Err(ENOTTY)
+ }
+
+ /// Syncs pending changes to this file.
+ ///
+ /// Corresponds to the `fsync` function pointer in `struct file_operations`.
+ fn fsync(
+ _data: <Self::Data as ForeignOwnable>::Borrowed<'_>,
+ _file: &File,
+ _start: u64,
+ _end: u64,
+ _datasync: bool,
+ ) -> Result<u32> {
+ Err(EINVAL)
+ }
+
+ /// Maps areas of the caller's virtual memory with device/file memory.
+ ///
+ /// Corresponds to the `mmap` function pointer in `struct file_operations`.
+ fn mmap(
+ _data: <Self::Data as ForeignOwnable>::Borrowed<'_>,
+ _file: &File,
+ _vma: &mut mm::virt::Area,
+ ) -> Result {
+ Err(EINVAL)
+ }
+
+ /// Checks the state of the file and optionally registers for notification when the state
+ /// changes.
+ ///
+ /// Corresponds to the `poll` function pointer in `struct file_operations`.
+ fn poll(
+ _data: <Self::Data as ForeignOwnable>::Borrowed<'_>,
+ _file: &File,
+ _table: &PollTable,
+ ) -> Result<u32> {
+ Ok(bindings::POLLIN | bindings::POLLOUT | bindings::POLLRDNORM | bindings::POLLWRNORM)
+ }
+}
+
+/// Writes the contents of a slice into a buffer writer.
+///
+/// This is used to help implement [`Operations::read`] when the contents are stored in a slice. It
+/// takes into account the offset and lengths, and returns the amount of data written.
+pub fn read_from_slice(s: &[u8], writer: &mut impl IoBufferWriter, offset: u64) -> Result<usize> {
+ let offset = offset.try_into()?;
+ if offset >= s.len() {
+ return Ok(0);
+ }
+
+ let len = core::cmp::min(s.len() - offset, writer.len());
+ writer.write_slice(&s[offset..][..len])?;
+ Ok(len)
+}
diff --git a/rust/kernel/io_buffer.rs b/rust/kernel/io_buffer.rs
new file mode 100644
index 000000000000..d5a258a5ff8f
--- /dev/null
+++ b/rust/kernel/io_buffer.rs
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Buffers used in IO.
+
+use crate::error::Result;
+use alloc::vec::Vec;
+use core::mem::{size_of, MaybeUninit};
+
+/// Represents a buffer to be read from during IO.
+pub trait IoBufferReader {
+ /// Returns the number of bytes left to be read from the io buffer.
+ ///
+ /// Note that even reading less than this number of bytes may fail.
+ fn len(&self) -> usize;
+
+ /// Returns `true` if no data is available in the io buffer.
+ fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ /// Reads raw data from the io buffer into a raw kernel buffer.
+ ///
+ /// # Safety
+ ///
+ /// The output buffer must be valid.
+ unsafe fn read_raw(&mut self, out: *mut u8, len: usize) -> Result;
+
+ /// Reads all data remaining in the io buffer.
+ ///
+ /// Returns `EFAULT` if the address does not currently point to mapped, readable memory.
+ fn read_all(&mut self) -> Result<Vec<u8>> {
+ let mut data = Vec::<u8>::new();
+ data.try_resize(self.len(), 0)?;
+
+ // SAFETY: The output buffer is valid as we just allocated it.
+ unsafe { self.read_raw(data.as_mut_ptr(), data.len())? };
+ Ok(data)
+ }
+
+ /// Reads a byte slice from the io buffer.
+ ///
+ /// Returns `EFAULT` if the byte slice is bigger than the remaining size of the user slice or
+ /// if the address does not currently point to mapped, readable memory.
+ fn read_slice(&mut self, data: &mut [u8]) -> Result {
+ // SAFETY: The output buffer is valid as it's coming from a live reference.
+ unsafe { self.read_raw(data.as_mut_ptr(), data.len()) }
+ }
+
+ /// Reads the contents of a plain old data (POD) type from the io buffer.
+ fn read<T: ReadableFromBytes>(&mut self) -> Result<T> {
+ let mut out = MaybeUninit::<T>::uninit();
+ // SAFETY: The buffer is valid as it was just allocated.
+ unsafe { self.read_raw(out.as_mut_ptr() as _, size_of::<T>()) }?;
+ // SAFETY: We just initialised the data.
+ Ok(unsafe { out.assume_init() })
+ }
+}
+
+/// Represents a buffer to be written to during IO.
+pub trait IoBufferWriter {
+ /// Returns the number of bytes left to be written into the io buffer.
+ ///
+ /// Note that even writing less than this number of bytes may fail.
+ fn len(&self) -> usize;
+
+ /// Returns `true` if the io buffer cannot hold any additional data.
+ fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ /// Writes zeroes to the io buffer.
+ ///
+ /// Differently from the other write functions, `clear` will zero as much as it can and update
+ /// the writer internal state to reflect this. It will, however, return an error if it cannot
+ /// clear `len` bytes.
+ ///
+ /// For example, if a caller requests that 100 bytes be cleared but a segfault happens after
+ /// 20 bytes, then EFAULT is returned and the writer is advanced by 20 bytes.
+ fn clear(&mut self, len: usize) -> Result;
+
+ /// Writes a byte slice into the io buffer.
+ ///
+ /// Returns `EFAULT` if the byte slice is bigger than the remaining size of the io buffer or if
+ /// the address does not currently point to mapped, writable memory.
+ fn write_slice(&mut self, data: &[u8]) -> Result {
+ // SAFETY: The input buffer is valid as it's coming from a live reference.
+ unsafe { self.write_raw(data.as_ptr(), data.len()) }
+ }
+
+ /// Writes raw data to the io buffer from a raw kernel buffer.
+ ///
+ /// # Safety
+ ///
+ /// The input buffer must be valid.
+ unsafe fn write_raw(&mut self, data: *const u8, len: usize) -> Result;
+
+ /// Writes the contents of the given data into the io buffer.
+ fn write<T: WritableToBytes>(&mut self, data: &T) -> Result {
+ // SAFETY: The input buffer is valid as it's coming from a live
+ // reference to a type that implements `WritableToBytes`.
+ unsafe { self.write_raw(data as *const T as _, size_of::<T>()) }
+ }
+}
+
+/// Specifies that a type is safely readable from byte slices.
+///
+/// Not all types can be safely read from byte slices; examples from
+/// <https://doc.rust-lang.org/reference/behavior-considered-undefined.html> include `bool`
+/// that must be either `0` or `1`, and `char` that cannot be a surrogate or above `char::MAX`.
+///
+/// # Safety
+///
+/// Implementers must ensure that the type is made up only of types that can be safely read from
+/// arbitrary byte sequences (e.g., `u32`, `u64`, etc.).
+pub unsafe trait ReadableFromBytes {}
+
+// SAFETY: All bit patterns are acceptable values of the types below.
+unsafe impl ReadableFromBytes for u8 {}
+unsafe impl ReadableFromBytes for u16 {}
+unsafe impl ReadableFromBytes for u32 {}
+unsafe impl ReadableFromBytes for u64 {}
+unsafe impl ReadableFromBytes for usize {}
+unsafe impl ReadableFromBytes for i8 {}
+unsafe impl ReadableFromBytes for i16 {}
+unsafe impl ReadableFromBytes for i32 {}
+unsafe impl ReadableFromBytes for i64 {}
+unsafe impl ReadableFromBytes for isize {}
+
+/// Specifies that a type is safely writable to byte slices.
+///
+/// This means that we don't read undefined values (which leads to UB) in preparation for writing
+/// to the byte slice. It also ensures that no potentially sensitive information is leaked into the
+/// byte slices.
+///
+/// # Safety
+///
+/// A type must not include padding bytes and must be fully initialised to safely implement
+/// [`WritableToBytes`] (i.e., it doesn't contain [`MaybeUninit`] fields). A composition of
+/// writable types in a structure is not necessarily writable because it may result in padding
+/// bytes.
+pub unsafe trait WritableToBytes {}
+
+// SAFETY: Initialised instances of the following types have no uninitialised portions.
+unsafe impl WritableToBytes for u8 {}
+unsafe impl WritableToBytes for u16 {}
+unsafe impl WritableToBytes for u32 {}
+unsafe impl WritableToBytes for u64 {}
+unsafe impl WritableToBytes for usize {}
+unsafe impl WritableToBytes for i8 {}
+unsafe impl WritableToBytes for i16 {}
+unsafe impl WritableToBytes for i32 {}
+unsafe impl WritableToBytes for i64 {}
+unsafe impl WritableToBytes for isize {}
diff --git a/rust/kernel/iov_iter.rs b/rust/kernel/iov_iter.rs
new file mode 100644
index 000000000000..01c7fa065dba
--- /dev/null
+++ b/rust/kernel/iov_iter.rs
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! IO vector iterators.
+//!
+//! C header: [`include/linux/uio.h`](../../../../include/linux/uio.h)
+
+use crate::{
+ bindings,
+ error::code::*,
+ io_buffer::{IoBufferReader, IoBufferWriter},
+ error::Result,
+};
+
+/// Wraps the kernel's `struct iov_iter`.
+///
+/// # Invariants
+///
+/// The pointer `IovIter::ptr` is non-null and valid.
+pub struct IovIter {
+ ptr: *mut bindings::iov_iter,
+}
+
+impl IovIter {
+ fn common_len(&self) -> usize {
+ // SAFETY: `IovIter::ptr` is guaranteed to be valid by the type invariants.
+ unsafe { (*self.ptr).count }
+ }
+
+ /// Constructs a new [`struct iov_iter`] wrapper.
+ ///
+ /// # Safety
+ ///
+ /// The pointer `ptr` must be non-null and valid for the lifetime of the object.
+ pub(crate) unsafe fn from_ptr(ptr: *mut bindings::iov_iter) -> Self {
+ // INVARIANTS: the safety contract ensures the type invariant will hold.
+ Self { ptr }
+ }
+}
+
+impl IoBufferWriter for IovIter {
+ fn len(&self) -> usize {
+ self.common_len()
+ }
+
+ fn clear(&mut self, mut len: usize) -> Result {
+ while len > 0 {
+ // SAFETY: `IovIter::ptr` is guaranteed to be valid by the type invariants.
+ let written = unsafe { bindings::iov_iter_zero(len, self.ptr) };
+ if written == 0 {
+ return Err(EFAULT);
+ }
+
+ len -= written;
+ }
+ Ok(())
+ }
+
+ unsafe fn write_raw(&mut self, data: *const u8, len: usize) -> Result {
+ let res = unsafe { bindings::_copy_to_iter(data as _, len, self.ptr) };
+ if res != len {
+ Err(EFAULT)
+ } else {
+ Ok(())
+ }
+ }
+}
+
+impl IoBufferReader for IovIter {
+ fn len(&self) -> usize {
+ self.common_len()
+ }
+
+ unsafe fn read_raw(&mut self, out: *mut u8, len: usize) -> Result {
+ let res = unsafe { bindings::_copy_from_iter(out as _, len, self.ptr) };
+ if res != len {
+ Err(EFAULT)
+ } else {
+ Ok(())
+ }
+ }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 6cf267119fda..dd9c39071391 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -13,7 +13,9 @@
#![no_std]
#![feature(allocator_api)]
+#![feature(associated_type_defaults)]
#![feature(coerce_unsized)]
+#![feature(c_size_t)]
#![feature(dispatch_from_dyn)]
#![feature(new_uninit)]
#![feature(receiver_trait)]
@@ -31,11 +33,17 @@
#[cfg(not(test))]
#[cfg(not(testlib))]
mod allocator;
-pub mod fs;
mod build_assert;
+pub mod cred;
pub mod error;
+pub mod file;
+pub mod fs;
pub mod init;
+pub mod io_buffer;
pub mod ioctl;
+pub mod iov_iter;
+pub mod mm;
+pub mod pages;
pub mod prelude;
pub mod print;
mod static_assert;
@@ -45,6 +53,7 @@
pub mod sync;
pub mod task;
pub mod types;
+pub mod user_ptr;
#[doc(hidden)]
pub use bindings;
diff --git a/rust/kernel/mm.rs b/rust/kernel/mm.rs
new file mode 100644
index 000000000000..779359d0c5cf
--- /dev/null
+++ b/rust/kernel/mm.rs
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Memory management.
+//!
+//! C header: [`include/linux/mm.h`](../../../../include/linux/mm.h)
+
+use crate::{bindings, pages, error::to_result, error::Result};
+
+/// Virtual memory.
+pub mod virt {
+ use super::*;
+
+ /// A wrapper for the kernel's `struct vm_area_struct`.
+ ///
+ /// It represents an area of virtual memory.
+ ///
+ /// # Invariants
+ ///
+ /// `vma` is always non-null and valid.
+ pub struct Area {
+ vma: *mut bindings::vm_area_struct,
+ }
+
+ impl Area {
+ /// Creates a new instance of a virtual memory area.
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that `vma` is non-null and valid for the duration of the new area's
+ /// lifetime.
+ pub(crate) unsafe fn from_ptr(vma: *mut bindings::vm_area_struct) -> Self {
+ // INVARIANTS: The safety requirements guarantee the invariants.
+ Self { vma }
+ }
+
+ /// Returns the flags associated with the virtual memory area.
+ ///
+ /// The possible flags are a combination of the constants in [`flags`].
+ pub fn flags(&self) -> usize {
+ // SAFETY: `self.vma` is valid by the type invariants.
+ unsafe { (*self.vma).vm_flags as _ }
+ }
+
+ /// Sets the flags associated with the virtual memory area.
+ ///
+ /// The possible flags are a combination of the constants in [`flags`].
+ pub fn set_flags(&mut self, flags: usize) {
+ // SAFETY: `self.vma` is valid by the type invariants.
+ unsafe { (*self.vma).vm_flags = flags as _ };
+ }
+
+ /// Returns the start address of the virtual memory area.
+ pub fn start(&self) -> usize {
+ // SAFETY: `self.vma` is valid by the type invariants.
+ unsafe { (*self.vma).vm_start as _ }
+ }
+
+ /// Returns the end address of the virtual memory area.
+ pub fn end(&self) -> usize {
+ // SAFETY: `self.vma` is valid by the type invariants.
+ unsafe { (*self.vma).vm_end as _ }
+ }
+
+ /// Maps a single page at the given address within the virtual memory area.
+ pub fn insert_page(&mut self, address: usize, page: &pages::Pages<0>) -> Result {
+ // SAFETY: The page is guaranteed to be order 0 by the type system. The range of
+ // `address` is already checked by `vm_insert_page`. `self.vma` and `page.pages` are
+ // guaranteed by their repective type invariants to be valid.
+ to_result(unsafe { bindings::vm_insert_page(self.vma, address as _, page.pages) })
+ }
+ }
+
+ /// Container for [`Area`] flags.
+ pub mod flags {
+ use crate::bindings;
+
+ /// No flags are set.
+ pub const NONE: usize = bindings::VM_NONE as _;
+
+ /// Mapping allows reads.
+ pub const READ: usize = bindings::VM_READ as _;
+
+ /// Mapping allows writes.
+ pub const WRITE: usize = bindings::VM_WRITE as _;
+
+ /// Mapping allows execution.
+ pub const EXEC: usize = bindings::VM_EXEC as _;
+
+ /// Mapping is shared.
+ pub const SHARED: usize = bindings::VM_SHARED as _;
+
+ /// Mapping may be updated to allow reads.
+ pub const MAYREAD: usize = bindings::VM_MAYREAD as _;
+
+ /// Mapping may be updated to allow writes.
+ pub const MAYWRITE: usize = bindings::VM_MAYWRITE as _;
+
+ /// Mapping may be updated to allow execution.
+ pub const MAYEXEC: usize = bindings::VM_MAYEXEC as _;
+
+ /// Mapping may be updated to be shared.
+ pub const MAYSHARE: usize = bindings::VM_MAYSHARE as _;
+
+ /// Do not copy this vma on fork.
+ pub const DONTCOPY: usize = bindings::VM_DONTCOPY as _;
+
+ /// Cannot expand with mremap().
+ pub const DONTEXPAND: usize = bindings::VM_DONTEXPAND as _;
+
+ /// Lock the pages covered when they are faulted in.
+ pub const LOCKONFAULT: usize = bindings::VM_LOCKONFAULT as _;
+
+ /// Is a VM accounted object.
+ pub const ACCOUNT: usize = bindings::VM_ACCOUNT as _;
+
+ /// should the VM suppress accounting.
+ pub const NORESERVE: usize = bindings::VM_NORESERVE as _;
+
+ /// Huge TLB Page VM.
+ pub const HUGETLB: usize = bindings::VM_HUGETLB as _;
+
+ /// Synchronous page faults.
+ pub const SYNC: usize = bindings::VM_SYNC as _;
+
+ /// Architecture-specific flag.
+ pub const ARCH_1: usize = bindings::VM_ARCH_1 as _;
+
+ /// Wipe VMA contents in child..
+ pub const WIPEONFORK: usize = bindings::VM_WIPEONFORK as _;
+
+ /// Do not include in the core dump.
+ pub const DONTDUMP: usize = bindings::VM_DONTDUMP as _;
+
+ /// Not soft dirty clean area.
+ pub const SOFTDIRTY: usize = bindings::VM_SOFTDIRTY as _;
+
+ /// Can contain "struct page" and pure PFN pages.
+ pub const MIXEDMAP: usize = bindings::VM_MIXEDMAP as _;
+
+ /// MADV_HUGEPAGE marked this vma.
+ pub const HUGEPAGE: usize = bindings::VM_HUGEPAGE as _;
+
+ /// MADV_NOHUGEPAGE marked this vma.
+ pub const NOHUGEPAGE: usize = bindings::VM_NOHUGEPAGE as _;
+
+ /// KSM may merge identical pages.
+ pub const MERGEABLE: usize = bindings::VM_MERGEABLE as _;
+ }
+}
diff --git a/rust/kernel/pages.rs b/rust/kernel/pages.rs
new file mode 100644
index 000000000000..c0c1f9fe03fc
--- /dev/null
+++ b/rust/kernel/pages.rs
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Kernel page allocation and management.
+//!
+//! TODO: This module is a work in progress.
+
+use crate::{
+ bindings, error::code::*, io_buffer::IoBufferReader, user_ptr::UserSlicePtrReader, error::Result,
+ PAGE_SIZE,
+};
+use core::{marker::PhantomData, ptr};
+
+/// A set of physical pages.
+///
+/// `Pages` holds a reference to a set of pages of order `ORDER`. Having the order as a generic
+/// const allows the struct to have the same size as a pointer.
+///
+/// # Invariants
+///
+/// The pointer `Pages::pages` is valid and points to 2^ORDER pages.
+pub struct Pages<const ORDER: u32> {
+ pub(crate) pages: *mut bindings::page,
+}
+
+impl<const ORDER: u32> Pages<ORDER> {
+ /// Allocates a new set of contiguous pages.
+ pub fn new() -> Result<Self> {
+ // TODO: Consider whether we want to allow callers to specify flags.
+ // SAFETY: This only allocates pages. We check that it succeeds in the next statement.
+ let pages = unsafe {
+ bindings::alloc_pages(
+ bindings::GFP_KERNEL | bindings::__GFP_ZERO | bindings::___GFP_HIGHMEM,
+ ORDER,
+ )
+ };
+ if pages.is_null() {
+ return Err(ENOMEM);
+ }
+ // INVARIANTS: We checked that the allocation above succeeded>
+ Ok(Self { pages })
+ }
+
+ /// Copies data from the given [`UserSlicePtrReader`] into the pages.
+ pub fn copy_into_page(
+ &self,
+ reader: &mut UserSlicePtrReader,
+ offset: usize,
+ len: usize,
+ ) -> Result {
+ // TODO: For now this only works on the first page.
+ let end = offset.checked_add(len).ok_or(EINVAL)?;
+ if end > PAGE_SIZE {
+ return Err(EINVAL);
+ }
+
+ let mapping = self.kmap(0).ok_or(EINVAL)?;
+
+ // SAFETY: We ensured that the buffer was valid with the check above.
+ unsafe { reader.read_raw((mapping.ptr as usize + offset) as _, len) }?;
+ Ok(())
+ }
+
+ /// Maps the pages and reads from them into the given buffer.
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that the destination buffer is valid for the given length.
+ /// Additionally, if the raw buffer is intended to be recast, they must ensure that the data
+ /// can be safely cast; [`crate::io_buffer::ReadableFromBytes`] has more details about it.
+ pub unsafe fn read(&self, dest: *mut u8, offset: usize, len: usize) -> Result {
+ // TODO: For now this only works on the first page.
+ let end = offset.checked_add(len).ok_or(EINVAL)?;
+ if end > PAGE_SIZE {
+ return Err(EINVAL);
+ }
+
+ let mapping = self.kmap(0).ok_or(EINVAL)?;
+ unsafe { ptr::copy((mapping.ptr as *mut u8).add(offset), dest, len) };
+ Ok(())
+ }
+
+ /// Maps the pages and writes into them from the given buffer.
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that the buffer is valid for the given length. Additionally, if the
+ /// page is (or will be) mapped by userspace, they must ensure that no kernel data is leaked
+ /// through padding if it was cast from another type; [`crate::io_buffer::WritableToBytes`] has
+ /// more details about it.
+ pub unsafe fn write(&self, src: *const u8, offset: usize, len: usize) -> Result {
+ // TODO: For now this only works on the first page.
+ let end = offset.checked_add(len).ok_or(EINVAL)?;
+ if end > PAGE_SIZE {
+ return Err(EINVAL);
+ }
+
+ let mapping = self.kmap(0).ok_or(EINVAL)?;
+ unsafe { ptr::copy(src, (mapping.ptr as *mut u8).add(offset), len) };
+ Ok(())
+ }
+
+ /// Maps the page at index `index`.
+ fn kmap(&self, index: usize) -> Option<PageMapping<'_>> {
+ if index >= 1usize << ORDER {
+ return None;
+ }
+
+ // SAFETY: We checked above that `index` is within range.
+ let page = unsafe { self.pages.add(index) };
+
+ // SAFETY: `page` is valid based on the checks above.
+ let ptr = unsafe { bindings::kmap(page) };
+ if ptr.is_null() {
+ return None;
+ }
+
+ Some(PageMapping {
+ page,
+ ptr,
+ _phantom: PhantomData,
+ })
+ }
+}
+
+impl<const ORDER: u32> Drop for Pages<ORDER> {
+ fn drop(&mut self) {
+ // SAFETY: By the type invariants, we know the pages are allocated with the given order.
+ unsafe { bindings::__free_pages(self.pages, ORDER) };
+ }
+}
+
+struct PageMapping<'a> {
+ page: *mut bindings::page,
+ ptr: *mut core::ffi::c_void,
+ _phantom: PhantomData<&'a i32>,
+}
+
+impl Drop for PageMapping<'_> {
+ fn drop(&mut self) {
+ // SAFETY: An instance of `PageMapping` is created only when `kmap` succeeded for the given
+ // page, so it is safe to unmap it here.
+ unsafe { bindings::kunmap(self.page) };
+ }
+}
diff --git a/rust/kernel/user_ptr.rs b/rust/kernel/user_ptr.rs
new file mode 100644
index 000000000000..9fdacc2826ef
--- /dev/null
+++ b/rust/kernel/user_ptr.rs
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! User pointers.
+//!
+//! C header: [`include/linux/uaccess.h`](../../../../include/linux/uaccess.h)
+
+use crate::{
+ bindings,
+ error::code::*,
+ io_buffer::{IoBufferReader, IoBufferWriter},
+ error::Result,
+};
+use alloc::vec::Vec;
+
+/// A reference to an area in userspace memory, which can be either
+/// read-only or read-write.
+///
+/// All methods on this struct are safe: invalid pointers return
+/// `EFAULT`. Concurrent access, *including data races to/from userspace
+/// memory*, is permitted, because fundamentally another userspace
+/// thread/process could always be modifying memory at the same time
+/// (in the same way that userspace Rust's [`std::io`] permits data races
+/// with the contents of files on disk). In the presence of a race, the
+/// exact byte values read/written are unspecified but the operation is
+/// well-defined. Kernelspace code should validate its copy of data
+/// after completing a read, and not expect that multiple reads of the
+/// same address will return the same value.
+///
+/// All APIs enforce the invariant that a given byte of memory from userspace
+/// may only be read once. By preventing double-fetches we avoid TOCTOU
+/// vulnerabilities. This is accomplished by taking `self` by value to prevent
+/// obtaining multiple readers on a given [`UserSlicePtr`], and the readers
+/// only permitting forward reads.
+///
+/// Constructing a [`UserSlicePtr`] performs no checks on the provided
+/// address and length, it can safely be constructed inside a kernel thread
+/// with no current userspace process. Reads and writes wrap the kernel APIs
+/// `copy_from_user` and `copy_to_user`, which check the memory map of the
+/// current process and enforce that the address range is within the user
+/// range (no additional calls to `access_ok` are needed).
+///
+/// [`std::io`]: https://doc.rust-lang.org/std/io/index.html
+pub struct UserSlicePtr(*mut core::ffi::c_void, usize);
+
+impl UserSlicePtr {
+ /// Constructs a user slice from a raw pointer and a length in bytes.
+ ///
+ /// # Safety
+ ///
+ /// Callers must be careful to avoid time-of-check-time-of-use
+ /// (TOCTOU) issues. The simplest way is to create a single instance of
+ /// [`UserSlicePtr`] per user memory block as it reads each byte at
+ /// most once.
+ pub unsafe fn new(ptr: *mut core::ffi::c_void, length: usize) -> Self {
+ UserSlicePtr(ptr, length)
+ }
+
+ /// Reads the entirety of the user slice.
+ ///
+ /// Returns `EFAULT` if the address does not currently point to
+ /// mapped, readable memory.
+ pub fn read_all(self) -> Result<Vec<u8>> {
+ self.reader().read_all()
+ }
+
+ /// Constructs a [`UserSlicePtrReader`].
+ pub fn reader(self) -> UserSlicePtrReader {
+ UserSlicePtrReader(self.0, self.1)
+ }
+
+ /// Writes the provided slice into the user slice.
+ ///
+ /// Returns `EFAULT` if the address does not currently point to
+ /// mapped, writable memory (in which case some data from before the
+ /// fault may be written), or `data` is larger than the user slice
+ /// (in which case no data is written).
+ pub fn write_all(self, data: &[u8]) -> Result {
+ self.writer().write_slice(data)
+ }
+
+ /// Constructs a [`UserSlicePtrWriter`].
+ pub fn writer(self) -> UserSlicePtrWriter {
+ UserSlicePtrWriter(self.0, self.1)
+ }
+
+ /// Constructs both a [`UserSlicePtrReader`] and a [`UserSlicePtrWriter`].
+ pub fn reader_writer(self) -> (UserSlicePtrReader, UserSlicePtrWriter) {
+ (
+ UserSlicePtrReader(self.0, self.1),
+ UserSlicePtrWriter(self.0, self.1),
+ )
+ }
+}
+
+/// A reader for [`UserSlicePtr`].
+///
+/// Used to incrementally read from the user slice.
+pub struct UserSlicePtrReader(*mut core::ffi::c_void, usize);
+
+impl IoBufferReader for UserSlicePtrReader {
+ /// Returns the number of bytes left to be read from this.
+ ///
+ /// Note that even reading less than this number of bytes may fail.
+ fn len(&self) -> usize {
+ self.1
+ }
+
+ /// Reads raw data from the user slice into a raw kernel buffer.
+ ///
+ /// # Safety
+ ///
+ /// The output buffer must be valid.
+ unsafe fn read_raw(&mut self, out: *mut u8, len: usize) -> Result {
+ if len > self.1 || len > u32::MAX as usize {
+ return Err(EFAULT);
+ }
+ let res = unsafe { bindings::_copy_from_user(out as _, self.0, len as _) };
+ if res != 0 {
+ return Err(EFAULT);
+ }
+ // Since this is not a pointer to a valid object in our program,
+ // we cannot use `add`, which has C-style rules for defined
+ // behavior.
+ self.0 = self.0.wrapping_add(len);
+ self.1 -= len;
+ Ok(())
+ }
+}
+
+/// A writer for [`UserSlicePtr`].
+///
+/// Used to incrementally write into the user slice.
+pub struct UserSlicePtrWriter(*mut core::ffi::c_void, usize);
+
+impl IoBufferWriter for UserSlicePtrWriter {
+ fn len(&self) -> usize {
+ self.1
+ }
+
+ fn clear(&mut self, mut len: usize) -> Result {
+ let mut ret = Ok(());
+ if len > self.1 {
+ ret = Err(EFAULT);
+ len = self.1;
+ }
+
+ // SAFETY: The buffer will be validated by `clear_user`. We ensure that `len` is within
+ // bounds in the check above.
+ let left = unsafe { bindings::clear_user(self.0, len as _) } as usize;
+ if left != 0 {
+ ret = Err(EFAULT);
+ len -= left;
+ }
+
+ self.0 = self.0.wrapping_add(len);
+ self.1 -= len;
+ ret
+ }
+
+ unsafe fn write_raw(&mut self, data: *const u8, len: usize) -> Result {
+ if len > self.1 || len > u32::MAX as usize {
+ return Err(EFAULT);
+ }
+ let res = unsafe { bindings::_copy_to_user(self.0, data as _, len as _) };
+ if res != 0 {
+ return Err(EFAULT);
+ }
+ // Since this is not a pointer to a valid object in our program,
+ // we cannot use `add`, which has C-style rules for defined
+ // behavior.
+ self.0 = self.0.wrapping_add(len);
+ self.1 -= len;
+ Ok(())
+ }
+}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 06/80] rust: allow fs driver to initialise new superblocks
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (4 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 05/80] rust: kernel: add libraries required by the filesystem abstractions Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 07/80] rust: add `module_fs` macro Ariel Miculas
` (71 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Wedson Almeida Filho
From: Wedson Almeida Filho <wedsonaf@google.com>
They are also allowed to specify the type of superblock keying.
With this change, drivers get a new callback, `fill_super`, that they
need to implement to initialise new superblocks.
In subsequent commits, they'll be able to populate the root with
entries, but for now it's empty.
Signed-off-by: Wedson Almeida Filho <wedsonaf@google.com>
---
rust/kernel/fs.rs | 406 ++++++++++++++++++++++++++++++++--------
rust/kernel/fs/param.rs | 17 +-
samples/rust/rust_fs.rs | 14 +-
3 files changed, 357 insertions(+), 80 deletions(-)
diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs
index e68b67b8adb0..fb4a814e1fc8 100644
--- a/rust/kernel/fs.rs
+++ b/rust/kernel/fs.rs
@@ -7,11 +7,31 @@
use crate::{bindings, error::code::*, str::CStr, ThisModule};
use crate::error::{to_result, from_kernel_result, Error, Result};
use crate::types::{AlwaysRefCounted, ForeignOwnable, ScopeGuard};
-use core::{cell::UnsafeCell, marker::PhantomPinned, pin::Pin, ptr};
+use core::{cell::UnsafeCell, marker::PhantomData, marker::PhantomPinned, pin::Pin, ptr};
use macros::vtable;
pub mod param;
+/// Type of superblock keying.
+///
+/// It determines how C's `fs_context_operations::get_tree` is implemented.
+pub enum Super {
+ /// Only one such superblock may exist.
+ Single,
+
+ /// As [`Super::Single`], but reconfigure if it exists.
+ SingleReconf,
+
+ /// Superblocks with different data pointers may exist.
+ Keyed,
+
+ /// Multiple independent superblocks may exist.
+ Independent,
+
+ /// Uses a block device.
+ BlockDev,
+}
+
/// A file system context.
///
/// It is used to gather configuration to then mount or reconfigure a file system.
@@ -48,6 +68,16 @@ fn parse_monolithic<'a>(
) -> Result<Option<&'a mut [u8]>> {
Ok(None)
}
+
+ /// Returns the superblock data to be used by this file system context.
+ ///
+ /// This is only needed when [`Type::SUPER_TYPE`] is [`Super::Keyed`], otherwise it is never
+ /// called. In the former case, when the fs is being mounted, an existing superblock is reused
+ /// if one can be found with the same data as the returned value; otherwise a new superblock is
+ /// created.
+ fn tree_key(_data: &mut Self::Data) -> Result<T::Data> {
+ Err(ENOTSUPP)
+ }
}
struct Tables<T: Type + ?Sized>(T);
@@ -67,12 +97,22 @@ impl<T: Type + ?Sized> Tables<T> {
unsafe extern "C" fn free_callback(fc: *mut bindings::fs_context) {
// SAFETY: The callback contract guarantees that `fc` is valid.
- let ptr = unsafe { (*fc).fs_private };
+ let fc = unsafe { &*fc };
+
+ let ptr = fc.fs_private;
if !ptr.is_null() {
// SAFETY: `fs_private` was initialised with the result of a `to_pointer` call in
// `init_fs_context_callback`, so it's ok to call `from_foreign` here.
unsafe { <T::Context as Context<T>>::Data::from_foreign(ptr) };
}
+
+ let ptr = fc.s_fs_info;
+ if !ptr.is_null() {
+ // SAFETY: `s_fs_info` may be initialised with the result of a `to_pointer` call in
+ // `get_tree_callback` when keyed superblocks are used (`get_tree_keyed` sets it), so
+ // it's ok to call `from_foreign` here.
+ unsafe { T::Data::from_foreign(ptr) };
+ }
}
unsafe extern "C" fn parse_param_callback(
@@ -84,8 +124,8 @@ impl<T: Type + ?Sized> Tables<T> {
let ptr = unsafe { (*fc).fs_private };
// SAFETY: The value of `ptr` (coming from `fs_private` was initialised in
- // `init_fs_context_callback` to the result of an `into_pointer` call. Since the
- // context is valid, `from_pointer` wasn't called yet, so `ptr` is valid. Additionally,
+ // `init_fs_context_callback` to the result of an `into_foreign` call. Since the
+ // context is valid, `from_foreign` wasn't called yet, so `ptr` is valid. Additionally,
// the callback contract guarantees that callbacks are serialised, so it is ok to
// mutably reference it.
let mut data =
@@ -126,73 +166,91 @@ impl<T: Type + ?Sized> Tables<T> {
unsafe extern "C" fn fill_super_callback(
sb_ptr: *mut bindings::super_block,
- _fc: *mut bindings::fs_context,
+ fc: *mut bindings::fs_context,
) -> core::ffi::c_int {
from_kernel_result! {
- // The following is temporary code to create the root inode and dentry. It will be
- // replaced with calls to Rust code.
-
- // SAFETY: The callback contract guarantees that `sb_ptr` is the only pointer to a
- // newly-allocated superblock, so it is safe to mutably reference it.
- let sb = unsafe { &mut *sb_ptr };
-
- sb.s_maxbytes = bindings::MAX_LFS_FILESIZE;
- sb.s_blocksize = crate::PAGE_SIZE as _;
- sb.s_blocksize_bits = bindings::PAGE_SHIFT as _;
- sb.s_magic = T::MAGIC as _;
- sb.s_op = &Tables::<T>::SUPER_BLOCK;
- sb.s_time_gran = 1;
-
- // Create and initialise the root inode.
-
- // SAFETY: `sb` was just created initialised, so it is safe pass it to `new_inode`.
- let inode = unsafe { bindings::new_inode(sb) };
- if inode.is_null() {
- return Err(ENOMEM);
- }
-
- {
- // SAFETY: This is a newly-created inode. No other references to it exist, so it is
- // safe to mutably dereference it.
- let inode = unsafe { &mut *inode };
-
- // SAFETY: `current_time` requires that `inode.sb` be valid, which is the case here
- // since we allocated the inode through the superblock.
- let time = unsafe { bindings::current_time(inode) };
- inode.i_ino = 1;
- inode.i_mode = (bindings::S_IFDIR | 0o755) as _;
- inode.i_mtime = time;
- inode.i_atime = time;
- inode.i_ctime = time;
-
- // SAFETY: `simple_dir_operations` never changes, it's safe to reference it.
- inode.__bindgen_anon_3.i_fop = unsafe { &bindings::simple_dir_operations };
-
- // SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it.
- inode.i_op = unsafe { &bindings::simple_dir_inode_operations };
-
- // SAFETY: `inode` is valid for write.
- unsafe { bindings::set_nlink(inode, 2) };
- }
-
- // SAFETY: `d_make_root` requires that `inode` be valid and referenced, which is the
- // case for this call.
- //
- // It takes over the inode, even on failure, so we don't need to clean it up.
- let dentry = unsafe { bindings::d_make_root(inode) };
- if dentry.is_null() {
- return Err(ENOMEM);
- }
+ // SAFETY: The callback contract guarantees that `fc` is valid. It also guarantees that
+ // the callbacks are serialised for a given `fc`, so it is safe to mutably dereference
+ // it.
+ let fc = unsafe { &mut *fc };
+ let ptr = core::mem::replace(&mut fc.fs_private, ptr::null_mut());
- sb.s_root = dentry;
+ // SAFETY: The value of `ptr` (coming from `fs_private` was initialised in
+ // `init_fs_context_callback` to the result of an `into_foreign` call. The context is
+ // being used to initialise a superblock, so we took over `ptr` (`fs_private` is set to
+ // null now) and call `from_foreign` below.
+ let data =
+ unsafe { <<T::Context as Context<T>>::Data as ForeignOwnable>::from_foreign(ptr) };
+
+ // SAFETY: The callback contract guarantees that `sb_ptr` is a unique pointer to a
+ // newly-created superblock.
+ let newsb = unsafe { NewSuperBlock::new(sb_ptr) };
+ T::fill_super(data, newsb)?;
Ok(0)
}
}
unsafe extern "C" fn get_tree_callback(fc: *mut bindings::fs_context) -> core::ffi::c_int {
- // SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has the
- // right type and is a valid callback.
- unsafe { bindings::get_tree_nodev(fc, Some(Self::fill_super_callback)) }
+ // N.B. When new types are added below, we may need to update `kill_sb_callback` to ensure
+ // that we're cleaning up properly.
+ match T::SUPER_TYPE {
+ Super::Single => unsafe {
+ // SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
+ // the right type and is a valid callback.
+ bindings::get_tree_single(fc, Some(Self::fill_super_callback))
+ },
+ Super::SingleReconf => unsafe {
+ // SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
+ // the right type and is a valid callback.
+ bindings::get_tree_single_reconf(fc, Some(Self::fill_super_callback))
+ },
+ Super::Independent => unsafe {
+ // SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
+ // the right type and is a valid callback.
+ bindings::get_tree_nodev(fc, Some(Self::fill_super_callback))
+ },
+ Super::BlockDev => unsafe {
+ // SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
+ // the right type and is a valid callback.
+ bindings::get_tree_bdev(fc, Some(Self::fill_super_callback))
+ },
+ Super::Keyed => {
+ from_kernel_result! {
+ // SAFETY: `fc` is valid per the callback contract.
+ let ctx = unsafe { &*fc };
+ let ptr = ctx.fs_private;
+
+ // SAFETY: The value of `ptr` (coming from `fs_private` was initialised in
+ // `init_fs_context_callback` to the result of an `into_foreign` call. Since
+ // the context is valid, `from_foreign` wasn't called yet, so `ptr` is valid.
+ // Additionally, the callback contract guarantees that callbacks are
+ // serialised, so it is ok to mutably reference it.
+ let mut data = unsafe {
+ <<T::Context as Context<T>>::Data as ForeignOwnable>::borrow_mut(ptr)
+ };
+ let fs_data = T::Context::tree_key(&mut data)?;
+ let fs_data_ptr = fs_data.into_foreign();
+
+ // `get_tree_keyed` reassigns `ctx.s_fs_info`, which should be ok because
+ // nowhere else is it assigned a non-null value. However, we add the assert
+ // below to ensure that there are no unexpected paths on the C side that may do
+ // this.
+ assert_eq!(ctx.s_fs_info, core::ptr::null_mut());
+
+ // SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also
+ // has the right type and is a valid callback. Lastly, we just called
+ // `into_foreign` above, so `fs_data_ptr` is also valid.
+ to_result(unsafe {
+ bindings::get_tree_keyed(
+ fc,
+ Some(Self::fill_super_callback),
+ fs_data_ptr as _,
+ )
+ })?;
+ Ok(0)
+ }
+ }
+ }
}
unsafe extern "C" fn reconfigure_callback(_fc: *mut bindings::fs_context) -> core::ffi::c_int {
@@ -208,8 +266,8 @@ impl<T: Type + ?Sized> Tables<T> {
let ptr = unsafe { (*fc).fs_private };
// SAFETY: The value of `ptr` (coming from `fs_private` was initialised in
- // `init_fs_context_callback` to the result of an `into_pointer` call. Since the
- // context is valid, `from_pointer` wasn't called yet, so `ptr` is valid. Additionally,
+ // `init_fs_context_callback` to the result of an `into_foreign` call. Since the
+ // context is valid, `from_foreign` wasn't called yet, so `ptr` is valid. Additionally,
// the callback contract guarantees that callbacks are serialised, so it is ok to
// mutably reference it.
let mut data =
@@ -270,18 +328,25 @@ pub trait Type {
/// The context used to build fs configuration before it is mounted or reconfigured.
type Context: Context<Self> + ?Sized;
+ /// Data associated with each file system instance.
+ type Data: ForeignOwnable + Send + Sync = ();
+
+ /// Determines how superblocks for this file system type are keyed.
+ const SUPER_TYPE: Super;
+
/// The name of the file system type.
const NAME: &'static CStr;
- /// The magic number associated with the file system.
- ///
- /// This is normally one of the values in `include/uapi/linux/magic.h`.
- const MAGIC: u32;
-
/// The flags of this file system type.
///
/// It is a combination of the flags in the [`flags`] module.
const FLAGS: i32;
+
+ /// Initialises a super block for this file system type.
+ fn fill_super(
+ data: <Self::Context as Context<Self>>::Data,
+ sb: NewSuperBlock<'_, Self>,
+ ) -> Result<&SuperBlock<Self>>;
}
/// File system flags.
@@ -425,14 +490,33 @@ unsafe fn unregister_keys(fs: *mut bindings::file_system_type) {
}
unsafe extern "C" fn kill_sb_callback<T: Type + ?Sized>(sb_ptr: *mut bindings::super_block) {
- // SAFETY: We always call `get_tree_nodev` from `get_tree_callback`, so we never have a
- // device, so it is ok to call the function below. Additionally, the callback contract
- // guarantees that `sb_ptr` is valid.
- unsafe { bindings::kill_anon_super(sb_ptr) }
-
- // SAFETY: The callback contract guarantees that `sb_ptr` is valid, and the `kill_sb`
- // callback being called implies that the `s_type` is also valid.
- unsafe { Self::unregister_keys((*sb_ptr).s_type) };
+ if let Super::BlockDev = T::SUPER_TYPE {
+ // SAFETY: When the superblock type is `BlockDev`, we have a block device so it's safe
+ // to call `kill_block_super`. Additionally, the callback contract guarantees that
+ // `sb_ptr` is valid.
+ unsafe { bindings::kill_block_super(sb_ptr) }
+ } else {
+ // SAFETY: We always call a `get_tree_nodev` variant from `get_tree_callback` without a
+ // device when `T::SUPER_TYPE` is not `BlockDev`, so we never have a device in such
+ // cases, therefore it is ok to call the function below. Additionally, the callback
+ // contract guarantees that `sb_ptr` is valid.
+ unsafe { bindings::kill_anon_super(sb_ptr) }
+ }
+
+ // SAFETY: The callback contract guarantees that `sb_ptr` is valid.
+ let sb = unsafe { &*sb_ptr };
+
+ // SAFETY: The `kill_sb` callback being called implies that the `s_type` field is valid.
+ unsafe { Self::unregister_keys(sb.s_type) };
+
+ let ptr = sb.s_fs_info;
+ if !ptr.is_null() {
+ // SAFETY: The only place where `s_fs_info` is assigned is `NewSuperBlock::init`, where
+ // it's initialised with the result of a `to_pointer` call. We checked above that ptr
+ // is non-null because it would be null if we never reached the point where we init the
+ // field.
+ unsafe { T::Data::from_foreign(ptr) };
+ }
}
}
@@ -446,6 +530,172 @@ fn drop(&mut self) {
}
}
+/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init`] needs to be called
+/// eventually.
+pub struct NeedsInit;
+
+/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init_root`] needs to be called
+/// eventually.
+pub struct NeedsRoot;
+
+/// Required superblock parameters.
+///
+/// This is used in [`NewSuperBlock::init`].
+pub struct SuperParams {
+ /// The magic number of the superblock.
+ pub magic: u32,
+
+ /// The size of a block in powers of 2 (i.e., for a value of `n`, the size is `2^n`.
+ pub blocksize_bits: u8,
+
+ /// Maximum size of a file.
+ pub maxbytes: i64,
+
+ /// Granularity of c/m/atime in ns (cannot be worse than a second).
+ pub time_gran: u32,
+}
+
+impl SuperParams {
+ /// Default value for instances of [`SuperParams`].
+ pub const DEFAULT: Self = Self {
+ magic: 0,
+ blocksize_bits: crate::PAGE_SIZE as _,
+ maxbytes: bindings::MAX_LFS_FILESIZE,
+ time_gran: 1,
+ };
+}
+
+/// A superblock that is still being initialised.
+///
+/// It uses type states to ensure that callers use the right sequence of calls.
+///
+/// # Invariants
+///
+/// The superblock is a newly-created one and this is the only active pointer to it.
+pub struct NewSuperBlock<'a, T: Type + ?Sized, S = NeedsInit> {
+ sb: *mut bindings::super_block,
+ _p: PhantomData<(&'a T, S)>,
+}
+
+impl<'a, T: Type + ?Sized> NewSuperBlock<'a, T, NeedsInit> {
+ /// Creates a new instance of [`NewSuperBlock`].
+ ///
+ /// # Safety
+ ///
+ /// `sb` must point to a newly-created superblock and it must be the only active pointer to it.
+ unsafe fn new(sb: *mut bindings::super_block) -> Self {
+ // INVARIANT: The invariants are satisfied by the safety requirements of this function.
+ Self {
+ sb,
+ _p: PhantomData,
+ }
+ }
+
+ /// Initialises the superblock so that it transitions to the [`NeedsRoot`] type state.
+ pub fn init(
+ self,
+ data: T::Data,
+ params: &SuperParams,
+ ) -> Result<NewSuperBlock<'a, T, NeedsRoot>> {
+ // SAFETY: The type invariant guarantees that `self.sb` is the only pointer to a
+ // newly-allocated superblock, so it is safe to mutably reference it.
+ let sb = unsafe { &mut *self.sb };
+
+ sb.s_magic = params.magic as _;
+ sb.s_op = &Tables::<T>::SUPER_BLOCK;
+ sb.s_maxbytes = params.maxbytes;
+ sb.s_time_gran = params.time_gran;
+ sb.s_blocksize_bits = params.blocksize_bits;
+ sb.s_blocksize = 1;
+ if sb.s_blocksize.leading_zeros() < params.blocksize_bits.into() {
+ return Err(EINVAL);
+ }
+ sb.s_blocksize = 1 << sb.s_blocksize_bits;
+
+ // Keyed file systems already have `s_fs_info` initialised.
+ let info = data.into_foreign() as *mut _;
+ if let Super::Keyed = T::SUPER_TYPE {
+ // SAFETY: We just called `into_foreign` above.
+ unsafe { T::Data::from_foreign(info) };
+
+ if sb.s_fs_info != info {
+ return Err(EINVAL);
+ }
+ } else {
+ sb.s_fs_info = info;
+ }
+
+ Ok(NewSuperBlock {
+ sb: self.sb,
+ _p: PhantomData,
+ })
+ }
+}
+
+impl<'a, T: Type + ?Sized> NewSuperBlock<'a, T, NeedsRoot> {
+ /// Initialises the root of the superblock.
+ pub fn init_root(self) -> Result<&'a SuperBlock<T>> {
+ // The following is temporary code to create the root inode and dentry. It will be replaced
+ // once we allow inodes and dentries to be created directly from Rust code.
+
+ // SAFETY: `sb` is initialised (`NeedsRoot` typestate implies it), so it is safe to pass it
+ // to `new_inode`.
+ let inode = unsafe { bindings::new_inode(self.sb) };
+ if inode.is_null() {
+ return Err(ENOMEM);
+ }
+
+ {
+ // SAFETY: This is a newly-created inode. No other references to it exist, so it is
+ // safe to mutably dereference it.
+ let inode = unsafe { &mut *inode };
+
+ // SAFETY: `current_time` requires that `inode.sb` be valid, which is the case here
+ // since we allocated the inode through the superblock.
+ let time = unsafe { bindings::current_time(inode) };
+ inode.i_ino = 1;
+ inode.i_mode = (bindings::S_IFDIR | 0o755) as _;
+ inode.i_mtime = time;
+ inode.i_atime = time;
+ inode.i_ctime = time;
+
+ // SAFETY: `simple_dir_operations` never changes, it's safe to reference it.
+ inode.__bindgen_anon_3.i_fop = unsafe { &bindings::simple_dir_operations };
+
+ // SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it.
+ inode.i_op = unsafe { &bindings::simple_dir_inode_operations };
+
+ // SAFETY: `inode` is valid for write.
+ unsafe { bindings::set_nlink(inode, 2) };
+ }
+
+ // SAFETY: `d_make_root` requires that `inode` be valid and referenced, which is the
+ // case for this call.
+ //
+ // It takes over the inode, even on failure, so we don't need to clean it up.
+ let dentry = unsafe { bindings::d_make_root(inode) };
+ if dentry.is_null() {
+ return Err(ENOMEM);
+ }
+
+ // SAFETY: The typestate guarantees that `self.sb` is valid.
+ unsafe { (*self.sb).s_root = dentry };
+
+ // SAFETY: The typestate guarantees that `self.sb` is initialised and we just finished
+ // setting its root, so it's a fully ready superblock.
+ Ok(unsafe { &mut *self.sb.cast() })
+ }
+}
+
+/// A file system super block.
+///
+/// Wraps the kernel's `struct super_block`.
+#[repr(transparent)]
+pub struct SuperBlock<T: Type + ?Sized>(
+ pub(crate) UnsafeCell<bindings::super_block>,
+ PhantomData<T>,
+);
+
/// Wraps the kernel's `struct inode`.
///
/// # Invariants
diff --git a/rust/kernel/fs/param.rs b/rust/kernel/fs/param.rs
index f0b4393b6b67..44b4e895a1eb 100644
--- a/rust/kernel/fs/param.rs
+++ b/rust/kernel/fs/param.rs
@@ -487,9 +487,24 @@ macro_rules! count_brace_items {
///
/// # impl fs::Type for Example {
/// # type Context = Self;
+/// # const SUPER_TYPE: fs::Super = fs::Super::Independent;
/// # const NAME: &'static CStr = c_str!("example");
/// # const FLAGS: i32 = 0;
-/// # const MAGIC: u32 = 0x6578616d;
+/// #
+/// # fn fill_super<'a>(
+/// # _data: Box<State>,
+/// # sb: fs::NewSuperBlock<'_, Self>,
+/// # ) -> Result<&fs::SuperBlock<Self>> {
+/// # let sb = sb.init(
+/// # (),
+/// # &fs::SuperParams {
+/// # magic: 0x6578616d,
+/// # ..fs::SuperParams::DEFAULT
+/// # },
+/// # )?;
+/// # let sb = sb.init_root()?;
+/// # Ok(sb)
+/// # }
/// # }
/// ```
#[macro_export]
diff --git a/samples/rust/rust_fs.rs b/samples/rust/rust_fs.rs
index acaa603b0bb2..5d8880d5830b 100644
--- a/samples/rust/rust_fs.rs
+++ b/samples/rust/rust_fs.rs
@@ -41,9 +41,21 @@ fn try_new() -> Result {
impl fs::Type for RustFs {
type Context = Self;
+ const SUPER_TYPE: fs::Super = fs::Super::Independent;
const NAME: &'static CStr = c_str!("rustfs");
const FLAGS: i32 = fs::flags::USERNS_MOUNT;
- const MAGIC: u32 = 0x72757374;
+
+ fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBlock<Self>> {
+ let sb = sb.init(
+ (),
+ &fs::SuperParams {
+ magic: 0x72757374,
+ ..fs::SuperParams::DEFAULT
+ },
+ )?;
+ let sb = sb.init_root()?;
+ Ok(sb)
+ }
}
struct FsModule {
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 07/80] rust: add `module_fs` macro
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (5 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 06/80] rust: allow fs driver to initialise new superblocks Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 08/80] WIP: rust: allow fs to be populated Ariel Miculas
` (70 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Wedson Almeida Filho
From: Wedson Almeida Filho <wedsonaf@google.com>
This reduces the amount of code needed to implement a module that
only implements a file system.
Signed-off-by: Wedson Almeida Filho <wedsonaf@google.com>
---
rust/kernel/fs.rs | 77 +++++++++++++++++++++++++++++++++++++++++
rust/kernel/prelude.rs | 1 +
samples/rust/rust_fs.rs | 16 ++-------
3 files changed, 80 insertions(+), 14 deletions(-)
diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs
index fb4a814e1fc8..86c306c19e0a 100644
--- a/rust/kernel/fs.rs
+++ b/rust/kernel/fs.rs
@@ -4,6 +4,7 @@
//!
//! C headers: [`include/linux/fs.h`](../../../../include/linux/fs.h)
+use alloc::boxed::Box;
use crate::{bindings, error::code::*, str::CStr, ThisModule};
use crate::error::{to_result, from_kernel_result, Error, Result};
use crate::types::{AlwaysRefCounted, ForeignOwnable, ScopeGuard};
@@ -757,3 +758,79 @@ pub(crate) unsafe fn from_ptr<'a>(ptr: *const bindings::filename) -> &'a Filenam
unsafe { &*ptr.cast() }
}
}
+
+/// Kernel module that exposes a single file system implemented by `T`.
+pub struct Module<T: Type> {
+ _fs: Pin<Box<Registration>>,
+ _p: PhantomData<T>,
+}
+
+impl<T: Type + Sync> crate::Module for Module<T> {
+ fn init(_name: &'static CStr, module: &'static ThisModule) -> Result<Self> {
+ let mut reg = Pin::from(Box::try_new(Registration::new())?);
+ reg.as_mut().register::<T>(module)?;
+ Ok(Self {
+ _fs: reg,
+ _p: PhantomData,
+ })
+ }
+}
+
+/// Declares a kernel module that exposes a single file system.
+///
+/// The `type` argument must be a type which implements the [`Type`] trait. Also accepts various
+/// forms of kernel metadata.
+///
+/// # Examples
+///
+/// ```ignore
+/// use kernel::prelude::*;
+/// use kernel::{c_str, fs};
+///
+/// module_fs! {
+/// type: MyFs,
+/// name: b"my_fs_kernel_module",
+/// author: b"Rust for Linux Contributors",
+/// description: b"My very own file system kernel module!",
+/// license: b"GPL",
+/// }
+///
+/// struct MyFs;
+///
+/// #[vtable]
+/// impl fs::Context<Self> for MyFs {
+/// type Data = ();
+/// fn try_new() -> Result {
+/// Ok(())
+/// }
+/// }
+///
+/// impl fs::Type for MyFs {
+/// type Context = Self;
+/// const SUPER_TYPE: fs::Super = fs::Super::Independent;
+/// const NAME: &'static CStr = c_str!("example");
+/// const FLAGS: i32 = 0;
+///
+/// fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBlock<Self>> {
+/// let sb = sb.init(
+/// (),
+/// &fs::SuperParams {
+/// magic: 0x6578616d,
+/// ..fs::SuperParams::DEFAULT
+/// },
+/// )?;
+/// let sb = sb.init_root()?;
+/// Ok(sb)
+/// }
+/// }
+/// ```
+#[macro_export]
+macro_rules! module_fs {
+ (type: $type:ty, $($f:tt)*) => {
+ type ModuleType = $crate::fs::Module<$type>;
+ $crate::macros::module! {
+ type: ModuleType,
+ $($f)*
+ }
+ }
+}
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index c28587d68ebc..37789bc8a796 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -28,6 +28,7 @@
pub use super::{pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn};
pub use super::{init, pin_init, try_init, try_pin_init};
+pub use super::{module_fs, module_misc_device};
pub use super::static_assert;
diff --git a/samples/rust/rust_fs.rs b/samples/rust/rust_fs.rs
index 5d8880d5830b..064ead97dd98 100644
--- a/samples/rust/rust_fs.rs
+++ b/samples/rust/rust_fs.rs
@@ -5,8 +5,8 @@
use kernel::prelude::*;
use kernel::{c_str, fs};
-module! {
- type: FsModule,
+module_fs! {
+ type: RustFs,
name: "rust_fs",
author: "Rust for Linux Contributors",
license: "GPL",
@@ -57,15 +57,3 @@ fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBl
Ok(sb)
}
}
-
-struct FsModule {
- _fs: Pin<Box<fs::Registration>>,
-}
-
-impl kernel::Module for FsModule {
- fn init(module: &'static ThisModule) -> Result<Self> {
- let mut reg = Pin::from(Box::try_new(fs::Registration::new())?);
- reg.as_mut().register::<RustFs>(module)?;
- Ok(Self { _fs: reg })
- }
-}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 08/80] WIP: rust: allow fs to be populated
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (6 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 07/80] rust: add `module_fs` macro Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 09/80] rust: kernel: backport the delay module from the rust branch Ariel Miculas
` (69 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Wedson Almeida Filho
From: Wedson Almeida Filho <wedsonaf@google.com>
---
rust/bindings/bindings_helper.h | 4 +
rust/bindings/lib.rs | 3 +
rust/helpers.c | 7 +
rust/kernel/fs.rs | 777 +++++++++++++++++++++++++++++---
rust/kernel/fs/param.rs | 8 +-
rust/kernel/prelude.rs | 1 -
samples/rust/rust_fs.rs | 51 ++-
7 files changed, 778 insertions(+), 73 deletions(-)
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index b4297f6cb99f..d15a698439e1 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -22,3 +22,7 @@ const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;
const gfp_t BINDINGS___GFP_ZERO = __GFP_ZERO;
const loff_t BINDINGS_MAX_LFS_FILESIZE = MAX_LFS_FILESIZE;
+
+const slab_flags_t BINDINGS_SLAB_RECLAIM_ACCOUNT = SLAB_RECLAIM_ACCOUNT;
+const slab_flags_t BINDINGS_SLAB_MEM_SPREAD = SLAB_MEM_SPREAD;
+const slab_flags_t BINDINGS_SLAB_ACCOUNT = SLAB_ACCOUNT;
diff --git a/rust/bindings/lib.rs b/rust/bindings/lib.rs
index cd1fceb31390..8655d73b6785 100644
--- a/rust/bindings/lib.rs
+++ b/rust/bindings/lib.rs
@@ -53,3 +53,6 @@ mod bindings_helper {
pub const __GFP_ZERO: gfp_t = BINDINGS___GFP_ZERO;
pub const MAX_LFS_FILESIZE: loff_t = BINDINGS_MAX_LFS_FILESIZE;
+
+pub const SLAB_RECLAIM_ACCOUNT: slab_flags_t = BINDINGS_SLAB_RECLAIM_ACCOUNT;
+pub const SLAB_MEM_SPREAD: slab_flags_t = BINDINGS_SLAB_MEM_SPREAD;
diff --git a/rust/helpers.c b/rust/helpers.c
index ffe62af5ee20..efbe9d917a57 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -191,6 +191,13 @@ void rust_helper_kunmap(struct page *page)
}
EXPORT_SYMBOL_GPL(rust_helper_kunmap);
+void *rust_helper_alloc_inode_sb(struct super_block *sb,
+ struct kmem_cache *cache, gfp_t gfp)
+{
+ return alloc_inode_sb(sb, cache, gfp);
+}
+EXPORT_SYMBOL_GPL(rust_helper_alloc_inode_sb);
+
/*
* We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
* as the Rust `usize` type, so we can use it in contexts where Rust
diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs
index 86c306c19e0a..2a0267b3c0b6 100644
--- a/rust/kernel/fs.rs
+++ b/rust/kernel/fs.rs
@@ -4,11 +4,20 @@
//!
//! C headers: [`include/linux/fs.h`](../../../../include/linux/fs.h)
+use crate::error::{from_kernel_result, to_result, Error, Result};
+use crate::file;
+use crate::types::{ARef, AlwaysRefCounted, ForeignOwnable, ScopeGuard};
+use crate::{
+ bindings, container_of, delay::coarse_sleep, error::code::*, pr_warn, str::CStr, ThisModule,
+};
use alloc::boxed::Box;
-use crate::{bindings, error::code::*, str::CStr, ThisModule};
-use crate::error::{to_result, from_kernel_result, Error, Result};
-use crate::types::{AlwaysRefCounted, ForeignOwnable, ScopeGuard};
-use core::{cell::UnsafeCell, marker::PhantomData, marker::PhantomPinned, pin::Pin, ptr};
+use core::mem::{align_of, size_of, ManuallyDrop, MaybeUninit};
+use core::sync::atomic::{AtomicU64, Ordering};
+use core::time::Duration;
+use core::{
+ cell::UnsafeCell, marker::PhantomData, marker::PhantomPinned, ops::Deref, pin::Pin, ptr,
+};
+
use macros::vtable;
pub mod param;
@@ -81,7 +90,21 @@ fn tree_key(_data: &mut Self::Data) -> Result<T::Data> {
}
}
-struct Tables<T: Type + ?Sized>(T);
+/// An empty file system context.
+///
+/// That is, one that doesn't take any arguments and doesn't hold any state. It is a convenience
+/// type for file systems that don't need context for mounting/reconfiguring.
+pub struct EmptyContext;
+
+#[vtable]
+impl<T: Type + ?Sized> Context<T> for EmptyContext {
+ type Data = ();
+ fn try_new() -> Result {
+ Ok(())
+ }
+}
+
+pub(crate) struct Tables<T: Type + ?Sized>(T);
impl<T: Type + ?Sized> Tables<T> {
const CONTEXT: bindings::fs_context_operations = bindings::fs_context_operations {
free: Some(Self::free_callback),
@@ -292,10 +315,18 @@ impl<T: Type + ?Sized> Tables<T> {
}
}
- const SUPER_BLOCK: bindings::super_operations = bindings::super_operations {
- alloc_inode: None,
+ pub(crate) const SUPER_BLOCK: bindings::super_operations = bindings::super_operations {
+ alloc_inode: if size_of::<T::INodeData>() != 0 {
+ Some(Self::alloc_inode_callback)
+ } else {
+ None
+ },
destroy_inode: None,
- free_inode: None,
+ free_inode: if size_of::<T::INodeData>() != 0 {
+ Some(Self::free_inode_callback)
+ } else {
+ None
+ },
dirty_inode: None,
write_inode: None,
drop_inode: None,
@@ -322,16 +353,76 @@ impl<T: Type + ?Sized> Tables<T> {
nr_cached_objects: None,
free_cached_objects: None,
};
+
+ unsafe extern "C" fn alloc_inode_callback(
+ sb: *mut bindings::super_block,
+ ) -> *mut bindings::inode {
+ // SAFETY: The callback contract guarantees that `sb` is valid for read.
+ let super_type = unsafe { (*sb).s_type };
+
+ // SAFETY: This callback is only used in `Registration`, so `super_type` is necessarily
+ // embedded in a `Registration`, which is guaranteed to be valid because it has a
+ // superblock associated to it.
+ let reg = unsafe { &*container_of!(super_type, Registration, fs) };
+
+ // SAFETY: `sb` and `reg.inode_cache` are guaranteed to be valid by the callback contract
+ // and by the existence of a superblock respectively.
+ let ptr = unsafe { bindings::alloc_inode_sb(sb, reg.inode_cache, bindings::GFP_KERNEL) }
+ as *mut INodeWithData<T::INodeData>;
+ if ptr.is_null() {
+ return ptr::null_mut();
+ }
+ reg.alloc_count.fetch_add(1, Ordering::Relaxed);
+ ptr::addr_of_mut!((*ptr).inode)
+ }
+
+ unsafe extern "C" fn free_inode_callback(inode: *mut bindings::inode) {
+ // SAFETY: The inode is guaranteed to be valid by the callback contract. Additionally, the
+ // superblock is also guaranteed to still be valid by the inode existence.
+ let super_type = unsafe { (*(*inode).i_sb).s_type };
+
+ // SAFETY: This callback is only used in `Registration`, so `super_type` is necessarily
+ // embedded in a `Registration`, which is guaranteed to be valid because it has a
+ // superblock associated to it.
+ let reg = unsafe { &*container_of!(super_type, Registration, fs) };
+ let ptr = container_of!(inode, INodeWithData<T::INodeData>, inode);
+
+ // SAFETY: The code in `try_new_inode` always initialises the inode data after allocating
+ // it, so it is safe to drop it here.
+ unsafe {
+ core::ptr::drop_in_place(
+ (*(ptr as *mut INodeWithData<T::INodeData>))
+ .data
+ .as_mut_ptr(),
+ )
+ };
+
+ // The callback contract guarantees that the inode was previously allocated via the
+ // `alloc_inode_callback` callback, so it is safe to free it back to the cache.
+ unsafe { bindings::kmem_cache_free(reg.inode_cache, ptr as _) };
+
+ reg.alloc_count.fetch_sub(1, Ordering::Release);
+ }
}
/// A file system type.
pub trait Type {
/// The context used to build fs configuration before it is mounted or reconfigured.
- type Context: Context<Self> + ?Sized;
+ type Context: Context<Self> + ?Sized = EmptyContext;
+
+ /// Type of data allocated for each inode.
+ type INodeData: Send + Sync = ();
/// Data associated with each file system instance.
type Data: ForeignOwnable + Send + Sync = ();
+ /// Determines whether the filesystem is based on the dcache.
+ ///
+ /// When this is `true`, adding a dentry results in an increased refcount. Removing them
+ /// results in a matching decrement, and `kill_litter_super` is used when killing the
+ /// superblock so that these extra references are removed.
+ const DCACHE_BASED: bool = false;
+
/// Determines how superblocks for this file system type are keyed.
const SUPER_TYPE: Super;
@@ -377,10 +468,11 @@ pub mod flags {
}
/// A file system registration.
-#[derive(Default)]
pub struct Registration {
is_registered: bool,
fs: UnsafeCell<bindings::file_system_type>,
+ inode_cache: *mut bindings::kmem_cache,
+ alloc_count: AtomicU64,
_pin: PhantomPinned,
}
@@ -401,6 +493,8 @@ pub fn new() -> Self {
Self {
is_registered: false,
fs: UnsafeCell::new(bindings::file_system_type::default()),
+ inode_cache: ptr::null_mut(),
+ alloc_count: AtomicU64::new(0),
_pin: PhantomPinned,
}
}
@@ -418,6 +512,29 @@ pub fn register<T: Type + ?Sized>(self: Pin<&mut Self>, module: &'static ThisMod
return Err(EINVAL);
}
+ if this.inode_cache.is_null() {
+ let size = size_of::<T::INodeData>();
+ if size != 0 {
+ // We only create the cache if the size is non-zero.
+ //
+ // SAFETY: `NAME` is static, so always valid.
+ this.inode_cache = unsafe {
+ bindings::kmem_cache_create(
+ T::NAME.as_char_ptr(),
+ size_of::<INodeWithData<T::INodeData>>() as _,
+ align_of::<INodeWithData<T::INodeData>>() as _,
+ bindings::SLAB_RECLAIM_ACCOUNT
+ | bindings::SLAB_MEM_SPREAD
+ | bindings::SLAB_ACCOUNT,
+ Some(Self::inode_init_once_callback::<T>),
+ )
+ };
+ if this.inode_cache.is_null() {
+ return Err(ENOMEM);
+ }
+ }
+ }
+
let mut fs = this.fs.get_mut();
fs.owner = module.0;
fs.name = T::NAME.as_char_ptr();
@@ -496,6 +613,13 @@ unsafe fn unregister_keys(fs: *mut bindings::file_system_type) {
// to call `kill_block_super`. Additionally, the callback contract guarantees that
// `sb_ptr` is valid.
unsafe { bindings::kill_block_super(sb_ptr) }
+ } else if T::DCACHE_BASED {
+ // SAFETY: We always call a `get_tree_nodev` variant from `get_tree_callback` without a
+ // device when `T::SUPER_TYPE` is not `BlockDev`, so we never have a device in such
+ // cases, therefore it is ok to call the function below. Additionally, the callback
+ // contract guarantees that `sb_ptr` is valid, and we have all positive dentries biased
+ // by +1 when `T::DCACHE_BASED`.
+ unsafe { bindings::kill_litter_super(sb_ptr) }
} else {
// SAFETY: We always call a `get_tree_nodev` variant from `get_tree_callback` without a
// device when `T::SUPER_TYPE` is not `BlockDev`, so we never have a device in such
@@ -519,6 +643,35 @@ unsafe fn unregister_keys(fs: *mut bindings::file_system_type) {
unsafe { T::Data::from_foreign(ptr) };
}
}
+
+ unsafe extern "C" fn inode_init_once_callback<T: Type + ?Sized>(
+ outer_inode: *mut core::ffi::c_void,
+ ) {
+ let ptr = outer_inode as *mut INodeWithData<T::INodeData>;
+ // This is only used in `register`, so we know that we have a valid `INodeWithData`
+ // instance whose inode part can be initialised.
+ unsafe { bindings::inode_init_once(ptr::addr_of_mut!((*ptr).inode)) };
+ }
+
+ fn has_super_blocks(&self) -> bool {
+ unsafe extern "C" fn fs_cb(_: *mut bindings::super_block, ptr: *mut core::ffi::c_void) {
+ // SAFETY: This function is only called below, while `ptr` is known to `has_sb`.
+ unsafe { *(ptr as *mut bool) = true };
+ }
+
+ let mut has_sb = false;
+ // SAFETY: `fs` is valid, and `fs_cb` only touches `has_sb` during the call.
+ unsafe {
+ bindings::iterate_supers_type(self.fs.get(), Some(fs_cb), (&mut has_sb) as *mut _ as _)
+ }
+ has_sb
+ }
+}
+
+impl Default for Registration {
+ fn default() -> Self {
+ Self::new()
+ }
}
impl Drop for Registration {
@@ -527,10 +680,59 @@ fn drop(&mut self) {
// SAFETY: When `is_registered` is `true`, a previous call to `register_filesystem` has
// succeeded, so it is safe to unregister here.
unsafe { bindings::unregister_filesystem(self.fs.get()) };
+
+ // TODO: Test this.
+ if self.has_super_blocks() {
+ // If there are mounted superblocks of this registration, we cannot release the
+ // memory because it may be referenced, which would be a memory violation.
+ pr_warn!(
+ "Attempting to unregister a file system (0x{:x}) with mounted super blocks\n",
+ self.fs.get() as usize
+ );
+ while self.has_super_blocks() {
+ pr_warn!("Sleeping 1s before retrying...\n");
+ coarse_sleep(Duration::from_secs(1));
+ }
+ }
+ }
+
+ if !self.inode_cache.is_null() {
+ // Check if all inodes have been freed. If that's not the case, we may run into
+ // user-after-frees of the registration and kmem cache, so wait for it to drop to zero
+ // before proceeding.
+ //
+ // The expectation is that developers will fix this if they run into this warning.
+ if self.alloc_count.load(Ordering::Acquire) > 0 {
+ pr_warn!(
+ "Attempting to unregister a file system (0x{:x}) with allocated inodes\n",
+ self.fs.get() as usize
+ );
+ while self.alloc_count.load(Ordering::Acquire) > 0 {
+ pr_warn!("Sleeping 1s before retrying...\n");
+ coarse_sleep(Duration::from_secs(1));
+ }
+ }
+
+ // SAFETY: Just an FFI call with no additional safety requirements.
+ unsafe { bindings::rcu_barrier() };
+
+ // SAFETY: We know there are no more allocations in this cache and that it won't be
+ // used to allocate anymore because the filesystem is unregistered (so new mounts can't
+ // be created) and there are no more superblocks nor inodes.
+ //
+ // TODO: Can a dentry keep a file system alive? It looks like the answer is yes because
+ // it has a pointer to the superblock. How do we keep it alive? `d_init` may be an
+ // option to increment some count.
+ unsafe { bindings::kmem_cache_destroy(self.inode_cache) };
}
}
}
+struct INodeWithData<T> {
+ data: MaybeUninit<T>,
+ inode: bindings::inode,
+}
+
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init`] needs to be called
/// eventually.
pub struct NeedsInit;
@@ -574,8 +776,10 @@ impl SuperParams {
///
/// The superblock is a newly-created one and this is the only active pointer to it.
pub struct NewSuperBlock<'a, T: Type + ?Sized, S = NeedsInit> {
- sb: *mut bindings::super_block,
- _p: PhantomData<(&'a T, S)>,
+ sb: &'a mut SuperBlock<T>,
+
+ // This also forces `'a` to be invariant.
+ _p: PhantomData<&'a mut &'a S>,
}
impl<'a, T: Type + ?Sized> NewSuperBlock<'a, T, NeedsInit> {
@@ -587,7 +791,8 @@ impl<'a, T: Type + ?Sized> NewSuperBlock<'a, T, NeedsInit> {
unsafe fn new(sb: *mut bindings::super_block) -> Self {
// INVARIANT: The invariants are satisfied by the safety requirements of this function.
Self {
- sb,
+ // SAFETY: The safety requirements ensure that `sb` is valid for dereference.
+ sb: unsafe { &mut *sb.cast() },
_p: PhantomData,
}
}
@@ -598,9 +803,7 @@ pub fn init(
data: T::Data,
params: &SuperParams,
) -> Result<NewSuperBlock<'a, T, NeedsRoot>> {
- // SAFETY: The type invariant guarantees that `self.sb` is the only pointer to a
- // newly-allocated superblock, so it is safe to mutably reference it.
- let sb = unsafe { &mut *self.sb };
+ let sb = self.sb.0.get_mut();
sb.s_magic = params.magic as _;
sb.s_op = &Tables::<T>::SUPER_BLOCK;
@@ -635,56 +838,214 @@ pub fn init(
impl<'a, T: Type + ?Sized> NewSuperBlock<'a, T, NeedsRoot> {
/// Initialises the root of the superblock.
- pub fn init_root(self) -> Result<&'a SuperBlock<T>> {
- // The following is temporary code to create the root inode and dentry. It will be replaced
- // once we allow inodes and dentries to be created directly from Rust code.
+ pub fn init_root(self, dentry: RootDEntry<T>) -> Result<&'a SuperBlock<T>> {
+ self.sb.0.get_mut().s_root = ManuallyDrop::new(dentry).ptr;
+ Ok(self.sb)
+ }
- // SAFETY: `sb` is initialised (`NeedsRoot` typestate implies it), so it is safe to pass it
- // to `new_inode`.
- let inode = unsafe { bindings::new_inode(self.sb) };
- if inode.is_null() {
- return Err(ENOMEM);
+ fn populate_dir(
+ &self,
+ parent: &DEntry<T>,
+ ino: &mut u64,
+ entries: &[Entry<'_, T>],
+ recursion: usize,
+ ) -> Result
+ where
+ T::INodeData: Clone,
+ {
+ if recursion == 0 {
+ return Err(E2BIG);
}
- {
- // SAFETY: This is a newly-created inode. No other references to it exist, so it is
- // safe to mutably dereference it.
- let inode = unsafe { &mut *inode };
+ for e in entries {
+ *ino += 1;
+ match e {
+ Entry::File(name, mode, value, inode_create) => {
+ let params = INodeParams {
+ mode: *mode,
+ ino: *ino,
+ value: value.clone(),
+ };
+ let inode = inode_create(self, params)?;
+ self.try_new_dentry(inode, parent, name)?;
+ }
+ Entry::Special(name, mode, value, typ, dev) => {
+ let params = INodeParams {
+ mode: *mode,
+ ino: *ino,
+ value: value.clone(),
+ };
+ let inode = self.sb.try_new_special_inode(*typ, *dev, params)?;
+ self.try_new_dentry(inode, parent, name)?;
+ }
+ Entry::Directory(name, mode, value, dir_entries) => {
+ let params = INodeParams {
+ mode: *mode,
+ ino: *ino,
+ value: value.clone(),
+ };
+ let inode = self.sb.try_new_dcache_dir_inode(params)?;
+ let new_parent = self.try_new_dentry(inode, parent, name)?;
+ self.populate_dir(&new_parent, ino, dir_entries, recursion - 1)?;
+ }
+ }
+ }
- // SAFETY: `current_time` requires that `inode.sb` be valid, which is the case here
- // since we allocated the inode through the superblock.
- let time = unsafe { bindings::current_time(inode) };
- inode.i_ino = 1;
- inode.i_mode = (bindings::S_IFDIR | 0o755) as _;
- inode.i_mtime = time;
- inode.i_atime = time;
- inode.i_ctime = time;
+ Ok(())
+ }
- // SAFETY: `simple_dir_operations` never changes, it's safe to reference it.
- inode.__bindgen_anon_3.i_fop = unsafe { &bindings::simple_dir_operations };
+ /// Creates a new root dentry populated with the given entries.
+ pub fn try_new_populated_root_dentry(
+ &self,
+ root_value: T::INodeData,
+ entries: &[Entry<'_, T>],
+ ) -> Result<RootDEntry<T>>
+ where
+ T::INodeData: Clone,
+ {
+ let root_inode = self.sb.try_new_dcache_dir_inode(INodeParams {
+ mode: 0o755,
+ ino: 1,
+ value: root_value,
+ })?;
+ let root = self.try_new_root_dentry(root_inode)?;
+ let mut ino = 1u64;
+ self.populate_dir(&root, &mut ino, entries, 10)?;
+ Ok(root)
+ }
- // SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it.
- inode.i_op = unsafe { &bindings::simple_dir_inode_operations };
+ /// Creates a new empty root dentry.
+ pub fn try_new_root_dentry(&self, inode: ARef<INode<T>>) -> Result<RootDEntry<T>> {
+ // SAFETY: The inode is referenced, so it is safe to read the read-only field `i_sb`.
+ if unsafe { (*inode.0.get()).i_sb } != self.sb.0.get() {
+ return Err(EINVAL);
+ }
- // SAFETY: `inode` is valid for write.
- unsafe { bindings::set_nlink(inode, 2) };
+ // SAFETY: The caller owns a reference to the inode, so it is valid. The reference is
+ // transferred to the callee.
+ let dentry =
+ ptr::NonNull::new(unsafe { bindings::d_make_root(ManuallyDrop::new(inode).0.get()) })
+ .ok_or(ENOMEM)?;
+ Ok(RootDEntry {
+ ptr: dentry.as_ptr(),
+ _p: PhantomData,
+ })
+ }
+
+ /// Creates a new dentry with the given name, under the given parent, and backed by the given
+ /// inode.
+ pub fn try_new_dentry(
+ &self,
+ inode: ARef<INode<T>>,
+ parent: &DEntry<T>,
+ name: &CStr,
+ ) -> Result<ARef<DEntry<T>>> {
+ // SAFETY: Both `inode` and `parent` are referenced, so it is safe to read the read-only
+ // fields `i_sb` and `d_sb`.
+ if unsafe { (*parent.0.get()).d_sb } != self.sb.0.get()
+ || unsafe { (*inode.0.get()).i_sb } != self.sb.0.get()
+ {
+ return Err(EINVAL);
}
- // SAFETY: `d_make_root` requires that `inode` be valid and referenced, which is the
- // case for this call.
- //
- // It takes over the inode, even on failure, so we don't need to clean it up.
- let dentry = unsafe { bindings::d_make_root(inode) };
- if dentry.is_null() {
- return Err(ENOMEM);
+ // SAFETY: `parent` is valid (we have a shared reference to it), and `name` is valid for
+ // the duration of the call (the callee makes a copy of the name).
+ let dentry = ptr::NonNull::new(unsafe {
+ bindings::d_alloc_name(parent.0.get(), name.as_char_ptr())
+ })
+ .ok_or(ENOMEM)?;
+
+ // SAFETY: `dentry` was just allocated so it is valid. The callee takes over the reference
+ // to the inode.
+ unsafe { bindings::d_add(dentry.as_ptr(), ManuallyDrop::new(inode).0.get()) };
+
+ // SAFETY: `dentry` was just allocated, and the caller holds a reference, which it
+ // transfers to `dref`.
+ let dref = unsafe { ARef::from_raw(dentry.cast::<DEntry<T>>()) };
+
+ if T::DCACHE_BASED {
+ // Bias the refcount by +1 when adding a positive dentry.
+ core::mem::forget(dref.clone());
}
- // SAFETY: The typestate guarantees that `self.sb` is valid.
- unsafe { (*self.sb).s_root = dentry };
+ Ok(dref)
+ }
+
+ /// Creates a new inode that is a directory.
+ ///
+ /// The directory is based on the dcache, implemented by `simple_dir_operations` and
+ /// `simple_dir_inode_operations`.
+ pub fn try_new_dcache_dir_inode(
+ &self,
+ params: INodeParams<T::INodeData>,
+ ) -> Result<ARef<INode<T>>> {
+ self.sb.try_new_dcache_dir_inode(params)
+ }
- // SAFETY: The typestate guarantees that `self.sb` is initialised and we just finished
- // setting its root, so it's a fully ready superblock.
- Ok(unsafe { &mut *self.sb.cast() })
+ /// Creates a new "special" inode.
+ pub fn try_new_special_inode(
+ &self,
+ typ: INodeSpecialType,
+ rdev: Option<u32>,
+ params: INodeParams<T::INodeData>,
+ ) -> Result<ARef<INode<T>>> {
+ self.sb.try_new_special_inode(typ, rdev, params)
+ }
+
+ /// Creates a new regular file inode.
+ pub fn try_new_file_inode<F: file::Operations<OpenData = T::INodeData>>(
+ &self,
+ params: INodeParams<T::INodeData>,
+ ) -> Result<ARef<INode<T>>> {
+ self.sb.try_new_file_inode::<F>(params)
+ }
+}
+
+/// The type of a special inode.
+///
+/// This is used in functions like [`SuperBlock::try_new_special_inode`] to specify the type of
+/// an special inode; in this example, it's for it to be created.
+#[derive(Clone, Copy)]
+#[repr(u16)]
+pub enum INodeSpecialType {
+ /// Character device.
+ Char = bindings::S_IFCHR as _,
+
+ /// Block device.
+ Block = bindings::S_IFBLK as _,
+
+ /// A pipe (FIFO, first-in first-out) inode.
+ Fifo = bindings::S_IFIFO as _,
+
+ /// A unix-domain socket.
+ Sock = bindings::S_IFSOCK as _,
+}
+
+/// Required inode parameters.
+///
+/// This is used when creating new inodes.
+pub struct INodeParams<T> {
+ /// The access mode. It's a mask that grants execute (1), write (2) and read (4) access to
+ /// everyone, the owner group, and the owner.
+ pub mode: u16,
+
+ /// Number of the inode.
+ pub ino: u64,
+
+ /// Value to attach to this node.
+ pub value: T,
+}
+
+struct FsAdapter<T: Type + ?Sized>(PhantomData<T>);
+impl<T: Type + ?Sized> file::OpenAdapter<T::INodeData> for FsAdapter<T> {
+ unsafe fn convert(
+ inode: *mut bindings::inode,
+ _file: *mut bindings::file,
+ ) -> *const T::INodeData {
+ let ptr = container_of!(inode, INodeWithData<T::INodeData>, inode);
+ // SAFETY: Add safety annotation.
+ let outer = unsafe { &*ptr };
+ outer.data.as_ptr()
}
}
@@ -697,6 +1058,95 @@ pub struct SuperBlock<T: Type + ?Sized>(
PhantomData<T>,
);
+impl<T: Type + ?Sized> SuperBlock<T> {
+ fn try_new_inode(
+ &self,
+ mode_type: u16,
+ params: INodeParams<T::INodeData>,
+ init: impl FnOnce(&mut bindings::inode),
+ ) -> Result<ARef<INode<T>>> {
+ // SAFETY: `sb` is initialised (`NeedsRoot` typestate implies it), so it is safe to pass it
+ // to `new_inode`.
+ let inode =
+ ptr::NonNull::new(unsafe { bindings::new_inode(self.0.get()) }).ok_or(ENOMEM)?;
+
+ {
+ let ptr = container_of!(inode.as_ptr(), INodeWithData<T::INodeData>, inode);
+
+ // SAFETY: This is a newly-created inode. No other references to it exist, so it is
+ // safe to mutably dereference it.
+ let outer = unsafe { &mut *(ptr as *mut INodeWithData<T::INodeData>) };
+
+ // N.B. We must always write this to a newly allocated inode because the free callback
+ // expects the data to be initialised and drops it.
+ outer.data.write(params.value);
+
+ // SAFETY: `current_time` requires that `inode.sb` be valid, which is the case here
+ // since we allocated the inode through the superblock.
+ let time = unsafe { bindings::current_time(&mut outer.inode) };
+ outer.inode.i_mtime = time;
+ outer.inode.i_atime = time;
+ outer.inode.i_ctime = time;
+
+ outer.inode.i_ino = params.ino;
+ outer.inode.i_mode = params.mode & 0o777 | mode_type;
+
+ init(&mut outer.inode);
+ }
+
+ // SAFETY: `inode` only has one reference, and it's being relinquished to the `ARef`
+ // instance.
+ Ok(unsafe { ARef::from_raw(inode.cast()) })
+ }
+
+ /// Creates a new inode that is a directory.
+ ///
+ /// The directory is based on the dcache, implemented by `simple_dir_operations` and
+ /// `simple_dir_inode_operations`.
+ pub fn try_new_dcache_dir_inode(
+ &self,
+ params: INodeParams<T::INodeData>,
+ ) -> Result<ARef<INode<T>>> {
+ self.try_new_inode(bindings::S_IFDIR as _, params, |inode| {
+ // SAFETY: `simple_dir_operations` never changes, it's safe to reference it.
+ inode.__bindgen_anon_3.i_fop = unsafe { &bindings::simple_dir_operations };
+
+ // SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it.
+ inode.i_op = unsafe { &bindings::simple_dir_inode_operations };
+
+ // Directory inodes start off with i_nlink == 2 (for "." entry).
+ // SAFETY: `inode` is valid for write.
+ unsafe { bindings::inc_nlink(inode) };
+ })
+ }
+
+ /// Creates a new "special" inode.
+ pub fn try_new_special_inode(
+ &self,
+ typ: INodeSpecialType,
+ rdev: Option<u32>,
+ params: INodeParams<T::INodeData>,
+ ) -> Result<ARef<INode<T>>> {
+ // SAFETY: `inode` is valid as it's a mutable reference.
+ self.try_new_inode(typ as _, params, |inode| unsafe {
+ bindings::init_special_inode(inode, inode.i_mode, rdev.unwrap_or(0))
+ })
+ }
+
+ /// Creates a new regular file inode.
+ pub fn try_new_file_inode<F: file::Operations<OpenData = T::INodeData>>(
+ &self,
+ params: INodeParams<T::INodeData>,
+ ) -> Result<ARef<INode<T>>> {
+ self.try_new_inode(bindings::S_IFREG as _, params, |inode| {
+ // SAFETY: The adapter is compatible because it assumes an inode created by a `T` file
+ // system, which is the case here.
+ inode.__bindgen_anon_3.i_fop =
+ unsafe { file::OperationsVtable::<FsAdapter<T>, F>::build() };
+ })
+ }
+}
+
/// Wraps the kernel's `struct inode`.
///
/// # Invariants
@@ -704,10 +1154,19 @@ pub struct SuperBlock<T: Type + ?Sized>(
/// Instances of this type are always ref-counted, that is, a call to `ihold` ensures that the
/// allocation remains valid at least until the matching call to `iput`.
#[repr(transparent)]
-pub struct INode(pub(crate) UnsafeCell<bindings::inode>);
+pub struct INode<T: Type + ?Sized>(pub(crate) UnsafeCell<bindings::inode>, PhantomData<T>);
+
+impl<T: Type + ?Sized> INode<T> {
+ /// Returns the file-system-determined data associated with the inode.
+ pub fn fs_data(&self) -> &T::INodeData {
+ let ptr = container_of!(self.0.get(), INodeWithData<T::INodeData>, inode);
+ // SAFETY: Add safety annotation.
+ unsafe { (*ptr::addr_of!((*ptr).data)).assume_init_ref() }
+ }
+}
// SAFETY: The type invariants guarantee that `INode` is always ref-counted.
-unsafe impl AlwaysRefCounted for INode {
+unsafe impl<T: Type + ?Sized> AlwaysRefCounted for INode<T> {
fn inc_ref(&self) {
// SAFETY: The existence of a shared reference means that the refcount is nonzero.
unsafe { bindings::ihold(self.0.get()) };
@@ -726,10 +1185,10 @@ unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
/// Instances of this type are always ref-counted, that is, a call to `dget` ensures that the
/// allocation remains valid at least until the matching call to `dput`.
#[repr(transparent)]
-pub struct DEntry(pub(crate) UnsafeCell<bindings::dentry>);
+pub struct DEntry<T: Type + ?Sized>(pub(crate) UnsafeCell<bindings::dentry>, PhantomData<T>);
// SAFETY: The type invariants guarantee that `DEntry` is always ref-counted.
-unsafe impl AlwaysRefCounted for DEntry {
+unsafe impl<T: Type + ?Sized> AlwaysRefCounted for DEntry<T> {
fn inc_ref(&self) {
// SAFETY: The existence of a shared reference means that the refcount is nonzero.
unsafe { bindings::dget(self.0.get()) };
@@ -741,6 +1200,45 @@ unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
}
}
+/// A dentry that is meant to be used as the root of a file system.
+///
+/// We have a specific type for the root dentry because we may need to do extra work when it is
+/// dropped. For example, if [`Type::DCACHE_BASED`] is `true`, we need to remove the extra
+/// reference held on each child dentry.
+///
+/// # Invariants
+///
+/// `ptr` is always valid and ref-counted.
+pub struct RootDEntry<T: Type + ?Sized> {
+ ptr: *mut bindings::dentry,
+ _p: PhantomData<T>,
+}
+
+impl<T: Type + ?Sized> Deref for RootDEntry<T> {
+ type Target = DEntry<T>;
+
+ fn deref(&self) -> &Self::Target {
+ // SAFETY: Add safety annotation.
+ unsafe { &*self.ptr.cast() }
+ }
+}
+
+impl<T: Type + ?Sized> Drop for RootDEntry<T> {
+ fn drop(&mut self) {
+ if T::DCACHE_BASED {
+ // All dentries have an extra ref on them, so we use `d_genocide` to drop it.
+ // SAFETY: Add safety annotation.
+ unsafe { bindings::d_genocide(self.ptr) };
+
+ // SAFETY: Add safety annotation.
+ unsafe { bindings::shrink_dcache_parent(self.ptr) };
+ }
+
+ // SAFETY: Add safety annotation.
+ unsafe { bindings::dput(self.ptr) };
+ }
+}
+
/// Wraps the kernel's `struct filename`.
#[repr(transparent)]
pub struct Filename(pub(crate) UnsafeCell<bindings::filename>);
@@ -776,6 +1274,11 @@ fn init(_name: &'static CStr, module: &'static ThisModule) -> Result<Self> {
}
}
+/// Returns a device id from its major and minor components.
+pub const fn mkdev(major: u16, minor: u32) -> u32 {
+ (major as u32) << bindings::MINORBITS | minor
+}
+
/// Declares a kernel module that exposes a single file system.
///
/// The `type` argument must be a type which implements the [`Type`] trait. Also accepts various
@@ -797,16 +1300,7 @@ fn init(_name: &'static CStr, module: &'static ThisModule) -> Result<Self> {
///
/// struct MyFs;
///
-/// #[vtable]
-/// impl fs::Context<Self> for MyFs {
-/// type Data = ();
-/// fn try_new() -> Result {
-/// Ok(())
-/// }
-/// }
-///
/// impl fs::Type for MyFs {
-/// type Context = Self;
/// const SUPER_TYPE: fs::Super = fs::Super::Independent;
/// const NAME: &'static CStr = c_str!("example");
/// const FLAGS: i32 = 0;
@@ -819,7 +1313,13 @@ fn init(_name: &'static CStr, module: &'static ThisModule) -> Result<Self> {
/// ..fs::SuperParams::DEFAULT
/// },
/// )?;
-/// let sb = sb.init_root()?;
+/// let root_inode = sb.try_new_dcache_dir_inode(fs::INodeParams {
+/// mode: 0o755,
+/// ino: 1,
+/// value: (),
+/// })?;
+/// let root = sb.try_new_root_dentry(root_inode)?;
+/// let sb = sb.init_root(root)?;
/// Ok(sb)
/// }
/// }
@@ -834,3 +1334,144 @@ macro_rules! module_fs {
}
}
}
+
+/// Defines a slice of file system entries.
+///
+/// This is meant as a helper for the definition of file system entries in a more compact form than
+/// if declared directly using the types.
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::prelude::*;
+/// use kernel::{c_str, file, fs};
+///
+/// struct MyFs;
+///
+/// impl fs::Type for MyFs {
+/// type INodeData = &'static [u8];
+///
+/// // ...
+/// # const SUPER_TYPE: fs::Super = fs::Super::Independent;
+/// # const NAME: &'static CStr = c_str!("example");
+/// # const FLAGS: i32 = fs::flags::USERNS_MOUNT;
+/// # const DCACHE_BASED: bool = true;
+/// #
+/// # fn fill_super(_: (), _: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBlock<Self>> {
+/// # todo!()
+/// # }
+/// }
+///
+/// struct MyFile;
+///
+/// #[vtable]
+/// impl file::Operations for MyFile {
+/// type OpenData = &'static [u8];
+///
+/// // ...
+/// # fn open(_context: &Self::OpenData, _file: &file::File) -> Result<Self::Data> {
+/// # Ok(())
+/// # }
+/// }
+///
+/// const ENTRIES: &[fs::Entry<'_, MyFs>] = kernel::fs_entries![
+/// file("test1", 0o600, "abc\n".as_bytes(), MyFile),
+/// file("test2", 0o600, "def\n".as_bytes(), MyFile),
+/// char("test3", 0o600, [].as_slice(), (10, 125)),
+/// sock("test4", 0o755, [].as_slice()),
+/// fifo("test5", 0o755, [].as_slice()),
+/// block("test6", 0o755, [].as_slice(), (1, 1)),
+/// dir(
+/// "dir1",
+/// 0o755,
+/// [].as_slice(),
+/// [
+/// file("test1", 0o600, "abc\n".as_bytes(), MyFile),
+/// file("test2", 0o600, "def\n".as_bytes(), MyFile),
+/// ],
+/// ),
+/// ];
+/// ```
+#[macro_export]
+macro_rules! fs_entries {
+ ($($kind:ident ($($t:tt)*)),* $(,)?) => {
+ &[
+ $($crate::fs_entries!(@single $kind($($t)*)),)*
+ ]
+ };
+ (@single file($name:literal, $mode:expr, $value:expr, $file_ops:ty $(,)?)) => {
+ $crate::fs::Entry::File(
+ $crate::c_str!($name),
+ $mode,
+ $value,
+ $crate::fs::file_creator::<_, $file_ops>(),
+ )
+ };
+ (@single dir($name:literal, $mode:expr, $value:expr, [$($t:tt)*] $(,)?)) => {
+ $crate::fs::Entry::Directory(
+ $crate::c_str!($name),
+ $mode,
+ $value,
+ $crate::fs_entries!($($t)*),
+ )
+ };
+ (@single nod($name:literal, $mode:expr, $value:expr, $nod_type:ident, $dev:expr $(,)?)) => {
+ $crate::fs::Entry::Special(
+ $crate::c_str!($name),
+ $mode,
+ $value,
+ $crate::fs::INodeSpecialType::$nod_type,
+ $dev,
+ )
+ };
+ (@single char($name:literal, $mode:expr, $value:expr, ($major:expr, $minor:expr) $(,)?)) => {
+ $crate::fs_entries!(
+ @single nod($name, $mode, $value, Char, Some($crate::fs::mkdev($major, $minor))))
+ };
+ (@single block($name:literal, $mode:expr, $value:expr, ($major:expr, $minor:expr) $(,)?)) => {
+ $crate::fs_entries!(
+ @single nod($name, $mode, $value, Block, Some($crate::fs::mkdev($major, $minor))))
+ };
+ (@single sock($name:literal, $mode:expr, $value:expr $(,)?)) => {
+ $crate::fs_entries!(@single nod($name, $mode, $value, Sock, None))
+ };
+ (@single fifo($name:literal, $mode:expr, $value:expr $(,)?)) => {
+ $crate::fs_entries!(@single nod($name, $mode, $value, Fifo, None))
+ };
+}
+
+/// A file system entry.
+///
+/// This is used statically describe the files and directories of a file system in functions that
+/// take such data as arguments, for example, [`NewSuperBlock::try_new_populated_root_dentry`].
+pub enum Entry<'a, T: Type + ?Sized> {
+ /// A regular file.
+ File(&'a CStr, u16, T::INodeData, INodeCreator<T>),
+
+ /// A directory and its children.
+ Directory(&'a CStr, u16, T::INodeData, &'a [Entry<'a, T>]),
+
+ /// A special file, the type of which is given by [`INodeSpecialType`].
+ Special(&'a CStr, u16, T::INodeData, INodeSpecialType, Option<u32>),
+}
+
+/// A function that creates and inode.
+pub type INodeCreator<T> = fn(
+ &NewSuperBlock<'_, T, NeedsRoot>,
+ INodeParams<<T as Type>::INodeData>,
+) -> Result<ARef<INode<T>>>;
+
+/// Returns an [`INodeCreator`] that creates a regular file with the given file operations.
+///
+/// This is used by the [`fs_entries`] macro to elide the type implementing the [`file::Operations`]
+/// trait.
+pub const fn file_creator<T: Type + ?Sized, F: file::Operations<OpenData = T::INodeData>>(
+) -> INodeCreator<T> {
+ fn file_creator<T: Type + ?Sized, F: file::Operations<OpenData = T::INodeData>>(
+ new_sb: &NewSuperBlock<'_, T, NeedsRoot>,
+ params: INodeParams<T::INodeData>,
+ ) -> Result<ARef<INode<T>>> {
+ new_sb.sb.try_new_file_inode::<F>(params)
+ }
+ file_creator::<T, F>
+}
diff --git a/rust/kernel/fs/param.rs b/rust/kernel/fs/param.rs
index 44b4e895a1eb..1a31130c6d1e 100644
--- a/rust/kernel/fs/param.rs
+++ b/rust/kernel/fs/param.rs
@@ -502,7 +502,13 @@ macro_rules! count_brace_items {
/// # ..fs::SuperParams::DEFAULT
/// # },
/// # )?;
-/// # let sb = sb.init_root()?;
+/// # let root_inode = sb.try_new_dcache_dir_inode(fs::INodeParams {
+/// # mode: 0o755,
+/// # ino: 1,
+/// # value: (),
+/// # })?;
+/// # let root = sb.try_new_root_dentry(root_inode)?;
+/// # let sb = sb.init_root(root)?;
/// # Ok(sb)
/// # }
/// # }
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 37789bc8a796..c28587d68ebc 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -28,7 +28,6 @@
pub use super::{pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn};
pub use super::{init, pin_init, try_init, try_pin_init};
-pub use super::{module_fs, module_misc_device};
pub use super::static_assert;
diff --git a/samples/rust/rust_fs.rs b/samples/rust/rust_fs.rs
index 064ead97dd98..18fd4542863b 100644
--- a/samples/rust/rust_fs.rs
+++ b/samples/rust/rust_fs.rs
@@ -3,7 +3,7 @@
//! Rust file system sample.
use kernel::prelude::*;
-use kernel::{c_str, fs};
+use kernel::{c_str, file, fs, io_buffer::IoBufferWriter};
module_fs! {
type: RustFs,
@@ -34,16 +34,17 @@ impl fs::Context<Self> for RustFs {
}
fn try_new() -> Result {
- pr_info!("context created!\n");
Ok(())
}
}
impl fs::Type for RustFs {
type Context = Self;
+ type INodeData = &'static [u8];
const SUPER_TYPE: fs::Super = fs::Super::Independent;
const NAME: &'static CStr = c_str!("rustfs");
const FLAGS: i32 = fs::flags::USERNS_MOUNT;
+ const DCACHE_BASED: bool = true;
fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBlock<Self>> {
let sb = sb.init(
@@ -53,7 +54,51 @@ fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBl
..fs::SuperParams::DEFAULT
},
)?;
- let sb = sb.init_root()?;
+ let root = sb.try_new_populated_root_dentry(
+ &[],
+ kernel::fs_entries![
+ file("test1", 0o600, "abc\n".as_bytes(), FsFile),
+ file("test2", 0o600, "def\n".as_bytes(), FsFile),
+ char("test3", 0o600, [].as_slice(), (10, 125)),
+ sock("test4", 0o755, [].as_slice()),
+ fifo("test5", 0o755, [].as_slice()),
+ block("test6", 0o755, [].as_slice(), (1, 1)),
+ dir(
+ "dir1",
+ 0o755,
+ [].as_slice(),
+ [
+ file("test1", 0o600, "abc\n".as_bytes(), FsFile),
+ file("test2", 0o600, "def\n".as_bytes(), FsFile),
+ ]
+ ),
+ ],
+ )?;
+ let sb = sb.init_root(root)?;
Ok(sb)
}
}
+
+struct FsFile;
+
+#[vtable]
+impl file::Operations for FsFile {
+ type OpenData = &'static [u8];
+
+ fn open(_context: &Self::OpenData, _file: &file::File) -> Result<Self::Data> {
+ Ok(())
+ }
+
+ fn read(
+ _data: (),
+ file: &file::File,
+ writer: &mut impl IoBufferWriter,
+ offset: u64,
+ ) -> Result<usize> {
+ file::read_from_slice(
+ file.inode::<RustFs>().ok_or(EINVAL)?.fs_data(),
+ writer,
+ offset,
+ )
+ }
+}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 09/80] rust: kernel: backport the delay module from the rust branch
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (7 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 08/80] WIP: rust: allow fs to be populated Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 9:29 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 10/80] rust: kernel: add container_of macro Ariel Miculas
` (68 subsequent siblings)
77 siblings, 1 reply; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/kernel/delay.rs | 104 +++++++++++++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 1 +
2 files changed, 105 insertions(+)
create mode 100644 rust/kernel/delay.rs
diff --git a/rust/kernel/delay.rs b/rust/kernel/delay.rs
new file mode 100644
index 000000000000..d28d45ec2e57
--- /dev/null
+++ b/rust/kernel/delay.rs
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Delay functions for operations like sleeping.
+//!
+//! C header: [`include/linux/delay.h`](../../../../include/linux/delay.h)
+
+use crate::bindings;
+use core::{cmp::min, time::Duration};
+
+const MILLIS_PER_SEC: u64 = 1_000;
+
+fn coarse_sleep_conversion(duration: Duration) -> core::ffi::c_uint {
+ let milli_as_nanos = Duration::MILLISECOND.subsec_nanos();
+
+ // Rounds the nanosecond component of `duration` up to the nearest millisecond.
+ let nanos_as_millis = duration.subsec_nanos().wrapping_add(milli_as_nanos - 1) / milli_as_nanos;
+
+ // Saturates the second component of `duration` to `c_uint::MAX`.
+ let seconds_as_millis = min(
+ duration.as_secs().saturating_mul(MILLIS_PER_SEC),
+ u64::from(core::ffi::c_uint::MAX),
+ ) as core::ffi::c_uint;
+
+ seconds_as_millis.saturating_add(nanos_as_millis)
+}
+
+/// Sleeps safely even with waitqueue interruptions.
+///
+/// This function forwards the call to the C side `msleep` function. As a result,
+/// `duration` will be rounded up to the nearest millisecond if granularity less
+/// than a millisecond is provided. Any [`Duration`] that exceeds
+/// [`c_uint::MAX`][core::ffi::c_uint::MAX] in milliseconds is saturated.
+///
+/// # Examples
+///
+// Keep these in sync with `test_coarse_sleep_examples`.
+/// ```
+/// # use core::time::Duration;
+/// # use kernel::delay::coarse_sleep;
+/// coarse_sleep(Duration::ZERO); // Equivalent to `msleep(0)`.
+/// coarse_sleep(Duration::from_nanos(1)); // Equivalent to `msleep(1)`.
+///
+/// coarse_sleep(Duration::from_nanos(1_000_000)); // Equivalent to `msleep(1)`.
+/// coarse_sleep(Duration::from_nanos(1_000_001)); // Equivalent to `msleep(2)`.
+/// coarse_sleep(Duration::from_nanos(1_999_999)); // Equivalent to `msleep(2)`.
+///
+/// coarse_sleep(Duration::from_millis(1)); // Equivalent to `msleep(1)`.
+/// coarse_sleep(Duration::from_millis(2)); // Equivalent to `msleep(2)`.
+///
+/// coarse_sleep(Duration::from_secs(1)); // Equivalent to `msleep(1000)`.
+/// coarse_sleep(Duration::new(1, 1)); // Equivalent to `msleep(1001)`.
+/// coarse_sleep(Duration::new(1, 2)); // Equivalent to `msleep(1001)`.
+/// ```
+pub fn coarse_sleep(duration: Duration) {
+ // SAFETY: msleep is safe for all values of an `unsigned int`.
+ unsafe { bindings::msleep(coarse_sleep_conversion(duration)) }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::{coarse_sleep_conversion, MILLIS_PER_SEC};
+ use core::time::Duration;
+
+ #[test]
+ fn test_coarse_sleep_examples() {
+ // Keep these in sync with `coarse_sleep`'s `# Examples` section.
+
+ assert_eq!(coarse_sleep_conversion(Duration::ZERO), 0);
+ assert_eq!(coarse_sleep_conversion(Duration::from_nanos(1)), 1);
+
+ assert_eq!(coarse_sleep_conversion(Duration::from_nanos(1_000_000)), 1);
+ assert_eq!(coarse_sleep_conversion(Duration::from_nanos(1_000_001)), 2);
+ assert_eq!(coarse_sleep_conversion(Duration::from_nanos(1_999_999)), 2);
+
+ assert_eq!(coarse_sleep_conversion(Duration::from_millis(1)), 1);
+ assert_eq!(coarse_sleep_conversion(Duration::from_millis(2)), 2);
+
+ assert_eq!(coarse_sleep_conversion(Duration::from_secs(1)), 1000);
+ assert_eq!(coarse_sleep_conversion(Duration::new(1, 1)), 1001);
+ assert_eq!(coarse_sleep_conversion(Duration::new(1, 2)), 1001);
+ }
+
+ #[test]
+ fn test_coarse_sleep_saturation() {
+ assert!(
+ coarse_sleep_conversion(Duration::new(
+ core::ffi::c_uint::MAX as u64 / MILLIS_PER_SEC,
+ 0
+ )) < core::ffi::c_uint::MAX
+ );
+ assert_eq!(
+ coarse_sleep_conversion(Duration::new(
+ core::ffi::c_uint::MAX as u64 / MILLIS_PER_SEC,
+ 999_999_999
+ )),
+ core::ffi::c_uint::MAX
+ );
+
+ assert_eq!(
+ coarse_sleep_conversion(Duration::MAX),
+ core::ffi::c_uint::MAX
+ );
+ }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index dd9c39071391..9dc9124a5bd7 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -35,6 +35,7 @@
mod allocator;
mod build_assert;
pub mod cred;
+pub mod delay;
pub mod error;
pub mod file;
pub mod fs;
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 10/80] rust: kernel: add container_of macro
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (8 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 09/80] rust: kernel: backport the delay module from the rust branch Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 11/80] rust: kernel: add offset_of macro Ariel Miculas
` (67 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/kernel/lib.rs | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 9dc9124a5bd7..9f68f804074d 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -99,6 +99,37 @@ impl ThisModule {
}
}
+/// Produces a pointer to an object from a pointer to one of its fields.
+///
+/// # Safety
+///
+/// Callers must ensure that the pointer to the field is in fact a pointer to the specified field,
+/// as opposed to a pointer to another object of the same type. If this condition is not met,
+/// any dereference of the resulting pointer is UB.
+///
+/// # Example
+///
+/// ```
+/// # use kernel::container_of;
+/// struct Test {
+/// a: u64,
+/// b: u32,
+/// }
+///
+/// let test = Test { a: 10, b: 20 };
+/// let b_ptr = &test.b;
+/// let test_alias = container_of!(b_ptr, Test, b);
+/// assert!(core::ptr::eq(&test, test_alias));
+/// ```
+#[macro_export]
+macro_rules! container_of {
+ ($ptr:expr, $type:ty, $($f:tt)*) => {{
+ let ptr = $ptr as *const _ as *const u8;
+ let offset = $crate::offset_of!($type, $($f)*);
+ ptr.wrapping_offset(-offset) as *const $type
+ }}
+}
+
#[cfg(not(any(testlib, test)))]
#[panic_handler]
fn panic(info: &core::panic::PanicInfo<'_>) -> ! {
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 11/80] rust: kernel: add offset_of macro
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (9 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 10/80] rust: kernel: add container_of macro Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 12/80] drop: Add crate::pr_warn declaration Ariel Miculas
` (66 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/kernel/lib.rs | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 9f68f804074d..7dfebbe39430 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -97,6 +97,38 @@ impl ThisModule {
pub const unsafe fn from_ptr(ptr: *mut bindings::module) -> ThisModule {
ThisModule(ptr)
}
+
+}
+/// Calculates the offset of a field from the beginning of the struct it belongs to.
+///
+/// # Example
+///
+/// ```
+/// # use kernel::prelude::*;
+/// # use kernel::offset_of;
+/// struct Test {
+/// a: u64,
+/// b: u32,
+/// }
+///
+/// assert_eq!(offset_of!(Test, b), 8);
+/// ```
+#[macro_export]
+macro_rules! offset_of {
+ ($type:ty, $($f:tt)*) => {{
+ let tmp = core::mem::MaybeUninit::<$type>::uninit();
+ let outer = tmp.as_ptr();
+ // To avoid warnings when nesting `unsafe` blocks.
+ #[allow(unused_unsafe)]
+ // SAFETY: The pointer is valid and aligned, just not initialised; `addr_of` ensures that
+ // we don't actually read from `outer` (which would be UB) nor create an intermediate
+ // reference.
+ let inner = unsafe { core::ptr::addr_of!((*outer).$($f)*) } as *const u8;
+ // To avoid warnings when nesting `unsafe` blocks.
+ #[allow(unused_unsafe)]
+ // SAFETY: The two pointers are within the same allocation block.
+ unsafe { inner.offset_from(outer as *const u8) }
+ }}
}
/// Produces a pointer to an object from a pointer to one of its fields.
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 12/80] drop: Add crate::pr_warn declaration
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (10 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 11/80] rust: kernel: add offset_of macro Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 9:29 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 13/80] rust: kernel: rename from_kernel_errno to from_errno Ariel Miculas
` (65 subsequent siblings)
77 siblings, 1 reply; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
---
rust/kernel/fs.rs | 1 -
1 file changed, 1 deletion(-)
diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs
index 2a0267b3c0b6..1d920fde4022 100644
--- a/rust/kernel/fs.rs
+++ b/rust/kernel/fs.rs
@@ -17,7 +17,6 @@
use core::{
cell::UnsafeCell, marker::PhantomData, marker::PhantomPinned, ops::Deref, pin::Pin, ptr,
};
-
use macros::vtable;
pub mod param;
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 13/80] rust: kernel: rename from_kernel_errno to from_errno
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (11 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 12/80] drop: Add crate::pr_warn declaration Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 9:56 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 14/80] rust: kernel: Rename from_pointer to from_foreing and into_pointer to into_foreign Ariel Miculas
` (64 subsequent siblings)
77 siblings, 1 reply; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/kernel/file.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs
index 1b5934838833..44086d398731 100644
--- a/rust/kernel/file.rs
+++ b/rust/kernel/file.rs
@@ -214,7 +214,7 @@ pub fn new(flags: u32) -> Result<Self> {
// SAFETY: FFI call, there are no safety requirements on `flags`.
let fd = unsafe { bindings::get_unused_fd_flags(flags) };
if fd < 0 {
- return Err(Error::from_kernel_errno(fd));
+ return Err(Error::from_errno(fd));
}
Ok(Self { fd: fd as _ })
}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 14/80] rust: kernel: Rename from_pointer to from_foreing and into_pointer to into_foreign
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (12 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 13/80] rust: kernel: rename from_kernel_errno to from_errno Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 9:57 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 15/80] rust: kernel: add count_paren_items macro, needed by define_fs_params macro Ariel Miculas
` (63 subsequent siblings)
77 siblings, 1 reply; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/kernel/file.rs | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs
index 44086d398731..586c0b386b5b 100644
--- a/rust/kernel/file.rs
+++ b/rust/kernel/file.rs
@@ -330,7 +330,7 @@ impl<A: OpenAdapter<T::OpenData>, T: Operations> OperationsVtable<A, T> {
let fileref = unsafe { File::from_ptr(file) };
// SAFETY: `arg` was previously returned by `A::convert` and must
// be a valid non-null pointer.
- let ptr = T::open(unsafe { &*arg }, fileref)?.into_pointer();
+ let ptr = T::open(unsafe { &*arg }, fileref)?.into_foreign();
// SAFETY: The C contract guarantees that `private_data` is available
// for implementers of the file operations (no other C code accesses
// it), so we know that there are no concurrent threads/CPUs accessing
@@ -349,7 +349,7 @@ impl<A: OpenAdapter<T::OpenData>, T: Operations> OperationsVtable<A, T> {
from_kernel_result! {
let mut data = unsafe { UserSlicePtr::new(buf as *mut core::ffi::c_void, len).writer() };
// SAFETY: `private_data` was initialised by `open_callback` with a value returned by
- // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the
+ // `T::Data::into_foreign`. `T::Data::from_foreign` is only called by the
// `release` callback, which the C API guarantees that will be called only when all
// references to `file` have been released, so we know it can't be called while this
// function is running.
@@ -376,7 +376,7 @@ impl<A: OpenAdapter<T::OpenData>, T: Operations> OperationsVtable<A, T> {
let file = unsafe { (*iocb).ki_filp };
let offset = unsafe { (*iocb).ki_pos };
// SAFETY: `private_data` was initialised by `open_callback` with a value returned by
- // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the
+ // `T::Data::into_foreign`. `T::Data::from_foreign` is only called by the
// `release` callback, which the C API guarantees that will be called only when all
// references to `file` have been released, so we know it can't be called while this
// function is running.
@@ -397,7 +397,7 @@ impl<A: OpenAdapter<T::OpenData>, T: Operations> OperationsVtable<A, T> {
from_kernel_result! {
let mut data = unsafe { UserSlicePtr::new(buf as *mut core::ffi::c_void, len).reader() };
// SAFETY: `private_data` was initialised by `open_callback` with a value returned by
- // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the
+ // `T::Data::into_foreign`. `T::Data::from_foreign` is only called by the
// `release` callback, which the C API guarantees that will be called only when all
// references to `file` have been released, so we know it can't be called while this
// function is running.
@@ -424,7 +424,7 @@ impl<A: OpenAdapter<T::OpenData>, T: Operations> OperationsVtable<A, T> {
let file = unsafe { (*iocb).ki_filp };
let offset = unsafe { (*iocb).ki_pos };
// SAFETY: `private_data` was initialised by `open_callback` with a value returned by
- // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the
+ // `T::Data::into_foreign`. `T::Data::from_foreign` is only called by the
// `release` callback, which the C API guarantees that will be called only when all
// references to `file` have been released, so we know it can't be called while this
// function is running.
@@ -441,7 +441,7 @@ impl<A: OpenAdapter<T::OpenData>, T: Operations> OperationsVtable<A, T> {
file: *mut bindings::file,
) -> core::ffi::c_int {
let ptr = mem::replace(unsafe { &mut (*file).private_data }, ptr::null_mut());
- T::release(unsafe { T::Data::from_pointer(ptr as _) }, unsafe {
+ T::release(unsafe { T::Data::from_foreign(ptr as _) }, unsafe {
File::from_ptr(file)
});
0
@@ -460,7 +460,7 @@ impl<A: OpenAdapter<T::OpenData>, T: Operations> OperationsVtable<A, T> {
_ => return Err(EINVAL),
};
// SAFETY: `private_data` was initialised by `open_callback` with a value returned by
- // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the
+ // `T::Data::into_foreign`. `T::Data::from_foreign` is only called by the
// `release` callback, which the C API guarantees that will be called only when all
// references to `file` have been released, so we know it can't be called while this
// function is running.
@@ -477,7 +477,7 @@ impl<A: OpenAdapter<T::OpenData>, T: Operations> OperationsVtable<A, T> {
) -> core::ffi::c_long {
from_kernel_result! {
// SAFETY: `private_data` was initialised by `open_callback` with a value returned by
- // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the
+ // `T::Data::into_foreign`. `T::Data::from_foreign` is only called by the
// `release` callback, which the C API guarantees that will be called only when all
// references to `file` have been released, so we know it can't be called while this
// function is running.
@@ -495,7 +495,7 @@ impl<A: OpenAdapter<T::OpenData>, T: Operations> OperationsVtable<A, T> {
) -> core::ffi::c_long {
from_kernel_result! {
// SAFETY: `private_data` was initialised by `open_callback` with a value returned by
- // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the
+ // `T::Data::into_foreign`. `T::Data::from_foreign` is only called by the
// `release` callback, which the C API guarantees that will be called only when all
// references to `file` have been released, so we know it can't be called while this
// function is running.
@@ -512,7 +512,7 @@ impl<A: OpenAdapter<T::OpenData>, T: Operations> OperationsVtable<A, T> {
) -> core::ffi::c_int {
from_kernel_result! {
// SAFETY: `private_data` was initialised by `open_callback` with a value returned by
- // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the
+ // `T::Data::into_foreign`. `T::Data::from_foreign` is only called by the
// `release` callback, which the C API guarantees that will be called only when all
// references to `file` have been released, so we know it can't be called while this
// function is running.
@@ -540,7 +540,7 @@ impl<A: OpenAdapter<T::OpenData>, T: Operations> OperationsVtable<A, T> {
let end = end.try_into()?;
let datasync = datasync != 0;
// SAFETY: `private_data` was initialised by `open_callback` with a value returned by
- // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the
+ // `T::Data::into_foreign`. `T::Data::from_foreign` is only called by the
// `release` callback, which the C API guarantees that will be called only when all
// references to `file` have been released, so we know it can't be called while this
// function is running.
@@ -555,7 +555,7 @@ impl<A: OpenAdapter<T::OpenData>, T: Operations> OperationsVtable<A, T> {
wait: *mut bindings::poll_table_struct,
) -> bindings::__poll_t {
// SAFETY: `private_data` was initialised by `open_callback` with a value returned by
- // `T::Data::into_pointer`. `T::Data::from_pointer` is only called by the `release`
+ // `T::Data::into_foreign`. `T::Data::from_foreign` is only called by the `release`
// callback, which the C API guarantees that will be called only when all references to
// `file` have been released, so we know it can't be called while this function is running.
let f = unsafe { T::Data::borrow((*file).private_data) };
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 15/80] rust: kernel: add count_paren_items macro, needed by define_fs_params macro
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (13 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 14/80] rust: kernel: Rename from_pointer to from_foreing and into_pointer to into_foreign Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 16/80] rust: helpers: add missing rust helper 'alloc_pages' Ariel Miculas
` (62 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Changes:
* add count_paren_items in rust/kernel/driver.rs, used by
define_fs_params macro in rust/kernel/fs/param.rs
* add missing uring_cmd_iopoll in file_operations VTABLE
* fix accesses to the vm_flags field in vm_area_struct
* fix init's signature in rust/kernel/fs.rs
* import module_fs in samples/rust/rust_fs.rs
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/bindings/bindings_helper.h | 1 +
rust/kernel/driver.rs | 28 ++++++++++++++++++++++++++++
rust/kernel/file.rs | 1 +
rust/kernel/fs.rs | 2 +-
rust/kernel/lib.rs | 3 ++-
rust/kernel/mm.rs | 4 ++--
samples/rust/rust_fs.rs | 1 +
7 files changed, 36 insertions(+), 4 deletions(-)
create mode 100644 rust/kernel/driver.rs
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index d15a698439e1..6182174663ba 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -16,6 +16,7 @@
#include <linux/poll.h>
#include <linux/uio.h>
#include <linux/uaccess.h>
+#include <linux/delay.h>
/* `bindgen` gets confused at certain things. */
const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;
diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs
new file mode 100644
index 000000000000..f743fcf5451b
--- /dev/null
+++ b/rust/kernel/driver.rs
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Generic support for drivers of different buses (e.g., PCI, Platform, Amba, etc.).
+//!
+//! Each bus/subsystem is expected to implement [`DriverOps`], which allows drivers to register
+//! using the [`Registration`] class.
+
+/// Counts the number of parenthesis-delimited, comma-separated items.
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::count_paren_items;
+///
+/// assert_eq!(0, count_paren_items!());
+/// assert_eq!(1, count_paren_items!((A)));
+/// assert_eq!(1, count_paren_items!((A),));
+/// assert_eq!(2, count_paren_items!((A), (B)));
+/// assert_eq!(2, count_paren_items!((A), (B),));
+/// assert_eq!(3, count_paren_items!((A), (B), (C)));
+/// assert_eq!(3, count_paren_items!((A), (B), (C),));
+/// ```
+#[macro_export]
+macro_rules! count_paren_items {
+ (($($item:tt)*), $($remaining:tt)*) => { 1 + $crate::count_paren_items!($($remaining)*) };
+ (($($item:tt)*)) => { 1 };
+ () => { 0 };
+}
diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs
index 586c0b386b5b..b3bbbf153925 100644
--- a/rust/kernel/file.rs
+++ b/rust/kernel/file.rs
@@ -637,6 +637,7 @@ impl<A: OpenAdapter<T::OpenData>, T: Operations> OperationsVtable<A, T> {
None
},
uring_cmd: None,
+ uring_cmd_iopoll: None,
write_iter: if T::HAS_WRITE {
Some(Self::write_iter_callback)
} else {
diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs
index 1d920fde4022..ba98ae7caf00 100644
--- a/rust/kernel/fs.rs
+++ b/rust/kernel/fs.rs
@@ -1263,7 +1263,7 @@ pub struct Module<T: Type> {
}
impl<T: Type + Sync> crate::Module for Module<T> {
- fn init(_name: &'static CStr, module: &'static ThisModule) -> Result<Self> {
+ fn init(module: &'static ThisModule) -> Result<Self> {
let mut reg = Pin::from(Box::try_new(Registration::new())?);
reg.as_mut().register::<T>(module)?;
Ok(Self {
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 7dfebbe39430..08f67833afcf 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -21,6 +21,7 @@
#![feature(receiver_trait)]
#![feature(unsize)]
#![feature(const_mut_refs)]
+#![feature(duration_constants)]
// Ensure conditional compilation based on the kernel configuration works;
// otherwise we may silently break things like initcall handling.
@@ -36,6 +37,7 @@
mod build_assert;
pub mod cred;
pub mod delay;
+pub mod driver;
pub mod error;
pub mod file;
pub mod fs;
@@ -97,7 +99,6 @@ impl ThisModule {
pub const unsafe fn from_ptr(ptr: *mut bindings::module) -> ThisModule {
ThisModule(ptr)
}
-
}
/// Calculates the offset of a field from the beginning of the struct it belongs to.
///
diff --git a/rust/kernel/mm.rs b/rust/kernel/mm.rs
index 779359d0c5cf..d159c45157a9 100644
--- a/rust/kernel/mm.rs
+++ b/rust/kernel/mm.rs
@@ -38,7 +38,7 @@ pub(crate) unsafe fn from_ptr(vma: *mut bindings::vm_area_struct) -> Self {
/// The possible flags are a combination of the constants in [`flags`].
pub fn flags(&self) -> usize {
// SAFETY: `self.vma` is valid by the type invariants.
- unsafe { (*self.vma).vm_flags as _ }
+ unsafe { (*self.vma).__bindgen_anon_1.vm_flags as _ }
}
/// Sets the flags associated with the virtual memory area.
@@ -46,7 +46,7 @@ pub fn flags(&self) -> usize {
/// The possible flags are a combination of the constants in [`flags`].
pub fn set_flags(&mut self, flags: usize) {
// SAFETY: `self.vma` is valid by the type invariants.
- unsafe { (*self.vma).vm_flags = flags as _ };
+ unsafe { (*self.vma).__bindgen_anon_1.vm_flags = flags as _ };
}
/// Returns the start address of the virtual memory area.
diff --git a/samples/rust/rust_fs.rs b/samples/rust/rust_fs.rs
index 18fd4542863b..7527681ee024 100644
--- a/samples/rust/rust_fs.rs
+++ b/samples/rust/rust_fs.rs
@@ -2,6 +2,7 @@
//! Rust file system sample.
+use kernel::module_fs;
use kernel::prelude::*;
use kernel::{c_str, file, fs, io_buffer::IoBufferWriter};
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 16/80] rust: helpers: add missing rust helper 'alloc_pages'
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (14 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 15/80] rust: kernel: add count_paren_items macro, needed by define_fs_params macro Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 9:57 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 17/80] kernel: configs: add qemu-busybox-min.config Ariel Miculas
` (61 subsequent siblings)
77 siblings, 1 reply; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/helpers.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/rust/helpers.c b/rust/helpers.c
index efbe9d917a57..00a4c90c15e3 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -198,6 +198,12 @@ void *rust_helper_alloc_inode_sb(struct super_block *sb,
}
EXPORT_SYMBOL_GPL(rust_helper_alloc_inode_sb);
+struct page *rust_helper_alloc_pages(gfp_t gfp_mask, unsigned int order)
+{
+ return alloc_pages(gfp_mask, order);
+}
+EXPORT_SYMBOL_GPL(rust_helper_alloc_pages);
+
/*
* We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
* as the Rust `usize` type, so we can use it in contexts where Rust
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 17/80] kernel: configs: add qemu-busybox-min.config
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (15 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 16/80] rust: helpers: add missing rust helper 'alloc_pages' Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 9:39 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 18/80] rust: kernel: format the rust code Ariel Miculas
` (60 subsequent siblings)
77 siblings, 1 reply; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
arch/x86/configs/qemu-busybox-min.config | 11 +++++
kernel/configs/qemu-busybox-min.config | 56 ++++++++++++++++++++++++
2 files changed, 67 insertions(+)
create mode 100644 arch/x86/configs/qemu-busybox-min.config
create mode 100644 kernel/configs/qemu-busybox-min.config
diff --git a/arch/x86/configs/qemu-busybox-min.config b/arch/x86/configs/qemu-busybox-min.config
new file mode 100644
index 000000000000..9a2bf2549053
--- /dev/null
+++ b/arch/x86/configs/qemu-busybox-min.config
@@ -0,0 +1,11 @@
+CONFIG_64BIT=y
+CONFIG_ACPI=y
+
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+
+CONFIG_HYPERVISOR_GUEST=y
+CONFIG_PVH=y
+
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttyS0 nokaslr rdinit=/sbin/init"
diff --git a/kernel/configs/qemu-busybox-min.config b/kernel/configs/qemu-busybox-min.config
new file mode 100644
index 000000000000..51435e178199
--- /dev/null
+++ b/kernel/configs/qemu-busybox-min.config
@@ -0,0 +1,56 @@
+# This is a minimal configuration for running a busybox initramfs image with
+# networking support.
+#
+# The following command can be used create the configuration for a minimal
+# kernel image:
+#
+# make allnoconfig qemu-busybox-min.config
+#
+# The following command can be used to build the configuration for a default
+# kernel image:
+#
+# make defconfig qemu-busybox-min.config
+#
+# On x86, the following command can be used to run qemu:
+#
+# qemu-system-x86_64 -nographic -kernel vmlinux -initrd initrd.img -nic user,model=rtl8139,hostfwd=tcp::5555-:23
+#
+# On arm64, the following command can be used to run qemu:
+#
+# qemu-system-aarch64 -M virt -cpu cortex-a72 -nographic -kernel arch/arm64/boot/Image -initrd initrd.img -nic user,model=rtl8139,hostfwd=tcp::5555-:23
+
+CONFIG_SMP=y
+CONFIG_PRINTK=y
+CONFIG_PRINTK_TIME=y
+
+CONFIG_PCI=y
+
+# We use an initramfs for busybox with elf binaries in it.
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_RD_GZIP=y
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_SCRIPT=y
+
+# This is for /dev file system.
+CONFIG_DEVTMPFS=y
+
+# Core networking (packet is for dhcp).
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_INET=y
+
+# RTL8139 NIC support.
+CONFIG_NETDEVICES=y
+CONFIG_ETHERNET=y
+CONFIG_NET_VENDOR_REALTEK=y
+CONFIG_8139CP=y
+
+# To get GDB symbols and script.
+CONFIG_DEBUG_KERNEL=y
+CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
+CONFIG_GDB_SCRIPTS=y
+
+# For the power-down button (triggered by qemu's `system_powerdown` command).
+CONFIG_INPUT=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYBOARD=y
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 18/80] rust: kernel: format the rust code
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (16 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 17/80] kernel: configs: add qemu-busybox-min.config Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 9:21 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 19/80] samples: puzzlefs: add initial puzzlefs sample, copied from rust_fs.rs Ariel Miculas
` (59 subsequent siblings)
77 siblings, 1 reply; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/kernel/file.rs | 3 ++-
rust/kernel/fs/param.rs | 2 +-
rust/kernel/iov_iter.rs | 2 +-
rust/kernel/mm.rs | 2 +-
rust/kernel/pages.rs | 4 ++--
rust/kernel/user_ptr.rs | 2 +-
6 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs
index b3bbbf153925..c090a19da9b2 100644
--- a/rust/kernel/file.rs
+++ b/rust/kernel/file.rs
@@ -14,9 +14,10 @@
iov_iter::IovIter,
mm,
sync::CondVar,
+ types::ARef,
+ types::AlwaysRefCounted,
types::ForeignOwnable,
user_ptr::{UserSlicePtr, UserSlicePtrReader, UserSlicePtrWriter},
- types::ARef, types::AlwaysRefCounted,
};
use core::convert::{TryFrom, TryInto};
use core::{cell::UnsafeCell, marker, mem, ptr};
diff --git a/rust/kernel/fs/param.rs b/rust/kernel/fs/param.rs
index 1a31130c6d1e..947acf0d217c 100644
--- a/rust/kernel/fs/param.rs
+++ b/rust/kernel/fs/param.rs
@@ -4,7 +4,7 @@
//!
//! C headers: [`include/linux/fs_parser.h`](../../../../../include/linux/fs_parser.h)
-use crate::{bindings, file, fs, str::CStr, error::Result};
+use crate::{bindings, error::Result, file, fs, str::CStr};
use core::{marker::PhantomData, ptr};
/// The value of a file system parameter.
diff --git a/rust/kernel/iov_iter.rs b/rust/kernel/iov_iter.rs
index 01c7fa065dba..4602cbd4b49c 100644
--- a/rust/kernel/iov_iter.rs
+++ b/rust/kernel/iov_iter.rs
@@ -7,8 +7,8 @@
use crate::{
bindings,
error::code::*,
- io_buffer::{IoBufferReader, IoBufferWriter},
error::Result,
+ io_buffer::{IoBufferReader, IoBufferWriter},
};
/// Wraps the kernel's `struct iov_iter`.
diff --git a/rust/kernel/mm.rs b/rust/kernel/mm.rs
index d159c45157a9..72b504c949c1 100644
--- a/rust/kernel/mm.rs
+++ b/rust/kernel/mm.rs
@@ -4,7 +4,7 @@
//!
//! C header: [`include/linux/mm.h`](../../../../include/linux/mm.h)
-use crate::{bindings, pages, error::to_result, error::Result};
+use crate::{bindings, error::to_result, error::Result, pages};
/// Virtual memory.
pub mod virt {
diff --git a/rust/kernel/pages.rs b/rust/kernel/pages.rs
index c0c1f9fe03fc..c5630c29cec8 100644
--- a/rust/kernel/pages.rs
+++ b/rust/kernel/pages.rs
@@ -5,8 +5,8 @@
//! TODO: This module is a work in progress.
use crate::{
- bindings, error::code::*, io_buffer::IoBufferReader, user_ptr::UserSlicePtrReader, error::Result,
- PAGE_SIZE,
+ bindings, error::code::*, error::Result, io_buffer::IoBufferReader,
+ user_ptr::UserSlicePtrReader, PAGE_SIZE,
};
use core::{marker::PhantomData, ptr};
diff --git a/rust/kernel/user_ptr.rs b/rust/kernel/user_ptr.rs
index 9fdacc2826ef..ba00147697ef 100644
--- a/rust/kernel/user_ptr.rs
+++ b/rust/kernel/user_ptr.rs
@@ -7,8 +7,8 @@
use crate::{
bindings,
error::code::*,
- io_buffer::{IoBufferReader, IoBufferWriter},
error::Result,
+ io_buffer::{IoBufferReader, IoBufferWriter},
};
use alloc::vec::Vec;
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 19/80] samples: puzzlefs: add initial puzzlefs sample, copied from rust_fs.rs
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (17 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 18/80] rust: kernel: format the rust code Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 20/80] kernel: configs: enable rust samples in rust.config Ariel Miculas
` (58 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
samples/rust/Kconfig | 10 ++++
samples/rust/Makefile | 1 +
samples/rust/puzzlefs.rs | 105 +++++++++++++++++++++++++++++++++++++++
3 files changed, 116 insertions(+)
create mode 100644 samples/rust/puzzlefs.rs
diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index 2bd736f99189..05ca21fbba06 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -40,6 +40,16 @@ config SAMPLE_RUST_FS
If unsure, say N.
+config SAMPLE_PUZZLEFS
+ tristate "Puzzlefs file system"
+ help
+ This option builds the Rust puzzlefs file system sample.
+
+ To compile this as a module, choose M here:
+ the module will be called puzzlefs.
+
+ If unsure, say N.
+
config SAMPLE_RUST_HOSTPROGS
bool "Host programs"
help
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index e5941037e673..364a38dbf90b 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -3,5 +3,6 @@
obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o
obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o
obj-$(CONFIG_SAMPLE_RUST_FS) += rust_fs.o
+obj-$(CONFIG_SAMPLE_PUZZLEFS) += puzzlefs.o
subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs
diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
new file mode 100644
index 000000000000..51aa41533a35
--- /dev/null
+++ b/samples/rust/puzzlefs.rs
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust file system sample.
+
+use kernel::module_fs;
+use kernel::prelude::*;
+use kernel::{c_str, file, fs, io_buffer::IoBufferWriter};
+
+module_fs! {
+ type: RustFs,
+ name: "puzzlefs",
+ author: "Ariel Miculas",
+ license: "GPL",
+}
+
+struct RustFs;
+
+#[vtable]
+impl fs::Context<Self> for RustFs {
+ type Data = ();
+
+ kernel::define_fs_params! {(),
+ {flag, "flag", |_, v| { pr_info!("flag passed-in: {v}\n"); Ok(()) } },
+ {flag_no, "flagno", |_, v| { pr_info!("flagno passed-in: {v}\n"); Ok(()) } },
+ {bool, "bool", |_, v| { pr_info!("bool passed-in: {v}\n"); Ok(()) } },
+ {u32, "u32", |_, v| { pr_info!("u32 passed-in: {v}\n"); Ok(()) } },
+ {u32oct, "u32oct", |_, v| { pr_info!("u32oct passed-in: {v}\n"); Ok(()) } },
+ {u32hex, "u32hex", |_, v| { pr_info!("u32hex passed-in: {v}\n"); Ok(()) } },
+ {s32, "s32", |_, v| { pr_info!("s32 passed-in: {v}\n"); Ok(()) } },
+ {u64, "u64", |_, v| { pr_info!("u64 passed-in: {v}\n"); Ok(()) } },
+ {string, "string", |_, v| { pr_info!("string passed-in: {v}\n"); Ok(()) } },
+ {enum, "enum", [("first", 10), ("second", 20)], |_, v| {
+ pr_info!("enum passed-in: {v}\n"); Ok(()) }
+ },
+ }
+
+ fn try_new() -> Result {
+ Ok(())
+ }
+}
+
+impl fs::Type for RustFs {
+ type Context = Self;
+ type INodeData = &'static [u8];
+ const SUPER_TYPE: fs::Super = fs::Super::Independent;
+ const NAME: &'static CStr = c_str!("puzzlefs");
+ const FLAGS: i32 = fs::flags::USERNS_MOUNT;
+ const DCACHE_BASED: bool = true;
+
+ fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBlock<Self>> {
+ let sb = sb.init(
+ (),
+ &fs::SuperParams {
+ magic: 0x72757374,
+ ..fs::SuperParams::DEFAULT
+ },
+ )?;
+ let root = sb.try_new_populated_root_dentry(
+ &[],
+ kernel::fs_entries![
+ file("test1", 0o600, "abc\n".as_bytes(), FsFile),
+ file("test2", 0o600, "def\n".as_bytes(), FsFile),
+ char("test3", 0o600, [].as_slice(), (10, 125)),
+ sock("test4", 0o755, [].as_slice()),
+ fifo("test5", 0o755, [].as_slice()),
+ block("test6", 0o755, [].as_slice(), (1, 1)),
+ dir(
+ "dir1",
+ 0o755,
+ [].as_slice(),
+ [
+ file("test1", 0o600, "abc\n".as_bytes(), FsFile),
+ file("test2", 0o600, "def\n".as_bytes(), FsFile),
+ ]
+ ),
+ ],
+ )?;
+ let sb = sb.init_root(root)?;
+ Ok(sb)
+ }
+}
+
+struct FsFile;
+
+#[vtable]
+impl file::Operations for FsFile {
+ type OpenData = &'static [u8];
+
+ fn open(_context: &Self::OpenData, _file: &file::File) -> Result<Self::Data> {
+ Ok(())
+ }
+
+ fn read(
+ _data: (),
+ file: &file::File,
+ writer: &mut impl IoBufferWriter,
+ offset: u64,
+ ) -> Result<usize> {
+ file::read_from_slice(
+ file.inode::<RustFs>().ok_or(EINVAL)?.fs_data(),
+ writer,
+ offset,
+ )
+ }
+}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 20/80] kernel: configs: enable rust samples in rust.config
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (18 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 19/80] samples: puzzlefs: add initial puzzlefs sample, copied from rust_fs.rs Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 9:25 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 22/80] rust: proc-macro2: add SPDX License Identifiers Ariel Miculas
` (57 subsequent siblings)
77 siblings, 1 reply; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
kernel/configs/rust.config | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/kernel/configs/rust.config b/kernel/configs/rust.config
index 38a7c5362c9c..422b49798d92 100644
--- a/kernel/configs/rust.config
+++ b/kernel/configs/rust.config
@@ -1 +1,11 @@
CONFIG_RUST=y
+
+CONFIG_MODULES=y
+CONFIG_SAMPLES=y
+CONFIG_SAMPLES_RUST=y
+CONFIG_SAMPLE_RUST_MINIMAL=m
+CONFIG_SAMPLE_RUST_PRINT=m
+CONFIG_SAMPLE_RUST_FS=m
+CONFIG_SAMPLE_PUZZLEFS=m
+CONFIG_SAMPLE_RUST_HOSTPROGS=y
+
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 22/80] rust: proc-macro2: add SPDX License Identifiers
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (19 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 20/80] kernel: configs: enable rust samples in rust.config Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 23/80] rust: proc-macro2: remove `unicode_ident` dependency Ariel Miculas
` (56 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Miguel Ojeda
From: Miguel Ojeda <ojeda@kernel.org>
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
---
rust/proc-macro2/detection.rs | 2 ++
rust/proc-macro2/fallback.rs | 2 ++
rust/proc-macro2/lib.rs | 2 ++
rust/proc-macro2/marker.rs | 2 ++
rust/proc-macro2/parse.rs | 2 ++
rust/proc-macro2/rcvec.rs | 2 ++
rust/proc-macro2/wrapper.rs | 2 ++
7 files changed, 14 insertions(+)
diff --git a/rust/proc-macro2/detection.rs b/rust/proc-macro2/detection.rs
index beba7b237395..3de448cb2dde 100644
--- a/rust/proc-macro2/detection.rs
+++ b/rust/proc-macro2/detection.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use core::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Once;
diff --git a/rust/proc-macro2/fallback.rs b/rust/proc-macro2/fallback.rs
index fe4f248d3d21..f795c7618118 100644
--- a/rust/proc-macro2/fallback.rs
+++ b/rust/proc-macro2/fallback.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use crate::parse::{self, Cursor};
use crate::rcvec::{RcVec, RcVecBuilder, RcVecIntoIter, RcVecMut};
use crate::{Delimiter, Spacing, TokenTree};
diff --git a/rust/proc-macro2/lib.rs b/rust/proc-macro2/lib.rs
index 47b48df22272..d3b0454a9c0f 100644
--- a/rust/proc-macro2/lib.rs
+++ b/rust/proc-macro2/lib.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! [![github]](https://github.com/dtolnay/proc-macro2) [![crates-io]](https://crates.io/crates/proc-macro2) [![docs-rs]](crate)
//!
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
diff --git a/rust/proc-macro2/marker.rs b/rust/proc-macro2/marker.rs
index 59fd0963056f..eea17649a9b9 100644
--- a/rust/proc-macro2/marker.rs
+++ b/rust/proc-macro2/marker.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use core::marker::PhantomData;
use std::panic::{RefUnwindSafe, UnwindSafe};
use std::rc::Rc;
diff --git a/rust/proc-macro2/parse.rs b/rust/proc-macro2/parse.rs
index 04c4833694dc..d1721a207da4 100644
--- a/rust/proc-macro2/parse.rs
+++ b/rust/proc-macro2/parse.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use crate::fallback::{
is_ident_continue, is_ident_start, Group, LexError, Literal, Span, TokenStream,
TokenStreamBuilder,
diff --git a/rust/proc-macro2/rcvec.rs b/rust/proc-macro2/rcvec.rs
index 86ca7d8089ac..da2398c789d9 100644
--- a/rust/proc-macro2/rcvec.rs
+++ b/rust/proc-macro2/rcvec.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use core::mem;
use core::slice;
use std::rc::Rc;
diff --git a/rust/proc-macro2/wrapper.rs b/rust/proc-macro2/wrapper.rs
index 47d149473244..dacb2e59bd04 100644
--- a/rust/proc-macro2/wrapper.rs
+++ b/rust/proc-macro2/wrapper.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use crate::detection::inside_proc_macro;
use crate::{fallback, Delimiter, Punct, Spacing, TokenTree};
use core::fmt::{self, Debug, Display};
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 23/80] rust: proc-macro2: remove `unicode_ident` dependency
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (20 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 22/80] rust: proc-macro2: add SPDX License Identifiers Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 24/80] rust: quote: import crate Ariel Miculas
` (55 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Miguel Ojeda
From: Miguel Ojeda <ojeda@kernel.org>
The `proc-macro2` crate depends on the `unicode-ident` crate
to determine whether characters have the XID_Start or XID_Continue
properties according to Unicode Standard Annex #31.
However, we only need ASCII identifiers in the kernel, thus we can
simplify the check and remove completely that dependency.
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
---
rust/proc-macro2/fallback.rs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/rust/proc-macro2/fallback.rs b/rust/proc-macro2/fallback.rs
index f795c7618118..0d576c32162d 100644
--- a/rust/proc-macro2/fallback.rs
+++ b/rust/proc-macro2/fallback.rs
@@ -724,11 +724,11 @@ pub fn set_span(&mut self, span: Span) {
}
pub(crate) fn is_ident_start(c: char) -> bool {
- c == '_' || unicode_ident::is_xid_start(c)
+ c == '_' || c.is_ascii_alphabetic()
}
pub(crate) fn is_ident_continue(c: char) -> bool {
- unicode_ident::is_xid_continue(c)
+ c == '_' || c.is_ascii_alphanumeric()
}
fn validate_ident(string: &str, raw: bool) {
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 24/80] rust: quote: import crate
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (21 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 23/80] rust: proc-macro2: remove `unicode_ident` dependency Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 25/80] rust: quote: add SPDX License Identifiers Ariel Miculas
` (54 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Miguel Ojeda
From: Miguel Ojeda <ojeda@kernel.org>
This is a subset of the Rust `quote` crate,
version 1.0.21, licensed under "Apache-2.0 OR MIT", from:
https://github.com/dtolnay/quote/raw/1.0.21/src
The files are copied as-is, with no modifications whatsoever
(not even adding the SPDX identifiers).
For copyright details, please see:
https://github.com/dtolnay/quote/blob/1.0.21/README.md#license
https://github.com/dtolnay/quote/blob/1.0.21/LICENSE-APACHE
https://github.com/dtolnay/quote/blob/1.0.21/LICENSE-MIT
The next patch modifies these files as needed for use within
the kernel. This patch split allows reviewers to double-check
the import and to clearly see the differences introduced.
The following script may be used to verify the contents:
for path in $(cd rust/quote/ && find . -type f -name '*.rs'); do
curl --silent --show-error --location \
https://github.com/dtolnay/quote/raw/1.0.21/src/$path \
| diff --unified rust/quote/$path - && echo $path: OK
done
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
---
rust/quote/ext.rs | 110 +++
rust/quote/format.rs | 168 ++++
rust/quote/ident_fragment.rs | 86 ++
rust/quote/lib.rs | 1434 ++++++++++++++++++++++++++++++++++
rust/quote/runtime.rs | 438 +++++++++++
rust/quote/spanned.rs | 43 +
rust/quote/to_tokens.rs | 209 +++++
7 files changed, 2488 insertions(+)
create mode 100644 rust/quote/ext.rs
create mode 100644 rust/quote/format.rs
create mode 100644 rust/quote/ident_fragment.rs
create mode 100644 rust/quote/lib.rs
create mode 100644 rust/quote/runtime.rs
create mode 100644 rust/quote/spanned.rs
create mode 100644 rust/quote/to_tokens.rs
diff --git a/rust/quote/ext.rs b/rust/quote/ext.rs
new file mode 100644
index 000000000000..92c2315b182d
--- /dev/null
+++ b/rust/quote/ext.rs
@@ -0,0 +1,110 @@
+use super::ToTokens;
+use core::iter;
+use proc_macro2::{TokenStream, TokenTree};
+
+/// TokenStream extension trait with methods for appending tokens.
+///
+/// This trait is sealed and cannot be implemented outside of the `quote` crate.
+pub trait TokenStreamExt: private::Sealed {
+ /// For use by `ToTokens` implementations.
+ ///
+ /// Appends the token specified to this list of tokens.
+ fn append<U>(&mut self, token: U)
+ where
+ U: Into<TokenTree>;
+
+ /// For use by `ToTokens` implementations.
+ ///
+ /// ```
+ /// # use quote::{quote, TokenStreamExt, ToTokens};
+ /// # use proc_macro2::TokenStream;
+ /// #
+ /// struct X;
+ ///
+ /// impl ToTokens for X {
+ /// fn to_tokens(&self, tokens: &mut TokenStream) {
+ /// tokens.append_all(&[true, false]);
+ /// }
+ /// }
+ ///
+ /// let tokens = quote!(#X);
+ /// assert_eq!(tokens.to_string(), "true false");
+ /// ```
+ fn append_all<I>(&mut self, iter: I)
+ where
+ I: IntoIterator,
+ I::Item: ToTokens;
+
+ /// For use by `ToTokens` implementations.
+ ///
+ /// Appends all of the items in the iterator `I`, separated by the tokens
+ /// `U`.
+ fn append_separated<I, U>(&mut self, iter: I, op: U)
+ where
+ I: IntoIterator,
+ I::Item: ToTokens,
+ U: ToTokens;
+
+ /// For use by `ToTokens` implementations.
+ ///
+ /// Appends all tokens in the iterator `I`, appending `U` after each
+ /// element, including after the last element of the iterator.
+ fn append_terminated<I, U>(&mut self, iter: I, term: U)
+ where
+ I: IntoIterator,
+ I::Item: ToTokens,
+ U: ToTokens;
+}
+
+impl TokenStreamExt for TokenStream {
+ fn append<U>(&mut self, token: U)
+ where
+ U: Into<TokenTree>,
+ {
+ self.extend(iter::once(token.into()));
+ }
+
+ fn append_all<I>(&mut self, iter: I)
+ where
+ I: IntoIterator,
+ I::Item: ToTokens,
+ {
+ for token in iter {
+ token.to_tokens(self);
+ }
+ }
+
+ fn append_separated<I, U>(&mut self, iter: I, op: U)
+ where
+ I: IntoIterator,
+ I::Item: ToTokens,
+ U: ToTokens,
+ {
+ for (i, token) in iter.into_iter().enumerate() {
+ if i > 0 {
+ op.to_tokens(self);
+ }
+ token.to_tokens(self);
+ }
+ }
+
+ fn append_terminated<I, U>(&mut self, iter: I, term: U)
+ where
+ I: IntoIterator,
+ I::Item: ToTokens,
+ U: ToTokens,
+ {
+ for token in iter {
+ token.to_tokens(self);
+ term.to_tokens(self);
+ }
+ }
+}
+
+mod private {
+ use proc_macro2::TokenStream;
+
+ pub trait Sealed {}
+
+ impl Sealed for TokenStream {}
+}
diff --git a/rust/quote/format.rs b/rust/quote/format.rs
new file mode 100644
index 000000000000..3cddbd2819d6
--- /dev/null
+++ b/rust/quote/format.rs
@@ -0,0 +1,168 @@
+/// Formatting macro for constructing `Ident`s.
+///
+/// <br>
+///
+/// # Syntax
+///
+/// Syntax is copied from the [`format!`] macro, supporting both positional and
+/// named arguments.
+///
+/// Only a limited set of formatting traits are supported. The current mapping
+/// of format types to traits is:
+///
+/// * `{}` ⇒ [`IdentFragment`]
+/// * `{:o}` ⇒ [`Octal`](std::fmt::Octal)
+/// * `{:x}` ⇒ [`LowerHex`](std::fmt::LowerHex)
+/// * `{:X}` ⇒ [`UpperHex`](std::fmt::UpperHex)
+/// * `{:b}` ⇒ [`Binary`](std::fmt::Binary)
+///
+/// See [`std::fmt`] for more information.
+///
+/// <br>
+///
+/// # IdentFragment
+///
+/// Unlike `format!`, this macro uses the [`IdentFragment`] formatting trait by
+/// default. This trait is like `Display`, with a few differences:
+///
+/// * `IdentFragment` is only implemented for a limited set of types, such as
+/// unsigned integers and strings.
+/// * [`Ident`] arguments will have their `r#` prefixes stripped, if present.
+///
+/// [`IdentFragment`]: crate::IdentFragment
+/// [`Ident`]: proc_macro2::Ident
+///
+/// <br>
+///
+/// # Hygiene
+///
+/// The [`Span`] of the first `Ident` argument is used as the span of the final
+/// identifier, falling back to [`Span::call_site`] when no identifiers are
+/// provided.
+///
+/// ```
+/// # use quote::format_ident;
+/// # let ident = format_ident!("Ident");
+/// // If `ident` is an Ident, the span of `my_ident` will be inherited from it.
+/// let my_ident = format_ident!("My{}{}", ident, "IsCool");
+/// assert_eq!(my_ident, "MyIdentIsCool");
+/// ```
+///
+/// Alternatively, the span can be overridden by passing the `span` named
+/// argument.
+///
+/// ```
+/// # use quote::format_ident;
+/// # const IGNORE_TOKENS: &'static str = stringify! {
+/// let my_span = /* ... */;
+/// # };
+/// # let my_span = proc_macro2::Span::call_site();
+/// format_ident!("MyIdent", span = my_span);
+/// ```
+///
+/// [`Span`]: proc_macro2::Span
+/// [`Span::call_site`]: proc_macro2::Span::call_site
+///
+/// <p><br></p>
+///
+/// # Panics
+///
+/// This method will panic if the resulting formatted string is not a valid
+/// identifier.
+///
+/// <br>
+///
+/// # Examples
+///
+/// Composing raw and non-raw identifiers:
+/// ```
+/// # use quote::format_ident;
+/// let my_ident = format_ident!("My{}", "Ident");
+/// assert_eq!(my_ident, "MyIdent");
+///
+/// let raw = format_ident!("r#Raw");
+/// assert_eq!(raw, "r#Raw");
+///
+/// let my_ident_raw = format_ident!("{}Is{}", my_ident, raw);
+/// assert_eq!(my_ident_raw, "MyIdentIsRaw");
+/// ```
+///
+/// Integer formatting options:
+/// ```
+/// # use quote::format_ident;
+/// let num: u32 = 10;
+///
+/// let decimal = format_ident!("Id_{}", num);
+/// assert_eq!(decimal, "Id_10");
+///
+/// let octal = format_ident!("Id_{:o}", num);
+/// assert_eq!(octal, "Id_12");
+///
+/// let binary = format_ident!("Id_{:b}", num);
+/// assert_eq!(binary, "Id_1010");
+///
+/// let lower_hex = format_ident!("Id_{:x}", num);
+/// assert_eq!(lower_hex, "Id_a");
+///
+/// let upper_hex = format_ident!("Id_{:X}", num);
+/// assert_eq!(upper_hex, "Id_A");
+/// ```
+#[macro_export]
+macro_rules! format_ident {
+ ($fmt:expr) => {
+ $crate::format_ident_impl!([
+ $crate::__private::Option::None,
+ $fmt
+ ])
+ };
+
+ ($fmt:expr, $($rest:tt)*) => {
+ $crate::format_ident_impl!([
+ $crate::__private::Option::None,
+ $fmt
+ ] $($rest)*)
+ };
+}
+
+#[macro_export]
+#[doc(hidden)]
+macro_rules! format_ident_impl {
+ // Final state
+ ([$span:expr, $($fmt:tt)*]) => {
+ $crate::__private::mk_ident(
+ &$crate::__private::format!($($fmt)*),
+ $span,
+ )
+ };
+
+ // Span argument
+ ([$old:expr, $($fmt:tt)*] span = $span:expr) => {
+ $crate::format_ident_impl!([$old, $($fmt)*] span = $span,)
+ };
+ ([$old:expr, $($fmt:tt)*] span = $span:expr, $($rest:tt)*) => {
+ $crate::format_ident_impl!([
+ $crate::__private::Option::Some::<$crate::__private::Span>($span),
+ $($fmt)*
+ ] $($rest)*)
+ };
+
+ // Named argument
+ ([$span:expr, $($fmt:tt)*] $name:ident = $arg:expr) => {
+ $crate::format_ident_impl!([$span, $($fmt)*] $name = $arg,)
+ };
+ ([$span:expr, $($fmt:tt)*] $name:ident = $arg:expr, $($rest:tt)*) => {
+ match $crate::__private::IdentFragmentAdapter(&$arg) {
+ arg => $crate::format_ident_impl!([$span.or(arg.span()), $($fmt)*, $name = arg] $($rest)*),
+ }
+ };
+
+ // Positional argument
+ ([$span:expr, $($fmt:tt)*] $arg:expr) => {
+ $crate::format_ident_impl!([$span, $($fmt)*] $arg,)
+ };
+ ([$span:expr, $($fmt:tt)*] $arg:expr, $($rest:tt)*) => {
+ match $crate::__private::IdentFragmentAdapter(&$arg) {
+ arg => $crate::format_ident_impl!([$span.or(arg.span()), $($fmt)*, arg] $($rest)*),
+ }
+ };
+}
diff --git a/rust/quote/ident_fragment.rs b/rust/quote/ident_fragment.rs
new file mode 100644
index 000000000000..cf74024b4825
--- /dev/null
+++ b/rust/quote/ident_fragment.rs
@@ -0,0 +1,86 @@
+use core::fmt;
+use proc_macro2::{Ident, Span};
+use std::borrow::Cow;
+
+/// Specialized formatting trait used by `format_ident!`.
+///
+/// [`Ident`] arguments formatted using this trait will have their `r#` prefix
+/// stripped, if present.
+///
+/// See [`format_ident!`] for more information.
+pub trait IdentFragment {
+ /// Format this value as an identifier fragment.
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result;
+
+ /// Span associated with this `IdentFragment`.
+ ///
+ /// If non-`None`, may be inherited by formatted identifiers.
+ fn span(&self) -> Option<Span> {
+ None
+ }
+}
+
+impl<T: IdentFragment + ?Sized> IdentFragment for &T {
+ fn span(&self) -> Option<Span> {
+ <T as IdentFragment>::span(*self)
+ }
+
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ IdentFragment::fmt(*self, f)
+ }
+}
+
+impl<T: IdentFragment + ?Sized> IdentFragment for &mut T {
+ fn span(&self) -> Option<Span> {
+ <T as IdentFragment>::span(*self)
+ }
+
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ IdentFragment::fmt(*self, f)
+ }
+}
+
+impl IdentFragment for Ident {
+ fn span(&self) -> Option<Span> {
+ Some(self.span())
+ }
+
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let id = self.to_string();
+ if id.starts_with("r#") {
+ fmt::Display::fmt(&id[2..], f)
+ } else {
+ fmt::Display::fmt(&id[..], f)
+ }
+ }
+}
+
+impl<T> IdentFragment for Cow<'_, T>
+where
+ T: IdentFragment + ToOwned + ?Sized,
+{
+ fn span(&self) -> Option<Span> {
+ T::span(self)
+ }
+
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ T::fmt(self, f)
+ }
+}
+
+// Limited set of types which this is implemented for, as we want to avoid types
+// which will often include non-identifier characters in their `Display` impl.
+macro_rules! ident_fragment_display {
+ ($($T:ty),*) => {
+ $(
+ impl IdentFragment for $T {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(self, f)
+ }
+ }
+ )*
+ };
+}
+
+ident_fragment_display!(bool, str, String, char);
+ident_fragment_display!(u8, u16, u32, u64, u128, usize);
diff --git a/rust/quote/lib.rs b/rust/quote/lib.rs
new file mode 100644
index 000000000000..35594827f869
--- /dev/null
+++ b/rust/quote/lib.rs
@@ -0,0 +1,1434 @@
+//! [![github]](https://github.com/dtolnay/quote) [![crates-io]](https://crates.io/crates/quote) [![docs-rs]](https://docs.rs/quote)
+//!
+//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
+//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
+//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
+//!
+//! <br>
+//!
+//! This crate provides the [`quote!`] macro for turning Rust syntax tree data
+//! structures into tokens of source code.
+//!
+//! [`quote!`]: macro.quote.html
+//!
+//! Procedural macros in Rust receive a stream of tokens as input, execute
+//! arbitrary Rust code to determine how to manipulate those tokens, and produce
+//! a stream of tokens to hand back to the compiler to compile into the caller's
+//! crate. Quasi-quoting is a solution to one piece of that — producing
+//! tokens to return to the compiler.
+//!
+//! The idea of quasi-quoting is that we write *code* that we treat as *data*.
+//! Within the `quote!` macro, we can write what looks like code to our text
+//! editor or IDE. We get all the benefits of the editor's brace matching,
+//! syntax highlighting, indentation, and maybe autocompletion. But rather than
+//! compiling that as code into the current crate, we can treat it as data, pass
+//! it around, mutate it, and eventually hand it back to the compiler as tokens
+//! to compile into the macro caller's crate.
+//!
+//! This crate is motivated by the procedural macro use case, but is a
+//! general-purpose Rust quasi-quoting library and is not specific to procedural
+//! macros.
+//!
+//! ```toml
+//! [dependencies]
+//! quote = "1.0"
+//! ```
+//!
+//! <br>
+//!
+//! # Example
+//!
+//! The following quasi-quoted block of code is something you might find in [a]
+//! procedural macro having to do with data structure serialization. The `#var`
+//! syntax performs interpolation of runtime variables into the quoted tokens.
+//! Check out the documentation of the [`quote!`] macro for more detail about
+//! the syntax. See also the [`quote_spanned!`] macro which is important for
+//! implementing hygienic procedural macros.
+//!
+//! [a]: https://serde.rs/
+//! [`quote_spanned!`]: macro.quote_spanned.html
+//!
+//! ```
+//! # use quote::quote;
+//! #
+//! # let generics = "";
+//! # let where_clause = "";
+//! # let field_ty = "";
+//! # let item_ty = "";
+//! # let path = "";
+//! # let value = "";
+//! #
+//! let tokens = quote! {
+//! struct SerializeWith #generics #where_clause {
+//! value: &'a #field_ty,
+//! phantom: core::marker::PhantomData<#item_ty>,
+//! }
+//!
+//! impl #generics serde::Serialize for SerializeWith #generics #where_clause {
+//! fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+//! where
+//! S: serde::Serializer,
+//! {
+//! #path(self.value, serializer)
+//! }
+//! }
+//!
+//! SerializeWith {
+//! value: #value,
+//! phantom: core::marker::PhantomData::<#item_ty>,
+//! }
+//! };
+//! ```
+
+// Quote types in rustdoc of other crates get linked to here.
+#![doc(html_root_url = "https://docs.rs/quote/1.0.21")]
+#![allow(
+ clippy::doc_markdown,
+ clippy::missing_errors_doc,
+ clippy::missing_panics_doc,
+ clippy::module_name_repetitions,
+ // false positive https://github.com/rust-lang/rust-clippy/issues/6983
+ clippy::wrong_self_convention,
+)]
+
+#[cfg(all(
+ not(all(target_arch = "wasm32", target_os = "unknown")),
+ feature = "proc-macro"
+))]
+extern crate proc_macro;
+
+mod ext;
+mod format;
+mod ident_fragment;
+mod to_tokens;
+
+// Not public API.
+#[doc(hidden)]
+#[path = "runtime.rs"]
+pub mod __private;
+
+pub use crate::ext::TokenStreamExt;
+pub use crate::ident_fragment::IdentFragment;
+pub use crate::to_tokens::ToTokens;
+
+// Not public API.
+#[doc(hidden)]
+pub mod spanned;
+
+/// The whole point.
+///
+/// Performs variable interpolation against the input and produces it as
+/// [`proc_macro2::TokenStream`].
+///
+/// Note: for returning tokens to the compiler in a procedural macro, use
+/// `.into()` on the result to convert to [`proc_macro::TokenStream`].
+///
+/// [`TokenStream`]: https://docs.rs/proc-macro2/1.0/proc_macro2/struct.TokenStream.html
+///
+/// <br>
+///
+/// # Interpolation
+///
+/// Variable interpolation is done with `#var` (similar to `$var` in
+/// `macro_rules!` macros). This grabs the `var` variable that is currently in
+/// scope and inserts it in that location in the output tokens. Any type
+/// implementing the [`ToTokens`] trait can be interpolated. This includes most
+/// Rust primitive types as well as most of the syntax tree types from the [Syn]
+/// crate.
+///
+/// [`ToTokens`]: trait.ToTokens.html
+/// [Syn]: https://github.com/dtolnay/syn
+///
+/// Repetition is done using `#(...)*` or `#(...),*` again similar to
+/// `macro_rules!`. This iterates through the elements of any variable
+/// interpolated within the repetition and inserts a copy of the repetition body
+/// for each one. The variables in an interpolation may be a `Vec`, slice,
+/// `BTreeSet`, or any `Iterator`.
+///
+/// - `#(#var)*` — no separators
+/// - `#(#var),*` — the character before the asterisk is used as a separator
+/// - `#( struct #var; )*` — the repetition can contain other tokens
+/// - `#( #k => println!("{}", #v), )*` — even multiple interpolations
+///
+/// <br>
+///
+/// # Hygiene
+///
+/// Any interpolated tokens preserve the `Span` information provided by their
+/// `ToTokens` implementation. Tokens that originate within the `quote!`
+/// invocation are spanned with [`Span::call_site()`].
+///
+/// [`Span::call_site()`]: https://docs.rs/proc-macro2/1.0/proc_macro2/struct.Span.html#method.call_site
+///
+/// A different span can be provided through the [`quote_spanned!`] macro.
+///
+/// [`quote_spanned!`]: macro.quote_spanned.html
+///
+/// <br>
+///
+/// # Return type
+///
+/// The macro evaluates to an expression of type `proc_macro2::TokenStream`.
+/// Meanwhile Rust procedural macros are expected to return the type
+/// `proc_macro::TokenStream`.
+///
+/// The difference between the two types is that `proc_macro` types are entirely
+/// specific to procedural macros and cannot ever exist in code outside of a
+/// procedural macro, while `proc_macro2` types may exist anywhere including
+/// tests and non-macro code like main.rs and build.rs. This is why even the
+/// procedural macro ecosystem is largely built around `proc_macro2`, because
+/// that ensures the libraries are unit testable and accessible in non-macro
+/// contexts.
+///
+/// There is a [`From`]-conversion in both directions so returning the output of
+/// `quote!` from a procedural macro usually looks like `tokens.into()` or
+/// `proc_macro::TokenStream::from(tokens)`.
+///
+/// [`From`]: https://doc.rust-lang.org/std/convert/trait.From.html
+///
+/// <br>
+///
+/// # Examples
+///
+/// ### Procedural macro
+///
+/// The structure of a basic procedural macro is as follows. Refer to the [Syn]
+/// crate for further useful guidance on using `quote!` as part of a procedural
+/// macro.
+///
+/// [Syn]: https://github.com/dtolnay/syn
+///
+/// ```
+/// # #[cfg(any())]
+/// extern crate proc_macro;
+/// # extern crate proc_macro2;
+///
+/// # #[cfg(any())]
+/// use proc_macro::TokenStream;
+/// # use proc_macro2::TokenStream;
+/// use quote::quote;
+///
+/// # const IGNORE_TOKENS: &'static str = stringify! {
+/// #[proc_macro_derive(HeapSize)]
+/// # };
+/// pub fn derive_heap_size(input: TokenStream) -> TokenStream {
+/// // Parse the input and figure out what implementation to generate...
+/// # const IGNORE_TOKENS: &'static str = stringify! {
+/// let name = /* ... */;
+/// let expr = /* ... */;
+/// # };
+/// #
+/// # let name = 0;
+/// # let expr = 0;
+///
+/// let expanded = quote! {
+/// // The generated impl.
+/// impl heapsize::HeapSize for #name {
+/// fn heap_size_of_children(&self) -> usize {
+/// #expr
+/// }
+/// }
+/// };
+///
+/// // Hand the output tokens back to the compiler.
+/// TokenStream::from(expanded)
+/// }
+/// ```
+///
+/// <p><br></p>
+///
+/// ### Combining quoted fragments
+///
+/// Usually you don't end up constructing an entire final `TokenStream` in one
+/// piece. Different parts may come from different helper functions. The tokens
+/// produced by `quote!` themselves implement `ToTokens` and so can be
+/// interpolated into later `quote!` invocations to build up a final result.
+///
+/// ```
+/// # use quote::quote;
+/// #
+/// let type_definition = quote! {...};
+/// let methods = quote! {...};
+///
+/// let tokens = quote! {
+/// #type_definition
+/// #methods
+/// };
+/// ```
+///
+/// <p><br></p>
+///
+/// ### Constructing identifiers
+///
+/// Suppose we have an identifier `ident` which came from somewhere in a macro
+/// input and we need to modify it in some way for the macro output. Let's
+/// consider prepending the identifier with an underscore.
+///
+/// Simply interpolating the identifier next to an underscore will not have the
+/// behavior of concatenating them. The underscore and the identifier will
+/// continue to be two separate tokens as if you had written `_ x`.
+///
+/// ```
+/// # use proc_macro2::{self as syn, Span};
+/// # use quote::quote;
+/// #
+/// # let ident = syn::Ident::new("i", Span::call_site());
+/// #
+/// // incorrect
+/// quote! {
+/// let mut _#ident = 0;
+/// }
+/// # ;
+/// ```
+///
+/// The solution is to build a new identifier token with the correct value. As
+/// this is such a common case, the [`format_ident!`] macro provides a
+/// convenient utility for doing so correctly.
+///
+/// ```
+/// # use proc_macro2::{Ident, Span};
+/// # use quote::{format_ident, quote};
+/// #
+/// # let ident = Ident::new("i", Span::call_site());
+/// #
+/// let varname = format_ident!("_{}", ident);
+/// quote! {
+/// let mut #varname = 0;
+/// }
+/// # ;
+/// ```
+///
+/// Alternatively, the APIs provided by Syn and proc-macro2 can be used to
+/// directly build the identifier. This is roughly equivalent to the above, but
+/// will not handle `ident` being a raw identifier.
+///
+/// ```
+/// # use proc_macro2::{self as syn, Span};
+/// # use quote::quote;
+/// #
+/// # let ident = syn::Ident::new("i", Span::call_site());
+/// #
+/// let concatenated = format!("_{}", ident);
+/// let varname = syn::Ident::new(&concatenated, ident.span());
+/// quote! {
+/// let mut #varname = 0;
+/// }
+/// # ;
+/// ```
+///
+/// <p><br></p>
+///
+/// ### Making method calls
+///
+/// Let's say our macro requires some type specified in the macro input to have
+/// a constructor called `new`. We have the type in a variable called
+/// `field_type` of type `syn::Type` and want to invoke the constructor.
+///
+/// ```
+/// # use quote::quote;
+/// #
+/// # let field_type = quote!(...);
+/// #
+/// // incorrect
+/// quote! {
+/// let value = #field_type::new();
+/// }
+/// # ;
+/// ```
+///
+/// This works only sometimes. If `field_type` is `String`, the expanded code
+/// contains `String::new()` which is fine. But if `field_type` is something
+/// like `Vec<i32>` then the expanded code is `Vec<i32>::new()` which is invalid
+/// syntax. Ordinarily in handwritten Rust we would write `Vec::<i32>::new()`
+/// but for macros often the following is more convenient.
+///
+/// ```
+/// # use quote::quote;
+/// #
+/// # let field_type = quote!(...);
+/// #
+/// quote! {
+/// let value = <#field_type>::new();
+/// }
+/// # ;
+/// ```
+///
+/// This expands to `<Vec<i32>>::new()` which behaves correctly.
+///
+/// A similar pattern is appropriate for trait methods.
+///
+/// ```
+/// # use quote::quote;
+/// #
+/// # let field_type = quote!(...);
+/// #
+/// quote! {
+/// let value = <#field_type as core::default::Default>::default();
+/// }
+/// # ;
+/// ```
+///
+/// <p><br></p>
+///
+/// ### Interpolating text inside of doc comments
+///
+/// Neither doc comments nor string literals get interpolation behavior in
+/// quote:
+///
+/// ```compile_fail
+/// quote! {
+/// /// try to interpolate: #ident
+/// ///
+/// /// ...
+/// }
+/// ```
+///
+/// ```compile_fail
+/// quote! {
+/// #[doc = "try to interpolate: #ident"]
+/// }
+/// ```
+///
+/// Instead the best way to build doc comments that involve variables is by
+/// formatting the doc string literal outside of quote.
+///
+/// ```rust
+/// # use proc_macro2::{Ident, Span};
+/// # use quote::quote;
+/// #
+/// # const IGNORE: &str = stringify! {
+/// let msg = format!(...);
+/// # };
+/// #
+/// # let ident = Ident::new("var", Span::call_site());
+/// # let msg = format!("try to interpolate: {}", ident);
+/// quote! {
+/// #[doc = #msg]
+/// ///
+/// /// ...
+/// }
+/// # ;
+/// ```
+///
+/// <p><br></p>
+///
+/// ### Indexing into a tuple struct
+///
+/// When interpolating indices of a tuple or tuple struct, we need them not to
+/// appears suffixed as integer literals by interpolating them as [`syn::Index`]
+/// instead.
+///
+/// [`syn::Index`]: https://docs.rs/syn/1.0/syn/struct.Index.html
+///
+/// ```compile_fail
+/// let i = 0usize..self.fields.len();
+///
+/// // expands to 0 + self.0usize.heap_size() + self.1usize.heap_size() + ...
+/// // which is not valid syntax
+/// quote! {
+/// 0 #( + self.#i.heap_size() )*
+/// }
+/// ```
+///
+/// ```
+/// # use proc_macro2::{Ident, TokenStream};
+/// # use quote::quote;
+/// #
+/// # mod syn {
+/// # use proc_macro2::{Literal, TokenStream};
+/// # use quote::{ToTokens, TokenStreamExt};
+/// #
+/// # pub struct Index(usize);
+/// #
+/// # impl From<usize> for Index {
+/// # fn from(i: usize) -> Self {
+/// # Index(i)
+/// # }
+/// # }
+/// #
+/// # impl ToTokens for Index {
+/// # fn to_tokens(&self, tokens: &mut TokenStream) {
+/// # tokens.append(Literal::usize_unsuffixed(self.0));
+/// # }
+/// # }
+/// # }
+/// #
+/// # struct Struct {
+/// # fields: Vec<Ident>,
+/// # }
+/// #
+/// # impl Struct {
+/// # fn example(&self) -> TokenStream {
+/// let i = (0..self.fields.len()).map(syn::Index::from);
+///
+/// // expands to 0 + self.0.heap_size() + self.1.heap_size() + ...
+/// quote! {
+/// 0 #( + self.#i.heap_size() )*
+/// }
+/// # }
+/// # }
+/// ```
+#[cfg(doc)]
+#[macro_export]
+macro_rules! quote {
+ ($($tt:tt)*) => {
+ ...
+ };
+}
+
+#[cfg(not(doc))]
+#[macro_export]
+macro_rules! quote {
+ () => {
+ $crate::__private::TokenStream::new()
+ };
+
+ // Special case rule for a single tt, for performance.
+ ($tt:tt) => {{
+ let mut _s = $crate::__private::TokenStream::new();
+ $crate::quote_token!{$tt _s}
+ _s
+ }};
+
+ // Special case rules for two tts, for performance.
+ (# $var:ident) => {{
+ let mut _s = $crate::__private::TokenStream::new();
+ $crate::ToTokens::to_tokens(&$var, &mut _s);
+ _s
+ }};
+ ($tt1:tt $tt2:tt) => {{
+ let mut _s = $crate::__private::TokenStream::new();
+ $crate::quote_token!{$tt1 _s}
+ $crate::quote_token!{$tt2 _s}
+ _s
+ }};
+
+ // Rule for any other number of tokens.
+ ($($tt:tt)*) => {{
+ let mut _s = $crate::__private::TokenStream::new();
+ $crate::quote_each_token!{_s $($tt)*}
+ _s
+ }};
+}
+
+/// Same as `quote!`, but applies a given span to all tokens originating within
+/// the macro invocation.
+///
+/// <br>
+///
+/// # Syntax
+///
+/// A span expression of type [`Span`], followed by `=>`, followed by the tokens
+/// to quote. The span expression should be brief — use a variable for
+/// anything more than a few characters. There should be no space before the
+/// `=>` token.
+///
+/// [`Span`]: https://docs.rs/proc-macro2/1.0/proc_macro2/struct.Span.html
+///
+/// ```
+/// # use proc_macro2::Span;
+/// # use quote::quote_spanned;
+/// #
+/// # const IGNORE_TOKENS: &'static str = stringify! {
+/// let span = /* ... */;
+/// # };
+/// # let span = Span::call_site();
+/// # let init = 0;
+///
+/// // On one line, use parentheses.
+/// let tokens = quote_spanned!(span=> Box::into_raw(Box::new(#init)));
+///
+/// // On multiple lines, place the span at the top and use braces.
+/// let tokens = quote_spanned! {span=>
+/// Box::into_raw(Box::new(#init))
+/// };
+/// ```
+///
+/// The lack of space before the `=>` should look jarring to Rust programmers
+/// and this is intentional. The formatting is designed to be visibly
+/// off-balance and draw the eye a particular way, due to the span expression
+/// being evaluated in the context of the procedural macro and the remaining
+/// tokens being evaluated in the generated code.
+///
+/// <br>
+///
+/// # Hygiene
+///
+/// Any interpolated tokens preserve the `Span` information provided by their
+/// `ToTokens` implementation. Tokens that originate within the `quote_spanned!`
+/// invocation are spanned with the given span argument.
+///
+/// <br>
+///
+/// # Example
+///
+/// The following procedural macro code uses `quote_spanned!` to assert that a
+/// particular Rust type implements the [`Sync`] trait so that references can be
+/// safely shared between threads.
+///
+/// [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
+///
+/// ```
+/// # use quote::{quote_spanned, TokenStreamExt, ToTokens};
+/// # use proc_macro2::{Span, TokenStream};
+/// #
+/// # struct Type;
+/// #
+/// # impl Type {
+/// # fn span(&self) -> Span {
+/// # Span::call_site()
+/// # }
+/// # }
+/// #
+/// # impl ToTokens for Type {
+/// # fn to_tokens(&self, _tokens: &mut TokenStream) {}
+/// # }
+/// #
+/// # let ty = Type;
+/// # let call_site = Span::call_site();
+/// #
+/// let ty_span = ty.span();
+/// let assert_sync = quote_spanned! {ty_span=>
+/// struct _AssertSync where #ty: Sync;
+/// };
+/// ```
+///
+/// If the assertion fails, the user will see an error like the following. The
+/// input span of their type is highlighted in the error.
+///
+/// ```text
+/// error[E0277]: the trait bound `*const (): std::marker::Sync` is not satisfied
+/// --> src/main.rs:10:21
+/// |
+/// 10 | static ref PTR: *const () = &();
+/// | ^^^^^^^^^ `*const ()` cannot be shared between threads safely
+/// ```
+///
+/// In this example it is important for the where-clause to be spanned with the
+/// line/column information of the user's input type so that error messages are
+/// placed appropriately by the compiler.
+#[cfg(doc)]
+#[macro_export]
+macro_rules! quote_spanned {
+ ($span:expr=> $($tt:tt)*) => {
+ ...
+ };
+}
+
+#[cfg(not(doc))]
+#[macro_export]
+macro_rules! quote_spanned {
+ ($span:expr=>) => {{
+ let _: $crate::__private::Span = $span;
+ $crate::__private::TokenStream::new()
+ }};
+
+ // Special case rule for a single tt, for performance.
+ ($span:expr=> $tt:tt) => {{
+ let mut _s = $crate::__private::TokenStream::new();
+ let _span: $crate::__private::Span = $span;
+ $crate::quote_token_spanned!{$tt _s _span}
+ _s
+ }};
+
+ // Special case rules for two tts, for performance.
+ ($span:expr=> # $var:ident) => {{
+ let mut _s = $crate::__private::TokenStream::new();
+ let _: $crate::__private::Span = $span;
+ $crate::ToTokens::to_tokens(&$var, &mut _s);
+ _s
+ }};
+ ($span:expr=> $tt1:tt $tt2:tt) => {{
+ let mut _s = $crate::__private::TokenStream::new();
+ let _span: $crate::__private::Span = $span;
+ $crate::quote_token_spanned!{$tt1 _s _span}
+ $crate::quote_token_spanned!{$tt2 _s _span}
+ _s
+ }};
+
+ // Rule for any other number of tokens.
+ ($span:expr=> $($tt:tt)*) => {{
+ let mut _s = $crate::__private::TokenStream::new();
+ let _span: $crate::__private::Span = $span;
+ $crate::quote_each_token_spanned!{_s _span $($tt)*}
+ _s
+ }};
+}
+
+// Extract the names of all #metavariables and pass them to the $call macro.
+//
+// in: pounded_var_names!(then!(...) a #b c #( #d )* #e)
+// out: then!(... b);
+// then!(... d);
+// then!(... e);
+#[macro_export]
+#[doc(hidden)]
+macro_rules! pounded_var_names {
+ ($call:ident! $extra:tt $($tts:tt)*) => {
+ $crate::pounded_var_names_with_context!{$call! $extra
+ (@ $($tts)*)
+ ($($tts)* @)
+ }
+ };
+}
+
+#[macro_export]
+#[doc(hidden)]
+macro_rules! pounded_var_names_with_context {
+ ($call:ident! $extra:tt ($($b1:tt)*) ($($curr:tt)*)) => {
+ $(
+ $crate::pounded_var_with_context!{$call! $extra $b1 $curr}
+ )*
+ };
+}
+
+#[macro_export]
+#[doc(hidden)]
+macro_rules! pounded_var_with_context {
+ ($call:ident! $extra:tt $b1:tt ( $($inner:tt)* )) => {
+ $crate::pounded_var_names!{$call! $extra $($inner)*}
+ };
+
+ ($call:ident! $extra:tt $b1:tt [ $($inner:tt)* ]) => {
+ $crate::pounded_var_names!{$call! $extra $($inner)*}
+ };
+
+ ($call:ident! $extra:tt $b1:tt { $($inner:tt)* }) => {
+ $crate::pounded_var_names!{$call! $extra $($inner)*}
+ };
+
+ ($call:ident!($($extra:tt)*) # $var:ident) => {
+ $crate::$call!($($extra)* $var);
+ };
+
+ ($call:ident! $extra:tt $b1:tt $curr:tt) => {};
+}
+
+#[macro_export]
+#[doc(hidden)]
+macro_rules! quote_bind_into_iter {
+ ($has_iter:ident $var:ident) => {
+ // `mut` may be unused if $var occurs multiple times in the list.
+ #[allow(unused_mut)]
+ let (mut $var, i) = $var.quote_into_iter();
+ let $has_iter = $has_iter | i;
+ };
+}
+
+#[macro_export]
+#[doc(hidden)]
+macro_rules! quote_bind_next_or_break {
+ ($var:ident) => {
+ let $var = match $var.next() {
+ Some(_x) => $crate::__private::RepInterp(_x),
+ None => break,
+ };
+ };
+}
+
+// The obvious way to write this macro is as a tt muncher. This implementation
+// does something more complex for two reasons.
+//
+// - With a tt muncher it's easy to hit Rust's built-in recursion_limit, which
+// this implementation avoids because it isn't tail recursive.
+//
+// - Compile times for a tt muncher are quadratic relative to the length of
+// the input. This implementation is linear, so it will be faster
+// (potentially much faster) for big inputs. However, the constant factors
+// of this implementation are higher than that of a tt muncher, so it is
+// somewhat slower than a tt muncher if there are many invocations with
+// short inputs.
+//
+// An invocation like this:
+//
+// quote_each_token!(_s a b c d e f g h i j);
+//
+// expands to this:
+//
+// quote_tokens_with_context!(_s
+// (@ @ @ @ @ @ a b c d e f g h i j)
+// (@ @ @ @ @ a b c d e f g h i j @)
+// (@ @ @ @ a b c d e f g h i j @ @)
+// (@ @ @ (a) (b) (c) (d) (e) (f) (g) (h) (i) (j) @ @ @)
+// (@ @ a b c d e f g h i j @ @ @ @)
+// (@ a b c d e f g h i j @ @ @ @ @)
+// (a b c d e f g h i j @ @ @ @ @ @)
+// );
+//
+// which gets transposed and expanded to this:
+//
+// quote_token_with_context!(_s @ @ @ @ @ @ a);
+// quote_token_with_context!(_s @ @ @ @ @ a b);
+// quote_token_with_context!(_s @ @ @ @ a b c);
+// quote_token_with_context!(_s @ @ @ (a) b c d);
+// quote_token_with_context!(_s @ @ a (b) c d e);
+// quote_token_with_context!(_s @ a b (c) d e f);
+// quote_token_with_context!(_s a b c (d) e f g);
+// quote_token_with_context!(_s b c d (e) f g h);
+// quote_token_with_context!(_s c d e (f) g h i);
+// quote_token_with_context!(_s d e f (g) h i j);
+// quote_token_with_context!(_s e f g (h) i j @);
+// quote_token_with_context!(_s f g h (i) j @ @);
+// quote_token_with_context!(_s g h i (j) @ @ @);
+// quote_token_with_context!(_s h i j @ @ @ @);
+// quote_token_with_context!(_s i j @ @ @ @ @);
+// quote_token_with_context!(_s j @ @ @ @ @ @);
+//
+// Without having used muncher-style recursion, we get one invocation of
+// quote_token_with_context for each original tt, with three tts of context on
+// either side. This is enough for the longest possible interpolation form (a
+// repetition with separator, as in `# (#var) , *`) to be fully represented with
+// the first or last tt in the middle.
+//
+// The middle tt (surrounded by parentheses) is the tt being processed.
+//
+// - When it is a `#`, quote_token_with_context can do an interpolation. The
+// interpolation kind will depend on the three subsequent tts.
+//
+// - When it is within a later part of an interpolation, it can be ignored
+// because the interpolation has already been done.
+//
+// - When it is not part of an interpolation it can be pushed as a single
+// token into the output.
+//
+// - When the middle token is an unparenthesized `@`, that call is one of the
+// first 3 or last 3 calls of quote_token_with_context and does not
+// correspond to one of the original input tokens, so turns into nothing.
+#[macro_export]
+#[doc(hidden)]
+macro_rules! quote_each_token {
+ ($tokens:ident $($tts:tt)*) => {
+ $crate::quote_tokens_with_context!{$tokens
+ (@ @ @ @ @ @ $($tts)*)
+ (@ @ @ @ @ $($tts)* @)
+ (@ @ @ @ $($tts)* @ @)
+ (@ @ @ $(($tts))* @ @ @)
+ (@ @ $($tts)* @ @ @ @)
+ (@ $($tts)* @ @ @ @ @)
+ ($($tts)* @ @ @ @ @ @)
+ }
+ };
+}
+
+// See the explanation on quote_each_token.
+#[macro_export]
+#[doc(hidden)]
+macro_rules! quote_each_token_spanned {
+ ($tokens:ident $span:ident $($tts:tt)*) => {
+ $crate::quote_tokens_with_context_spanned!{$tokens $span
+ (@ @ @ @ @ @ $($tts)*)
+ (@ @ @ @ @ $($tts)* @)
+ (@ @ @ @ $($tts)* @ @)
+ (@ @ @ $(($tts))* @ @ @)
+ (@ @ $($tts)* @ @ @ @)
+ (@ $($tts)* @ @ @ @ @)
+ ($($tts)* @ @ @ @ @ @)
+ }
+ };
+}
+
+// See the explanation on quote_each_token.
+#[macro_export]
+#[doc(hidden)]
+macro_rules! quote_tokens_with_context {
+ ($tokens:ident
+ ($($b3:tt)*) ($($b2:tt)*) ($($b1:tt)*)
+ ($($curr:tt)*)
+ ($($a1:tt)*) ($($a2:tt)*) ($($a3:tt)*)
+ ) => {
+ $(
+ $crate::quote_token_with_context!{$tokens $b3 $b2 $b1 $curr $a1 $a2 $a3}
+ )*
+ };
+}
+
+// See the explanation on quote_each_token.
+#[macro_export]
+#[doc(hidden)]
+macro_rules! quote_tokens_with_context_spanned {
+ ($tokens:ident $span:ident
+ ($($b3:tt)*) ($($b2:tt)*) ($($b1:tt)*)
+ ($($curr:tt)*)
+ ($($a1:tt)*) ($($a2:tt)*) ($($a3:tt)*)
+ ) => {
+ $(
+ $crate::quote_token_with_context_spanned!{$tokens $span $b3 $b2 $b1 $curr $a1 $a2 $a3}
+ )*
+ };
+}
+
+// See the explanation on quote_each_token.
+#[macro_export]
+#[doc(hidden)]
+macro_rules! quote_token_with_context {
+ // Unparenthesized `@` indicates this call does not correspond to one of the
+ // original input tokens. Ignore it.
+ ($tokens:ident $b3:tt $b2:tt $b1:tt @ $a1:tt $a2:tt $a3:tt) => {};
+
+ // A repetition with no separator.
+ ($tokens:ident $b3:tt $b2:tt $b1:tt (#) ( $($inner:tt)* ) * $a3:tt) => {{
+ use $crate::__private::ext::*;
+ let has_iter = $crate::__private::ThereIsNoIteratorInRepetition;
+ $crate::pounded_var_names!{quote_bind_into_iter!(has_iter) () $($inner)*}
+ let _: $crate::__private::HasIterator = has_iter;
+ // This is `while true` instead of `loop` because if there are no
+ // iterators used inside of this repetition then the body would not
+ // contain any `break`, so the compiler would emit unreachable code
+ // warnings on anything below the loop. We use has_iter to detect and
+ // fail to compile when there are no iterators, so here we just work
+ // around the unneeded extra warning.
+ while true {
+ $crate::pounded_var_names!{quote_bind_next_or_break!() () $($inner)*}
+ $crate::quote_each_token!{$tokens $($inner)*}
+ }
+ }};
+ // ... and one step later.
+ ($tokens:ident $b3:tt $b2:tt # (( $($inner:tt)* )) * $a2:tt $a3:tt) => {};
+ // ... and one step later.
+ ($tokens:ident $b3:tt # ( $($inner:tt)* ) (*) $a1:tt $a2:tt $a3:tt) => {};
+
+ // A repetition with separator.
+ ($tokens:ident $b3:tt $b2:tt $b1:tt (#) ( $($inner:tt)* ) $sep:tt *) => {{
+ use $crate::__private::ext::*;
+ let mut _i = 0usize;
+ let has_iter = $crate::__private::ThereIsNoIteratorInRepetition;
+ $crate::pounded_var_names!{quote_bind_into_iter!(has_iter) () $($inner)*}
+ let _: $crate::__private::HasIterator = has_iter;
+ while true {
+ $crate::pounded_var_names!{quote_bind_next_or_break!() () $($inner)*}
+ if _i > 0 {
+ $crate::quote_token!{$sep $tokens}
+ }
+ _i += 1;
+ $crate::quote_each_token!{$tokens $($inner)*}
+ }
+ }};
+ // ... and one step later.
+ ($tokens:ident $b3:tt $b2:tt # (( $($inner:tt)* )) $sep:tt * $a3:tt) => {};
+ // ... and one step later.
+ ($tokens:ident $b3:tt # ( $($inner:tt)* ) ($sep:tt) * $a2:tt $a3:tt) => {};
+ // (A special case for `#(var)**`, where the first `*` is treated as the
+ // repetition symbol and the second `*` is treated as an ordinary token.)
+ ($tokens:ident # ( $($inner:tt)* ) * (*) $a1:tt $a2:tt $a3:tt) => {
+ // https://github.com/dtolnay/quote/issues/130
+ $crate::quote_token!{* $tokens}
+ };
+ // ... and one step later.
+ ($tokens:ident # ( $($inner:tt)* ) $sep:tt (*) $a1:tt $a2:tt $a3:tt) => {};
+
+ // A non-repetition interpolation.
+ ($tokens:ident $b3:tt $b2:tt $b1:tt (#) $var:ident $a2:tt $a3:tt) => {
+ $crate::ToTokens::to_tokens(&$var, &mut $tokens);
+ };
+ // ... and one step later.
+ ($tokens:ident $b3:tt $b2:tt # ($var:ident) $a1:tt $a2:tt $a3:tt) => {};
+
+ // An ordinary token, not part of any interpolation.
+ ($tokens:ident $b3:tt $b2:tt $b1:tt ($curr:tt) $a1:tt $a2:tt $a3:tt) => {
+ $crate::quote_token!{$curr $tokens}
+ };
+}
+
+// See the explanation on quote_each_token, and on the individual rules of
+// quote_token_with_context.
+#[macro_export]
+#[doc(hidden)]
+macro_rules! quote_token_with_context_spanned {
+ ($tokens:ident $span:ident $b3:tt $b2:tt $b1:tt @ $a1:tt $a2:tt $a3:tt) => {};
+
+ ($tokens:ident $span:ident $b3:tt $b2:tt $b1:tt (#) ( $($inner:tt)* ) * $a3:tt) => {{
+ use $crate::__private::ext::*;
+ let has_iter = $crate::__private::ThereIsNoIteratorInRepetition;
+ $crate::pounded_var_names!{quote_bind_into_iter!(has_iter) () $($inner)*}
+ let _: $crate::__private::HasIterator = has_iter;
+ while true {
+ $crate::pounded_var_names!{quote_bind_next_or_break!() () $($inner)*}
+ $crate::quote_each_token_spanned!{$tokens $span $($inner)*}
+ }
+ }};
+ ($tokens:ident $span:ident $b3:tt $b2:tt # (( $($inner:tt)* )) * $a2:tt $a3:tt) => {};
+ ($tokens:ident $span:ident $b3:tt # ( $($inner:tt)* ) (*) $a1:tt $a2:tt $a3:tt) => {};
+
+ ($tokens:ident $span:ident $b3:tt $b2:tt $b1:tt (#) ( $($inner:tt)* ) $sep:tt *) => {{
+ use $crate::__private::ext::*;
+ let mut _i = 0usize;
+ let has_iter = $crate::__private::ThereIsNoIteratorInRepetition;
+ $crate::pounded_var_names!{quote_bind_into_iter!(has_iter) () $($inner)*}
+ let _: $crate::__private::HasIterator = has_iter;
+ while true {
+ $crate::pounded_var_names!{quote_bind_next_or_break!() () $($inner)*}
+ if _i > 0 {
+ $crate::quote_token_spanned!{$sep $tokens $span}
+ }
+ _i += 1;
+ $crate::quote_each_token_spanned!{$tokens $span $($inner)*}
+ }
+ }};
+ ($tokens:ident $span:ident $b3:tt $b2:tt # (( $($inner:tt)* )) $sep:tt * $a3:tt) => {};
+ ($tokens:ident $span:ident $b3:tt # ( $($inner:tt)* ) ($sep:tt) * $a2:tt $a3:tt) => {};
+ ($tokens:ident $span:ident # ( $($inner:tt)* ) * (*) $a1:tt $a2:tt $a3:tt) => {
+ // https://github.com/dtolnay/quote/issues/130
+ $crate::quote_token_spanned!{* $tokens $span}
+ };
+ ($tokens:ident $span:ident # ( $($inner:tt)* ) $sep:tt (*) $a1:tt $a2:tt $a3:tt) => {};
+
+ ($tokens:ident $span:ident $b3:tt $b2:tt $b1:tt (#) $var:ident $a2:tt $a3:tt) => {
+ $crate::ToTokens::to_tokens(&$var, &mut $tokens);
+ };
+ ($tokens:ident $span:ident $b3:tt $b2:tt # ($var:ident) $a1:tt $a2:tt $a3:tt) => {};
+
+ ($tokens:ident $span:ident $b3:tt $b2:tt $b1:tt ($curr:tt) $a1:tt $a2:tt $a3:tt) => {
+ $crate::quote_token_spanned!{$curr $tokens $span}
+ };
+}
+
+// These rules are ordered by approximate token frequency, at least for the
+// first 10 or so, to improve compile times. Having `ident` first is by far the
+// most important because it's typically 2-3x more common than the next most
+// common token.
+//
+// Separately, we put the token being matched in the very front so that failing
+// rules may fail to match as quickly as possible.
+#[macro_export]
+#[doc(hidden)]
+macro_rules! quote_token {
+ ($ident:ident $tokens:ident) => {
+ $crate::__private::push_ident(&mut $tokens, stringify!($ident));
+ };
+
+ (:: $tokens:ident) => {
+ $crate::__private::push_colon2(&mut $tokens);
+ };
+
+ (( $($inner:tt)* ) $tokens:ident) => {
+ $crate::__private::push_group(
+ &mut $tokens,
+ $crate::__private::Delimiter::Parenthesis,
+ $crate::quote!($($inner)*),
+ );
+ };
+
+ ([ $($inner:tt)* ] $tokens:ident) => {
+ $crate::__private::push_group(
+ &mut $tokens,
+ $crate::__private::Delimiter::Bracket,
+ $crate::quote!($($inner)*),
+ );
+ };
+
+ ({ $($inner:tt)* } $tokens:ident) => {
+ $crate::__private::push_group(
+ &mut $tokens,
+ $crate::__private::Delimiter::Brace,
+ $crate::quote!($($inner)*),
+ );
+ };
+
+ (# $tokens:ident) => {
+ $crate::__private::push_pound(&mut $tokens);
+ };
+
+ (, $tokens:ident) => {
+ $crate::__private::push_comma(&mut $tokens);
+ };
+
+ (. $tokens:ident) => {
+ $crate::__private::push_dot(&mut $tokens);
+ };
+
+ (; $tokens:ident) => {
+ $crate::__private::push_semi(&mut $tokens);
+ };
+
+ (: $tokens:ident) => {
+ $crate::__private::push_colon(&mut $tokens);
+ };
+
+ (+ $tokens:ident) => {
+ $crate::__private::push_add(&mut $tokens);
+ };
+
+ (+= $tokens:ident) => {
+ $crate::__private::push_add_eq(&mut $tokens);
+ };
+
+ (& $tokens:ident) => {
+ $crate::__private::push_and(&mut $tokens);
+ };
+
+ (&& $tokens:ident) => {
+ $crate::__private::push_and_and(&mut $tokens);
+ };
+
+ (&= $tokens:ident) => {
+ $crate::__private::push_and_eq(&mut $tokens);
+ };
+
+ (@ $tokens:ident) => {
+ $crate::__private::push_at(&mut $tokens);
+ };
+
+ (! $tokens:ident) => {
+ $crate::__private::push_bang(&mut $tokens);
+ };
+
+ (^ $tokens:ident) => {
+ $crate::__private::push_caret(&mut $tokens);
+ };
+
+ (^= $tokens:ident) => {
+ $crate::__private::push_caret_eq(&mut $tokens);
+ };
+
+ (/ $tokens:ident) => {
+ $crate::__private::push_div(&mut $tokens);
+ };
+
+ (/= $tokens:ident) => {
+ $crate::__private::push_div_eq(&mut $tokens);
+ };
+
+ (.. $tokens:ident) => {
+ $crate::__private::push_dot2(&mut $tokens);
+ };
+
+ (... $tokens:ident) => {
+ $crate::__private::push_dot3(&mut $tokens);
+ };
+
+ (..= $tokens:ident) => {
+ $crate::__private::push_dot_dot_eq(&mut $tokens);
+ };
+
+ (= $tokens:ident) => {
+ $crate::__private::push_eq(&mut $tokens);
+ };
+
+ (== $tokens:ident) => {
+ $crate::__private::push_eq_eq(&mut $tokens);
+ };
+
+ (>= $tokens:ident) => {
+ $crate::__private::push_ge(&mut $tokens);
+ };
+
+ (> $tokens:ident) => {
+ $crate::__private::push_gt(&mut $tokens);
+ };
+
+ (<= $tokens:ident) => {
+ $crate::__private::push_le(&mut $tokens);
+ };
+
+ (< $tokens:ident) => {
+ $crate::__private::push_lt(&mut $tokens);
+ };
+
+ (*= $tokens:ident) => {
+ $crate::__private::push_mul_eq(&mut $tokens);
+ };
+
+ (!= $tokens:ident) => {
+ $crate::__private::push_ne(&mut $tokens);
+ };
+
+ (| $tokens:ident) => {
+ $crate::__private::push_or(&mut $tokens);
+ };
+
+ (|= $tokens:ident) => {
+ $crate::__private::push_or_eq(&mut $tokens);
+ };
+
+ (|| $tokens:ident) => {
+ $crate::__private::push_or_or(&mut $tokens);
+ };
+
+ (? $tokens:ident) => {
+ $crate::__private::push_question(&mut $tokens);
+ };
+
+ (-> $tokens:ident) => {
+ $crate::__private::push_rarrow(&mut $tokens);
+ };
+
+ (<- $tokens:ident) => {
+ $crate::__private::push_larrow(&mut $tokens);
+ };
+
+ (% $tokens:ident) => {
+ $crate::__private::push_rem(&mut $tokens);
+ };
+
+ (%= $tokens:ident) => {
+ $crate::__private::push_rem_eq(&mut $tokens);
+ };
+
+ (=> $tokens:ident) => {
+ $crate::__private::push_fat_arrow(&mut $tokens);
+ };
+
+ (<< $tokens:ident) => {
+ $crate::__private::push_shl(&mut $tokens);
+ };
+
+ (<<= $tokens:ident) => {
+ $crate::__private::push_shl_eq(&mut $tokens);
+ };
+
+ (>> $tokens:ident) => {
+ $crate::__private::push_shr(&mut $tokens);
+ };
+
+ (>>= $tokens:ident) => {
+ $crate::__private::push_shr_eq(&mut $tokens);
+ };
+
+ (* $tokens:ident) => {
+ $crate::__private::push_star(&mut $tokens);
+ };
+
+ (- $tokens:ident) => {
+ $crate::__private::push_sub(&mut $tokens);
+ };
+
+ (-= $tokens:ident) => {
+ $crate::__private::push_sub_eq(&mut $tokens);
+ };
+
+ ($lifetime:lifetime $tokens:ident) => {
+ $crate::__private::push_lifetime(&mut $tokens, stringify!($lifetime));
+ };
+
+ (_ $tokens:ident) => {
+ $crate::__private::push_underscore(&mut $tokens);
+ };
+
+ ($other:tt $tokens:ident) => {
+ $crate::__private::parse(&mut $tokens, stringify!($other));
+ };
+}
+
+// See the comment above `quote_token!` about the rule ordering.
+#[macro_export]
+#[doc(hidden)]
+macro_rules! quote_token_spanned {
+ ($ident:ident $tokens:ident $span:ident) => {
+ $crate::__private::push_ident_spanned(&mut $tokens, $span, stringify!($ident));
+ };
+
+ (:: $tokens:ident $span:ident) => {
+ $crate::__private::push_colon2_spanned(&mut $tokens, $span);
+ };
+
+ (( $($inner:tt)* ) $tokens:ident $span:ident) => {
+ $crate::__private::push_group_spanned(
+ &mut $tokens,
+ $span,
+ $crate::__private::Delimiter::Parenthesis,
+ $crate::quote_spanned!($span=> $($inner)*),
+ );
+ };
+
+ ([ $($inner:tt)* ] $tokens:ident $span:ident) => {
+ $crate::__private::push_group_spanned(
+ &mut $tokens,
+ $span,
+ $crate::__private::Delimiter::Bracket,
+ $crate::quote_spanned!($span=> $($inner)*),
+ );
+ };
+
+ ({ $($inner:tt)* } $tokens:ident $span:ident) => {
+ $crate::__private::push_group_spanned(
+ &mut $tokens,
+ $span,
+ $crate::__private::Delimiter::Brace,
+ $crate::quote_spanned!($span=> $($inner)*),
+ );
+ };
+
+ (# $tokens:ident $span:ident) => {
+ $crate::__private::push_pound_spanned(&mut $tokens, $span);
+ };
+
+ (, $tokens:ident $span:ident) => {
+ $crate::__private::push_comma_spanned(&mut $tokens, $span);
+ };
+
+ (. $tokens:ident $span:ident) => {
+ $crate::__private::push_dot_spanned(&mut $tokens, $span);
+ };
+
+ (; $tokens:ident $span:ident) => {
+ $crate::__private::push_semi_spanned(&mut $tokens, $span);
+ };
+
+ (: $tokens:ident $span:ident) => {
+ $crate::__private::push_colon_spanned(&mut $tokens, $span);
+ };
+
+ (+ $tokens:ident $span:ident) => {
+ $crate::__private::push_add_spanned(&mut $tokens, $span);
+ };
+
+ (+= $tokens:ident $span:ident) => {
+ $crate::__private::push_add_eq_spanned(&mut $tokens, $span);
+ };
+
+ (& $tokens:ident $span:ident) => {
+ $crate::__private::push_and_spanned(&mut $tokens, $span);
+ };
+
+ (&& $tokens:ident $span:ident) => {
+ $crate::__private::push_and_and_spanned(&mut $tokens, $span);
+ };
+
+ (&= $tokens:ident $span:ident) => {
+ $crate::__private::push_and_eq_spanned(&mut $tokens, $span);
+ };
+
+ (@ $tokens:ident $span:ident) => {
+ $crate::__private::push_at_spanned(&mut $tokens, $span);
+ };
+
+ (! $tokens:ident $span:ident) => {
+ $crate::__private::push_bang_spanned(&mut $tokens, $span);
+ };
+
+ (^ $tokens:ident $span:ident) => {
+ $crate::__private::push_caret_spanned(&mut $tokens, $span);
+ };
+
+ (^= $tokens:ident $span:ident) => {
+ $crate::__private::push_caret_eq_spanned(&mut $tokens, $span);
+ };
+
+ (/ $tokens:ident $span:ident) => {
+ $crate::__private::push_div_spanned(&mut $tokens, $span);
+ };
+
+ (/= $tokens:ident $span:ident) => {
+ $crate::__private::push_div_eq_spanned(&mut $tokens, $span);
+ };
+
+ (.. $tokens:ident $span:ident) => {
+ $crate::__private::push_dot2_spanned(&mut $tokens, $span);
+ };
+
+ (... $tokens:ident $span:ident) => {
+ $crate::__private::push_dot3_spanned(&mut $tokens, $span);
+ };
+
+ (..= $tokens:ident $span:ident) => {
+ $crate::__private::push_dot_dot_eq_spanned(&mut $tokens, $span);
+ };
+
+ (= $tokens:ident $span:ident) => {
+ $crate::__private::push_eq_spanned(&mut $tokens, $span);
+ };
+
+ (== $tokens:ident $span:ident) => {
+ $crate::__private::push_eq_eq_spanned(&mut $tokens, $span);
+ };
+
+ (>= $tokens:ident $span:ident) => {
+ $crate::__private::push_ge_spanned(&mut $tokens, $span);
+ };
+
+ (> $tokens:ident $span:ident) => {
+ $crate::__private::push_gt_spanned(&mut $tokens, $span);
+ };
+
+ (<= $tokens:ident $span:ident) => {
+ $crate::__private::push_le_spanned(&mut $tokens, $span);
+ };
+
+ (< $tokens:ident $span:ident) => {
+ $crate::__private::push_lt_spanned(&mut $tokens, $span);
+ };
+
+ (*= $tokens:ident $span:ident) => {
+ $crate::__private::push_mul_eq_spanned(&mut $tokens, $span);
+ };
+
+ (!= $tokens:ident $span:ident) => {
+ $crate::__private::push_ne_spanned(&mut $tokens, $span);
+ };
+
+ (| $tokens:ident $span:ident) => {
+ $crate::__private::push_or_spanned(&mut $tokens, $span);
+ };
+
+ (|= $tokens:ident $span:ident) => {
+ $crate::__private::push_or_eq_spanned(&mut $tokens, $span);
+ };
+
+ (|| $tokens:ident $span:ident) => {
+ $crate::__private::push_or_or_spanned(&mut $tokens, $span);
+ };
+
+ (? $tokens:ident $span:ident) => {
+ $crate::__private::push_question_spanned(&mut $tokens, $span);
+ };
+
+ (-> $tokens:ident $span:ident) => {
+ $crate::__private::push_rarrow_spanned(&mut $tokens, $span);
+ };
+
+ (<- $tokens:ident $span:ident) => {
+ $crate::__private::push_larrow_spanned(&mut $tokens, $span);
+ };
+
+ (% $tokens:ident $span:ident) => {
+ $crate::__private::push_rem_spanned(&mut $tokens, $span);
+ };
+
+ (%= $tokens:ident $span:ident) => {
+ $crate::__private::push_rem_eq_spanned(&mut $tokens, $span);
+ };
+
+ (=> $tokens:ident $span:ident) => {
+ $crate::__private::push_fat_arrow_spanned(&mut $tokens, $span);
+ };
+
+ (<< $tokens:ident $span:ident) => {
+ $crate::__private::push_shl_spanned(&mut $tokens, $span);
+ };
+
+ (<<= $tokens:ident $span:ident) => {
+ $crate::__private::push_shl_eq_spanned(&mut $tokens, $span);
+ };
+
+ (>> $tokens:ident $span:ident) => {
+ $crate::__private::push_shr_spanned(&mut $tokens, $span);
+ };
+
+ (>>= $tokens:ident $span:ident) => {
+ $crate::__private::push_shr_eq_spanned(&mut $tokens, $span);
+ };
+
+ (* $tokens:ident $span:ident) => {
+ $crate::__private::push_star_spanned(&mut $tokens, $span);
+ };
+
+ (- $tokens:ident $span:ident) => {
+ $crate::__private::push_sub_spanned(&mut $tokens, $span);
+ };
+
+ (-= $tokens:ident $span:ident) => {
+ $crate::__private::push_sub_eq_spanned(&mut $tokens, $span);
+ };
+
+ ($lifetime:lifetime $tokens:ident $span:ident) => {
+ $crate::__private::push_lifetime_spanned(&mut $tokens, $span, stringify!($lifetime));
+ };
+
+ (_ $tokens:ident $span:ident) => {
+ $crate::__private::push_underscore_spanned(&mut $tokens, $span);
+ };
+
+ ($other:tt $tokens:ident $span:ident) => {
+ $crate::__private::parse_spanned(&mut $tokens, $span, stringify!($other));
+ };
+}
diff --git a/rust/quote/runtime.rs b/rust/quote/runtime.rs
new file mode 100644
index 000000000000..f3cdded3afb2
--- /dev/null
+++ b/rust/quote/runtime.rs
@@ -0,0 +1,438 @@
+use crate::{IdentFragment, ToTokens, TokenStreamExt};
+use core::fmt;
+use core::iter;
+use core::ops::BitOr;
+
+pub use core::option::Option;
+pub use proc_macro2::*;
+pub use std::format;
+
+pub struct HasIterator; // True
+pub struct ThereIsNoIteratorInRepetition; // False
+
+impl BitOr<ThereIsNoIteratorInRepetition> for ThereIsNoIteratorInRepetition {
+ type Output = ThereIsNoIteratorInRepetition;
+ fn bitor(self, _rhs: ThereIsNoIteratorInRepetition) -> ThereIsNoIteratorInRepetition {
+ ThereIsNoIteratorInRepetition
+ }
+}
+
+impl BitOr<ThereIsNoIteratorInRepetition> for HasIterator {
+ type Output = HasIterator;
+ fn bitor(self, _rhs: ThereIsNoIteratorInRepetition) -> HasIterator {
+ HasIterator
+ }
+}
+
+impl BitOr<HasIterator> for ThereIsNoIteratorInRepetition {
+ type Output = HasIterator;
+ fn bitor(self, _rhs: HasIterator) -> HasIterator {
+ HasIterator
+ }
+}
+
+impl BitOr<HasIterator> for HasIterator {
+ type Output = HasIterator;
+ fn bitor(self, _rhs: HasIterator) -> HasIterator {
+ HasIterator
+ }
+}
+
+/// Extension traits used by the implementation of `quote!`. These are defined
+/// in separate traits, rather than as a single trait due to ambiguity issues.
+///
+/// These traits expose a `quote_into_iter` method which should allow calling
+/// whichever impl happens to be applicable. Calling that method repeatedly on
+/// the returned value should be idempotent.
+pub mod ext {
+ use super::RepInterp;
+ use super::{HasIterator as HasIter, ThereIsNoIteratorInRepetition as DoesNotHaveIter};
+ use crate::ToTokens;
+ use core::slice;
+ use std::collections::btree_set::{self, BTreeSet};
+
+ /// Extension trait providing the `quote_into_iter` method on iterators.
+ pub trait RepIteratorExt: Iterator + Sized {
+ fn quote_into_iter(self) -> (Self, HasIter) {
+ (self, HasIter)
+ }
+ }
+
+ impl<T: Iterator> RepIteratorExt for T {}
+
+ /// Extension trait providing the `quote_into_iter` method for
+ /// non-iterable types. These types interpolate the same value in each
+ /// iteration of the repetition.
+ pub trait RepToTokensExt {
+ /// Pretend to be an iterator for the purposes of `quote_into_iter`.
+ /// This allows repeated calls to `quote_into_iter` to continue
+ /// correctly returning DoesNotHaveIter.
+ fn next(&self) -> Option<&Self> {
+ Some(self)
+ }
+
+ fn quote_into_iter(&self) -> (&Self, DoesNotHaveIter) {
+ (self, DoesNotHaveIter)
+ }
+ }
+
+ impl<T: ToTokens + ?Sized> RepToTokensExt for T {}
+
+ /// Extension trait providing the `quote_into_iter` method for types that
+ /// can be referenced as an iterator.
+ pub trait RepAsIteratorExt<'q> {
+ type Iter: Iterator;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter);
+ }
+
+ impl<'q, 'a, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &'a T {
+ type Iter = T::Iter;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ <T as RepAsIteratorExt>::quote_into_iter(*self)
+ }
+ }
+
+ impl<'q, 'a, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &'a mut T {
+ type Iter = T::Iter;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ <T as RepAsIteratorExt>::quote_into_iter(*self)
+ }
+ }
+
+ impl<'q, T: 'q> RepAsIteratorExt<'q> for [T] {
+ type Iter = slice::Iter<'q, T>;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ (self.iter(), HasIter)
+ }
+ }
+
+ impl<'q, T: 'q> RepAsIteratorExt<'q> for Vec<T> {
+ type Iter = slice::Iter<'q, T>;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ (self.iter(), HasIter)
+ }
+ }
+
+ impl<'q, T: 'q> RepAsIteratorExt<'q> for BTreeSet<T> {
+ type Iter = btree_set::Iter<'q, T>;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ (self.iter(), HasIter)
+ }
+ }
+
+ impl<'q, T: RepAsIteratorExt<'q>> RepAsIteratorExt<'q> for RepInterp<T> {
+ type Iter = T::Iter;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ self.0.quote_into_iter()
+ }
+ }
+}
+
+// Helper type used within interpolations to allow for repeated binding names.
+// Implements the relevant traits, and exports a dummy `next()` method.
+#[derive(Copy, Clone)]
+pub struct RepInterp<T>(pub T);
+
+impl<T> RepInterp<T> {
+ // This method is intended to look like `Iterator::next`, and is called when
+ // a name is bound multiple times, as the previous binding will shadow the
+ // original `Iterator` object. This allows us to avoid advancing the
+ // iterator multiple times per iteration.
+ pub fn next(self) -> Option<T> {
+ Some(self.0)
+ }
+}
+
+impl<T: Iterator> Iterator for RepInterp<T> {
+ type Item = T::Item;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.0.next()
+ }
+}
+
+impl<T: ToTokens> ToTokens for RepInterp<T> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ self.0.to_tokens(tokens);
+ }
+}
+
+pub fn push_group(tokens: &mut TokenStream, delimiter: Delimiter, inner: TokenStream) {
+ tokens.append(Group::new(delimiter, inner));
+}
+
+pub fn push_group_spanned(
+ tokens: &mut TokenStream,
+ span: Span,
+ delimiter: Delimiter,
+ inner: TokenStream,
+) {
+ let mut g = Group::new(delimiter, inner);
+ g.set_span(span);
+ tokens.append(g);
+}
+
+pub fn parse(tokens: &mut TokenStream, s: &str) {
+ let s: TokenStream = s.parse().expect("invalid token stream");
+ tokens.extend(iter::once(s));
+}
+
+pub fn parse_spanned(tokens: &mut TokenStream, span: Span, s: &str) {
+ let s: TokenStream = s.parse().expect("invalid token stream");
+ tokens.extend(s.into_iter().map(|t| respan_token_tree(t, span)));
+}
+
+// Token tree with every span replaced by the given one.
+fn respan_token_tree(mut token: TokenTree, span: Span) -> TokenTree {
+ match &mut token {
+ TokenTree::Group(g) => {
+ let stream = g
+ .stream()
+ .into_iter()
+ .map(|token| respan_token_tree(token, span))
+ .collect();
+ *g = Group::new(g.delimiter(), stream);
+ g.set_span(span);
+ }
+ other => other.set_span(span),
+ }
+ token
+}
+
+pub fn push_ident(tokens: &mut TokenStream, s: &str) {
+ let span = Span::call_site();
+ push_ident_spanned(tokens, span, s);
+}
+
+pub fn push_ident_spanned(tokens: &mut TokenStream, span: Span, s: &str) {
+ tokens.append(ident_maybe_raw(s, span));
+}
+
+pub fn push_lifetime(tokens: &mut TokenStream, lifetime: &str) {
+ struct Lifetime<'a> {
+ name: &'a str,
+ state: u8,
+ }
+
+ impl<'a> Iterator for Lifetime<'a> {
+ type Item = TokenTree;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self.state {
+ 0 => {
+ self.state = 1;
+ Some(TokenTree::Punct(Punct::new('\'', Spacing::Joint)))
+ }
+ 1 => {
+ self.state = 2;
+ Some(TokenTree::Ident(Ident::new(self.name, Span::call_site())))
+ }
+ _ => None,
+ }
+ }
+ }
+
+ tokens.extend(Lifetime {
+ name: &lifetime[1..],
+ state: 0,
+ });
+}
+
+pub fn push_lifetime_spanned(tokens: &mut TokenStream, span: Span, lifetime: &str) {
+ struct Lifetime<'a> {
+ name: &'a str,
+ span: Span,
+ state: u8,
+ }
+
+ impl<'a> Iterator for Lifetime<'a> {
+ type Item = TokenTree;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self.state {
+ 0 => {
+ self.state = 1;
+ let mut apostrophe = Punct::new('\'', Spacing::Joint);
+ apostrophe.set_span(self.span);
+ Some(TokenTree::Punct(apostrophe))
+ }
+ 1 => {
+ self.state = 2;
+ Some(TokenTree::Ident(Ident::new(self.name, self.span)))
+ }
+ _ => None,
+ }
+ }
+ }
+
+ tokens.extend(Lifetime {
+ name: &lifetime[1..],
+ span,
+ state: 0,
+ });
+}
+
+macro_rules! push_punct {
+ ($name:ident $spanned:ident $char1:tt) => {
+ pub fn $name(tokens: &mut TokenStream) {
+ tokens.append(Punct::new($char1, Spacing::Alone));
+ }
+ pub fn $spanned(tokens: &mut TokenStream, span: Span) {
+ let mut punct = Punct::new($char1, Spacing::Alone);
+ punct.set_span(span);
+ tokens.append(punct);
+ }
+ };
+ ($name:ident $spanned:ident $char1:tt $char2:tt) => {
+ pub fn $name(tokens: &mut TokenStream) {
+ tokens.append(Punct::new($char1, Spacing::Joint));
+ tokens.append(Punct::new($char2, Spacing::Alone));
+ }
+ pub fn $spanned(tokens: &mut TokenStream, span: Span) {
+ let mut punct = Punct::new($char1, Spacing::Joint);
+ punct.set_span(span);
+ tokens.append(punct);
+ let mut punct = Punct::new($char2, Spacing::Alone);
+ punct.set_span(span);
+ tokens.append(punct);
+ }
+ };
+ ($name:ident $spanned:ident $char1:tt $char2:tt $char3:tt) => {
+ pub fn $name(tokens: &mut TokenStream) {
+ tokens.append(Punct::new($char1, Spacing::Joint));
+ tokens.append(Punct::new($char2, Spacing::Joint));
+ tokens.append(Punct::new($char3, Spacing::Alone));
+ }
+ pub fn $spanned(tokens: &mut TokenStream, span: Span) {
+ let mut punct = Punct::new($char1, Spacing::Joint);
+ punct.set_span(span);
+ tokens.append(punct);
+ let mut punct = Punct::new($char2, Spacing::Joint);
+ punct.set_span(span);
+ tokens.append(punct);
+ let mut punct = Punct::new($char3, Spacing::Alone);
+ punct.set_span(span);
+ tokens.append(punct);
+ }
+ };
+}
+
+push_punct!(push_add push_add_spanned '+');
+push_punct!(push_add_eq push_add_eq_spanned '+' '=');
+push_punct!(push_and push_and_spanned '&');
+push_punct!(push_and_and push_and_and_spanned '&' '&');
+push_punct!(push_and_eq push_and_eq_spanned '&' '=');
+push_punct!(push_at push_at_spanned '@');
+push_punct!(push_bang push_bang_spanned '!');
+push_punct!(push_caret push_caret_spanned '^');
+push_punct!(push_caret_eq push_caret_eq_spanned '^' '=');
+push_punct!(push_colon push_colon_spanned ':');
+push_punct!(push_colon2 push_colon2_spanned ':' ':');
+push_punct!(push_comma push_comma_spanned ',');
+push_punct!(push_div push_div_spanned '/');
+push_punct!(push_div_eq push_div_eq_spanned '/' '=');
+push_punct!(push_dot push_dot_spanned '.');
+push_punct!(push_dot2 push_dot2_spanned '.' '.');
+push_punct!(push_dot3 push_dot3_spanned '.' '.' '.');
+push_punct!(push_dot_dot_eq push_dot_dot_eq_spanned '.' '.' '=');
+push_punct!(push_eq push_eq_spanned '=');
+push_punct!(push_eq_eq push_eq_eq_spanned '=' '=');
+push_punct!(push_ge push_ge_spanned '>' '=');
+push_punct!(push_gt push_gt_spanned '>');
+push_punct!(push_le push_le_spanned '<' '=');
+push_punct!(push_lt push_lt_spanned '<');
+push_punct!(push_mul_eq push_mul_eq_spanned '*' '=');
+push_punct!(push_ne push_ne_spanned '!' '=');
+push_punct!(push_or push_or_spanned '|');
+push_punct!(push_or_eq push_or_eq_spanned '|' '=');
+push_punct!(push_or_or push_or_or_spanned '|' '|');
+push_punct!(push_pound push_pound_spanned '#');
+push_punct!(push_question push_question_spanned '?');
+push_punct!(push_rarrow push_rarrow_spanned '-' '>');
+push_punct!(push_larrow push_larrow_spanned '<' '-');
+push_punct!(push_rem push_rem_spanned '%');
+push_punct!(push_rem_eq push_rem_eq_spanned '%' '=');
+push_punct!(push_fat_arrow push_fat_arrow_spanned '=' '>');
+push_punct!(push_semi push_semi_spanned ';');
+push_punct!(push_shl push_shl_spanned '<' '<');
+push_punct!(push_shl_eq push_shl_eq_spanned '<' '<' '=');
+push_punct!(push_shr push_shr_spanned '>' '>');
+push_punct!(push_shr_eq push_shr_eq_spanned '>' '>' '=');
+push_punct!(push_star push_star_spanned '*');
+push_punct!(push_sub push_sub_spanned '-');
+push_punct!(push_sub_eq push_sub_eq_spanned '-' '=');
+
+pub fn push_underscore(tokens: &mut TokenStream) {
+ push_underscore_spanned(tokens, Span::call_site());
+}
+
+pub fn push_underscore_spanned(tokens: &mut TokenStream, span: Span) {
+ tokens.append(Ident::new("_", span));
+}
+
+// Helper method for constructing identifiers from the `format_ident!` macro,
+// handling `r#` prefixes.
+pub fn mk_ident(id: &str, span: Option<Span>) -> Ident {
+ let span = span.unwrap_or_else(Span::call_site);
+ ident_maybe_raw(id, span)
+}
+
+fn ident_maybe_raw(id: &str, span: Span) -> Ident {
+ if id.starts_with("r#") {
+ Ident::new_raw(&id[2..], span)
+ } else {
+ Ident::new(id, span)
+ }
+}
+
+// Adapts from `IdentFragment` to `fmt::Display` for use by the `format_ident!`
+// macro, and exposes span information from these fragments.
+//
+// This struct also has forwarding implementations of the formatting traits
+// `Octal`, `LowerHex`, `UpperHex`, and `Binary` to allow for their use within
+// `format_ident!`.
+#[derive(Copy, Clone)]
+pub struct IdentFragmentAdapter<T: IdentFragment>(pub T);
+
+impl<T: IdentFragment> IdentFragmentAdapter<T> {
+ pub fn span(&self) -> Option<Span> {
+ self.0.span()
+ }
+}
+
+impl<T: IdentFragment> fmt::Display for IdentFragmentAdapter<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ IdentFragment::fmt(&self.0, f)
+ }
+}
+
+impl<T: IdentFragment + fmt::Octal> fmt::Octal for IdentFragmentAdapter<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Octal::fmt(&self.0, f)
+ }
+}
+
+impl<T: IdentFragment + fmt::LowerHex> fmt::LowerHex for IdentFragmentAdapter<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::LowerHex::fmt(&self.0, f)
+ }
+}
+
+impl<T: IdentFragment + fmt::UpperHex> fmt::UpperHex for IdentFragmentAdapter<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::UpperHex::fmt(&self.0, f)
+ }
+}
+
+impl<T: IdentFragment + fmt::Binary> fmt::Binary for IdentFragmentAdapter<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Binary::fmt(&self.0, f)
+ }
+}
diff --git a/rust/quote/spanned.rs b/rust/quote/spanned.rs
new file mode 100644
index 000000000000..f64c8f5dfec7
--- /dev/null
+++ b/rust/quote/spanned.rs
@@ -0,0 +1,43 @@
+use crate::ToTokens;
+use proc_macro2::{Span, TokenStream};
+
+pub trait Spanned {
+ fn __span(&self) -> Span;
+}
+
+impl Spanned for Span {
+ fn __span(&self) -> Span {
+ *self
+ }
+}
+
+impl<T: ?Sized + ToTokens> Spanned for T {
+ fn __span(&self) -> Span {
+ join_spans(self.into_token_stream())
+ }
+}
+
+fn join_spans(tokens: TokenStream) -> Span {
+ #[cfg(not(needs_invalid_span_workaround))]
+ let mut iter = tokens.into_iter().map(|tt| tt.span());
+
+ #[cfg(needs_invalid_span_workaround)]
+ let mut iter = tokens.into_iter().filter_map(|tt| {
+ let span = tt.span();
+ let debug = format!("{:?}", span);
+ if debug.ends_with("bytes(0..0)") {
+ None
+ } else {
+ Some(span)
+ }
+ });
+
+ let first = match iter.next() {
+ Some(span) => span,
+ None => return Span::call_site(),
+ };
+
+ iter.fold(None, |_prev, next| Some(next))
+ .and_then(|last| first.join(last))
+ .unwrap_or(first)
+}
diff --git a/rust/quote/to_tokens.rs b/rust/quote/to_tokens.rs
new file mode 100644
index 000000000000..57487217eeae
--- /dev/null
+++ b/rust/quote/to_tokens.rs
@@ -0,0 +1,209 @@
+use super::TokenStreamExt;
+use core::iter;
+use proc_macro2::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree};
+use std::borrow::Cow;
+use std::rc::Rc;
+
+/// Types that can be interpolated inside a `quote!` invocation.
+///
+/// [`quote!`]: macro.quote.html
+pub trait ToTokens {
+ /// Write `self` to the given `TokenStream`.
+ ///
+ /// The token append methods provided by the [`TokenStreamExt`] extension
+ /// trait may be useful for implementing `ToTokens`.
+ ///
+ /// [`TokenStreamExt`]: trait.TokenStreamExt.html
+ ///
+ /// # Example
+ ///
+ /// Example implementation for a struct representing Rust paths like
+ /// `std::cmp::PartialEq`:
+ ///
+ /// ```
+ /// use proc_macro2::{TokenTree, Spacing, Span, Punct, TokenStream};
+ /// use quote::{TokenStreamExt, ToTokens};
+ ///
+ /// pub struct Path {
+ /// pub global: bool,
+ /// pub segments: Vec<PathSegment>,
+ /// }
+ ///
+ /// impl ToTokens for Path {
+ /// fn to_tokens(&self, tokens: &mut TokenStream) {
+ /// for (i, segment) in self.segments.iter().enumerate() {
+ /// if i > 0 || self.global {
+ /// // Double colon `::`
+ /// tokens.append(Punct::new(':', Spacing::Joint));
+ /// tokens.append(Punct::new(':', Spacing::Alone));
+ /// }
+ /// segment.to_tokens(tokens);
+ /// }
+ /// }
+ /// }
+ /// #
+ /// # pub struct PathSegment;
+ /// #
+ /// # impl ToTokens for PathSegment {
+ /// # fn to_tokens(&self, tokens: &mut TokenStream) {
+ /// # unimplemented!()
+ /// # }
+ /// # }
+ /// ```
+ fn to_tokens(&self, tokens: &mut TokenStream);
+
+ /// Convert `self` directly into a `TokenStream` object.
+ ///
+ /// This method is implicitly implemented using `to_tokens`, and acts as a
+ /// convenience method for consumers of the `ToTokens` trait.
+ fn to_token_stream(&self) -> TokenStream {
+ let mut tokens = TokenStream::new();
+ self.to_tokens(&mut tokens);
+ tokens
+ }
+
+ /// Convert `self` directly into a `TokenStream` object.
+ ///
+ /// This method is implicitly implemented using `to_tokens`, and acts as a
+ /// convenience method for consumers of the `ToTokens` trait.
+ fn into_token_stream(self) -> TokenStream
+ where
+ Self: Sized,
+ {
+ self.to_token_stream()
+ }
+}
+
+impl<'a, T: ?Sized + ToTokens> ToTokens for &'a T {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ (**self).to_tokens(tokens);
+ }
+}
+
+impl<'a, T: ?Sized + ToTokens> ToTokens for &'a mut T {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ (**self).to_tokens(tokens);
+ }
+}
+
+impl<'a, T: ?Sized + ToOwned + ToTokens> ToTokens for Cow<'a, T> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ (**self).to_tokens(tokens);
+ }
+}
+
+impl<T: ?Sized + ToTokens> ToTokens for Box<T> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ (**self).to_tokens(tokens);
+ }
+}
+
+impl<T: ?Sized + ToTokens> ToTokens for Rc<T> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ (**self).to_tokens(tokens);
+ }
+}
+
+impl<T: ToTokens> ToTokens for Option<T> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ if let Some(ref t) = *self {
+ t.to_tokens(tokens);
+ }
+ }
+}
+
+impl ToTokens for str {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ tokens.append(Literal::string(self));
+ }
+}
+
+impl ToTokens for String {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ self.as_str().to_tokens(tokens);
+ }
+}
+
+macro_rules! primitive {
+ ($($t:ident => $name:ident)*) => {
+ $(
+ impl ToTokens for $t {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ tokens.append(Literal::$name(*self));
+ }
+ }
+ )*
+ };
+}
+
+primitive! {
+ i8 => i8_suffixed
+ i16 => i16_suffixed
+ i32 => i32_suffixed
+ i64 => i64_suffixed
+ i128 => i128_suffixed
+ isize => isize_suffixed
+
+ u8 => u8_suffixed
+ u16 => u16_suffixed
+ u32 => u32_suffixed
+ u64 => u64_suffixed
+ u128 => u128_suffixed
+ usize => usize_suffixed
+
+ f32 => f32_suffixed
+ f64 => f64_suffixed
+}
+
+impl ToTokens for char {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ tokens.append(Literal::character(*self));
+ }
+}
+
+impl ToTokens for bool {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let word = if *self { "true" } else { "false" };
+ tokens.append(Ident::new(word, Span::call_site()));
+ }
+}
+
+impl ToTokens for Group {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ tokens.append(self.clone());
+ }
+}
+
+impl ToTokens for Ident {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ tokens.append(self.clone());
+ }
+}
+
+impl ToTokens for Punct {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ tokens.append(self.clone());
+ }
+}
+
+impl ToTokens for Literal {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ tokens.append(self.clone());
+ }
+}
+
+impl ToTokens for TokenTree {
+ fn to_tokens(&self, dst: &mut TokenStream) {
+ dst.append(self.clone());
+ }
+}
+
+impl ToTokens for TokenStream {
+ fn to_tokens(&self, dst: &mut TokenStream) {
+ dst.extend(iter::once(self.clone()));
+ }
+
+ fn into_token_stream(self) -> TokenStream {
+ self
+ }
+}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 25/80] rust: quote: add SPDX License Identifiers
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (22 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 24/80] rust: quote: import crate Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 27/80] rust: syn: " Ariel Miculas
` (53 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Miguel Ojeda
From: Miguel Ojeda <ojeda@kernel.org>
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
---
rust/quote/ext.rs | 2 ++
rust/quote/format.rs | 2 ++
rust/quote/ident_fragment.rs | 2 ++
rust/quote/lib.rs | 2 ++
rust/quote/runtime.rs | 2 ++
rust/quote/spanned.rs | 2 ++
rust/quote/to_tokens.rs | 2 ++
7 files changed, 14 insertions(+)
diff --git a/rust/quote/ext.rs b/rust/quote/ext.rs
index 92c2315b182d..977d2f0c5919 100644
--- a/rust/quote/ext.rs
+++ b/rust/quote/ext.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use super::ToTokens;
use core::iter;
use proc_macro2::{TokenStream, TokenTree};
diff --git a/rust/quote/format.rs b/rust/quote/format.rs
index 3cddbd2819d6..9fbf9c5949a9 100644
--- a/rust/quote/format.rs
+++ b/rust/quote/format.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
/// Formatting macro for constructing `Ident`s.
///
/// <br>
diff --git a/rust/quote/ident_fragment.rs b/rust/quote/ident_fragment.rs
index cf74024b4825..e4a8d6cd30e9 100644
--- a/rust/quote/ident_fragment.rs
+++ b/rust/quote/ident_fragment.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use core::fmt;
use proc_macro2::{Ident, Span};
use std::borrow::Cow;
diff --git a/rust/quote/lib.rs b/rust/quote/lib.rs
index 35594827f869..ddab15fe4ebf 100644
--- a/rust/quote/lib.rs
+++ b/rust/quote/lib.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! [![github]](https://github.com/dtolnay/quote) [![crates-io]](https://crates.io/crates/quote) [![docs-rs]](https://docs.rs/quote)
//!
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
diff --git a/rust/quote/runtime.rs b/rust/quote/runtime.rs
index f3cdded3afb2..966025bc44ab 100644
--- a/rust/quote/runtime.rs
+++ b/rust/quote/runtime.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use crate::{IdentFragment, ToTokens, TokenStreamExt};
use core::fmt;
use core::iter;
diff --git a/rust/quote/spanned.rs b/rust/quote/spanned.rs
index f64c8f5dfec7..aac63de0ba2f 100644
--- a/rust/quote/spanned.rs
+++ b/rust/quote/spanned.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use crate::ToTokens;
use proc_macro2::{Span, TokenStream};
diff --git a/rust/quote/to_tokens.rs b/rust/quote/to_tokens.rs
index 57487217eeae..a91ba61573e0 100644
--- a/rust/quote/to_tokens.rs
+++ b/rust/quote/to_tokens.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use super::TokenStreamExt;
use core::iter;
use proc_macro2::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree};
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 27/80] rust: syn: add SPDX License Identifiers
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (23 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 25/80] rust: quote: add SPDX License Identifiers Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 28/80] rust: syn: remove `unicode-ident` dependency Ariel Miculas
` (52 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Miguel Ojeda
From: Miguel Ojeda <ojeda@kernel.org>
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
---
rust/syn/attr.rs | 2 ++
rust/syn/await.rs | 2 ++
rust/syn/bigint.rs | 2 ++
rust/syn/buffer.rs | 2 ++
rust/syn/custom_keyword.rs | 2 ++
rust/syn/custom_punctuation.rs | 2 ++
rust/syn/data.rs | 2 ++
rust/syn/derive.rs | 2 ++
rust/syn/discouraged.rs | 2 ++
rust/syn/error.rs | 2 ++
rust/syn/export.rs | 2 ++
rust/syn/expr.rs | 2 ++
rust/syn/ext.rs | 2 ++
rust/syn/file.rs | 2 ++
rust/syn/gen/clone.rs | 2 ++
rust/syn/gen/debug.rs | 2 ++
rust/syn/gen/eq.rs | 2 ++
rust/syn/gen/fold.rs | 2 ++
rust/syn/gen/hash.rs | 2 ++
rust/syn/gen/visit.rs | 2 ++
rust/syn/gen/visit_mut.rs | 2 ++
rust/syn/gen_helper.rs | 2 ++
rust/syn/generics.rs | 2 ++
rust/syn/group.rs | 2 ++
rust/syn/ident.rs | 2 ++
rust/syn/item.rs | 2 ++
rust/syn/lib.rs | 2 ++
rust/syn/lifetime.rs | 2 ++
rust/syn/lit.rs | 2 ++
rust/syn/lookahead.rs | 2 ++
rust/syn/mac.rs | 2 ++
rust/syn/macros.rs | 2 ++
rust/syn/op.rs | 2 ++
rust/syn/parse.rs | 2 ++
rust/syn/parse_macro_input.rs | 2 ++
rust/syn/parse_quote.rs | 2 ++
rust/syn/pat.rs | 2 ++
rust/syn/path.rs | 2 ++
rust/syn/print.rs | 2 ++
rust/syn/punctuated.rs | 2 ++
rust/syn/reserved.rs | 2 ++
rust/syn/sealed.rs | 2 ++
rust/syn/span.rs | 2 ++
rust/syn/spanned.rs | 2 ++
rust/syn/stmt.rs | 2 ++
rust/syn/thread.rs | 2 ++
rust/syn/token.rs | 2 ++
rust/syn/tt.rs | 2 ++
rust/syn/ty.rs | 2 ++
rust/syn/verbatim.rs | 2 ++
rust/syn/whitespace.rs | 2 ++
51 files changed, 102 insertions(+)
diff --git a/rust/syn/attr.rs b/rust/syn/attr.rs
index bace94f43c85..cbcd158f45f6 100644
--- a/rust/syn/attr.rs
+++ b/rust/syn/attr.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use super::*;
use crate::punctuated::Punctuated;
use proc_macro2::TokenStream;
diff --git a/rust/syn/await.rs b/rust/syn/await.rs
index 038c6a5d12cd..1aae244cafd3 100644
--- a/rust/syn/await.rs
+++ b/rust/syn/await.rs
@@ -1,2 +1,4 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
// See include!("await.rs") in token.rs.
export_token_macro! {[await]}
diff --git a/rust/syn/bigint.rs b/rust/syn/bigint.rs
index 5397d6beee1d..d8f7b8ca6863 100644
--- a/rust/syn/bigint.rs
+++ b/rust/syn/bigint.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use std::ops::{AddAssign, MulAssign};
// For implementing base10_digits() accessor on LitInt.
diff --git a/rust/syn/buffer.rs b/rust/syn/buffer.rs
index 0d5cf30d5794..9a600f527f7a 100644
--- a/rust/syn/buffer.rs
+++ b/rust/syn/buffer.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! A stably addressed token buffer supporting efficient traversal based on a
//! cheaply copyable cursor.
//!
diff --git a/rust/syn/custom_keyword.rs b/rust/syn/custom_keyword.rs
index a3ec9d4cb701..916f4c1bb820 100644
--- a/rust/syn/custom_keyword.rs
+++ b/rust/syn/custom_keyword.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
/// Define a type that supports parsing and printing a given identifier as if it
/// were a keyword.
///
diff --git a/rust/syn/custom_punctuation.rs b/rust/syn/custom_punctuation.rs
index 118a8453daab..5cb8281623f8 100644
--- a/rust/syn/custom_punctuation.rs
+++ b/rust/syn/custom_punctuation.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
/// Define a type that supports parsing and printing a multi-character symbol
/// as if it were a punctuation token.
///
diff --git a/rust/syn/data.rs b/rust/syn/data.rs
index 3b466618f8ab..e4002648c635 100644
--- a/rust/syn/data.rs
+++ b/rust/syn/data.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use super::*;
use crate::punctuated::Punctuated;
diff --git a/rust/syn/derive.rs b/rust/syn/derive.rs
index af9bb91b7a8d..455e72938067 100644
--- a/rust/syn/derive.rs
+++ b/rust/syn/derive.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use super::*;
use crate::punctuated::Punctuated;
diff --git a/rust/syn/discouraged.rs b/rust/syn/discouraged.rs
index a46129b6a159..9843c82d54d2 100644
--- a/rust/syn/discouraged.rs
+++ b/rust/syn/discouraged.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! Extensions to the parsing API with niche applicability.
use super::*;
diff --git a/rust/syn/error.rs b/rust/syn/error.rs
index e301367d5e4e..262f3ab8cab7 100644
--- a/rust/syn/error.rs
+++ b/rust/syn/error.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
#[cfg(feature = "parsing")]
use crate::buffer::Cursor;
use crate::thread::ThreadBound;
diff --git a/rust/syn/export.rs b/rust/syn/export.rs
index f478d091ea12..5ea4dd75eb4a 100644
--- a/rust/syn/export.rs
+++ b/rust/syn/export.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
pub use std::clone::Clone;
pub use std::cmp::{Eq, PartialEq};
pub use std::default::Default;
diff --git a/rust/syn/expr.rs b/rust/syn/expr.rs
index 93a59b0e20d7..54155892fe04 100644
--- a/rust/syn/expr.rs
+++ b/rust/syn/expr.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use super::*;
use crate::punctuated::Punctuated;
#[cfg(feature = "full")]
diff --git a/rust/syn/ext.rs b/rust/syn/ext.rs
index 98d5550f4836..c30a89902f7f 100644
--- a/rust/syn/ext.rs
+++ b/rust/syn/ext.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! Extension traits to provide parsing methods on foreign types.
//!
//! *This module is available only if Syn is built with the `"parsing"` feature.*
diff --git a/rust/syn/file.rs b/rust/syn/file.rs
index 280484f980d1..39796e1e3862 100644
--- a/rust/syn/file.rs
+++ b/rust/syn/file.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use super::*;
ast_struct! {
diff --git a/rust/syn/gen/clone.rs b/rust/syn/gen/clone.rs
index a413e3ec700c..82a3f51d8c52 100644
--- a/rust/syn/gen/clone.rs
+++ b/rust/syn/gen/clone.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
// This file is @generated by syn-internal-codegen.
// It is not intended for manual editing.
diff --git a/rust/syn/gen/debug.rs b/rust/syn/gen/debug.rs
index a1f0afa790c0..488433cd8f01 100644
--- a/rust/syn/gen/debug.rs
+++ b/rust/syn/gen/debug.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
// This file is @generated by syn-internal-codegen.
// It is not intended for manual editing.
diff --git a/rust/syn/gen/eq.rs b/rust/syn/gen/eq.rs
index 20acb809d858..4af1c9c6a3d1 100644
--- a/rust/syn/gen/eq.rs
+++ b/rust/syn/gen/eq.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
// This file is @generated by syn-internal-codegen.
// It is not intended for manual editing.
diff --git a/rust/syn/gen/fold.rs b/rust/syn/gen/fold.rs
index 98bb5794aab9..d503f617d631 100644
--- a/rust/syn/gen/fold.rs
+++ b/rust/syn/gen/fold.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
// This file is @generated by syn-internal-codegen.
// It is not intended for manual editing.
diff --git a/rust/syn/gen/hash.rs b/rust/syn/gen/hash.rs
index d0400e19d6d8..8fbc237079ef 100644
--- a/rust/syn/gen/hash.rs
+++ b/rust/syn/gen/hash.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
// This file is @generated by syn-internal-codegen.
// It is not intended for manual editing.
diff --git a/rust/syn/gen/visit.rs b/rust/syn/gen/visit.rs
index 19ddd2e72602..0285e4dbee23 100644
--- a/rust/syn/gen/visit.rs
+++ b/rust/syn/gen/visit.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
// This file is @generated by syn-internal-codegen.
// It is not intended for manual editing.
diff --git a/rust/syn/gen/visit_mut.rs b/rust/syn/gen/visit_mut.rs
index 239709d19410..4a46346c1807 100644
--- a/rust/syn/gen/visit_mut.rs
+++ b/rust/syn/gen/visit_mut.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
// This file is @generated by syn-internal-codegen.
// It is not intended for manual editing.
diff --git a/rust/syn/gen_helper.rs b/rust/syn/gen_helper.rs
index b2796126a339..3850100b247f 100644
--- a/rust/syn/gen_helper.rs
+++ b/rust/syn/gen_helper.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
#[cfg(feature = "fold")]
pub mod fold {
use crate::fold::Fold;
diff --git a/rust/syn/generics.rs b/rust/syn/generics.rs
index 9c2802f87b8e..de8a4b2ce1d5 100644
--- a/rust/syn/generics.rs
+++ b/rust/syn/generics.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use super::*;
use crate::punctuated::{Iter, IterMut, Punctuated};
#[cfg(all(feature = "printing", feature = "extra-traits"))]
diff --git a/rust/syn/group.rs b/rust/syn/group.rs
index 7fd273c1de00..b02e79db2c20 100644
--- a/rust/syn/group.rs
+++ b/rust/syn/group.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use crate::error::Result;
use crate::parse::ParseBuffer;
use crate::token;
diff --git a/rust/syn/ident.rs b/rust/syn/ident.rs
index 8e3d8bda9746..6292a05f59f7 100644
--- a/rust/syn/ident.rs
+++ b/rust/syn/ident.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
#[cfg(feature = "parsing")]
use crate::buffer::Cursor;
#[cfg(feature = "parsing")]
diff --git a/rust/syn/item.rs b/rust/syn/item.rs
index a1ef7ab43eff..b256d94a25e2 100644
--- a/rust/syn/item.rs
+++ b/rust/syn/item.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use super::*;
use crate::derive::{Data, DataEnum, DataStruct, DataUnion, DeriveInput};
use crate::punctuated::Punctuated;
diff --git a/rust/syn/lib.rs b/rust/syn/lib.rs
index 7d4df7110c6b..c32d6b3cb8cb 100644
--- a/rust/syn/lib.rs
+++ b/rust/syn/lib.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! [![github]](https://github.com/dtolnay/syn) [![crates-io]](https://crates.io/crates/syn) [![docs-rs]](crate)
//!
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
diff --git a/rust/syn/lifetime.rs b/rust/syn/lifetime.rs
index 5dc1753a84fa..23b644e8ea72 100644
--- a/rust/syn/lifetime.rs
+++ b/rust/syn/lifetime.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use proc_macro2::{Ident, Span};
use std::cmp::Ordering;
use std::fmt::{self, Display};
diff --git a/rust/syn/lit.rs b/rust/syn/lit.rs
index 130b40ed1fb7..51229eb165f1 100644
--- a/rust/syn/lit.rs
+++ b/rust/syn/lit.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
#[cfg(feature = "parsing")]
use crate::lookahead;
#[cfg(feature = "parsing")]
diff --git a/rust/syn/lookahead.rs b/rust/syn/lookahead.rs
index 0cf5cf5a83be..118fdbbe11ff 100644
--- a/rust/syn/lookahead.rs
+++ b/rust/syn/lookahead.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use crate::buffer::Cursor;
use crate::error::{self, Error};
use crate::sealed::lookahead::Sealed;
diff --git a/rust/syn/mac.rs b/rust/syn/mac.rs
index 3d84f8e48bf1..99dd6accd95d 100644
--- a/rust/syn/mac.rs
+++ b/rust/syn/mac.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use super::*;
use crate::token::{Brace, Bracket, Paren};
use proc_macro2::TokenStream;
diff --git a/rust/syn/macros.rs b/rust/syn/macros.rs
index da10a1a55057..86e64dc38992 100644
--- a/rust/syn/macros.rs
+++ b/rust/syn/macros.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
#[cfg_attr(
not(any(feature = "full", feature = "derive")),
allow(unknown_lints, unused_macro_rules)
diff --git a/rust/syn/op.rs b/rust/syn/op.rs
index b8ef9a7fbd08..96ccd7d3e1f3 100644
--- a/rust/syn/op.rs
+++ b/rust/syn/op.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
ast_enum! {
/// A binary operator: `+`, `+=`, `&`.
///
diff --git a/rust/syn/parse.rs b/rust/syn/parse.rs
index bac4ca05abf9..aa07edc885dc 100644
--- a/rust/syn/parse.rs
+++ b/rust/syn/parse.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! Parsing interface for parsing a token stream into a syntax tree node.
//!
//! Parsing in Syn is built on parser functions that take in a [`ParseStream`]
diff --git a/rust/syn/parse_macro_input.rs b/rust/syn/parse_macro_input.rs
index 6163cd70af7b..8c68fdda2f71 100644
--- a/rust/syn/parse_macro_input.rs
+++ b/rust/syn/parse_macro_input.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
/// Parse the input TokenStream of a macro, triggering a compile error if the
/// tokens fail to parse.
///
diff --git a/rust/syn/parse_quote.rs b/rust/syn/parse_quote.rs
index 8deae4e52652..4eb592d099f6 100644
--- a/rust/syn/parse_quote.rs
+++ b/rust/syn/parse_quote.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
/// Quasi-quotation macro that accepts input like the [`quote!`] macro but uses
/// type inference to figure out a return type for those tokens.
///
diff --git a/rust/syn/pat.rs b/rust/syn/pat.rs
index b279186aa063..a2c2004e2b64 100644
--- a/rust/syn/pat.rs
+++ b/rust/syn/pat.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use super::*;
use crate::punctuated::Punctuated;
use proc_macro2::TokenStream;
diff --git a/rust/syn/path.rs b/rust/syn/path.rs
index 6cdb43ac5416..b69c9ceb11d9 100644
--- a/rust/syn/path.rs
+++ b/rust/syn/path.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use super::*;
use crate::punctuated::Punctuated;
diff --git a/rust/syn/print.rs b/rust/syn/print.rs
index da4e07e3b3cb..abc287ee9501 100644
--- a/rust/syn/print.rs
+++ b/rust/syn/print.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use proc_macro2::TokenStream;
use quote::ToTokens;
diff --git a/rust/syn/punctuated.rs b/rust/syn/punctuated.rs
index 0fe1078cf404..b1ad84b301fa 100644
--- a/rust/syn/punctuated.rs
+++ b/rust/syn/punctuated.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! A punctuated sequence of syntax tree nodes separated by punctuation.
//!
//! Lots of things in Rust are punctuated sequences.
diff --git a/rust/syn/reserved.rs b/rust/syn/reserved.rs
index abfdf43a91e0..f6bb508a6907 100644
--- a/rust/syn/reserved.rs
+++ b/rust/syn/reserved.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
// Type for a syntax tree node that is reserved for future use.
//
// For example ExprReference contains a field `raw` of type Reserved. If `&raw
diff --git a/rust/syn/sealed.rs b/rust/syn/sealed.rs
index 0b11bc99a1e1..02abcb1cd90d 100644
--- a/rust/syn/sealed.rs
+++ b/rust/syn/sealed.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
#[cfg(feature = "parsing")]
pub mod lookahead {
pub trait Sealed: Copy {}
diff --git a/rust/syn/span.rs b/rust/syn/span.rs
index 27a7fe846d39..0574c49d7835 100644
--- a/rust/syn/span.rs
+++ b/rust/syn/span.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use proc_macro2::Span;
pub trait IntoSpans<S> {
diff --git a/rust/syn/spanned.rs b/rust/syn/spanned.rs
index d51ffb3fa5f5..ee65ea85841a 100644
--- a/rust/syn/spanned.rs
+++ b/rust/syn/spanned.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! A trait that can provide the `Span` of the complete contents of a syntax
//! tree node.
//!
diff --git a/rust/syn/stmt.rs b/rust/syn/stmt.rs
index 58bd013ecf4c..41b9378537ba 100644
--- a/rust/syn/stmt.rs
+++ b/rust/syn/stmt.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use super::*;
ast_struct! {
diff --git a/rust/syn/thread.rs b/rust/syn/thread.rs
index 9e5d8ad85efc..d535c913a9aa 100644
--- a/rust/syn/thread.rs
+++ b/rust/syn/thread.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use std::fmt::{self, Debug};
use std::thread::{self, ThreadId};
diff --git a/rust/syn/token.rs b/rust/syn/token.rs
index 2208b07d0454..b9984c5782fb 100644
--- a/rust/syn/token.rs
+++ b/rust/syn/token.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! Tokens representing Rust punctuation, keywords, and delimiters.
//!
//! The type names in this module can be difficult to keep straight, so we
diff --git a/rust/syn/tt.rs b/rust/syn/tt.rs
index d87c0ed4dc1c..b4a9a3876f7d 100644
--- a/rust/syn/tt.rs
+++ b/rust/syn/tt.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use proc_macro2::{Delimiter, TokenStream, TokenTree};
use std::hash::{Hash, Hasher};
diff --git a/rust/syn/ty.rs b/rust/syn/ty.rs
index 4068be3c754b..ecdf93b952a5 100644
--- a/rust/syn/ty.rs
+++ b/rust/syn/ty.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use super::*;
use crate::punctuated::Punctuated;
use proc_macro2::TokenStream;
diff --git a/rust/syn/verbatim.rs b/rust/syn/verbatim.rs
index 58cf68d17af8..15e651164a02 100644
--- a/rust/syn/verbatim.rs
+++ b/rust/syn/verbatim.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use crate::parse::{ParseBuffer, ParseStream};
use proc_macro2::{Delimiter, TokenStream};
use std::cmp::Ordering;
diff --git a/rust/syn/whitespace.rs b/rust/syn/whitespace.rs
index 7be082e1a26d..99c2d3ab28f9 100644
--- a/rust/syn/whitespace.rs
+++ b/rust/syn/whitespace.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
pub fn skip(mut s: &str) -> &str {
'skip: while !s.is_empty() {
let byte = s.as_bytes()[0];
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 28/80] rust: syn: remove `unicode-ident` dependency
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (24 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 27/80] rust: syn: " Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 30/80] rust: serde: add `no_fp_fmt_parse` support Ariel Miculas
` (51 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Miguel Ojeda
From: Miguel Ojeda <ojeda@kernel.org>
The `syn` crate depends on the `unicode-ident` crate
to determine whether characters have the XID_Start or XID_Continue
properties according to Unicode Standard Annex #31.
However, we only need ASCII identifiers in the kernel, thus we can
simplify the check and remove completely that dependency.
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
---
rust/syn/ident.rs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/rust/syn/ident.rs b/rust/syn/ident.rs
index 6292a05f59f7..9ae2f0cbd83e 100644
--- a/rust/syn/ident.rs
+++ b/rust/syn/ident.rs
@@ -91,11 +91,11 @@ fn from(token: Token![_]) -> Ident {
pub fn xid_ok(symbol: &str) -> bool {
let mut chars = symbol.chars();
let first = chars.next().unwrap();
- if !(first == '_' || unicode_ident::is_xid_start(first)) {
+ if !(first == '_' || first.is_ascii_alphabetic()) {
return false;
}
for ch in chars {
- if !unicode_ident::is_xid_continue(ch) {
+ if !(ch == '_' || ch.is_ascii_alphanumeric()) {
return false;
}
}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 30/80] rust: serde: add `no_fp_fmt_parse` support
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (25 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 28/80] rust: syn: remove `unicode-ident` dependency Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 31/80] rust: serde: add SPDX License Identifiers Ariel Miculas
` (50 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Miguel Ojeda
From: Miguel Ojeda <ojeda@kernel.org>
We do not have formatting for floating point in the kernel,
thus simple compile out all that.
Probably we should name it differently, more similar to `integer128`.
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
---
rust/serde/de/ignored_any.rs | 1 +
rust/serde/de/impls.rs | 2 ++
rust/serde/de/mod.rs | 16 ++++++++++++++--
rust/serde/de/value.rs | 2 ++
rust/serde/private/ser.rs | 4 ++++
rust/serde/ser/fmt.rs | 8 ++++++--
rust/serde/ser/impls.rs | 2 ++
rust/serde/ser/mod.rs | 2 ++
8 files changed, 33 insertions(+), 4 deletions(-)
diff --git a/rust/serde/de/ignored_any.rs b/rust/serde/de/ignored_any.rs
index 9ed438e79795..7b65792e77c0 100644
--- a/rust/serde/de/ignored_any.rs
+++ b/rust/serde/de/ignored_any.rs
@@ -152,6 +152,7 @@ fn visit_u128<E>(self, x: u128) -> Result<Self::Value, E> {
}
}
+ #[cfg(not(no_fp_fmt_parse))]
#[inline]
fn visit_f64<E>(self, x: f64) -> Result<Self::Value, E> {
let _ = x;
diff --git a/rust/serde/de/impls.rs b/rust/serde/de/impls.rs
index a2e2c4856df3..002bbf74d65b 100644
--- a/rust/serde/de/impls.rs
+++ b/rust/serde/de/impls.rs
@@ -350,6 +350,7 @@ fn $visit<E>(self, v: $ty) -> Result<Self::Value, E>
uint_to_self!(u32:visit_u32 u64:visit_u64);
}
+#[cfg(not(no_fp_fmt_parse))]
impl_deserialize_num! {
f32, deserialize_f32
num_self!(f32:visit_f32);
@@ -358,6 +359,7 @@ fn $visit<E>(self, v: $ty) -> Result<Self::Value, E>
num_as_self!(u8:visit_u8 u16:visit_u16 u32:visit_u32 u64:visit_u64);
}
+#[cfg(not(no_fp_fmt_parse))]
impl_deserialize_num! {
f64, deserialize_f64
num_self!(f64:visit_f64);
diff --git a/rust/serde/de/mod.rs b/rust/serde/de/mod.rs
index ca29ec610bfb..017964f0d0d0 100644
--- a/rust/serde/de/mod.rs
+++ b/rust/serde/de/mod.rs
@@ -344,6 +344,7 @@ pub enum Unexpected<'a> {
/// The input contained a floating point `f32` or `f64` that was not
/// expected.
+ #[cfg(not(no_fp_fmt_parse))]
Float(f64),
/// The input contained a `char` that was not expected.
@@ -400,6 +401,7 @@ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
Bool(b) => write!(formatter, "boolean `{}`", b),
Unsigned(i) => write!(formatter, "integer `{}`", i),
Signed(i) => write!(formatter, "integer `{}`", i),
+ #[cfg(not(no_fp_fmt_parse))]
Float(f) => write!(formatter, "floating point `{}`", f),
Char(c) => write!(formatter, "character `{}`", c),
Str(s) => write!(formatter, "string {:?}", s),
@@ -996,12 +998,20 @@ fn deserialize_u128<V>(self, visitor: V) -> Result<V::Value, Self::Error>
/// Hint that the `Deserialize` type is expecting a `f32` value.
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
- V: Visitor<'de>;
+ V: Visitor<'de>
+ {
+ let _ = visitor;
+ Err(Error::custom("f32 is not supported"))
+ }
/// Hint that the `Deserialize` type is expecting a `f64` value.
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
- V: Visitor<'de>;
+ V: Visitor<'de>
+ {
+ let _ = visitor;
+ Err(Error::custom("f64 is not supported"))
+ }
/// Hint that the `Deserialize` type is expecting a `char` value.
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
@@ -1446,6 +1456,7 @@ fn visit_u128<E>(self, v: u128) -> Result<Self::Value, E>
/// The default implementation forwards to [`visit_f64`].
///
/// [`visit_f64`]: #method.visit_f64
+ #[cfg(not(no_fp_fmt_parse))]
fn visit_f32<E>(self, v: f32) -> Result<Self::Value, E>
where
E: Error,
@@ -1456,6 +1467,7 @@ fn visit_f32<E>(self, v: f32) -> Result<Self::Value, E>
/// The input contains an `f64`.
///
/// The default implementation fails with a type error.
+ #[cfg(not(no_fp_fmt_parse))]
fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
where
E: Error,
diff --git a/rust/serde/de/value.rs b/rust/serde/de/value.rs
index 5d88862159b8..d847b8ec071c 100644
--- a/rust/serde/de/value.rs
+++ b/rust/serde/de/value.rs
@@ -298,7 +298,9 @@ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
primitive_deserializer!(u16, "a `u16`.", U16Deserializer, visit_u16);
primitive_deserializer!(u64, "a `u64`.", U64Deserializer, visit_u64);
primitive_deserializer!(usize, "a `usize`.", UsizeDeserializer, visit_u64 as u64);
+#[cfg(not(no_fp_fmt_parse))]
primitive_deserializer!(f32, "an `f32`.", F32Deserializer, visit_f32);
+#[cfg(not(no_fp_fmt_parse))]
primitive_deserializer!(f64, "an `f64`.", F64Deserializer, visit_f64);
primitive_deserializer!(char, "a `char`.", CharDeserializer, visit_char);
diff --git a/rust/serde/private/ser.rs b/rust/serde/private/ser.rs
index 528e8c125a39..fc9e7e6d7426 100644
--- a/rust/serde/private/ser.rs
+++ b/rust/serde/private/ser.rs
@@ -46,6 +46,7 @@ struct TaggedSerializer<S> {
enum Unsupported {
Boolean,
Integer,
+ #[cfg(not(no_fp_fmt_parse))]
Float,
Char,
String,
@@ -64,6 +65,7 @@ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match *self {
Unsupported::Boolean => formatter.write_str("a boolean"),
Unsupported::Integer => formatter.write_str("an integer"),
+ #[cfg(not(no_fp_fmt_parse))]
Unsupported::Float => formatter.write_str("a float"),
Unsupported::Char => formatter.write_str("a char"),
Unsupported::String => formatter.write_str("a string"),
@@ -150,10 +152,12 @@ fn serialize_u64(self, _: u64) -> Result<Self::Ok, Self::Error> {
Err(self.bad_type(Unsupported::Integer))
}
+ #[cfg(not(no_fp_fmt_parse))]
fn serialize_f32(self, _: f32) -> Result<Self::Ok, Self::Error> {
Err(self.bad_type(Unsupported::Float))
}
+ #[cfg(not(no_fp_fmt_parse))]
fn serialize_f64(self, _: f64) -> Result<Self::Ok, Self::Error> {
Err(self.bad_type(Unsupported::Float))
}
diff --git a/rust/serde/ser/fmt.rs b/rust/serde/ser/fmt.rs
index e7e09a1bfe94..94c3054fe36b 100644
--- a/rust/serde/ser/fmt.rs
+++ b/rust/serde/ser/fmt.rs
@@ -55,13 +55,17 @@ impl<'a, 'b> Serializer for &'a mut fmt::Formatter<'b> {
serialize_u16: u16,
serialize_u32: u32,
serialize_u64: u64,
- serialize_f32: f32,
- serialize_f64: f64,
serialize_char: char,
serialize_str: &str,
serialize_unit_struct: &'static str,
}
+ #[cfg(not(no_fp_fmt_parse))]
+ fmt_primitives! {
+ serialize_f32: f32,
+ serialize_f64: f64,
+ }
+
serde_if_integer128! {
fmt_primitives! {
serialize_i128: i128,
diff --git a/rust/serde/ser/impls.rs b/rust/serde/ser/impls.rs
index a79326e5c558..639e480e252d 100644
--- a/rust/serde/ser/impls.rs
+++ b/rust/serde/ser/impls.rs
@@ -29,7 +29,9 @@ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
primitive_impl!(u16, serialize_u16);
primitive_impl!(u32, serialize_u32);
primitive_impl!(u64, serialize_u64);
+#[cfg(not(no_fp_fmt_parse))]
primitive_impl!(f32, serialize_f32);
+#[cfg(not(no_fp_fmt_parse))]
primitive_impl!(f64, serialize_f64);
primitive_impl!(char, serialize_char);
diff --git a/rust/serde/ser/mod.rs b/rust/serde/ser/mod.rs
index 5c45426e802e..13ea792c58ef 100644
--- a/rust/serde/ser/mod.rs
+++ b/rust/serde/ser/mod.rs
@@ -644,6 +644,7 @@ fn serialize_u128(self, v: u128) -> Result<Self::Ok, Self::Error> {
/// }
/// }
/// ```
+ #[cfg(not(no_fp_fmt_parse))]
fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error>;
/// Serialize an `f64` value.
@@ -662,6 +663,7 @@ fn serialize_u128(self, v: u128) -> Result<Self::Ok, Self::Error> {
/// }
/// }
/// ```
+ #[cfg(not(no_fp_fmt_parse))]
fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error>;
/// Serialize a character.
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 31/80] rust: serde: add SPDX License Identifiers
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (26 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 30/80] rust: serde: add `no_fp_fmt_parse` support Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-10 0:19 ` Kent Overstreet
2023-06-10 0:25 ` Kent Overstreet
2023-06-09 6:30 ` [PATCH 33/80] rust: serde_derive: " Ariel Miculas
` (49 subsequent siblings)
77 siblings, 2 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Miguel Ojeda
From: Miguel Ojeda <ojeda@kernel.org>
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
---
rust/serde/de/format.rs | 2 ++
rust/serde/de/ignored_any.rs | 2 ++
rust/serde/de/impls.rs | 2 ++
rust/serde/de/mod.rs | 2 ++
rust/serde/de/seed.rs | 2 ++
rust/serde/de/utf8.rs | 2 ++
rust/serde/de/value.rs | 2 ++
rust/serde/integer128.rs | 2 ++
rust/serde/lib.rs | 2 ++
rust/serde/macros.rs | 2 ++
rust/serde/private/de.rs | 2 ++
rust/serde/private/doc.rs | 2 ++
rust/serde/private/mod.rs | 2 ++
rust/serde/private/ser.rs | 2 ++
rust/serde/private/size_hint.rs | 2 ++
rust/serde/ser/fmt.rs | 2 ++
rust/serde/ser/impls.rs | 2 ++
rust/serde/ser/impossible.rs | 2 ++
rust/serde/ser/mod.rs | 2 ++
rust/serde/std_error.rs | 2 ++
20 files changed, 40 insertions(+)
diff --git a/rust/serde/de/format.rs b/rust/serde/de/format.rs
index f14580b8d162..33233766fc27 100644
--- a/rust/serde/de/format.rs
+++ b/rust/serde/de/format.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use lib::fmt::{self, Write};
use lib::str;
diff --git a/rust/serde/de/ignored_any.rs b/rust/serde/de/ignored_any.rs
index 7b65792e77c0..d2cc83978b50 100644
--- a/rust/serde/de/ignored_any.rs
+++ b/rust/serde/de/ignored_any.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use lib::*;
use de::{
diff --git a/rust/serde/de/impls.rs b/rust/serde/de/impls.rs
index 002bbf74d65b..9377f5eee650 100644
--- a/rust/serde/de/impls.rs
+++ b/rust/serde/de/impls.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use lib::*;
use de::{
diff --git a/rust/serde/de/mod.rs b/rust/serde/de/mod.rs
index 017964f0d0d0..6da226da8ffc 100644
--- a/rust/serde/de/mod.rs
+++ b/rust/serde/de/mod.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! Generic data structure deserialization framework.
//!
//! The two most important traits in this module are [`Deserialize`] and
diff --git a/rust/serde/de/seed.rs b/rust/serde/de/seed.rs
index 13b7ea461b0c..e0ceeddb100a 100644
--- a/rust/serde/de/seed.rs
+++ b/rust/serde/de/seed.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use de::{Deserialize, DeserializeSeed, Deserializer};
/// A DeserializeSeed helper for implementing deserialize_in_place Visitors.
diff --git a/rust/serde/de/utf8.rs b/rust/serde/de/utf8.rs
index 576fd03cfa79..1fb30488967c 100644
--- a/rust/serde/de/utf8.rs
+++ b/rust/serde/de/utf8.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use lib::*;
const TAG_CONT: u8 = 0b1000_0000;
diff --git a/rust/serde/de/value.rs b/rust/serde/de/value.rs
index d847b8ec071c..dc7be06e9257 100644
--- a/rust/serde/de/value.rs
+++ b/rust/serde/de/value.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! Building blocks for deserializing basic values using the `IntoDeserializer`
//! trait.
//!
diff --git a/rust/serde/integer128.rs b/rust/serde/integer128.rs
index 904c2a233f8a..b47862335f28 100644
--- a/rust/serde/integer128.rs
+++ b/rust/serde/integer128.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
/// Conditional compilation depending on whether Serde is built with support for
/// 128-bit integers.
///
diff --git a/rust/serde/lib.rs b/rust/serde/lib.rs
index 699573d95e4b..c3694fef3321 100644
--- a/rust/serde/lib.rs
+++ b/rust/serde/lib.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! # Serde
//!
//! Serde is a framework for ***ser***ializing and ***de***serializing Rust data
diff --git a/rust/serde/macros.rs b/rust/serde/macros.rs
index 6502a23a7af7..0b0a9ad823e2 100644
--- a/rust/serde/macros.rs
+++ b/rust/serde/macros.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
// Super explicit first paragraph because this shows up at the top level and
// trips up people who are just looking for basic Serialize / Deserialize
// documentation.
diff --git a/rust/serde/private/de.rs b/rust/serde/private/de.rs
index e9c693d4dd31..707c053b7fce 100644
--- a/rust/serde/private/de.rs
+++ b/rust/serde/private/de.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use lib::*;
use de::value::{BorrowedBytesDeserializer, BytesDeserializer};
diff --git a/rust/serde/private/doc.rs b/rust/serde/private/doc.rs
index f597af84451d..271e0c4f7b70 100644
--- a/rust/serde/private/doc.rs
+++ b/rust/serde/private/doc.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
// Used only by Serde doc tests. Not public API.
use lib::*;
diff --git a/rust/serde/private/mod.rs b/rust/serde/private/mod.rs
index e896902360ba..c05a0ea5c8e3 100644
--- a/rust/serde/private/mod.rs
+++ b/rust/serde/private/mod.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
#[cfg(not(no_serde_derive))]
pub mod de;
#[cfg(not(no_serde_derive))]
diff --git a/rust/serde/private/ser.rs b/rust/serde/private/ser.rs
index fc9e7e6d7426..6efd84e5fe57 100644
--- a/rust/serde/private/ser.rs
+++ b/rust/serde/private/ser.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use lib::*;
use ser::{self, Impossible, Serialize, SerializeMap, SerializeStruct, Serializer};
diff --git a/rust/serde/private/size_hint.rs b/rust/serde/private/size_hint.rs
index ca71e616bb69..f050fca59deb 100644
--- a/rust/serde/private/size_hint.rs
+++ b/rust/serde/private/size_hint.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use lib::*;
pub fn from_bounds<I>(iter: &I) -> Option<usize>
diff --git a/rust/serde/ser/fmt.rs b/rust/serde/ser/fmt.rs
index 94c3054fe36b..7906cf76d99f 100644
--- a/rust/serde/ser/fmt.rs
+++ b/rust/serde/ser/fmt.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use lib::*;
use ser::{Error, Impossible, Serialize, Serializer};
diff --git a/rust/serde/ser/impls.rs b/rust/serde/ser/impls.rs
index 639e480e252d..d70533eb41c3 100644
--- a/rust/serde/ser/impls.rs
+++ b/rust/serde/ser/impls.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use lib::*;
use ser::{Error, Serialize, SerializeTuple, Serializer};
diff --git a/rust/serde/ser/impossible.rs b/rust/serde/ser/impossible.rs
index e8df9ca7aad2..e25270ba70c1 100644
--- a/rust/serde/ser/impossible.rs
+++ b/rust/serde/ser/impossible.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! This module contains `Impossible` serializer and its implementations.
use lib::*;
diff --git a/rust/serde/ser/mod.rs b/rust/serde/ser/mod.rs
index 13ea792c58ef..9a5e47d8c5c8 100644
--- a/rust/serde/ser/mod.rs
+++ b/rust/serde/ser/mod.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! Generic data structure serialization framework.
//!
//! The two most important traits in this module are [`Serialize`] and
diff --git a/rust/serde/std_error.rs b/rust/serde/std_error.rs
index 1055e0ffbcb2..b730d38a1e07 100644
--- a/rust/serde/std_error.rs
+++ b/rust/serde/std_error.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use lib::{Debug, Display};
/// Either a re-export of std::error::Error or a new identical trait, depending
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 33/80] rust: serde_derive: add SPDX License Identifiers
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (27 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 31/80] rust: serde: add SPDX License Identifiers Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 34/80] rust: Kbuild: enable `proc-macro2`, `quote`, `syn`, `serde` and `serde_derive` Ariel Miculas
` (48 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Miguel Ojeda
From: Miguel Ojeda <ojeda@kernel.org>
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
---
rust/serde_derive/bound.rs | 2 ++
rust/serde_derive/de.rs | 2 ++
rust/serde_derive/dummy.rs | 2 ++
rust/serde_derive/fragment.rs | 2 ++
rust/serde_derive/internals/ast.rs | 2 ++
rust/serde_derive/internals/attr.rs | 2 ++
rust/serde_derive/internals/case.rs | 2 ++
rust/serde_derive/internals/check.rs | 2 ++
rust/serde_derive/internals/ctxt.rs | 2 ++
rust/serde_derive/internals/mod.rs | 2 ++
rust/serde_derive/internals/receiver.rs | 2 ++
rust/serde_derive/internals/respan.rs | 2 ++
rust/serde_derive/internals/symbol.rs | 2 ++
rust/serde_derive/lib.rs | 2 ++
rust/serde_derive/pretend.rs | 2 ++
rust/serde_derive/ser.rs | 2 ++
rust/serde_derive/this.rs | 2 ++
rust/serde_derive/try.rs | 2 ++
18 files changed, 36 insertions(+)
diff --git a/rust/serde_derive/bound.rs b/rust/serde_derive/bound.rs
index 74c95069c353..dc6b5907ff80 100644
--- a/rust/serde_derive/bound.rs
+++ b/rust/serde_derive/bound.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use std::collections::HashSet;
use syn;
diff --git a/rust/serde_derive/de.rs b/rust/serde_derive/de.rs
index 884c6335c7ae..06780ac192e8 100644
--- a/rust/serde_derive/de.rs
+++ b/rust/serde_derive/de.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use proc_macro2::{Literal, Span, TokenStream};
use quote::ToTokens;
use syn::punctuated::Punctuated;
diff --git a/rust/serde_derive/dummy.rs b/rust/serde_derive/dummy.rs
index 2be502713293..3530303f23f2 100644
--- a/rust/serde_derive/dummy.rs
+++ b/rust/serde_derive/dummy.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use proc_macro2::{Ident, TokenStream};
use quote::format_ident;
diff --git a/rust/serde_derive/fragment.rs b/rust/serde_derive/fragment.rs
index 324504aa4661..a62ed9114224 100644
--- a/rust/serde_derive/fragment.rs
+++ b/rust/serde_derive/fragment.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::token;
diff --git a/rust/serde_derive/internals/ast.rs b/rust/serde_derive/internals/ast.rs
index 2a6950b2a710..139540571820 100644
--- a/rust/serde_derive/internals/ast.rs
+++ b/rust/serde_derive/internals/ast.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! A Serde ast, parsed from the Syn ast and ready to generate Rust code.
use internals::attr;
diff --git a/rust/serde_derive/internals/attr.rs b/rust/serde_derive/internals/attr.rs
index 5ff190f02e77..d013215a5e91 100644
--- a/rust/serde_derive/internals/attr.rs
+++ b/rust/serde_derive/internals/attr.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use internals::symbol::*;
use internals::{ungroup, Ctxt};
use proc_macro2::{Spacing, Span, TokenStream, TokenTree};
diff --git a/rust/serde_derive/internals/case.rs b/rust/serde_derive/internals/case.rs
index 554505160ea4..6a75c7460e67 100644
--- a/rust/serde_derive/internals/case.rs
+++ b/rust/serde_derive/internals/case.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! Code to convert the Rust-styled field/variant (e.g. `my_field`, `MyType`) to the
//! case of the source (e.g. `my-field`, `MY_FIELD`).
diff --git a/rust/serde_derive/internals/check.rs b/rust/serde_derive/internals/check.rs
index eb1297aa73bf..188499508684 100644
--- a/rust/serde_derive/internals/check.rs
+++ b/rust/serde_derive/internals/check.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use internals::ast::{Container, Data, Field, Style};
use internals::attr::{Identifier, TagType};
use internals::{ungroup, Ctxt, Derive};
diff --git a/rust/serde_derive/internals/ctxt.rs b/rust/serde_derive/internals/ctxt.rs
index d692c2a449d3..7a0a9f92f992 100644
--- a/rust/serde_derive/internals/ctxt.rs
+++ b/rust/serde_derive/internals/ctxt.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use quote::ToTokens;
use std::cell::RefCell;
use std::fmt::Display;
diff --git a/rust/serde_derive/internals/mod.rs b/rust/serde_derive/internals/mod.rs
index 5e9f416c46f2..9de56241adf3 100644
--- a/rust/serde_derive/internals/mod.rs
+++ b/rust/serde_derive/internals/mod.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
pub mod ast;
pub mod attr;
diff --git a/rust/serde_derive/internals/receiver.rs b/rust/serde_derive/internals/receiver.rs
index b08c67096844..fc5bc1b89db2 100644
--- a/rust/serde_derive/internals/receiver.rs
+++ b/rust/serde_derive/internals/receiver.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use internals::respan::respan;
use proc_macro2::Span;
use quote::ToTokens;
diff --git a/rust/serde_derive/internals/respan.rs b/rust/serde_derive/internals/respan.rs
index dcec7017b315..41aca1250c21 100644
--- a/rust/serde_derive/internals/respan.rs
+++ b/rust/serde_derive/internals/respan.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use proc_macro2::{Group, Span, TokenStream, TokenTree};
pub(crate) fn respan(stream: TokenStream, span: Span) -> TokenStream {
diff --git a/rust/serde_derive/internals/symbol.rs b/rust/serde_derive/internals/symbol.rs
index 9606edb5f236..fb7cda06056f 100644
--- a/rust/serde_derive/internals/symbol.rs
+++ b/rust/serde_derive/internals/symbol.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use std::fmt::{self, Display};
use syn::{Ident, Path};
diff --git a/rust/serde_derive/lib.rs b/rust/serde_derive/lib.rs
index d3d136cb092c..de83885b9a4c 100644
--- a/rust/serde_derive/lib.rs
+++ b/rust/serde_derive/lib.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! This crate provides Serde's two derive macros.
//!
//! ```edition2018
diff --git a/rust/serde_derive/pretend.rs b/rust/serde_derive/pretend.rs
index d7b953d6384f..522fc8c2f2d9 100644
--- a/rust/serde_derive/pretend.rs
+++ b/rust/serde_derive/pretend.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use proc_macro2::TokenStream;
use quote::format_ident;
diff --git a/rust/serde_derive/ser.rs b/rust/serde_derive/ser.rs
index 43695dd0c39a..00a667cea7fa 100644
--- a/rust/serde_derive/ser.rs
+++ b/rust/serde_derive/ser.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use proc_macro2::{Span, TokenStream};
use syn::spanned::Spanned;
use syn::{self, Ident, Index, Member};
diff --git a/rust/serde_derive/this.rs b/rust/serde_derive/this.rs
index 32731d08969d..7a27a7c7dbc2 100644
--- a/rust/serde_derive/this.rs
+++ b/rust/serde_derive/this.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use internals::ast::Container;
use syn::{Path, PathArguments, Token};
diff --git a/rust/serde_derive/try.rs b/rust/serde_derive/try.rs
index 48cceebaafe1..afed0fedb938 100644
--- a/rust/serde_derive/try.rs
+++ b/rust/serde_derive/try.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use proc_macro2::{Punct, Spacing, TokenStream};
// None of our generated code requires the `From::from` error conversion
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 34/80] rust: Kbuild: enable `proc-macro2`, `quote`, `syn`, `serde` and `serde_derive`
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (28 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 33/80] rust: serde_derive: " Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 35/80] rust: test `serde` support Ariel Miculas
` (47 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Miguel Ojeda
From: Miguel Ojeda <ojeda@kernel.org>
With all the new files in place from the new crates, this patch
adds support for them in the build system.
Note that, if we decide to add third-party crates support to upstream, we would
probably want to do it in a different way. But this is useful for playing around
with these crates, experimenting, etc.
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
---
Makefile | 12 ++-
rust/.gitignore | 1 +
rust/Makefile | 194 ++++++++++++++++++++++++++++++++++++-----
scripts/Makefile.build | 2 +-
4 files changed, 185 insertions(+), 24 deletions(-)
diff --git a/Makefile b/Makefile
index 836643eaefee..3b7f37977f04 100644
--- a/Makefile
+++ b/Makefile
@@ -1606,7 +1606,7 @@ MRPROPER_FILES += include/config include/generated \
certs/x509.genkey \
vmlinux-gdb.py \
*.spec rpmbuild \
- rust/libmacros.so
+ rust/libmacros.so rust/libserde_derive.so
# clean - Delete most, but leave enough to build external modules
#
@@ -1848,8 +1848,18 @@ PHONY += rustfmt rustfmtcheck
rustfmt:
$(Q)find $(abs_srctree) -type f -name '*.rs' \
-o -path $(abs_srctree)/rust/alloc -prune \
+ -o -path $(abs_srctree)/rust/proc-macro2 -prune \
+ -o -path $(abs_srctree)/rust/quote -prune \
+ -o -path $(abs_srctree)/rust/syn -prune \
+ -o -path $(abs_srctree)/rust/serde -prune \
+ -o -path $(abs_srctree)/rust/serde_derive -prune \
-o -path $(abs_objtree)/rust/test -prune \
| grep -Fv $(abs_srctree)/rust/alloc \
+ | grep -Fv $(abs_srctree)/rust/proc-macro2 \
+ | grep -Fv $(abs_srctree)/rust/quote \
+ | grep -Fv $(abs_srctree)/rust/syn \
+ | grep -Fv $(abs_srctree)/rust/serde \
+ | grep -Fv $(abs_srctree)/rust/serde_derive \
| grep -Fv $(abs_objtree)/rust/test \
| grep -Fv generated \
| xargs $(RUSTFMT) $(rustfmt_flags)
diff --git a/rust/.gitignore b/rust/.gitignore
index 21552992b401..f6c521160123 100644
--- a/rust/.gitignore
+++ b/rust/.gitignore
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
+*.rlib
bindings_generated.rs
bindings_helpers_generated.rs
uapi_generated.rs
diff --git a/rust/Makefile b/rust/Makefile
index 7c9d9f11aec5..37952e93ff06 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -8,11 +8,12 @@ always-$(CONFIG_RUST) += exports_core_generated.h
obj-$(CONFIG_RUST) += helpers.o
CFLAGS_REMOVE_helpers.o = -Wmissing-prototypes -Wmissing-declarations
-always-$(CONFIG_RUST) += libmacros.so
-no-clean-files += libmacros.so
+always-$(CONFIG_RUST) += libproc_macro2.rlib libquote.rlib libsyn.rlib
+always-$(CONFIG_RUST) += libserde_derive.so libmacros.so
+no-clean-files += libserde_derive.so libmacros.so
always-$(CONFIG_RUST) += bindings/bindings_generated.rs bindings/bindings_helpers_generated.rs
-obj-$(CONFIG_RUST) += alloc.o bindings.o kernel.o
+obj-$(CONFIG_RUST) += alloc.o bindings.o serde.o kernel.o
always-$(CONFIG_RUST) += exports_alloc_generated.h exports_bindings_generated.h \
exports_kernel_generated.h
@@ -60,11 +61,73 @@ alloc-cfgs = \
--cfg no_sync \
--cfg no_thin
+proc_macro2-skip_flags := \
+ --edition=2021 \
+ -Drust_2018_idioms \
+ -Dunreachable_pub \
+ -Dunsafe_op_in_unsafe_fn
+
+proc_macro2-flags := \
+ --edition=2018 \
+ -Amissing_docs \
+ --cfg 'feature="proc-macro"' \
+ --cfg use_proc_macro \
+ --cfg wrap_proc_macro
+
+quote-skip_flags := \
+ --edition=2021 \
+ -Drust_2018_idioms
+
+quote-flags := \
+ --edition=2018 \
+ -Amissing_docs \
+ --extern proc_macro2 \
+ --cfg 'feature="proc-macro"'
+
+syn-skip_flags := \
+ --edition=2021 \
+ -Drust_2018_idioms \
+ -Dunreachable_pub \
+ -Dunsafe_op_in_unsafe_fn
+
+syn-flags := \
+ --edition=2018 \
+ -Amissing_docs \
+ --cfg 'feature="clone-impls"' \
+ --cfg 'feature="derive"' \
+ --cfg 'feature="extra-traits"' \
+ --cfg 'feature="fold"' \
+ --cfg 'feature="full"' \
+ --cfg 'feature="parsing"' \
+ --cfg 'feature="printing"' \
+ --cfg 'feature="proc-macro"' \
+ --cfg 'feature="quote"' \
+ --cfg 'feature="visit"' \
+ --cfg 'feature="visit-mut"'
+
+serde_derive-skip_flags := \
+ --edition=2021 \
+ -Drust_2018_idioms \
+ -Dunreachable_pub
+
+serde_derive-flags := \
+ -Amissing_docs
+
+serde-skip_flags := \
+ --edition=2021 \
+ -Drust_2018_idioms \
+ -Dunreachable_pub
+
+serde-flags := \
+ -Amissing_docs \
+ --cfg no_fp_fmt_parse
+
quiet_cmd_rustdoc = RUSTDOC $(if $(rustdoc_host),H, ) $<
cmd_rustdoc = \
OBJTREE=$(abspath $(objtree)) \
- $(RUSTDOC) $(if $(rustdoc_host),$(rust_common_flags),$(rust_flags)) \
+ $(RUSTDOC) $(filter-out $(skip_flags),$(if $(rustdoc_host),$(rust_common_flags),$(rust_flags))) \
$(rustc_target_flags) -L$(objtree)/$(obj) \
+ --extern quote --extern syn \
--output $(objtree)/$(obj)/doc \
--crate-name $(subst rustdoc-,,$@) \
@$(objtree)/include/generated/rustc_cfg $<
@@ -81,7 +144,7 @@ quiet_cmd_rustdoc = RUSTDOC $(if $(rustdoc_host),H, ) $<
# command-like flags to solve the issue. Meanwhile, we use the non-custom case
# and then retouch the generated files.
rustdoc: rustdoc-core rustdoc-macros rustdoc-compiler_builtins \
- rustdoc-alloc rustdoc-kernel
+ rustdoc-alloc rustdoc-kernel rustdoc-serde rustdoc-serde_derive
$(Q)cp $(srctree)/Documentation/images/logo.svg $(objtree)/$(obj)/doc
$(Q)cp $(srctree)/Documentation/images/COPYING-logo $(objtree)/$(obj)/doc
$(Q)find $(objtree)/$(obj)/doc -name '*.html' -type f -print0 | xargs -0 sed -Ei \
@@ -116,17 +179,31 @@ rustdoc-alloc: $(src)/alloc/lib.rs rustdoc-core rustdoc-compiler_builtins FORCE
rustdoc-kernel: private rustc_target_flags = --extern alloc \
--extern build_error --extern macros=$(objtree)/$(obj)/libmacros.so \
- --extern bindings --extern uapi
+ --extern bindings --extern uapi --extern serde --extern serde_derive
rustdoc-kernel: $(src)/kernel/lib.rs rustdoc-core rustdoc-macros \
- rustdoc-compiler_builtins rustdoc-alloc $(obj)/libmacros.so \
- $(obj)/bindings.o FORCE
+ rustdoc-compiler_builtins rustdoc-alloc rustdoc-serde $(obj)/libmacros.so \
+ $(obj)/bindings.o $(obj)/libserde_derive.so FORCE
+ $(call if_changed,rustdoc)
+
+rustdoc-serde_derive: private rustdoc_host = yes
+rustdoc-serde_derive: private skip_flags = $(serde_derive-skip_flags)
+rustdoc-serde_derive: private rustc_target_flags = --crate-type proc-macro \
+ --extern proc_macro -Amissing_docs
+rustdoc-serde_derive: $(src)/serde_derive/lib.rs FORCE
+ $(call if_changed,rustdoc)
+
+rustdoc-serde: private skip_flags = $(serde-skip_flags)
+rustdoc-serde: private rustc_target_flags = --extern alloc --extern serde \
+ -Arustdoc::broken_intra_doc_links
+rustdoc-serde: $(src)/serde/lib.rs rustdoc-core FORCE
$(call if_changed,rustdoc)
quiet_cmd_rustc_test_library = RUSTC TL $<
cmd_rustc_test_library = \
OBJTREE=$(abspath $(objtree)) \
- $(RUSTC) $(rust_common_flags) \
- @$(objtree)/include/generated/rustc_cfg $(rustc_target_flags) \
+ $(RUSTC) \
+ $(filter-out $(skip_flags),$(rust_common_flags) $(rustc_target_flags)) \
+ @$(objtree)/include/generated/rustc_cfg \
--crate-type $(if $(rustc_test_library_proc),proc-macro,rlib) \
--out-dir $(objtree)/$(obj)/test --cfg testlib \
--sysroot $(objtree)/$(obj)/test/sysroot \
@@ -136,9 +213,25 @@ quiet_cmd_rustc_test_library = RUSTC TL $<
rusttestlib-build_error: $(src)/build_error.rs rusttest-prepare FORCE
$(call if_changed,rustc_test_library)
-rusttestlib-macros: private rustc_target_flags = --extern proc_macro
+rusttestlib-proc_macro2: private skip_flags = $(proc_macro2-skip_flags)
+rusttestlib-proc_macro2: private rustc_target_flags = $(proc_macro2-flags)
+rusttestlib-proc_macro2: $(src)/proc-macro2/lib.rs rusttest-prepare FORCE
+ $(call if_changed,rustc_test_library)
+
+rusttestlib-quote: private skip_flags = $(quote-skip_flags)
+rusttestlib-quote: private rustc_target_flags = $(quote-flags)
+rusttestlib-quote: $(src)/quote/lib.rs rusttestlib-proc_macro2 FORCE
+ $(call if_changed,rustc_test_library)
+
+rusttestlib-syn: private skip_flags = $(syn-skip_flags)
+rusttestlib-syn: private rustc_target_flags = $(syn-flags)
+rusttestlib-syn: $(src)/syn/lib.rs rusttestlib-quote FORCE
+ $(call if_changed,rustc_test_library)
+
+rusttestlib-macros: private rustc_target_flags = --extern proc_macro \
+ --extern quote --extern syn
rusttestlib-macros: private rustc_test_library_proc = yes
-rusttestlib-macros: $(src)/macros/lib.rs rusttest-prepare FORCE
+rusttestlib-macros: $(src)/macros/lib.rs rusttestlib-syn FORCE
$(call if_changed,rustc_test_library)
rusttestlib-bindings: $(src)/bindings/lib.rs rusttest-prepare FORCE
@@ -147,6 +240,18 @@ rusttestlib-bindings: $(src)/bindings/lib.rs rusttest-prepare FORCE
rusttestlib-uapi: $(src)/uapi/lib.rs rusttest-prepare FORCE
$(call if_changed,rustc_test_library)
+rusttestlib-serde_derive: private skip_flags = $(serde_derive-skip_flags)
+rusttestlib-serde_derive: private rustc_target_flags = --extern proc_macro \
+ --extern quote --extern syn $(serde_derive-flags)
+rusttestlib-serde_derive: private rustc_test_library_proc = yes
+rusttestlib-serde_derive: $(src)/serde_derive/lib.rs rusttestlib-syn FORCE
+ $(call if_changed,rustc_test_library)
+
+rusttestlib-serde: private skip_flags = $(serde-skip_flags)
+rusttestlib-serde: private rustc_target_flags = $(serde-flags)
+rusttestlib-serde: $(src)/serde/lib.rs rusttest-prepare FORCE
+ $(call if_changed,rustc_test_library)
+
quiet_cmd_rustdoc_test = RUSTDOC T $<
cmd_rustdoc_test = \
OBJTREE=$(abspath $(objtree)) \
@@ -222,17 +327,19 @@ quiet_cmd_rustsysroot = RUSTSYSROOT
rusttest-prepare: FORCE
$(call if_changed,rustsysroot)
-rusttest-macros: private rustc_target_flags = --extern proc_macro
+rusttest-macros: private rustc_target_flags = --extern proc_macro \
+ --extern quote --extern syn
rusttest-macros: private rustdoc_test_target_flags = --crate-type proc-macro
-rusttest-macros: $(src)/macros/lib.rs rusttest-prepare FORCE
+rusttest-macros: $(src)/macros/lib.rs rusttestlib-syn FORCE
$(call if_changed,rustc_test)
$(call if_changed,rustdoc_test)
rusttest-kernel: private rustc_target_flags = --extern alloc \
- --extern build_error --extern macros --extern bindings --extern uapi
+ --extern build_error --extern macros --extern bindings --extern uapi \
+ --extern serde --extern serde_derive
rusttest-kernel: $(src)/kernel/lib.rs rusttest-prepare \
rusttestlib-build_error rusttestlib-macros rusttestlib-bindings \
- rusttestlib-uapi FORCE
+ rusttestlib-uapi rusttestlib-serde rusttestlib-serde_derive FORCE
$(call if_changed,rustc_test)
$(call if_changed,rustc_test_library)
@@ -348,17 +455,52 @@ $(obj)/exports_bindings_generated.h: $(obj)/bindings.o FORCE
$(obj)/exports_kernel_generated.h: $(obj)/kernel.o FORCE
$(call if_changed,exports)
+quiet_cmd_rustc_hostlibrary = $(RUSTC_OR_CLIPPY_QUIET) H $@
+ cmd_rustc_hostlibrary = \
+ $(if $(skip_clippy),$(RUSTC),$(RUSTC_OR_CLIPPY)) \
+ $(filter-out $(skip_flags),$(rust_common_flags) $(rustc_target_flags)) \
+ --emit=dep-info,link --crate-type rlib -O \
+ --out-dir $(objtree)/$(obj) -L$(objtree)/$(obj) \
+ --crate-name $(patsubst lib%.rlib,%,$(notdir $@)) $<; \
+ mv $(objtree)/$(obj)/$(patsubst lib%.rlib,%,$(notdir $@)).d $(depfile); \
+ sed -i '/^\#/d' $(depfile)
+
+$(obj)/libproc_macro2.rlib: private skip_clippy = 1
+$(obj)/libproc_macro2.rlib: private skip_flags = $(proc_macro2-skip_flags)
+$(obj)/libproc_macro2.rlib: private rustc_target_flags = $(proc_macro2-flags)
+$(obj)/libproc_macro2.rlib: $(src)/proc-macro2/lib.rs FORCE
+ $(call if_changed_dep,rustc_hostlibrary)
+
+$(obj)/libquote.rlib: private skip_clippy = 1
+$(obj)/libquote.rlib: private skip_flags = $(quote-skip_flags)
+$(obj)/libquote.rlib: private rustc_target_flags = $(quote-flags)
+$(obj)/libquote.rlib: $(src)/quote/lib.rs $(obj)/libproc_macro2.rlib FORCE
+ $(call if_changed_dep,rustc_hostlibrary)
+
+$(obj)/libsyn.rlib: private skip_clippy = 1
+$(obj)/libsyn.rlib: private skip_flags = $(syn-skip_flags)
+$(obj)/libsyn.rlib: private rustc_target_flags = $(syn-flags)
+$(obj)/libsyn.rlib: $(src)/syn/lib.rs $(obj)/libquote.rlib FORCE
+ $(call if_changed_dep,rustc_hostlibrary)
+
quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@
cmd_rustc_procmacro = \
- $(RUSTC_OR_CLIPPY) $(rust_common_flags) \
+ $(if $(skip_clippy),$(RUSTC),$(RUSTC_OR_CLIPPY)) \
+ $(filter-out $(skip_flags),$(rust_common_flags) $(rustc_target_flags)) \
--emit=dep-info=$(depfile) --emit=link=$@ --extern proc_macro \
- --crate-type proc-macro \
- --crate-name $(patsubst lib%.so,%,$(notdir $@)) $<
+ --extern quote --extern syn --crate-type proc-macro \
+ -L$(objtree)/$(obj) --crate-name $(patsubst lib%.so,%,$(notdir $@)) $<
# Procedural macros can only be used with the `rustc` that compiled it.
# Therefore, to get `libmacros.so` automatically recompiled when the compiler
# version changes, we add `core.o` as a dependency (even if it is not needed).
-$(obj)/libmacros.so: $(src)/macros/lib.rs $(obj)/core.o FORCE
+$(obj)/libserde_derive.so: private skip_clippy = 1
+$(obj)/libserde_derive.so: private skip_flags = $(serde_derive-skip_flags)
+$(obj)/libserde_derive.so: private rustc_target_flags = $(serde_derive-flags)
+$(obj)/libserde_derive.so: $(src)/serde_derive/lib.rs $(obj)/libsyn.rlib $(obj)/core.o FORCE
+ $(call if_changed_dep,rustc_procmacro)
+
+$(obj)/libmacros.so: $(src)/macros/lib.rs $(obj)/libsyn.rlib $(obj)/core.o FORCE
$(call if_changed_dep,rustc_procmacro)
quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L $@
@@ -420,10 +562,18 @@ $(obj)/uapi.o: $(src)/uapi/lib.rs \
$(obj)/uapi/uapi_generated.rs FORCE
$(call if_changed_dep,rustc_library)
+$(obj)/serde.o: private skip_clippy = 1
+$(obj)/serde.o: private skip_flags = $(serde-skip_flags)
+$(obj)/serde.o: private rustc_target_flags = $(serde-flags)
+$(obj)/serde.o: $(src)/serde/lib.rs $(obj)/compiler_builtins.o FORCE
+ $(call if_changed_dep,rustc_library)
+
$(obj)/kernel.o: private rustc_target_flags = --extern alloc \
- --extern build_error --extern macros --extern bindings --extern uapi
+ --extern build_error --extern macros --extern bindings --extern uapi \
+ --extern serde --extern serde_derive
$(obj)/kernel.o: $(src)/kernel/lib.rs $(obj)/alloc.o $(obj)/build_error.o \
- $(obj)/libmacros.so $(obj)/bindings.o $(obj)/uapi.o FORCE
+ $(obj)/libmacros.so $(obj)/bindings.o $(obj)/uapi.o \
+ $(obj)/serde.o $(obj)/libserde_derive.so FORCE
$(call if_changed_dep,rustc_library)
endif # CONFIG_RUST
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 1404967e908e..3fa298bb7c31 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -284,7 +284,7 @@ rust_common_cmd = \
-Zallow-features=$(rust_allowed_features) \
-Zcrate-attr=no_std \
-Zcrate-attr='feature($(rust_allowed_features))' \
- --extern alloc --extern kernel \
+ --extern alloc --extern kernel --extern serde --extern serde_derive \
--crate-type rlib -L $(objtree)/rust/ \
--crate-name $(basename $(notdir $@)) \
--emit=dep-info=$(depfile)
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 35/80] rust: test `serde` support
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (29 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 34/80] rust: Kbuild: enable `proc-macro2`, `quote`, `syn`, `serde` and `serde_derive` Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 36/80] Add SAMPLE_RUST_SERDE in rust.config Ariel Miculas
` (46 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Miguel Ojeda
From: Miguel Ojeda <ojeda@kernel.org>
A trivial example based on `serde`'s `example-format' [1].
It contains both a in-`kernel` data format later used by
the kernel module, as well as a local data format in
the module.
The kernel module gives an output such as:
[ 0.801425] rust_serde: Rust serde sample (init)
[ 0.801634] rust_serde: original = S { a: (), b: false, c: true, d: () }
[ 0.802079] rust_serde: serialized = [2, 0, 1, 0, 1, 1, 0, 3]
[ 0.802506] rust_serde: deserialized = S { a: (), b: false, c: true, d: () }
[ 0.802718] rust_serde: serialized (local) = [2, 0, 1, 42, 1, 43, 0, 3]
[ 0.802895] rust_serde: deserialized (local) = S { a: (), b: false, c: true, d: () }
[ 0.808954] rust_serde: Rust serde sample (exit)
Note that this is just a quick draft/hack to check the previous
commits work. It is not intended to be merged at all.
Link: https://github.com/serde-rs/example-format [1]
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
---
rust/kernel/lib.rs | 1 +
rust/kernel/test_serde.rs | 26 ++
rust/kernel/test_serde/de.rs | 442 ++++++++++++++++++++++
rust/kernel/test_serde/error.rs | 73 ++++
rust/kernel/test_serde/ser.rs | 469 ++++++++++++++++++++++++
samples/rust/Kconfig | 8 +
samples/rust/Makefile | 1 +
samples/rust/local_data_format/de.rs | 422 +++++++++++++++++++++
samples/rust/local_data_format/error.rs | 73 ++++
samples/rust/local_data_format/ser.rs | 443 ++++++++++++++++++++++
samples/rust/rust_serde.rs | 73 ++++
11 files changed, 2031 insertions(+)
create mode 100644 rust/kernel/test_serde.rs
create mode 100644 rust/kernel/test_serde/de.rs
create mode 100644 rust/kernel/test_serde/error.rs
create mode 100644 rust/kernel/test_serde/ser.rs
create mode 100644 samples/rust/local_data_format/de.rs
create mode 100644 samples/rust/local_data_format/error.rs
create mode 100644 samples/rust/local_data_format/ser.rs
create mode 100644 samples/rust/rust_serde.rs
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 08f67833afcf..a94fb784d576 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -178,3 +178,4 @@ fn panic(info: &core::panic::PanicInfo<'_>) -> ! {
///
/// [`PAGE_SHIFT`]: ../../../include/asm-generic/page.h
pub const PAGE_SIZE: usize = 1 << bindings::PAGE_SHIFT;
+pub mod test_serde;
diff --git a/rust/kernel/test_serde.rs b/rust/kernel/test_serde.rs
new file mode 100644
index 000000000000..012bf58213a8
--- /dev/null
+++ b/rust/kernel/test_serde.rs
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+//! Test `serde`.
+//!
+//! It contains a data format used by the `rust_serde` sample, as well
+//! as a quick check that `serde_derive` works in the `kernel` crate too.
+
+#![allow(missing_docs)]
+
+mod de;
+mod error;
+mod ser;
+
+pub use de::{from_bytes, Deserializer};
+pub use error::{Error, Result};
+pub use ser::{to_vec, Serializer};
+
+use serde_derive::{Deserialize, Serialize};
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct S {
+ a: (),
+ b: bool,
+ c: bool,
+ d: (),
+}
diff --git a/rust/kernel/test_serde/de.rs b/rust/kernel/test_serde/de.rs
new file mode 100644
index 000000000000..84c98d1c1c66
--- /dev/null
+++ b/rust/kernel/test_serde/de.rs
@@ -0,0 +1,442 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+// Copyright 2018 Serde Developers
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::error::{Error, Result};
+use serde::de::{self, Deserialize, DeserializeSeed, SeqAccess, Visitor};
+
+pub struct Deserializer<'de> {
+ // This string starts with the input data and characters are truncated off
+ // the beginning as data is parsed.
+ input: &'de [u8],
+}
+
+impl<'de> Deserializer<'de> {
+ // By convention, `Deserializer` constructors are named like `from_xyz`.
+ // That way basic use cases are satisfied by something like
+ // `serde_json::from_str(...)` while advanced use cases that require a
+ // deserializer can make one with `serde_json::Deserializer::from_str(...)`.
+ #[allow(clippy::should_implement_trait)]
+ pub fn from_bytes(input: &'de [u8]) -> Self {
+ Deserializer { input }
+ }
+}
+
+// By convention, the public API of a Serde deserializer is one or more
+// `from_xyz` methods such as `from_str`, `from_bytes`, or `from_reader`
+// depending on what Rust types the deserializer is able to consume as input.
+//
+// This basic deserializer supports only `from_str`.
+pub fn from_bytes<'a, T>(s: &'a [u8]) -> Result<T>
+where
+ T: Deserialize<'a>,
+{
+ let mut deserializer = Deserializer::from_bytes(s);
+ let t = T::deserialize(&mut deserializer)?;
+ if deserializer.input.is_empty() {
+ Ok(t)
+ } else {
+ Err(Error::TrailingCharacters)
+ }
+}
+
+// SERDE IS NOT A PARSING LIBRARY. This impl block defines a few basic parsing
+// functions from scratch. More complicated formats may wish to use a dedicated
+// parsing library to help implement their Serde deserializer.
+impl<'de> Deserializer<'de> {
+ // Look at the first character in the input without consuming it.
+ fn peek_byte(&mut self) -> Result<u8> {
+ self.input.iter().next().ok_or(Error::Eof).map(|v| *v)
+ }
+
+ // Consume the first character in the input.
+ fn next_byte(&mut self) -> Result<u8> {
+ let ch = self.peek_byte()?;
+ self.input = &self.input[1..];
+ Ok(ch)
+ }
+
+ // Parse the JSON identifier `true` or `false`.
+ fn parse_bool(&mut self) -> Result<bool> {
+ if self.input.starts_with(&[1]) {
+ self.input = &self.input[1..];
+ match self.next_byte()? {
+ 0 => Ok(false),
+ 1 => Ok(true),
+ _ => Err(Error::InvalidBooleanValue),
+ }
+ } else {
+ Err(Error::ExpectedBoolean)
+ }
+ }
+
+ // Parse a group of decimal digits as an unsigned integer of type T.
+ //
+ // This implementation is a bit too lenient, for example `001` is not
+ // allowed in JSON. Also the various arithmetic operations can overflow and
+ // panic or return bogus data. But it is good enough for example code!
+ fn parse_unsigned<T>(&mut self) -> Result<T> {
+ unimplemented!()
+ }
+
+ // Parse a possible minus sign followed by a group of decimal digits as a
+ // signed integer of type T.
+ fn parse_signed<T>(&mut self) -> Result<T> {
+ // Optional minus sign, delegate to `parse_unsigned`, negate if negative.
+ unimplemented!()
+ }
+}
+
+impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
+ type Error = Error;
+
+ // Look at the input data to decide what Serde data model type to
+ // deserialize as. Not all data formats are able to support this operation.
+ // Formats that support `deserialize_any` are known as self-describing.
+ fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ match self.peek_byte()? {
+ 0 => self.deserialize_unit(visitor),
+ 1 => self.deserialize_bool(visitor),
+ 2 => self.deserialize_map(visitor),
+ _ => Err(Error::Syntax),
+ }
+ }
+
+ // Uses the `parse_bool` parsing function defined above to read the JSON
+ // identifier `true` or `false` from the input.
+ //
+ // Parsing refers to looking at the input and deciding that it contains the
+ // JSON value `true` or `false`.
+ //
+ // Deserialization refers to mapping that JSON value into Serde's data
+ // model by invoking one of the `Visitor` methods. In the case of JSON and
+ // bool that mapping is straightforward so the distinction may seem silly,
+ // but in other cases Deserializers sometimes perform non-obvious mappings.
+ // For example the TOML format has a Datetime type and Serde's data model
+ // does not. In the `toml` crate, a Datetime in the input is deserialized by
+ // mapping it to a Serde data model "struct" type with a special name and a
+ // single field containing the Datetime represented as a string.
+ fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_bool(self.parse_bool()?)
+ }
+
+ // The `parse_signed` function is generic over the integer type `T` so here
+ // it is invoked with `T=i8`. The next 8 methods are similar.
+ fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_i8(self.parse_signed()?)
+ }
+
+ fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_i16(self.parse_signed()?)
+ }
+
+ fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_i32(self.parse_signed()?)
+ }
+
+ fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_i64(self.parse_signed()?)
+ }
+
+ fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_u8(self.parse_unsigned()?)
+ }
+
+ fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_u16(self.parse_unsigned()?)
+ }
+
+ fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_u32(self.parse_unsigned()?)
+ }
+
+ fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_u64(self.parse_unsigned()?)
+ }
+
+ // The `Serializer` implementation on the previous page serialized chars as
+ // single-character strings so handle that representation here.
+ fn deserialize_char<V>(self, _visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ unimplemented!()
+ }
+
+ // Refer to the "Understanding deserializer lifetimes" page for information
+ // about the three deserialization flavors of strings in Serde.
+ fn deserialize_str<V>(self, _visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ unimplemented!()
+ }
+
+ fn deserialize_string<V>(self, _visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ unimplemented!()
+ }
+
+ // The `Serializer` implementation on the previous page serialized byte
+ // arrays as JSON arrays of bytes. Handle that representation here.
+ fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ unimplemented!()
+ }
+
+ fn deserialize_byte_buf<V>(self, _visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ unimplemented!()
+ }
+
+ // An absent optional is represented as the JSON `null` and a present
+ // optional is represented as just the contained value.
+ //
+ // As commented in `Serializer` implementation, this is a lossy
+ // representation. For example the values `Some(())` and `None` both
+ // serialize as just `null`. Unfortunately this is typically what people
+ // expect when working with JSON. Other formats are encouraged to behave
+ // more intelligently if possible.
+ fn deserialize_option<V>(self, _visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ unimplemented!()
+ }
+
+ // In Serde, unit means an anonymous value containing no data.
+ fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ if self.input.starts_with(&[0]) {
+ self.input = &self.input[1..];
+ visitor.visit_unit()
+ } else {
+ Err(Error::ExpectedNull)
+ }
+ }
+
+ // Unit struct means a named value containing no data.
+ fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ self.deserialize_unit(visitor)
+ }
+
+ // As is done here, serializers are encouraged to treat newtype structs as
+ // insignificant wrappers around the data they contain. That means not
+ // parsing anything other than the contained value.
+ fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_newtype_struct(self)
+ }
+
+ // Deserialization of compound types like sequences and maps happens by
+ // passing the visitor an "Access" object that gives it the ability to
+ // iterate through the data contained in the sequence.
+ fn deserialize_seq<V>(self, _visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ unimplemented!()
+ }
+
+ // Tuples look just like sequences in JSON. Some formats may be able to
+ // represent tuples more efficiently.
+ //
+ // As indicated by the length parameter, the `Deserialize` implementation
+ // for a tuple in the Serde data model is required to know the length of the
+ // tuple before even looking at the input data.
+ fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ self.deserialize_seq(visitor)
+ }
+
+ // Tuple structs look just like sequences in JSON.
+ fn deserialize_tuple_struct<V>(
+ self,
+ _name: &'static str,
+ _len: usize,
+ visitor: V,
+ ) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ self.deserialize_seq(visitor)
+ }
+
+ // Much like `deserialize_seq` but calls the visitors `visit_map` method
+ // with a `MapAccess` implementation, rather than the visitor's `visit_seq`
+ // method with a `SeqAccess` implementation.
+ fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ // Parse the opening brace of the map.
+ if self.next_byte()? == 2 {
+ // Give the visitor access to each entry of the map.
+ let value = visitor.visit_seq(StructFieldsVisitor::new(self))?;
+ // Parse the closing brace of the map.
+ if self.next_byte()? == 3 {
+ Ok(value)
+ } else {
+ Err(Error::ExpectedMapEnd)
+ }
+ } else {
+ Err(Error::ExpectedMap)
+ }
+ }
+
+ // Structs look just like maps in JSON.
+ //
+ // Notice the `fields` parameter - a "struct" in the Serde data model means
+ // that the `Deserialize` implementation is required to know what the fields
+ // are before even looking at the input data. Any key-value pairing in which
+ // the fields cannot be known ahead of time is probably a map.
+ fn deserialize_struct<V>(
+ self,
+ _name: &'static str,
+ _fields: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ self.deserialize_map(visitor)
+ }
+
+ fn deserialize_enum<V>(
+ self,
+ _name: &'static str,
+ _variants: &'static [&'static str],
+ _visitor: V,
+ ) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ unimplemented!()
+ }
+
+ // An identifier in Serde is the type that identifies a field of a struct or
+ // the variant of an enum. In JSON, struct fields and enum variants are
+ // represented as strings. In other formats they may be represented as
+ // numeric indices.
+ fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ self.deserialize_str(visitor)
+ }
+
+ // Like `deserialize_any` but indicates to the `Deserializer` that it makes
+ // no difference which `Visitor` method is called because the data is
+ // ignored.
+ //
+ // Some deserializers are able to implement this more efficiently than
+ // `deserialize_any`, for example by rapidly skipping over matched
+ // delimiters without paying close attention to the data in between.
+ //
+ // Some formats are not able to implement this at all. Formats that can
+ // implement `deserialize_any` and `deserialize_ignored_any` are known as
+ // self-describing.
+ fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ self.deserialize_any(visitor)
+ }
+}
+
+struct StructFieldsVisitor<'a, 'de> {
+ de: &'a mut Deserializer<'de>,
+}
+
+impl<'a, 'de> StructFieldsVisitor<'a, 'de> {
+ fn new(de: &'a mut Deserializer<'de>) -> Self {
+ StructFieldsVisitor { de }
+ }
+}
+
+impl<'de, 'a> SeqAccess<'de> for StructFieldsVisitor<'a, 'de> {
+ type Error = Error;
+
+ fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
+ where
+ T: DeserializeSeed<'de>,
+ {
+ // Check if there are no more elements.
+ if self.de.peek_byte()? == 3 {
+ return Ok(None);
+ }
+ // Deserialize an array element.
+ seed.deserialize(&mut *self.de).map(Some)
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+#[test]
+fn test_struct() {
+ use serde_derive::Deserialize;
+
+ #[derive(Deserialize, PartialEq, Debug)]
+ struct Test {
+ a: (),
+ b: bool,
+ }
+
+ let j = &[2, 0, 1, 0, 3];
+ let expected = Test {
+ a: (),
+ b: false,
+ };
+ assert_eq!(expected, from_bytes(j).unwrap());
+}
\ No newline at end of file
diff --git a/rust/kernel/test_serde/error.rs b/rust/kernel/test_serde/error.rs
new file mode 100644
index 000000000000..a1eb3720ce67
--- /dev/null
+++ b/rust/kernel/test_serde/error.rs
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+// Copyright 2018 Serde Developers
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use core::fmt::{self, Display};
+use serde::{de, ser};
+
+pub type Result<T> = crate::error::Result<T, Error>;
+
+// This is a bare-bones implementation. A real library would provide additional
+// information in its error type, for example the line and column at which the
+// error occurred, the byte offset into the input, or the current key being
+// processed.
+#[derive(Debug)]
+pub enum Error {
+ // One or more variants that can be created by data structures through the
+ // `ser::Error` and `de::Error` traits. For example the Serialize impl for
+ // Mutex<T> might return an error because the mutex is poisoned, or the
+ // Deserialize impl for a struct may return an error because a required
+ // field is missing.
+ Message,
+
+ // Zero or more variants that can be created directly by the Serializer and
+ // Deserializer without going through `ser::Error` and `de::Error`. These
+ // are specific to the format, in this case JSON.
+ Eof,
+ Syntax,
+ ExpectedBoolean,
+ InvalidBooleanValue,
+ ExpectedInteger,
+ ExpectedString,
+ ExpectedNull,
+ ExpectedArray,
+ ExpectedArrayComma,
+ ExpectedArrayEnd,
+ ExpectedMap,
+ ExpectedMapColon,
+ ExpectedMapComma,
+ ExpectedMapEnd,
+ ExpectedEnum,
+ TrailingCharacters,
+}
+
+impl ser::Error for Error {
+ fn custom<T: Display>(_msg: T) -> Self {
+ Error::Message
+ }
+}
+
+impl de::Error for Error {
+ fn custom<T: Display>(_msg: T) -> Self {
+ Error::Message
+ }
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Error::Message => write!(f, "message"),
+ Error::Eof => f.write_str("unexpected end of input"),
+ /* and so forth */
+ _ => unimplemented!(),
+ }
+ }
+}
+
+//impl core::error::Error for Error {}
diff --git a/rust/kernel/test_serde/ser.rs b/rust/kernel/test_serde/ser.rs
new file mode 100644
index 000000000000..56abe7095a5f
--- /dev/null
+++ b/rust/kernel/test_serde/ser.rs
@@ -0,0 +1,469 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+// Copyright 2018 Serde Developers
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::error::{Error, Result};
+use alloc::vec::Vec;
+use serde::ser::{self, Serialize};
+
+pub struct Serializer {
+ // This string starts empty and JSON is appended as values are serialized.
+ output: Vec<u8>,
+}
+
+// By convention, the public API of a Serde serializer is one or more `to_abc`
+// functions such as `to_string`, `to_bytes`, or `to_writer` depending on what
+// Rust types the serializer is able to produce as output.
+//
+// This basic serializer supports only `to_string`.
+pub fn to_vec<T>(value: &T) -> Result<Vec<u8>>
+where
+ T: Serialize,
+{
+ let mut serializer = Serializer { output: Vec::new() };
+ value.serialize(&mut serializer)?;
+ Ok(serializer.output)
+}
+
+impl<'a> ser::Serializer for &'a mut Serializer {
+ // The output type produced by this `Serializer` during successful
+ // serialization. Most serializers that produce text or binary output should
+ // set `Ok = ()` and serialize into an `io::Write` or buffer contained
+ // within the `Serializer` instance, as happens here. Serializers that build
+ // in-memory data structures may be simplified by using `Ok` to propagate
+ // the data structure around.
+ type Ok = ();
+
+ // The error type when some error occurs during serialization.
+ type Error = Error;
+
+ // Associated types for keeping track of additional state while serializing
+ // compound data structures like sequences and maps. In this case no
+ // additional state is required beyond what is already stored in the
+ // Serializer struct.
+ type SerializeSeq = Self;
+ type SerializeTuple = Self;
+ type SerializeTupleStruct = Self;
+ type SerializeTupleVariant = Self;
+ type SerializeMap = Self;
+ type SerializeStruct = Self;
+ type SerializeStructVariant = Self;
+
+ // Here we go with the simple methods. The following 12 methods receive one
+ // of the primitive types of the data model and map it to JSON by appending
+ // into the output string.
+ fn serialize_bool(self, v: bool) -> Result<()> {
+ self.output.try_push(1).unwrap();
+ self.output.try_push(if v { 1 } else { 0 }).unwrap();
+ Ok(())
+ }
+
+ // JSON does not distinguish between different sizes of integers, so all
+ // signed integers will be serialized the same and all unsigned integers
+ // will be serialized the same. Other formats, especially compact binary
+ // formats, may need independent logic for the different sizes.
+ fn serialize_i8(self, v: i8) -> Result<()> {
+ self.serialize_i64(i64::from(v))
+ }
+
+ fn serialize_i16(self, v: i16) -> Result<()> {
+ self.serialize_i64(i64::from(v))
+ }
+
+ fn serialize_i32(self, v: i32) -> Result<()> {
+ self.serialize_i64(i64::from(v))
+ }
+
+ // Not particularly efficient but this is example code anyway. A more
+ // performant approach would be to use the `itoa` crate.
+ fn serialize_i64(self, _v: i64) -> Result<()> {
+ unimplemented!();
+ }
+
+ fn serialize_u8(self, v: u8) -> Result<()> {
+ self.serialize_u64(u64::from(v))
+ }
+
+ fn serialize_u16(self, v: u16) -> Result<()> {
+ self.serialize_u64(u64::from(v))
+ }
+
+ fn serialize_u32(self, v: u32) -> Result<()> {
+ self.serialize_u64(u64::from(v))
+ }
+
+ fn serialize_u64(self, _v: u64) -> Result<()> {
+ unimplemented!();
+ }
+
+ // Serialize a char as a single-character string. Other formats may
+ // represent this differently.
+ fn serialize_char(self, _v: char) -> Result<()> {
+ unimplemented!();
+ }
+
+ // This only works for strings that don't require escape sequences but you
+ // get the idea. For example it would emit invalid JSON if the input string
+ // contains a '"' character.
+ fn serialize_str(self, _v: &str) -> Result<()> {
+ unimplemented!();
+ }
+
+ // Serialize a byte array as an array of bytes. Could also use a base64
+ // string here. Binary formats will typically represent byte arrays more
+ // compactly.
+ fn serialize_bytes(self, v: &[u8]) -> Result<()> {
+ use serde::ser::SerializeSeq;
+ let mut seq = self.serialize_seq(Some(v.len()))?;
+ for byte in v {
+ seq.serialize_element(byte)?;
+ }
+ seq.end()
+ }
+
+ // An absent optional is represented as the JSON `null`.
+ fn serialize_none(self) -> Result<()> {
+ self.serialize_unit()
+ }
+
+ // A present optional is represented as just the contained value. Note that
+ // this is a lossy representation. For example the values `Some(())` and
+ // `None` both serialize as just `null`. Unfortunately this is typically
+ // what people expect when working with JSON. Other formats are encouraged
+ // to behave more intelligently if possible.
+ fn serialize_some<T>(self, value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ value.serialize(self)
+ }
+
+ // In Serde, unit means an anonymous value containing no data. Map this to
+ // JSON as `null`.
+ fn serialize_unit(self) -> Result<()> {
+ self.output.try_push(0).unwrap();
+ Ok(())
+ }
+
+ // Unit struct means a named value containing no data. Again, since there is
+ // no data, map this to JSON as `null`. There is no need to serialize the
+ // name in most formats.
+ fn serialize_unit_struct(self, _name: &'static str) -> Result<()> {
+ self.serialize_unit()
+ }
+
+ // When serializing a unit variant (or any other kind of variant), formats
+ // can choose whether to keep track of it by index or by name. Binary
+ // formats typically use the index of the variant and human-readable formats
+ // typically use the name.
+ fn serialize_unit_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ variant: &'static str,
+ ) -> Result<()> {
+ self.serialize_str(variant)
+ }
+
+ // As is done here, serializers are encouraged to treat newtype structs as
+ // insignificant wrappers around the data they contain.
+ fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ value.serialize(self)
+ }
+
+ // Note that newtype variant (and all of the other variant serialization
+ // methods) refer exclusively to the "externally tagged" enum
+ // representation.
+ //
+ // Serialize this to JSON in externally tagged form as `{ NAME: VALUE }`.
+ fn serialize_newtype_variant<T>(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ _variant: &'static str,
+ _value: &T,
+ ) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ unimplemented!();
+ }
+
+ // Now we get to the serialization of compound types.
+ //
+ // The start of the sequence, each value, and the end are three separate
+ // method calls. This one is responsible only for serializing the start,
+ // which in JSON is `[`.
+ //
+ // The length of the sequence may or may not be known ahead of time. This
+ // doesn't make a difference in JSON because the length is not represented
+ // explicitly in the serialized form. Some serializers may only be able to
+ // support sequences for which the length is known up front.
+ fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
+ unimplemented!();
+ }
+
+ // Tuples look just like sequences in JSON. Some formats may be able to
+ // represent tuples more efficiently by omitting the length, since tuple
+ // means that the corresponding `Deserialize implementation will know the
+ // length without needing to look at the serialized data.
+ fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
+ self.serialize_seq(Some(len))
+ }
+
+ // Tuple structs look just like sequences in JSON.
+ fn serialize_tuple_struct(
+ self,
+ _name: &'static str,
+ len: usize,
+ ) -> Result<Self::SerializeTupleStruct> {
+ self.serialize_seq(Some(len))
+ }
+
+ // Tuple variants are represented in JSON as `{ NAME: [DATA...] }`. Again
+ // this method is only responsible for the externally tagged representation.
+ fn serialize_tuple_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ _variant: &'static str,
+ _len: usize,
+ ) -> Result<Self::SerializeTupleVariant> {
+ unimplemented!();
+ }
+
+ // Maps are represented in JSON as `{ K: V, K: V, ... }`.
+ fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
+ self.output.try_push(2).unwrap();
+ Ok(self)
+ }
+
+ // Structs look just like maps in JSON. In particular, JSON requires that we
+ // serialize the field names of the struct. Other formats may be able to
+ // omit the field names when serializing structs because the corresponding
+ // Deserialize implementation is required to know what the keys are without
+ // looking at the serialized data.
+ fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
+ self.serialize_map(Some(len))
+ }
+
+ // Struct variants are represented in JSON as `{ NAME: { K: V, ... } }`.
+ // This is the externally tagged representation.
+ fn serialize_struct_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ _variant: &'static str,
+ _len: usize,
+ ) -> Result<Self::SerializeStructVariant> {
+ unimplemented!();
+ }
+
+ fn collect_str<T: ?Sized>(self, _: &T) -> Result<()>
+ where
+ T: core::fmt::Display,
+ {
+ unimplemented!()
+ }
+}
+
+// The following 7 impls deal with the serialization of compound types like
+// sequences and maps. Serialization of such types is begun by a Serializer
+// method and followed by zero or more calls to serialize individual elements of
+// the compound type and one call to end the compound type.
+//
+// This impl is SerializeSeq so these methods are called after `serialize_seq`
+// is called on the Serializer.
+impl<'a> ser::SerializeSeq for &'a mut Serializer {
+ // Must match the `Ok` type of the serializer.
+ type Ok = ();
+ // Must match the `Error` type of the serializer.
+ type Error = Error;
+
+ // Serialize a single element of the sequence.
+ fn serialize_element<T>(&mut self, _value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ unimplemented!();
+ }
+
+ // Close the sequence.
+ fn end(self) -> Result<()> {
+ unimplemented!();
+ }
+}
+
+// Same thing but for tuples.
+impl<'a> ser::SerializeTuple for &'a mut Serializer {
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_element<T>(&mut self, _value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ unimplemented!();
+ }
+
+ fn end(self) -> Result<()> {
+ unimplemented!();
+ }
+}
+
+// Same thing but for tuple structs.
+impl<'a> ser::SerializeTupleStruct for &'a mut Serializer {
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_field<T>(&mut self, _value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ unimplemented!();
+ }
+
+ fn end(self) -> Result<()> {
+ unimplemented!();
+ }
+}
+
+// Tuple variants are a little different. Refer back to the
+// `serialize_tuple_variant` method above:
+//
+// self.output += "{";
+// variant.serialize(&mut *self)?;
+// self.output += ":[";
+//
+// So the `end` method in this impl is responsible for closing both the `]` and
+// the `}`.
+impl<'a> ser::SerializeTupleVariant for &'a mut Serializer {
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_field<T>(&mut self, _value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ unimplemented!();
+ }
+
+ fn end(self) -> Result<()> {
+ unimplemented!();
+ }
+}
+
+// Some `Serialize` types are not able to hold a key and value in memory at the
+// same time so `SerializeMap` implementations are required to support
+// `serialize_key` and `serialize_value` individually.
+//
+// There is a third optional method on the `SerializeMap` trait. The
+// `serialize_entry` method allows serializers to optimize for the case where
+// key and value are both available simultaneously. In JSON it doesn't make a
+// difference so the default behavior for `serialize_entry` is fine.
+impl<'a> ser::SerializeMap for &'a mut Serializer {
+ type Ok = ();
+ type Error = Error;
+
+ // The Serde data model allows map keys to be any serializable type. JSON
+ // only allows string keys so the implementation below will produce invalid
+ // JSON if the key serializes as something other than a string.
+ //
+ // A real JSON serializer would need to validate that map keys are strings.
+ // This can be done by using a different Serializer to serialize the key
+ // (instead of `&mut **self`) and having that other serializer only
+ // implement `serialize_str` and return an error on any other data type.
+ fn serialize_key<T>(&mut self, _key: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ unimplemented!()
+ }
+
+ // It doesn't make a difference whether the colon is printed at the end of
+ // `serialize_key` or at the beginning of `serialize_value`. In this case
+ // the code is a bit simpler having it here.
+ fn serialize_value<T>(&mut self, _value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ unimplemented!()
+ }
+
+ fn end(self) -> Result<()> {
+ self.output.try_push(3).unwrap();
+ Ok(())
+ }
+}
+
+// Structs are like maps in which the keys are constrained to be compile-time
+// constant strings.
+impl<'a> ser::SerializeStruct for &'a mut Serializer {
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ value.serialize(&mut **self)
+ }
+
+ fn end(self) -> Result<()> {
+ self.output.try_push(3).unwrap();
+ Ok(())
+ }
+}
+
+// Similar to `SerializeTupleVariant`, here the `end` method is responsible for
+// closing both of the curly braces opened by `serialize_struct_variant`.
+impl<'a> ser::SerializeStructVariant for &'a mut Serializer {
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_field<T>(&mut self, _key: &'static str, _value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ unimplemented!();
+ }
+
+ fn end(self) -> Result<()> {
+ unimplemented!();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+#[test]
+fn test_struct() {
+ use serde_derive::Serialize;
+
+ #[derive(Serialize)]
+ struct Test {
+ a: (),
+ b: bool,
+ }
+
+ let test = Test {
+ a: (),
+ b: false,
+ };
+
+ let mut expected = Vec::new();
+ expected.try_push(2).unwrap();
+ expected.try_push(0).unwrap();
+ expected.try_push(1).unwrap();
+ expected.try_push(0).unwrap();
+ expected.try_push(3).unwrap();
+ assert_eq!(to_vec(&test).unwrap(), expected);
+}
diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index 05ca21fbba06..a408f098fbfd 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -48,6 +48,14 @@ config SAMPLE_PUZZLEFS
To compile this as a module, choose M here:
the module will be called puzzlefs.
+config SAMPLE_RUST_SERDE
+ tristate "Serde"
+ help
+ This option builds the Rust `serde` sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_serde.
+
If unsure, say N.
config SAMPLE_RUST_HOSTPROGS
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index 364a38dbf90b..bc27f97d71ad 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -4,5 +4,6 @@ obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o
obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o
obj-$(CONFIG_SAMPLE_RUST_FS) += rust_fs.o
obj-$(CONFIG_SAMPLE_PUZZLEFS) += puzzlefs.o
+obj-$(CONFIG_SAMPLE_RUST_SERDE) += rust_serde.o
subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs
diff --git a/samples/rust/local_data_format/de.rs b/samples/rust/local_data_format/de.rs
new file mode 100644
index 000000000000..32cfc53f98b3
--- /dev/null
+++ b/samples/rust/local_data_format/de.rs
@@ -0,0 +1,422 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+// Copyright 2018 Serde Developers
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::error::{Error, Result};
+use serde::de::{self, Deserialize, DeserializeSeed, SeqAccess, Visitor};
+
+pub struct Deserializer<'de> {
+ // This string starts with the input data and characters are truncated off
+ // the beginning as data is parsed.
+ input: &'de [u8],
+}
+
+impl<'de> Deserializer<'de> {
+ // By convention, `Deserializer` constructors are named like `from_xyz`.
+ // That way basic use cases are satisfied by something like
+ // `serde_json::from_str(...)` while advanced use cases that require a
+ // deserializer can make one with `serde_json::Deserializer::from_str(...)`.
+ #[allow(clippy::should_implement_trait)]
+ pub fn from_bytes(input: &'de [u8]) -> Self {
+ Deserializer { input }
+ }
+}
+
+// By convention, the public API of a Serde deserializer is one or more
+// `from_xyz` methods such as `from_str`, `from_bytes`, or `from_reader`
+// depending on what Rust types the deserializer is able to consume as input.
+//
+// This basic deserializer supports only `from_str`.
+pub fn from_bytes<'a, T>(s: &'a [u8]) -> Result<T>
+where
+ T: Deserialize<'a>,
+{
+ let mut deserializer = Deserializer::from_bytes(s);
+ let t = T::deserialize(&mut deserializer)?;
+ if deserializer.input.is_empty() {
+ Ok(t)
+ } else {
+ Err(Error::TrailingCharacters)
+ }
+}
+
+// SERDE IS NOT A PARSING LIBRARY. This impl block defines a few basic parsing
+// functions from scratch. More complicated formats may wish to use a dedicated
+// parsing library to help implement their Serde deserializer.
+impl<'de> Deserializer<'de> {
+ // Look at the first character in the input without consuming it.
+ fn peek_byte(&mut self) -> Result<u8> {
+ self.input.iter().next().ok_or(Error::Eof).map(|v| *v)
+ }
+
+ // Consume the first character in the input.
+ fn next_byte(&mut self) -> Result<u8> {
+ let ch = self.peek_byte()?;
+ self.input = &self.input[1..];
+ Ok(ch)
+ }
+
+ // Parse the JSON identifier `true` or `false`.
+ fn parse_bool(&mut self) -> Result<bool> {
+ if self.input.starts_with(&[1]) {
+ self.input = &self.input[1..];
+ match self.next_byte()? {
+ 42 => Ok(false),
+ 43 => Ok(true),
+ _ => Err(Error::InvalidBooleanValue),
+ }
+ } else {
+ Err(Error::ExpectedBoolean)
+ }
+ }
+
+ // Parse a group of decimal digits as an unsigned integer of type T.
+ //
+ // This implementation is a bit too lenient, for example `001` is not
+ // allowed in JSON. Also the various arithmetic operations can overflow and
+ // panic or return bogus data. But it is good enough for example code!
+ fn parse_unsigned<T>(&mut self) -> Result<T> {
+ unimplemented!()
+ }
+
+ // Parse a possible minus sign followed by a group of decimal digits as a
+ // signed integer of type T.
+ fn parse_signed<T>(&mut self) -> Result<T> {
+ // Optional minus sign, delegate to `parse_unsigned`, negate if negative.
+ unimplemented!()
+ }
+}
+
+impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
+ type Error = Error;
+
+ // Look at the input data to decide what Serde data model type to
+ // deserialize as. Not all data formats are able to support this operation.
+ // Formats that support `deserialize_any` are known as self-describing.
+ fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ match self.peek_byte()? {
+ 0 => self.deserialize_unit(visitor),
+ 1 => self.deserialize_bool(visitor),
+ 2 => self.deserialize_map(visitor),
+ _ => Err(Error::Syntax),
+ }
+ }
+
+ // Uses the `parse_bool` parsing function defined above to read the JSON
+ // identifier `true` or `false` from the input.
+ //
+ // Parsing refers to looking at the input and deciding that it contains the
+ // JSON value `true` or `false`.
+ //
+ // Deserialization refers to mapping that JSON value into Serde's data
+ // model by invoking one of the `Visitor` methods. In the case of JSON and
+ // bool that mapping is straightforward so the distinction may seem silly,
+ // but in other cases Deserializers sometimes perform non-obvious mappings.
+ // For example the TOML format has a Datetime type and Serde's data model
+ // does not. In the `toml` crate, a Datetime in the input is deserialized by
+ // mapping it to a Serde data model "struct" type with a special name and a
+ // single field containing the Datetime represented as a string.
+ fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_bool(self.parse_bool()?)
+ }
+
+ // The `parse_signed` function is generic over the integer type `T` so here
+ // it is invoked with `T=i8`. The next 8 methods are similar.
+ fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_i8(self.parse_signed()?)
+ }
+
+ fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_i16(self.parse_signed()?)
+ }
+
+ fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_i32(self.parse_signed()?)
+ }
+
+ fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_i64(self.parse_signed()?)
+ }
+
+ fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_u8(self.parse_unsigned()?)
+ }
+
+ fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_u16(self.parse_unsigned()?)
+ }
+
+ fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_u32(self.parse_unsigned()?)
+ }
+
+ fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_u64(self.parse_unsigned()?)
+ }
+
+ // The `Serializer` implementation on the previous page serialized chars as
+ // single-character strings so handle that representation here.
+ fn deserialize_char<V>(self, _visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ unimplemented!()
+ }
+
+ // Refer to the "Understanding deserializer lifetimes" page for information
+ // about the three deserialization flavors of strings in Serde.
+ fn deserialize_str<V>(self, _visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ unimplemented!()
+ }
+
+ fn deserialize_string<V>(self, _visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ unimplemented!()
+ }
+
+ // The `Serializer` implementation on the previous page serialized byte
+ // arrays as JSON arrays of bytes. Handle that representation here.
+ fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ unimplemented!()
+ }
+
+ fn deserialize_byte_buf<V>(self, _visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ unimplemented!()
+ }
+
+ // An absent optional is represented as the JSON `null` and a present
+ // optional is represented as just the contained value.
+ //
+ // As commented in `Serializer` implementation, this is a lossy
+ // representation. For example the values `Some(())` and `None` both
+ // serialize as just `null`. Unfortunately this is typically what people
+ // expect when working with JSON. Other formats are encouraged to behave
+ // more intelligently if possible.
+ fn deserialize_option<V>(self, _visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ unimplemented!()
+ }
+
+ // In Serde, unit means an anonymous value containing no data.
+ fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ if self.input.starts_with(&[0]) {
+ self.input = &self.input[1..];
+ visitor.visit_unit()
+ } else {
+ Err(Error::ExpectedNull)
+ }
+ }
+
+ // Unit struct means a named value containing no data.
+ fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ self.deserialize_unit(visitor)
+ }
+
+ // As is done here, serializers are encouraged to treat newtype structs as
+ // insignificant wrappers around the data they contain. That means not
+ // parsing anything other than the contained value.
+ fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ visitor.visit_newtype_struct(self)
+ }
+
+ // Deserialization of compound types like sequences and maps happens by
+ // passing the visitor an "Access" object that gives it the ability to
+ // iterate through the data contained in the sequence.
+ fn deserialize_seq<V>(self, _visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ unimplemented!()
+ }
+
+ // Tuples look just like sequences in JSON. Some formats may be able to
+ // represent tuples more efficiently.
+ //
+ // As indicated by the length parameter, the `Deserialize` implementation
+ // for a tuple in the Serde data model is required to know the length of the
+ // tuple before even looking at the input data.
+ fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ self.deserialize_seq(visitor)
+ }
+
+ // Tuple structs look just like sequences in JSON.
+ fn deserialize_tuple_struct<V>(
+ self,
+ _name: &'static str,
+ _len: usize,
+ visitor: V,
+ ) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ self.deserialize_seq(visitor)
+ }
+
+ // Much like `deserialize_seq` but calls the visitors `visit_map` method
+ // with a `MapAccess` implementation, rather than the visitor's `visit_seq`
+ // method with a `SeqAccess` implementation.
+ fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ // Parse the opening brace of the map.
+ if self.next_byte()? == 2 {
+ // Give the visitor access to each entry of the map.
+ let value = visitor.visit_seq(StructFieldsVisitor::new(self))?;
+ // Parse the closing brace of the map.
+ if self.next_byte()? == 3 {
+ Ok(value)
+ } else {
+ Err(Error::ExpectedMapEnd)
+ }
+ } else {
+ Err(Error::ExpectedMap)
+ }
+ }
+
+ // Structs look just like maps in JSON.
+ //
+ // Notice the `fields` parameter - a "struct" in the Serde data model means
+ // that the `Deserialize` implementation is required to know what the fields
+ // are before even looking at the input data. Any key-value pairing in which
+ // the fields cannot be known ahead of time is probably a map.
+ fn deserialize_struct<V>(
+ self,
+ _name: &'static str,
+ _fields: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ self.deserialize_map(visitor)
+ }
+
+ fn deserialize_enum<V>(
+ self,
+ _name: &'static str,
+ _variants: &'static [&'static str],
+ _visitor: V,
+ ) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ unimplemented!()
+ }
+
+ // An identifier in Serde is the type that identifies a field of a struct or
+ // the variant of an enum. In JSON, struct fields and enum variants are
+ // represented as strings. In other formats they may be represented as
+ // numeric indices.
+ fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ self.deserialize_str(visitor)
+ }
+
+ // Like `deserialize_any` but indicates to the `Deserializer` that it makes
+ // no difference which `Visitor` method is called because the data is
+ // ignored.
+ //
+ // Some deserializers are able to implement this more efficiently than
+ // `deserialize_any`, for example by rapidly skipping over matched
+ // delimiters without paying close attention to the data in between.
+ //
+ // Some formats are not able to implement this at all. Formats that can
+ // implement `deserialize_any` and `deserialize_ignored_any` are known as
+ // self-describing.
+ fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ self.deserialize_any(visitor)
+ }
+}
+
+struct StructFieldsVisitor<'a, 'de> {
+ de: &'a mut Deserializer<'de>,
+}
+
+impl<'a, 'de> StructFieldsVisitor<'a, 'de> {
+ fn new(de: &'a mut Deserializer<'de>) -> Self {
+ StructFieldsVisitor { de }
+ }
+}
+
+impl<'de, 'a> SeqAccess<'de> for StructFieldsVisitor<'a, 'de> {
+ type Error = Error;
+
+ fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
+ where
+ T: DeserializeSeed<'de>,
+ {
+ // Check if there are no more elements.
+ if self.de.peek_byte()? == 3 {
+ return Ok(None);
+ }
+ // Deserialize an array element.
+ seed.deserialize(&mut *self.de).map(Some)
+ }
+}
diff --git a/samples/rust/local_data_format/error.rs b/samples/rust/local_data_format/error.rs
new file mode 100644
index 000000000000..8d913580f713
--- /dev/null
+++ b/samples/rust/local_data_format/error.rs
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+// Copyright 2018 Serde Developers
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use core::fmt::{self, Display};
+use serde::{de, ser};
+
+pub type Result<T> = kernel::error::Result<T, Error>;
+
+// This is a bare-bones implementation. A real library would provide additional
+// information in its error type, for example the line and column at which the
+// error occurred, the byte offset into the input, or the current key being
+// processed.
+#[derive(Debug)]
+pub enum Error {
+ // One or more variants that can be created by data structures through the
+ // `ser::Error` and `de::Error` traits. For example the Serialize impl for
+ // Mutex<T> might return an error because the mutex is poisoned, or the
+ // Deserialize impl for a struct may return an error because a required
+ // field is missing.
+ Message,
+
+ // Zero or more variants that can be created directly by the Serializer and
+ // Deserializer without going through `ser::Error` and `de::Error`. These
+ // are specific to the format, in this case JSON.
+ Eof,
+ Syntax,
+ ExpectedBoolean,
+ InvalidBooleanValue,
+ ExpectedInteger,
+ ExpectedString,
+ ExpectedNull,
+ ExpectedArray,
+ ExpectedArrayComma,
+ ExpectedArrayEnd,
+ ExpectedMap,
+ ExpectedMapColon,
+ ExpectedMapComma,
+ ExpectedMapEnd,
+ ExpectedEnum,
+ TrailingCharacters,
+}
+
+impl ser::Error for Error {
+ fn custom<T: Display>(_msg: T) -> Self {
+ Error::Message
+ }
+}
+
+impl de::Error for Error {
+ fn custom<T: Display>(_msg: T) -> Self {
+ Error::Message
+ }
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Error::Message => write!(f, "message"),
+ Error::Eof => f.write_str("unexpected end of input"),
+ /* and so forth */
+ _ => unimplemented!(),
+ }
+ }
+}
+
+//impl core::error::Error for Error {}
diff --git a/samples/rust/local_data_format/ser.rs b/samples/rust/local_data_format/ser.rs
new file mode 100644
index 000000000000..f4f17eb6da66
--- /dev/null
+++ b/samples/rust/local_data_format/ser.rs
@@ -0,0 +1,443 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+// Copyright 2018 Serde Developers
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::error::{Error, Result};
+use alloc::vec::Vec;
+use serde::ser::{self, Serialize};
+
+pub struct Serializer {
+ // This string starts empty and JSON is appended as values are serialized.
+ output: Vec<u8>,
+}
+
+// By convention, the public API of a Serde serializer is one or more `to_abc`
+// functions such as `to_string`, `to_bytes`, or `to_writer` depending on what
+// Rust types the serializer is able to produce as output.
+//
+// This basic serializer supports only `to_string`.
+pub fn to_vec<T>(value: &T) -> Result<Vec<u8>>
+where
+ T: Serialize,
+{
+ let mut serializer = Serializer { output: Vec::new() };
+ value.serialize(&mut serializer)?;
+ Ok(serializer.output)
+}
+
+impl<'a> ser::Serializer for &'a mut Serializer {
+ // The output type produced by this `Serializer` during successful
+ // serialization. Most serializers that produce text or binary output should
+ // set `Ok = ()` and serialize into an `io::Write` or buffer contained
+ // within the `Serializer` instance, as happens here. Serializers that build
+ // in-memory data structures may be simplified by using `Ok` to propagate
+ // the data structure around.
+ type Ok = ();
+
+ // The error type when some error occurs during serialization.
+ type Error = Error;
+
+ // Associated types for keeping track of additional state while serializing
+ // compound data structures like sequences and maps. In this case no
+ // additional state is required beyond what is already stored in the
+ // Serializer struct.
+ type SerializeSeq = Self;
+ type SerializeTuple = Self;
+ type SerializeTupleStruct = Self;
+ type SerializeTupleVariant = Self;
+ type SerializeMap = Self;
+ type SerializeStruct = Self;
+ type SerializeStructVariant = Self;
+
+ // Here we go with the simple methods. The following 12 methods receive one
+ // of the primitive types of the data model and map it to JSON by appending
+ // into the output string.
+ fn serialize_bool(self, v: bool) -> Result<()> {
+ self.output.try_push(1).unwrap();
+ self.output.try_push(if v { 43 } else { 42 }).unwrap();
+ Ok(())
+ }
+
+ // JSON does not distinguish between different sizes of integers, so all
+ // signed integers will be serialized the same and all unsigned integers
+ // will be serialized the same. Other formats, especially compact binary
+ // formats, may need independent logic for the different sizes.
+ fn serialize_i8(self, v: i8) -> Result<()> {
+ self.serialize_i64(i64::from(v))
+ }
+
+ fn serialize_i16(self, v: i16) -> Result<()> {
+ self.serialize_i64(i64::from(v))
+ }
+
+ fn serialize_i32(self, v: i32) -> Result<()> {
+ self.serialize_i64(i64::from(v))
+ }
+
+ // Not particularly efficient but this is example code anyway. A more
+ // performant approach would be to use the `itoa` crate.
+ fn serialize_i64(self, _v: i64) -> Result<()> {
+ unimplemented!();
+ }
+
+ fn serialize_u8(self, v: u8) -> Result<()> {
+ self.serialize_u64(u64::from(v))
+ }
+
+ fn serialize_u16(self, v: u16) -> Result<()> {
+ self.serialize_u64(u64::from(v))
+ }
+
+ fn serialize_u32(self, v: u32) -> Result<()> {
+ self.serialize_u64(u64::from(v))
+ }
+
+ fn serialize_u64(self, _v: u64) -> Result<()> {
+ unimplemented!();
+ }
+
+ // Serialize a char as a single-character string. Other formats may
+ // represent this differently.
+ fn serialize_char(self, _v: char) -> Result<()> {
+ unimplemented!();
+ }
+
+ // This only works for strings that don't require escape sequences but you
+ // get the idea. For example it would emit invalid JSON if the input string
+ // contains a '"' character.
+ fn serialize_str(self, _v: &str) -> Result<()> {
+ unimplemented!();
+ }
+
+ // Serialize a byte array as an array of bytes. Could also use a base64
+ // string here. Binary formats will typically represent byte arrays more
+ // compactly.
+ fn serialize_bytes(self, v: &[u8]) -> Result<()> {
+ use serde::ser::SerializeSeq;
+ let mut seq = self.serialize_seq(Some(v.len()))?;
+ for byte in v {
+ seq.serialize_element(byte)?;
+ }
+ seq.end()
+ }
+
+ // An absent optional is represented as the JSON `null`.
+ fn serialize_none(self) -> Result<()> {
+ self.serialize_unit()
+ }
+
+ // A present optional is represented as just the contained value. Note that
+ // this is a lossy representation. For example the values `Some(())` and
+ // `None` both serialize as just `null`. Unfortunately this is typically
+ // what people expect when working with JSON. Other formats are encouraged
+ // to behave more intelligently if possible.
+ fn serialize_some<T>(self, value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ value.serialize(self)
+ }
+
+ // In Serde, unit means an anonymous value containing no data. Map this to
+ // JSON as `null`.
+ fn serialize_unit(self) -> Result<()> {
+ self.output.try_push(0).unwrap();
+ Ok(())
+ }
+
+ // Unit struct means a named value containing no data. Again, since there is
+ // no data, map this to JSON as `null`. There is no need to serialize the
+ // name in most formats.
+ fn serialize_unit_struct(self, _name: &'static str) -> Result<()> {
+ self.serialize_unit()
+ }
+
+ // When serializing a unit variant (or any other kind of variant), formats
+ // can choose whether to keep track of it by index or by name. Binary
+ // formats typically use the index of the variant and human-readable formats
+ // typically use the name.
+ fn serialize_unit_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ variant: &'static str,
+ ) -> Result<()> {
+ self.serialize_str(variant)
+ }
+
+ // As is done here, serializers are encouraged to treat newtype structs as
+ // insignificant wrappers around the data they contain.
+ fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ value.serialize(self)
+ }
+
+ // Note that newtype variant (and all of the other variant serialization
+ // methods) refer exclusively to the "externally tagged" enum
+ // representation.
+ //
+ // Serialize this to JSON in externally tagged form as `{ NAME: VALUE }`.
+ fn serialize_newtype_variant<T>(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ _variant: &'static str,
+ _value: &T,
+ ) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ unimplemented!();
+ }
+
+ // Now we get to the serialization of compound types.
+ //
+ // The start of the sequence, each value, and the end are three separate
+ // method calls. This one is responsible only for serializing the start,
+ // which in JSON is `[`.
+ //
+ // The length of the sequence may or may not be known ahead of time. This
+ // doesn't make a difference in JSON because the length is not represented
+ // explicitly in the serialized form. Some serializers may only be able to
+ // support sequences for which the length is known up front.
+ fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
+ unimplemented!();
+ }
+
+ // Tuples look just like sequences in JSON. Some formats may be able to
+ // represent tuples more efficiently by omitting the length, since tuple
+ // means that the corresponding `Deserialize implementation will know the
+ // length without needing to look at the serialized data.
+ fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
+ self.serialize_seq(Some(len))
+ }
+
+ // Tuple structs look just like sequences in JSON.
+ fn serialize_tuple_struct(
+ self,
+ _name: &'static str,
+ len: usize,
+ ) -> Result<Self::SerializeTupleStruct> {
+ self.serialize_seq(Some(len))
+ }
+
+ // Tuple variants are represented in JSON as `{ NAME: [DATA...] }`. Again
+ // this method is only responsible for the externally tagged representation.
+ fn serialize_tuple_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ _variant: &'static str,
+ _len: usize,
+ ) -> Result<Self::SerializeTupleVariant> {
+ unimplemented!();
+ }
+
+ // Maps are represented in JSON as `{ K: V, K: V, ... }`.
+ fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
+ self.output.try_push(2).unwrap();
+ Ok(self)
+ }
+
+ // Structs look just like maps in JSON. In particular, JSON requires that we
+ // serialize the field names of the struct. Other formats may be able to
+ // omit the field names when serializing structs because the corresponding
+ // Deserialize implementation is required to know what the keys are without
+ // looking at the serialized data.
+ fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
+ self.serialize_map(Some(len))
+ }
+
+ // Struct variants are represented in JSON as `{ NAME: { K: V, ... } }`.
+ // This is the externally tagged representation.
+ fn serialize_struct_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ _variant: &'static str,
+ _len: usize,
+ ) -> Result<Self::SerializeStructVariant> {
+ unimplemented!();
+ }
+
+ fn collect_str<T: ?Sized>(self, _: &T) -> Result<()>
+ where
+ T: core::fmt::Display,
+ {
+ unimplemented!()
+ }
+}
+
+// The following 7 impls deal with the serialization of compound types like
+// sequences and maps. Serialization of such types is begun by a Serializer
+// method and followed by zero or more calls to serialize individual elements of
+// the compound type and one call to end the compound type.
+//
+// This impl is SerializeSeq so these methods are called after `serialize_seq`
+// is called on the Serializer.
+impl<'a> ser::SerializeSeq for &'a mut Serializer {
+ // Must match the `Ok` type of the serializer.
+ type Ok = ();
+ // Must match the `Error` type of the serializer.
+ type Error = Error;
+
+ // Serialize a single element of the sequence.
+ fn serialize_element<T>(&mut self, _value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ unimplemented!();
+ }
+
+ // Close the sequence.
+ fn end(self) -> Result<()> {
+ unimplemented!();
+ }
+}
+
+// Same thing but for tuples.
+impl<'a> ser::SerializeTuple for &'a mut Serializer {
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_element<T>(&mut self, _value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ unimplemented!();
+ }
+
+ fn end(self) -> Result<()> {
+ unimplemented!();
+ }
+}
+
+// Same thing but for tuple structs.
+impl<'a> ser::SerializeTupleStruct for &'a mut Serializer {
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_field<T>(&mut self, _value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ unimplemented!();
+ }
+
+ fn end(self) -> Result<()> {
+ unimplemented!();
+ }
+}
+
+// Tuple variants are a little different. Refer back to the
+// `serialize_tuple_variant` method above:
+//
+// self.output += "{";
+// variant.serialize(&mut *self)?;
+// self.output += ":[";
+//
+// So the `end` method in this impl is responsible for closing both the `]` and
+// the `}`.
+impl<'a> ser::SerializeTupleVariant for &'a mut Serializer {
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_field<T>(&mut self, _value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ unimplemented!();
+ }
+
+ fn end(self) -> Result<()> {
+ unimplemented!();
+ }
+}
+
+// Some `Serialize` types are not able to hold a key and value in memory at the
+// same time so `SerializeMap` implementations are required to support
+// `serialize_key` and `serialize_value` individually.
+//
+// There is a third optional method on the `SerializeMap` trait. The
+// `serialize_entry` method allows serializers to optimize for the case where
+// key and value are both available simultaneously. In JSON it doesn't make a
+// difference so the default behavior for `serialize_entry` is fine.
+impl<'a> ser::SerializeMap for &'a mut Serializer {
+ type Ok = ();
+ type Error = Error;
+
+ // The Serde data model allows map keys to be any serializable type. JSON
+ // only allows string keys so the implementation below will produce invalid
+ // JSON if the key serializes as something other than a string.
+ //
+ // A real JSON serializer would need to validate that map keys are strings.
+ // This can be done by using a different Serializer to serialize the key
+ // (instead of `&mut **self`) and having that other serializer only
+ // implement `serialize_str` and return an error on any other data type.
+ fn serialize_key<T>(&mut self, _key: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ unimplemented!()
+ }
+
+ // It doesn't make a difference whether the colon is printed at the end of
+ // `serialize_key` or at the beginning of `serialize_value`. In this case
+ // the code is a bit simpler having it here.
+ fn serialize_value<T>(&mut self, _value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ unimplemented!()
+ }
+
+ fn end(self) -> Result<()> {
+ self.output.try_push(3).unwrap();
+ Ok(())
+ }
+}
+
+// Structs are like maps in which the keys are constrained to be compile-time
+// constant strings.
+impl<'a> ser::SerializeStruct for &'a mut Serializer {
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ value.serialize(&mut **self)
+ }
+
+ fn end(self) -> Result<()> {
+ self.output.try_push(3).unwrap();
+ Ok(())
+ }
+}
+
+// Similar to `SerializeTupleVariant`, here the `end` method is responsible for
+// closing both of the curly braces opened by `serialize_struct_variant`.
+impl<'a> ser::SerializeStructVariant for &'a mut Serializer {
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_field<T>(&mut self, _key: &'static str, _value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ unimplemented!();
+ }
+
+ fn end(self) -> Result<()> {
+ unimplemented!();
+ }
+}
diff --git a/samples/rust/rust_serde.rs b/samples/rust/rust_serde.rs
new file mode 100644
index 000000000000..3d4957dd0822
--- /dev/null
+++ b/samples/rust/rust_serde.rs
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust `serde` sample.
+//!
+//! It uses a data format from the `kernel` crate, as well as defining
+//! one here ("local"). Then it uses both on a type that uses `serve_derive`.
+
+use kernel::prelude::*;
+use serde_derive::{Deserialize, Serialize};
+
+module! {
+ type: RustSerde,
+ name: "rust_serde",
+ author: "Rust for Linux Contributors",
+ description: "Rust `serde` sample",
+ license: "GPL",
+}
+
+struct RustSerde;
+
+pub mod local_data_format {
+ #![allow(missing_docs)]
+
+ mod de;
+ mod error;
+ mod ser;
+
+ pub use de::{from_bytes, Deserializer};
+ pub use error::{Error, Result};
+ pub use ser::{to_vec, Serializer};
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+struct S {
+ a: (),
+ b: bool,
+ c: bool,
+ d: (),
+}
+
+impl kernel::Module for RustSerde {
+ fn init(_module: &'static ThisModule) -> Result<Self> {
+ pr_info!("Rust serde sample (init)\n");
+
+ let original = S {
+ a: (),
+ b: false,
+ c: true,
+ d: (),
+ };
+ crate::pr_info!(" original = {:?}", original);
+
+ let serialized = kernel::test_serde::to_vec(&original).unwrap();
+ crate::pr_info!(" serialized = {:?}", serialized);
+
+ let deserialized: S = kernel::test_serde::from_bytes(&serialized).unwrap();
+ crate::pr_info!(" deserialized = {:?}", deserialized);
+
+ let serialized = local_data_format::to_vec(&deserialized).unwrap();
+ crate::pr_info!(" serialized (local) = {:?}", serialized);
+
+ let deserialized: S = local_data_format::from_bytes(&serialized).unwrap();
+ crate::pr_info!("deserialized (local) = {:?}", deserialized);
+
+ Ok(RustSerde)
+ }
+}
+
+impl Drop for RustSerde {
+ fn drop(&mut self) {
+ pr_info!("Rust serde sample (exit)\n");
+ }
+}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 36/80] Add SAMPLE_RUST_SERDE in rust.config
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (30 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 35/80] rust: test `serde` support Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 37/80] rust: kernel: fix compile errors after rebase to rust-next Ariel Miculas
` (45 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
---
kernel/configs/rust.config | 1 +
1 file changed, 1 insertion(+)
diff --git a/kernel/configs/rust.config b/kernel/configs/rust.config
index 422b49798d92..5e29e66e8f7d 100644
--- a/kernel/configs/rust.config
+++ b/kernel/configs/rust.config
@@ -8,4 +8,5 @@ CONFIG_SAMPLE_RUST_PRINT=m
CONFIG_SAMPLE_RUST_FS=m
CONFIG_SAMPLE_PUZZLEFS=m
CONFIG_SAMPLE_RUST_HOSTPROGS=y
+CONFIG_SAMPLE_RUST_SERDE=y
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 37/80] rust: kernel: fix compile errors after rebase to rust-next
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (31 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 36/80] Add SAMPLE_RUST_SERDE in rust.config Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 9:38 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 39/80] rust: serde_cbor: add SPDX License Identifiers Ariel Miculas
` (44 subsequent siblings)
77 siblings, 1 reply; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/kernel/iov_iter.rs | 2 +-
rust/kernel/mm.rs | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/rust/kernel/iov_iter.rs b/rust/kernel/iov_iter.rs
index 4602cbd4b49c..78d573e03175 100644
--- a/rust/kernel/iov_iter.rs
+++ b/rust/kernel/iov_iter.rs
@@ -23,7 +23,7 @@ pub struct IovIter {
impl IovIter {
fn common_len(&self) -> usize {
// SAFETY: `IovIter::ptr` is guaranteed to be valid by the type invariants.
- unsafe { (*self.ptr).count }
+ unsafe { (*self.ptr).__bindgen_anon_2.__bindgen_anon_1.count }
}
/// Constructs a new [`struct iov_iter`] wrapper.
diff --git a/rust/kernel/mm.rs b/rust/kernel/mm.rs
index 72b504c949c1..edd022762aaa 100644
--- a/rust/kernel/mm.rs
+++ b/rust/kernel/mm.rs
@@ -38,7 +38,7 @@ pub(crate) unsafe fn from_ptr(vma: *mut bindings::vm_area_struct) -> Self {
/// The possible flags are a combination of the constants in [`flags`].
pub fn flags(&self) -> usize {
// SAFETY: `self.vma` is valid by the type invariants.
- unsafe { (*self.vma).__bindgen_anon_1.vm_flags as _ }
+ unsafe { (*self.vma).__bindgen_anon_2.vm_flags as _ }
}
/// Sets the flags associated with the virtual memory area.
@@ -46,19 +46,19 @@ pub fn flags(&self) -> usize {
/// The possible flags are a combination of the constants in [`flags`].
pub fn set_flags(&mut self, flags: usize) {
// SAFETY: `self.vma` is valid by the type invariants.
- unsafe { (*self.vma).__bindgen_anon_1.vm_flags = flags as _ };
+ unsafe { (*self.vma).__bindgen_anon_2.vm_flags = flags as _ };
}
/// Returns the start address of the virtual memory area.
pub fn start(&self) -> usize {
// SAFETY: `self.vma` is valid by the type invariants.
- unsafe { (*self.vma).vm_start as _ }
+ unsafe { (*self.vma).__bindgen_anon_1.__bindgen_anon_1.vm_start as _ }
}
/// Returns the end address of the virtual memory area.
pub fn end(&self) -> usize {
// SAFETY: `self.vma` is valid by the type invariants.
- unsafe { (*self.vma).vm_end as _ }
+ unsafe { (*self.vma).__bindgen_anon_1.__bindgen_anon_1.vm_end as _ }
}
/// Maps a single page at the given address within the virtual memory area.
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 39/80] rust: serde_cbor: add SPDX License Identifiers
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (32 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 37/80] rust: kernel: fix compile errors after rebase to rust-next Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 40/80] rust: serde_cbor: add no_fp_fmt_parse support Ariel Miculas
` (43 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/serde_cbor/de.rs | 2 ++
rust/serde_cbor/error.rs | 2 ++
rust/serde_cbor/lib.rs | 2 ++
rust/serde_cbor/read.rs | 2 ++
rust/serde_cbor/ser.rs | 2 ++
rust/serde_cbor/tags.rs | 2 ++
rust/serde_cbor/value/de.rs | 2 ++
rust/serde_cbor/value/mod.rs | 2 ++
rust/serde_cbor/value/ser.rs | 2 ++
rust/serde_cbor/write.rs | 2 ++
10 files changed, 20 insertions(+)
diff --git a/rust/serde_cbor/de.rs b/rust/serde_cbor/de.rs
index 170e0593cf44..1c9a9acc8eb5 100644
--- a/rust/serde_cbor/de.rs
+++ b/rust/serde_cbor/de.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! Deserialization.
use core::f32;
diff --git a/rust/serde_cbor/error.rs b/rust/serde_cbor/error.rs
index b1a6a459e540..0b4b5be8edcc 100644
--- a/rust/serde_cbor/error.rs
+++ b/rust/serde_cbor/error.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! When serializing or deserializing CBOR goes wrong.
use core::fmt;
use core::result;
diff --git a/rust/serde_cbor/lib.rs b/rust/serde_cbor/lib.rs
index 55668541539b..cf406a439340 100644
--- a/rust/serde_cbor/lib.rs
+++ b/rust/serde_cbor/lib.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! CBOR and serialization.
//!
//! # Usage
diff --git a/rust/serde_cbor/read.rs b/rust/serde_cbor/read.rs
index 1b53018df870..6c17282d2c5d 100644
--- a/rust/serde_cbor/read.rs
+++ b/rust/serde_cbor/read.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
#[cfg(feature = "alloc")]
use alloc::{vec, vec::Vec};
#[cfg(feature = "std")]
diff --git a/rust/serde_cbor/ser.rs b/rust/serde_cbor/ser.rs
index 7016dc340a84..9f5bb67f0479 100644
--- a/rust/serde_cbor/ser.rs
+++ b/rust/serde_cbor/ser.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! Serialize a Rust data structure to CBOR data.
#[cfg(feature = "alloc")]
diff --git a/rust/serde_cbor/tags.rs b/rust/serde_cbor/tags.rs
index 8adccb8ea8b5..057765b9d2f8 100644
--- a/rust/serde_cbor/tags.rs
+++ b/rust/serde_cbor/tags.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! Support for cbor tags
use core::fmt;
use core::marker::PhantomData;
diff --git a/rust/serde_cbor/value/de.rs b/rust/serde_cbor/value/de.rs
index f5bdbb74676c..366eda1e27ac 100644
--- a/rust/serde_cbor/value/de.rs
+++ b/rust/serde_cbor/value/de.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use std::collections::BTreeMap;
use std::fmt;
diff --git a/rust/serde_cbor/value/mod.rs b/rust/serde_cbor/value/mod.rs
index 7bd2255314ca..cc93c2b40a52 100644
--- a/rust/serde_cbor/value/mod.rs
+++ b/rust/serde_cbor/value/mod.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! CBOR values, keys and serialization routines.
mod de;
diff --git a/rust/serde_cbor/value/ser.rs b/rust/serde_cbor/value/ser.rs
index 347aae9601fa..e52987778288 100644
--- a/rust/serde_cbor/value/ser.rs
+++ b/rust/serde_cbor/value/ser.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
diff --git a/rust/serde_cbor/write.rs b/rust/serde_cbor/write.rs
index 94c326ef96b0..21baf7de9711 100644
--- a/rust/serde_cbor/write.rs
+++ b/rust/serde_cbor/write.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(not(feature = "std"))]
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 40/80] rust: serde_cbor: add no_fp_fmt_parse support
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (33 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 39/80] rust: serde_cbor: add SPDX License Identifiers Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 41/80] rust: Kbuild: enable serde_cbor Ariel Miculas
` (42 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
We do not have formatting for floating point in the kernel,
thus simple compile out all that.
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/serde_cbor/de.rs | 8 ++++++++
rust/serde_cbor/ser.rs | 3 +++
rust/serde_cbor/tags.rs | 2 ++
rust/serde_cbor/value/ser.rs | 2 ++
4 files changed, 15 insertions(+)
diff --git a/rust/serde_cbor/de.rs b/rust/serde_cbor/de.rs
index 1c9a9acc8eb5..534f9d53aa3b 100644
--- a/rust/serde_cbor/de.rs
+++ b/rust/serde_cbor/de.rs
@@ -2,10 +2,12 @@
//! Deserialization.
+#[cfg(not(no_fp_fmt_parse))]
use core::f32;
use core::marker::PhantomData;
use core::result;
use core::str;
+#[cfg(not(no_fp_fmt_parse))]
use half::f16;
use serde::de;
#[cfg(feature = "std")]
@@ -566,14 +568,17 @@ fn parse_indefinite_enum<V>(&mut self, visitor: V) -> Result<V::Value>
})
}
+ #[cfg(not(no_fp_fmt_parse))]
fn parse_f16(&mut self) -> Result<f32> {
Ok(f32::from(f16::from_bits(self.parse_u16()?)))
}
+ #[cfg(not(no_fp_fmt_parse))]
fn parse_f32(&mut self) -> Result<f32> {
self.parse_u32().map(|i| f32::from_bits(i))
}
+ #[cfg(not(no_fp_fmt_parse))]
fn parse_f64(&mut self) -> Result<f64> {
self.parse_u64().map(|i| f64::from_bits(i))
}
@@ -756,14 +761,17 @@ fn parse_value<V>(&mut self, visitor: V) -> Result<V::Value>
0xf6 => visitor.visit_unit(),
0xf7 => visitor.visit_unit(),
0xf8 => Err(self.error(ErrorCode::UnassignedCode)),
+ #[cfg(not(no_fp_fmt_parse))]
0xf9 => {
let value = self.parse_f16()?;
visitor.visit_f32(value)
}
+ #[cfg(not(no_fp_fmt_parse))]
0xfa => {
let value = self.parse_f32()?;
visitor.visit_f32(value)
}
+ #[cfg(not(no_fp_fmt_parse))]
0xfb => {
let value = self.parse_f64()?;
visitor.visit_f64(value)
diff --git a/rust/serde_cbor/ser.rs b/rust/serde_cbor/ser.rs
index 9f5bb67f0479..8c2273f9f544 100644
--- a/rust/serde_cbor/ser.rs
+++ b/rust/serde_cbor/ser.rs
@@ -10,6 +10,7 @@
pub use crate::write::{SliceWrite, Write};
use crate::error::{Error, Result};
+#[cfg(not(no_fp_fmt_parse))]
use half::f16;
use serde::ser::{self, Serialize};
#[cfg(feature = "std")]
@@ -311,6 +312,7 @@ fn serialize_u128(self, value: u128) -> Result<()> {
#[inline]
#[allow(clippy::float_cmp)]
+ #[cfg(not(no_fp_fmt_parse))]
fn serialize_f32(self, value: f32) -> Result<()> {
if value.is_infinite() {
if value.is_sign_positive() {
@@ -334,6 +336,7 @@ fn serialize_f32(self, value: f32) -> Result<()> {
#[inline]
#[allow(clippy::float_cmp)]
+ #[cfg(not(no_fp_fmt_parse))]
fn serialize_f64(self, value: f64) -> Result<()> {
if !value.is_finite() || f64::from(value as f32) == value {
self.serialize_f32(value as f32)
diff --git a/rust/serde_cbor/tags.rs b/rust/serde_cbor/tags.rs
index 057765b9d2f8..81f749fa68e8 100644
--- a/rust/serde_cbor/tags.rs
+++ b/rust/serde_cbor/tags.rs
@@ -129,7 +129,9 @@ fn expecting(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
delegate!(visit_u32, u32);
delegate!(visit_u64, u64);
+ #[cfg(not(no_fp_fmt_parse))]
delegate!(visit_f32, f32);
+ #[cfg(not(no_fp_fmt_parse))]
delegate!(visit_f64, f64);
delegate!(visit_char, char);
diff --git a/rust/serde_cbor/value/ser.rs b/rust/serde_cbor/value/ser.rs
index e52987778288..b4ff47c2e106 100644
--- a/rust/serde_cbor/value/ser.rs
+++ b/rust/serde_cbor/value/ser.rs
@@ -101,11 +101,13 @@ fn serialize_u64(self, value: u64) -> Result<Value, Error> {
}
#[inline]
+ #[cfg(not(no_fp_fmt_parse))]
fn serialize_f32(self, value: f32) -> Result<Value, Error> {
self.serialize_f64(f64::from(value))
}
#[inline]
+ #[cfg(not(no_fp_fmt_parse))]
fn serialize_f64(self, value: f64) -> Result<Value, Error> {
Ok(Value::Float(value))
}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 41/80] rust: Kbuild: enable serde_cbor
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (34 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 40/80] rust: serde_cbor: add no_fp_fmt_parse support Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 10:21 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 42/80] samples: rust: add cbor serialize/deserialize example Ariel Miculas
` (41 subsequent siblings)
77 siblings, 1 reply; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
Makefile | 2 ++
rust/Makefile | 23 ++++++++++++++++++++---
scripts/Makefile.build | 2 +-
3 files changed, 23 insertions(+), 4 deletions(-)
diff --git a/Makefile b/Makefile
index 3b7f37977f04..0abe8aaee974 100644
--- a/Makefile
+++ b/Makefile
@@ -1853,6 +1853,7 @@ rustfmt:
-o -path $(abs_srctree)/rust/syn -prune \
-o -path $(abs_srctree)/rust/serde -prune \
-o -path $(abs_srctree)/rust/serde_derive -prune \
+ -o -path $(abs_srctree)/rust/serde_cbor -prune \
-o -path $(abs_objtree)/rust/test -prune \
| grep -Fv $(abs_srctree)/rust/alloc \
| grep -Fv $(abs_srctree)/rust/proc-macro2 \
@@ -1860,6 +1861,7 @@ rustfmt:
| grep -Fv $(abs_srctree)/rust/syn \
| grep -Fv $(abs_srctree)/rust/serde \
| grep -Fv $(abs_srctree)/rust/serde_derive \
+ | grep -Fv $(abs_srctree)/rust/serde_cbor \
| grep -Fv $(abs_objtree)/rust/test \
| grep -Fv generated \
| xargs $(RUSTFMT) $(rustfmt_flags)
diff --git a/rust/Makefile b/rust/Makefile
index 37952e93ff06..129d5a6dd07e 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -13,7 +13,7 @@ always-$(CONFIG_RUST) += libserde_derive.so libmacros.so
no-clean-files += libserde_derive.so libmacros.so
always-$(CONFIG_RUST) += bindings/bindings_generated.rs bindings/bindings_helpers_generated.rs
-obj-$(CONFIG_RUST) += alloc.o bindings.o serde.o kernel.o
+obj-$(CONFIG_RUST) += alloc.o bindings.o serde.o serde_cbor.o kernel.o
always-$(CONFIG_RUST) += exports_alloc_generated.h exports_bindings_generated.h \
exports_kernel_generated.h
@@ -122,6 +122,17 @@ serde-flags := \
-Amissing_docs \
--cfg no_fp_fmt_parse
+serde_cbor-skip_flags := \
+ --edition=2021 \
+ -Drust_2018_idioms \
+ -Dunreachable_pub
+
+serde_cbor-flags := \
+ --edition=2018 \
+ -Amissing_docs \
+ --cfg no_fp_fmt_parse \
+ --extern serde
+
quiet_cmd_rustdoc = RUSTDOC $(if $(rustdoc_host),H, ) $<
cmd_rustdoc = \
OBJTREE=$(abspath $(objtree)) \
@@ -568,12 +579,18 @@ $(obj)/serde.o: private rustc_target_flags = $(serde-flags)
$(obj)/serde.o: $(src)/serde/lib.rs $(obj)/compiler_builtins.o FORCE
$(call if_changed_dep,rustc_library)
+$(obj)/serde_cbor.o: private skip_clippy = 1
+$(obj)/serde_cbor.o: private skip_flags = $(serde_cbor-skip_flags)
+$(obj)/serde_cbor.o: private rustc_target_flags = $(serde_cbor-flags)
+$(obj)/serde_cbor.o: $(src)/serde_cbor/lib.rs $(obj)/compiler_builtins.o FORCE
+ $(call if_changed_dep,rustc_library)
+
$(obj)/kernel.o: private rustc_target_flags = --extern alloc \
--extern build_error --extern macros --extern bindings --extern uapi \
- --extern serde --extern serde_derive
+ --extern serde --extern serde_derive --extern serde_cbor
$(obj)/kernel.o: $(src)/kernel/lib.rs $(obj)/alloc.o $(obj)/build_error.o \
$(obj)/libmacros.so $(obj)/bindings.o $(obj)/uapi.o \
- $(obj)/serde.o $(obj)/libserde_derive.so FORCE
+ $(obj)/serde.o $(obj)/libserde_derive.so $(obj)/serde_cbor.o FORCE
$(call if_changed_dep,rustc_library)
endif # CONFIG_RUST
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 3fa298bb7c31..758b327fd54d 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -284,7 +284,7 @@ rust_common_cmd = \
-Zallow-features=$(rust_allowed_features) \
-Zcrate-attr=no_std \
-Zcrate-attr='feature($(rust_allowed_features))' \
- --extern alloc --extern kernel --extern serde --extern serde_derive \
+ --extern alloc --extern kernel --extern serde --extern serde_derive --extern serde_cbor \
--crate-type rlib -L $(objtree)/rust/ \
--crate-name $(basename $(notdir $@)) \
--emit=dep-info=$(depfile)
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 42/80] samples: rust: add cbor serialize/deserialize example
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (35 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 41/80] rust: Kbuild: enable serde_cbor Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 43/80] rust: serde_cbor: add support for serde_cbor's from_slice method by using a custom alloc_kernel feature Ariel Miculas
` (40 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
samples/rust/rust_serde.rs | 56 ++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/samples/rust/rust_serde.rs b/samples/rust/rust_serde.rs
index 3d4957dd0822..0578f0fa137c 100644
--- a/samples/rust/rust_serde.rs
+++ b/samples/rust/rust_serde.rs
@@ -7,6 +7,8 @@
use kernel::prelude::*;
use serde_derive::{Deserialize, Serialize};
+use serde_cbor::ser::SliceWrite;
+use serde::Serialize;
module! {
type: RustSerde,
@@ -38,6 +40,57 @@ struct S {
d: (),
}
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+struct User {
+ user_id: u32,
+ password_hash: [u8; 4],
+}
+
+fn cbor_serialize() -> Result<(), serde_cbor::Error> {
+ let mut buf = [0u8; 100];
+ let writer = SliceWrite::new(&mut buf[..]);
+ let mut ser = serde_cbor::Serializer::new(writer);
+ let user = User {
+ user_id: 42,
+ password_hash: [1, 2, 3, 4],
+ };
+ user.serialize(&mut ser)?;
+ let writer = ser.into_inner();
+ let size = writer.bytes_written();
+ let expected = [
+ 0xa2, 0x67, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x2a, 0x6d,
+ 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73,
+ 0x68, 0x84, 0x1, 0x2, 0x3, 0x4
+ ];
+ assert_eq!(&buf[..size], expected);
+
+ crate::pr_info!("cbor serialized = {:?}", buf);
+
+ Ok(())
+}
+
+fn cbor_deserialize() -> Result<(), serde_cbor::Error> {
+ let value = [
+ 0xa2, 0x67, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x2a, 0x6d,
+ 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73,
+ 0x68, 0x84, 0x1, 0x2, 0x3, 0x4
+ ];
+
+ // from_slice_with_scratch will not alter input data, use it whenever you
+ // borrow from somewhere else.
+ // You will have to size your scratch according to the input data you
+ // expect.
+ let mut scratch = [0u8; 32];
+ let user: User = serde_cbor::de::from_slice_with_scratch(&value[..], &mut scratch)?;
+ assert_eq!(user, User {
+ user_id: 42,
+ password_hash: [1, 2, 3, 4],
+ });
+
+ crate::pr_info!("cbor deserialized = {:?}", user);
+ Ok(())
+}
+
impl kernel::Module for RustSerde {
fn init(_module: &'static ThisModule) -> Result<Self> {
pr_info!("Rust serde sample (init)\n");
@@ -62,6 +115,9 @@ fn init(_module: &'static ThisModule) -> Result<Self> {
let deserialized: S = local_data_format::from_bytes(&serialized).unwrap();
crate::pr_info!("deserialized (local) = {:?}", deserialized);
+ cbor_serialize().unwrap();
+ cbor_deserialize().unwrap();
+
Ok(RustSerde)
}
}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 43/80] rust: serde_cbor: add support for serde_cbor's from_slice method by using a custom alloc_kernel feature
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (36 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 42/80] samples: rust: add cbor serialize/deserialize example Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 9:55 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 44/80] rust: serde: add support for deserializing Vec with kernel_alloc feature Ariel Miculas
` (39 subsequent siblings)
77 siblings, 1 reply; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/Makefile | 2 ++
rust/kernel/test_serde/de.rs | 7 ++-----
rust/kernel/test_serde/ser.rs | 5 +----
rust/serde_cbor/de.rs | 6 +++---
rust/serde_cbor/lib.rs | 2 +-
rust/serde_cbor/read.rs | 18 +++++++++++++-----
samples/rust/rust_serde.rs | 32 ++++++++++++++------------------
7 files changed, 36 insertions(+), 36 deletions(-)
diff --git a/rust/Makefile b/rust/Makefile
index 129d5a6dd07e..0b67547d8007 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -131,6 +131,8 @@ serde_cbor-flags := \
--edition=2018 \
-Amissing_docs \
--cfg no_fp_fmt_parse \
+ --cfg 'feature="kernel_alloc"' \
+ --extern alloc \
--extern serde
quiet_cmd_rustdoc = RUSTDOC $(if $(rustdoc_host),H, ) $<
diff --git a/rust/kernel/test_serde/de.rs b/rust/kernel/test_serde/de.rs
index 84c98d1c1c66..68f4da2ace23 100644
--- a/rust/kernel/test_serde/de.rs
+++ b/rust/kernel/test_serde/de.rs
@@ -434,9 +434,6 @@ struct Test {
}
let j = &[2, 0, 1, 0, 3];
- let expected = Test {
- a: (),
- b: false,
- };
+ let expected = Test { a: (), b: false };
assert_eq!(expected, from_bytes(j).unwrap());
-}
\ No newline at end of file
+}
diff --git a/rust/kernel/test_serde/ser.rs b/rust/kernel/test_serde/ser.rs
index 56abe7095a5f..56439b81d4e3 100644
--- a/rust/kernel/test_serde/ser.rs
+++ b/rust/kernel/test_serde/ser.rs
@@ -454,10 +454,7 @@ struct Test {
b: bool,
}
- let test = Test {
- a: (),
- b: false,
- };
+ let test = Test { a: (), b: false };
let mut expected = Vec::new();
expected.try_push(2).unwrap();
diff --git a/rust/serde_cbor/de.rs b/rust/serde_cbor/de.rs
index 534f9d53aa3b..ab7572f24002 100644
--- a/rust/serde_cbor/de.rs
+++ b/rust/serde_cbor/de.rs
@@ -21,7 +21,7 @@
#[cfg(feature = "std")]
pub use crate::read::IoRead;
use crate::read::Offset;
-#[cfg(any(feature = "std", feature = "alloc"))]
+#[cfg(any(feature = "std", feature = "alloc", feature = "kernel_alloc"))]
pub use crate::read::SliceRead;
pub use crate::read::{MutSliceRead, Read, SliceReadFixed};
#[cfg(feature = "tags")]
@@ -47,7 +47,7 @@
/// let value: &str = de::from_slice(&v[..]).unwrap();
/// assert_eq!(value, "foobar");
/// ```
-#[cfg(any(feature = "std", feature = "alloc"))]
+#[cfg(any(feature = "std", feature = "alloc", feature = "kernel_alloc"))]
pub fn from_slice<'a, T>(slice: &'a [u8]) -> Result<T>
where
T: de::Deserialize<'a>,
@@ -150,7 +150,7 @@ pub fn from_reader(reader: R) -> Deserializer<IoRead<R>> {
}
}
-#[cfg(any(feature = "std", feature = "alloc"))]
+#[cfg(any(feature = "std", feature = "alloc", feature = "kernel_alloc"))]
impl<'a> Deserializer<SliceRead<'a>> {
/// Constructs a `Deserializer` which reads from a slice.
///
diff --git a/rust/serde_cbor/lib.rs b/rust/serde_cbor/lib.rs
index cf406a439340..c9751972dfc2 100644
--- a/rust/serde_cbor/lib.rs
+++ b/rust/serde_cbor/lib.rs
@@ -324,7 +324,7 @@
#[cfg(all(not(feature = "std"), test))]
extern crate std;
-#[cfg(feature = "alloc")]
+#[cfg(any(feature = "alloc", feature = "kernel_alloc"))]
extern crate alloc;
pub mod de;
diff --git a/rust/serde_cbor/read.rs b/rust/serde_cbor/read.rs
index 6c17282d2c5d..35ef08b84f31 100644
--- a/rust/serde_cbor/read.rs
+++ b/rust/serde_cbor/read.rs
@@ -2,6 +2,8 @@
#[cfg(feature = "alloc")]
use alloc::{vec, vec::Vec};
+#[cfg(feature = "kernel_alloc")]
+use alloc::{vec::Vec};
#[cfg(feature = "std")]
use core::cmp;
use core::mem;
@@ -298,7 +300,7 @@ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
}
/// A CBOR input source that reads from a slice of bytes.
-#[cfg(any(feature = "std", feature = "alloc"))]
+#[cfg(any(feature = "std", feature = "alloc", feature = "kernel_alloc"))]
#[derive(Debug)]
pub struct SliceRead<'a> {
slice: &'a [u8],
@@ -306,13 +308,16 @@ pub struct SliceRead<'a> {
index: usize,
}
-#[cfg(any(feature = "std", feature = "alloc"))]
+#[cfg(any(feature = "std", feature = "alloc", feature = "kernel_alloc"))]
impl<'a> SliceRead<'a> {
/// Creates a CBOR input source to read from a slice of bytes.
pub fn new(slice: &'a [u8]) -> SliceRead<'a> {
SliceRead {
slice,
+ #[cfg(not(feature = "kernel_alloc"))]
scratch: vec![],
+ #[cfg(feature = "kernel_alloc")]
+ scratch: Vec::new(),
index: 0,
}
}
@@ -328,7 +333,7 @@ fn end(&self, n: usize) -> Result<usize> {
}
}
-#[cfg(any(feature = "std", feature = "alloc"))]
+#[cfg(any(feature = "std", feature = "alloc", feature = "kernel_alloc"))]
impl<'a> Offset for SliceRead<'a> {
#[inline]
fn byte_offset(&self) -> usize {
@@ -337,12 +342,12 @@ fn byte_offset(&self) -> usize {
}
#[cfg(all(
- any(feature = "std", feature = "alloc"),
+ any(feature = "std", feature = "alloc", feature = "kernel_alloc"),
not(feature = "unsealed_read_write")
))]
impl<'a> private::Sealed for SliceRead<'a> {}
-#[cfg(any(feature = "std", feature = "alloc"))]
+#[cfg(any(feature = "std", feature = "alloc", feature = "kernel_alloc"))]
impl<'a> Read<'a> for SliceRead<'a> {
#[inline]
fn next(&mut self) -> Result<Option<u8>> {
@@ -371,7 +376,10 @@ fn clear_buffer(&mut self) {
fn read_to_buffer(&mut self, n: usize) -> Result<()> {
let end = self.end(n)?;
let slice = &self.slice[self.index..end];
+ #[cfg(not(feature = "kernel_alloc"))]
self.scratch.extend_from_slice(slice);
+ #[cfg(feature = "kernel_alloc")]
+ self.scratch.try_extend_from_slice(slice).unwrap();
self.index = end;
Ok(())
diff --git a/samples/rust/rust_serde.rs b/samples/rust/rust_serde.rs
index 0578f0fa137c..29286e763ca3 100644
--- a/samples/rust/rust_serde.rs
+++ b/samples/rust/rust_serde.rs
@@ -6,9 +6,9 @@
//! one here ("local"). Then it uses both on a type that uses `serve_derive`.
use kernel::prelude::*;
-use serde_derive::{Deserialize, Serialize};
-use serde_cbor::ser::SliceWrite;
use serde::Serialize;
+use serde_cbor::ser::SliceWrite;
+use serde_derive::{Deserialize, Serialize};
module! {
type: RustSerde,
@@ -58,9 +58,8 @@ fn cbor_serialize() -> Result<(), serde_cbor::Error> {
let writer = ser.into_inner();
let size = writer.bytes_written();
let expected = [
- 0xa2, 0x67, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x2a, 0x6d,
- 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73,
- 0x68, 0x84, 0x1, 0x2, 0x3, 0x4
+ 0xa2, 0x67, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x2a, 0x6d, 0x70, 0x61, 0x73,
+ 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x84, 0x1, 0x2, 0x3, 0x4,
];
assert_eq!(&buf[..size], expected);
@@ -71,21 +70,18 @@ fn cbor_serialize() -> Result<(), serde_cbor::Error> {
fn cbor_deserialize() -> Result<(), serde_cbor::Error> {
let value = [
- 0xa2, 0x67, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x2a, 0x6d,
- 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73,
- 0x68, 0x84, 0x1, 0x2, 0x3, 0x4
+ 0xa2, 0x67, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x2a, 0x6d, 0x70, 0x61, 0x73,
+ 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x84, 0x1, 0x2, 0x3, 0x4,
];
- // from_slice_with_scratch will not alter input data, use it whenever you
- // borrow from somewhere else.
- // You will have to size your scratch according to the input data you
- // expect.
- let mut scratch = [0u8; 32];
- let user: User = serde_cbor::de::from_slice_with_scratch(&value[..], &mut scratch)?;
- assert_eq!(user, User {
- user_id: 42,
- password_hash: [1, 2, 3, 4],
- });
+ let user: User = serde_cbor::de::from_slice(&value[..])?;
+ assert_eq!(
+ user,
+ User {
+ user_id: 42,
+ password_hash: [1, 2, 3, 4],
+ }
+ );
crate::pr_info!("cbor deserialized = {:?}", user);
Ok(())
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 44/80] rust: serde: add support for deserializing Vec with kernel_alloc feature
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (37 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 43/80] rust: serde_cbor: add support for serde_cbor's from_slice method by using a custom alloc_kernel feature Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 10:10 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 45/80] rust: file: Replace UnsafeCell with Opaque for File Ariel Miculas
` (38 subsequent siblings)
77 siblings, 1 reply; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/Makefile | 4 +++-
rust/serde/de/impls.rs | 19 +++++++++++++++++--
rust/serde/lib.rs | 2 +-
rust/serde/private/size_hint.rs | 2 +-
4 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/rust/Makefile b/rust/Makefile
index 0b67547d8007..c9feb8e1afe1 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -120,7 +120,9 @@ serde-skip_flags := \
serde-flags := \
-Amissing_docs \
- --cfg no_fp_fmt_parse
+ --cfg no_fp_fmt_parse \
+ --cfg 'feature="kernel_alloc"' \
+ --extern alloc
serde_cbor-skip_flags := \
--edition=2021 \
diff --git a/rust/serde/de/impls.rs b/rust/serde/de/impls.rs
index 9377f5eee650..6b860b2d9450 100644
--- a/rust/serde/de/impls.rs
+++ b/rust/serde/de/impls.rs
@@ -11,9 +11,12 @@
use seed::InPlaceSeed;
-#[cfg(any(feature = "std", feature = "alloc"))]
+#[cfg(any(feature = "std", feature = "alloc", feature = "kernel_alloc"))]
use __private::size_hint;
+#[cfg(feature = "kernel_alloc")]
+use alloc::vec::Vec;
+
////////////////////////////////////////////////////////////////////////////////
struct UnitVisitor;
@@ -1012,7 +1015,7 @@ fn nop_reserve<T>(_seq: T, _n: usize) {}
////////////////////////////////////////////////////////////////////////////////
-#[cfg(any(feature = "std", feature = "alloc"))]
+#[cfg(any(feature = "std", feature = "alloc", feature = "kernel_alloc"))]
impl<'de, T> Deserialize<'de> for Vec<T>
where
T: Deserialize<'de>,
@@ -1039,10 +1042,16 @@ fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
+ #[cfg(not(feature = "kernel_alloc"))]
let mut values = Vec::with_capacity(size_hint::cautious(seq.size_hint()));
+ #[cfg(feature = "kernel_alloc")]
+ let mut values = Vec::try_with_capacity(size_hint::cautious(seq.size_hint())).unwrap();
while let Some(value) = try!(seq.next_element()) {
+ #[cfg(not(feature = "kernel_alloc"))]
values.push(value);
+ #[cfg(feature = "kernel_alloc")]
+ values.try_push(value).unwrap();
}
Ok(values)
@@ -1077,7 +1086,10 @@ fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
{
let hint = size_hint::cautious(seq.size_hint());
if let Some(additional) = hint.checked_sub(self.0.len()) {
+ #[cfg(not(feature = "kernel_alloc"))]
self.0.reserve(additional);
+ #[cfg(feature = "kernel_alloc")]
+ self.0.try_reserve(additional).unwrap();
}
for i in 0..self.0.len() {
@@ -1092,7 +1104,10 @@ fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
}
while let Some(value) = try!(seq.next_element()) {
+ #[cfg(not(feature = "kernel_alloc"))]
self.0.push(value);
+ #[cfg(feature = "kernel_alloc")]
+ self.0.try_push(value).unwrap();
}
Ok(())
diff --git a/rust/serde/lib.rs b/rust/serde/lib.rs
index c3694fef3321..bfc845e98075 100644
--- a/rust/serde/lib.rs
+++ b/rust/serde/lib.rs
@@ -156,7 +156,7 @@
////////////////////////////////////////////////////////////////////////////////
-#[cfg(feature = "alloc")]
+#[cfg(any(feature = "alloc", feature = "kernel_alloc"))]
extern crate alloc;
/// A facade around all the types we need from the `std`, `core`, and `alloc`
diff --git a/rust/serde/private/size_hint.rs b/rust/serde/private/size_hint.rs
index f050fca59deb..ac3c9394e7d3 100644
--- a/rust/serde/private/size_hint.rs
+++ b/rust/serde/private/size_hint.rs
@@ -9,7 +9,7 @@ pub fn from_bounds<I>(iter: &I) -> Option<usize>
helper(iter.size_hint())
}
-#[cfg(any(feature = "std", feature = "alloc"))]
+#[cfg(any(feature = "std", feature = "alloc", feature = "kernel_alloc"))]
#[inline]
pub fn cautious(hint: Option<usize>) -> usize {
cmp::min(hint.unwrap_or(0), 4096)
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 45/80] rust: file: Replace UnsafeCell with Opaque for File
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (38 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 44/80] rust: serde: add support for deserializing Vec with kernel_alloc feature Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 46/80] rust: kernel: implement fmt::Debug for CString Ariel Miculas
` (37 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/kernel/file.rs | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs
index c090a19da9b2..086c62000533 100644
--- a/rust/kernel/file.rs
+++ b/rust/kernel/file.rs
@@ -17,10 +17,11 @@
types::ARef,
types::AlwaysRefCounted,
types::ForeignOwnable,
+ types::Opaque,
user_ptr::{UserSlicePtr, UserSlicePtrReader, UserSlicePtrWriter},
};
use core::convert::{TryFrom, TryInto};
-use core::{cell::UnsafeCell, marker, mem, ptr};
+use core::{marker, mem, ptr};
use macros::vtable;
/// Flags associated with a [`File`].
@@ -113,7 +114,7 @@ pub mod flags {
/// Instances of this type are always ref-counted, that is, a call to `get_file` ensures that the
/// allocation remains valid at least until the matching call to `fput`.
#[repr(transparent)]
-pub struct File(pub(crate) UnsafeCell<bindings::file>);
+pub struct File(pub(crate) Opaque<bindings::file>);
// TODO: Accessing fields of `struct file` through the pointer is UB because other threads may be
// writing to them. However, this is how the C code currently operates: naked reads and writes to
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 46/80] rust: kernel: implement fmt::Debug for CString
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (39 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 45/80] rust: file: Replace UnsafeCell with Opaque for File Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 47/80] samples: puzzlefs: rename RustFs to PuzzleFs Ariel Miculas
` (36 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/kernel/str.rs | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index c9dd3bf59e34..bd535522323a 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -236,6 +236,12 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
}
}
+impl fmt::Debug for CString {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(&**self, f)
+ }
+}
+
impl fmt::Debug for CStr {
/// Formats printable ASCII characters with a double quote on either end, escaping the rest.
///
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 47/80] samples: puzzlefs: rename RustFs to PuzzleFs
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (40 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 46/80] rust: kernel: implement fmt::Debug for CString Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 48/80] samples: puzzlefs: add basic deserializing support for the puzzlefs metadata Ariel Miculas
` (35 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
samples/rust/puzzlefs.rs | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
index 51aa41533a35..e7ce5078bc99 100644
--- a/samples/rust/puzzlefs.rs
+++ b/samples/rust/puzzlefs.rs
@@ -7,16 +7,16 @@
use kernel::{c_str, file, fs, io_buffer::IoBufferWriter};
module_fs! {
- type: RustFs,
+ type: PuzzleFs,
name: "puzzlefs",
author: "Ariel Miculas",
license: "GPL",
}
-struct RustFs;
+struct PuzzleFs;
#[vtable]
-impl fs::Context<Self> for RustFs {
+impl fs::Context<Self> for PuzzleFs {
type Data = ();
kernel::define_fs_params! {(),
@@ -39,7 +39,7 @@ fn try_new() -> Result {
}
}
-impl fs::Type for RustFs {
+impl fs::Type for PuzzleFs {
type Context = Self;
type INodeData = &'static [u8];
const SUPER_TYPE: fs::Super = fs::Super::Independent;
@@ -97,7 +97,7 @@ fn read(
offset: u64,
) -> Result<usize> {
file::read_from_slice(
- file.inode::<RustFs>().ok_or(EINVAL)?.fs_data(),
+ file.inode::<PuzzleFs>().ok_or(EINVAL)?.fs_data(),
writer,
offset,
)
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 48/80] samples: puzzlefs: add basic deserializing support for the puzzlefs metadata
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (41 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 47/80] samples: puzzlefs: rename RustFs to PuzzleFs Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 49/80] rust: file: present the filesystem context to the open function Ariel Miculas
` (34 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
samples/rust/puzzle.rs | 2 +
samples/rust/puzzle/error.rs | 32 +++
samples/rust/puzzle/types.rs | 304 ++++++++++++++++++++++
samples/rust/puzzle/types/cbor_helpers.rs | 9 +
samples/rust/puzzlefs.rs | 2 +
5 files changed, 349 insertions(+)
create mode 100644 samples/rust/puzzle.rs
create mode 100644 samples/rust/puzzle/error.rs
create mode 100644 samples/rust/puzzle/types.rs
create mode 100644 samples/rust/puzzle/types/cbor_helpers.rs
diff --git a/samples/rust/puzzle.rs b/samples/rust/puzzle.rs
new file mode 100644
index 000000000000..4d558561974d
--- /dev/null
+++ b/samples/rust/puzzle.rs
@@ -0,0 +1,2 @@
+pub(crate) mod error;
+mod types;
diff --git a/samples/rust/puzzle/error.rs b/samples/rust/puzzle/error.rs
new file mode 100644
index 000000000000..3427c5d2f7e3
--- /dev/null
+++ b/samples/rust/puzzle/error.rs
@@ -0,0 +1,32 @@
+use core::fmt::{self, Display};
+
+// TODO use String in error types (when it's available from the kernel)
+
+pub(crate) enum WireFormatError {
+ LocalRefError,
+ SeekOtherError,
+ ValueMissing,
+ CBORError(serde_cbor::Error),
+}
+
+impl Display for WireFormatError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ WireFormatError::LocalRefError => f.write_str("cannot turn local ref into a digest"),
+ WireFormatError::SeekOtherError => f.write_str("cannot seek to other blob"),
+ WireFormatError::ValueMissing => f.write_str("no value present"),
+ WireFormatError::CBORError(_) => f.write_str("CBOR error"),
+ }
+ }
+}
+
+pub(crate) type Result<T> = kernel::error::Result<T, WireFormatError>;
+
+// TODO figure out how to use thiserror
+#[allow(unused_qualifications)]
+impl core::convert::From<serde_cbor::Error> for WireFormatError {
+ #[allow(deprecated)]
+ fn from(source: serde_cbor::Error) -> Self {
+ WireFormatError::CBORError(source)
+ }
+}
diff --git a/samples/rust/puzzle/types.rs b/samples/rust/puzzle/types.rs
new file mode 100644
index 000000000000..207e5c4f86fa
--- /dev/null
+++ b/samples/rust/puzzle/types.rs
@@ -0,0 +1,304 @@
+use crate::puzzle::error::WireFormatError;
+use alloc::boxed::Box;
+use alloc::vec::Vec;
+use core::mem::size_of;
+use serde::de::Error as SerdeError;
+use serde::de::Visitor;
+use serde::{Deserialize, Deserializer};
+use serde_derive::Deserialize;
+mod cbor_helpers;
+use crate::puzzle::error::Result;
+pub(crate) use cbor_helpers::cbor_size_of_list_header;
+
+#[derive(Deserialize, Debug)]
+pub(crate) struct InodeAdditional {
+ #[allow(dead_code)]
+ pub(crate) xattrs: Vec<Xattr>,
+ #[allow(dead_code)]
+ pub(crate) symlink_target: Option<Vec<u8>>,
+}
+
+#[derive(Deserialize, Debug)]
+pub(crate) struct Xattr {
+ #[allow(dead_code)]
+ pub(crate) key: Vec<u8>,
+ #[allow(dead_code)]
+ pub(crate) val: Vec<u8>,
+}
+
+pub(crate) struct MetadataBlob {
+ mmapped_region: Box<[u8]>,
+ inode_count: usize,
+}
+
+fn read_one_from_slice<'a, T: Deserialize<'a>>(bytes: &'a [u8]) -> Result<T> {
+ // serde complains when we leave extra bytes on the wire, which we often want to do. as a
+ // hack, we create a streaming deserializer for the type we're about to read, and then only
+ // read one value.
+ let mut iter = serde_cbor::Deserializer::from_slice(bytes).into_iter::<T>();
+ let v = iter.next().transpose()?;
+ v.ok_or(WireFormatError::ValueMissing)
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) enum BlobRefKind {
+ Local,
+ Other { digest: [u8; 32] },
+}
+
+const BLOB_REF_SIZE: usize = 1 /* mode */ + 32 /* digest */ + 8 /* offset */;
+
+// TODO: should this be an ociv1 digest and include size and media type?
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) struct BlobRef {
+ pub(crate) offset: u64,
+ pub(crate) kind: BlobRefKind,
+ pub(crate) compressed: bool,
+}
+
+const COMPRESSED_BIT: u8 = 1 << 7;
+
+impl BlobRef {
+ fn fixed_length_deserialize<E: SerdeError>(
+ state: &[u8; BLOB_REF_SIZE],
+ ) -> kernel::error::Result<BlobRef, E> {
+ let offset = u64::from_le_bytes(state[0..8].try_into().unwrap());
+
+ let compressed = (state[8] & COMPRESSED_BIT) != 0;
+ let kind = match state[8] & !COMPRESSED_BIT {
+ 0 => BlobRefKind::Local,
+ 1 => BlobRefKind::Other {
+ digest: state[9..41].try_into().unwrap(),
+ },
+ _ => {
+ return Err(SerdeError::custom(format_args!(
+ "bad blob ref kind {}",
+ state[0]
+ )))
+ }
+ };
+
+ Ok(BlobRef {
+ offset,
+ kind,
+ compressed,
+ })
+ }
+}
+
+impl<'de> Deserialize<'de> for BlobRef {
+ fn deserialize<D>(deserializer: D) -> kernel::error::Result<BlobRef, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ struct BlobRefVisitor;
+
+ impl<'de> Visitor<'de> for BlobRefVisitor {
+ type Value = BlobRef;
+
+ fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
+ formatter.write_fmt(format_args!("expected {BLOB_REF_SIZE} bytes for BlobRef"))
+ }
+
+ fn visit_bytes<E>(self, v: &[u8]) -> kernel::error::Result<BlobRef, E>
+ where
+ E: SerdeError,
+ {
+ let state: [u8; BLOB_REF_SIZE] = v
+ .try_into()
+ .map_err(|_| SerdeError::invalid_length(v.len(), &self))?;
+ BlobRef::fixed_length_deserialize(&state)
+ }
+ }
+
+ deserializer.deserialize_bytes(BlobRefVisitor)
+ }
+}
+
+impl MetadataBlob {
+ pub(crate) fn seek_ref(&mut self, r: &BlobRef) -> Result<u64> {
+ match r.kind {
+ BlobRefKind::Other { .. } => Err(WireFormatError::SeekOtherError),
+ BlobRefKind::Local => Ok(r.offset),
+ }
+ }
+
+ pub(crate) fn read_file_chunks(&mut self, offset: u64) -> Result<Vec<FileChunk>> {
+ read_one_from_slice::<FileChunkList>(&self.mmapped_region[offset as usize..])
+ .map(|cl| cl.chunks)
+ }
+
+ pub(crate) fn read_dir_list(&mut self, offset: u64) -> Result<DirList> {
+ read_one_from_slice(&self.mmapped_region[offset as usize..])
+ }
+
+ pub(crate) fn read_inode_additional(&mut self, r: &BlobRef) -> Result<InodeAdditional> {
+ let offset = self.seek_ref(r)? as usize;
+ read_one_from_slice(&self.mmapped_region[offset..])
+ }
+
+ pub(crate) fn find_inode(&mut self, ino: Ino) -> Result<Option<Inode>> {
+ let mut left = 0;
+ let mut right = self.inode_count;
+
+ while left <= right {
+ let mid = left + (right - left) / 2;
+ let mid_offset = cbor_size_of_list_header(self.inode_count) + mid * INODE_WIRE_SIZE;
+ let i = read_one_from_slice::<Inode>(
+ &self.mmapped_region[mid_offset..mid_offset + INODE_WIRE_SIZE],
+ )?;
+ if i.ino == ino {
+ return Ok(Some(i));
+ }
+
+ if i.ino < ino {
+ left = mid + 1;
+ } else {
+ // don't underflow...
+ if mid == 0 {
+ break;
+ }
+ right = mid - 1;
+ };
+ }
+
+ Ok(None)
+ }
+}
+
+#[derive(Deserialize, Debug)]
+pub(crate) struct DirEnt {
+ pub(crate) ino: Ino,
+ pub(crate) name: Vec<u8>,
+}
+
+#[derive(Deserialize, Debug)]
+pub(crate) struct DirList {
+ // TODO: flags instead?
+ #[allow(dead_code)]
+ pub(crate) look_below: bool,
+ pub(crate) entries: Vec<DirEnt>,
+}
+
+#[derive(Deserialize, Debug)]
+pub(crate) struct FileChunkList {
+ pub(crate) chunks: Vec<FileChunk>,
+}
+
+#[derive(Deserialize, Debug)]
+pub(crate) struct FileChunk {
+ pub(crate) blob: BlobRef,
+ pub(crate) len: u64,
+}
+
+const INODE_MODE_SIZE: usize = 1 /* mode */ + size_of::<u64>() * 2 /* major/minor/offset */;
+
+// InodeMode needs to have custom serialization because inodes must be a fixed size.
+#[derive(Debug, PartialEq, Eq)]
+pub(crate) enum InodeMode {
+ Unknown,
+ Fifo,
+ Chr { major: u64, minor: u64 },
+ Dir { offset: u64 },
+ Blk { major: u64, minor: u64 },
+ Reg { offset: u64 },
+ Lnk,
+ Sock,
+ Wht,
+}
+
+pub(crate) type Ino = u64;
+
+const INODE_SIZE: usize = size_of::<Ino>() + INODE_MODE_SIZE + 2 * size_of::<u32>() /* uid and gid */
++ size_of::<u16>() /* permissions */ + 1 /* Option<BlobRef> */ + BLOB_REF_SIZE;
+
+pub(crate) const INODE_WIRE_SIZE: usize = cbor_size_of_list_header(INODE_SIZE) + INODE_SIZE;
+
+#[derive(Debug, PartialEq, Eq)]
+pub(crate) struct Inode {
+ pub(crate) ino: Ino,
+ pub(crate) mode: InodeMode,
+ pub(crate) uid: u32,
+ pub(crate) gid: u32,
+ pub(crate) permissions: u16,
+ pub(crate) additional: Option<BlobRef>,
+}
+
+impl<'de> Deserialize<'de> for Inode {
+ fn deserialize<D>(deserializer: D) -> kernel::error::Result<Inode, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ struct InodeVisitor;
+
+ impl<'de> Visitor<'de> for InodeVisitor {
+ type Value = Inode;
+
+ fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
+ formatter.write_fmt(format_args!("expected {INODE_MODE_SIZE} bytes for Inode"))
+ }
+
+ fn visit_bytes<E>(self, v: &[u8]) -> kernel::error::Result<Inode, E>
+ where
+ E: SerdeError,
+ {
+ let state: [u8; INODE_SIZE] = v
+ .try_into()
+ .map_err(|_| SerdeError::invalid_length(v.len(), &self))?;
+
+ let mode = match state[8] {
+ 0 => InodeMode::Unknown,
+ 1 => InodeMode::Fifo,
+ 2 => {
+ let major = u64::from_le_bytes(state[9..17].try_into().unwrap());
+ let minor = u64::from_le_bytes(state[17..25].try_into().unwrap());
+ InodeMode::Chr { major, minor }
+ }
+ 4 => {
+ let offset = u64::from_le_bytes(state[9..17].try_into().unwrap());
+ InodeMode::Dir { offset }
+ }
+ 6 => {
+ let major = u64::from_le_bytes(state[9..17].try_into().unwrap());
+ let minor = u64::from_le_bytes(state[17..25].try_into().unwrap());
+ InodeMode::Blk { major, minor }
+ }
+ 8 => {
+ let offset = u64::from_le_bytes(state[9..17].try_into().unwrap());
+ InodeMode::Reg { offset }
+ }
+ 10 => InodeMode::Lnk,
+ 12 => InodeMode::Sock,
+ 14 => InodeMode::Wht,
+ _ => {
+ return Err(SerdeError::custom(format_args!(
+ "bad inode mode value {}",
+ state[8]
+ )))
+ }
+ };
+
+ let additional = if state[35] > 0 {
+ Some(BlobRef::fixed_length_deserialize(
+ state[36..36 + BLOB_REF_SIZE].try_into().unwrap(),
+ )?)
+ } else {
+ None
+ };
+
+ Ok(Inode {
+ // ugh there must be a nicer way to do this with arrays, which we already have
+ // from above...
+ ino: u64::from_le_bytes(state[0..8].try_into().unwrap()),
+ mode,
+ uid: u32::from_le_bytes(state[25..29].try_into().unwrap()),
+ gid: u32::from_le_bytes(state[29..33].try_into().unwrap()),
+ permissions: u16::from_le_bytes(state[33..35].try_into().unwrap()),
+ additional,
+ })
+ }
+ }
+
+ deserializer.deserialize_bytes(InodeVisitor)
+ }
+}
diff --git a/samples/rust/puzzle/types/cbor_helpers.rs b/samples/rust/puzzle/types/cbor_helpers.rs
new file mode 100644
index 000000000000..ae2aa4609428
--- /dev/null
+++ b/samples/rust/puzzle/types/cbor_helpers.rs
@@ -0,0 +1,9 @@
+pub(crate) const fn cbor_size_of_list_header(size: usize) -> usize {
+ match size {
+ 0..=23 => 1,
+ 24..=255 => 2,
+ 256..=65535 => 3,
+ 65536..=4294967295 => 4,
+ _ => 8,
+ }
+}
diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
index e7ce5078bc99..b149af4e66ce 100644
--- a/samples/rust/puzzlefs.rs
+++ b/samples/rust/puzzlefs.rs
@@ -6,6 +6,8 @@
use kernel::prelude::*;
use kernel::{c_str, file, fs, io_buffer::IoBufferWriter};
+mod puzzle;
+
module_fs! {
type: PuzzleFs,
name: "puzzlefs",
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 49/80] rust: file: present the filesystem context to the open function
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (42 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 48/80] samples: puzzlefs: add basic deserializing support for the puzzlefs metadata Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 50/80] rust: kernel: add an abstraction over vfsmount to allow cloning a new private mount Ariel Miculas
` (33 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
This is not a good implementation because it does not ensure that the
type Operations::FSData in file.rs is the same as Type::Data in fs.rs.
This means that we could store a Box in the field s_fs_info and retrieve
an Arc from there, which is obviously wrong and it leads to UB.
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/kernel/file.rs | 18 ++++++++++++++++--
samples/rust/puzzlefs.rs | 21 ++++++++++++++++++---
samples/rust/rust_fs.rs | 2 +-
3 files changed, 35 insertions(+), 6 deletions(-)
diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs
index 086c62000533..0062d8b17990 100644
--- a/rust/kernel/file.rs
+++ b/rust/kernel/file.rs
@@ -330,9 +330,14 @@ impl<A: OpenAdapter<T::OpenData>, T: Operations> OperationsVtable<A, T> {
// `fileref` never outlives this function, so it is guaranteed to be
// valid.
let fileref = unsafe { File::from_ptr(file) };
+
+ // SAFETY: into_foreign was called in fs::NewSuperBlock<..., NeedsInit>::init and
+ // it is valid until from_foreign will be called in fs::Tables::free_callback
+ let fs_info = unsafe { T::FSData::borrow((*(*inode).i_sb).s_fs_info) };
+
// SAFETY: `arg` was previously returned by `A::convert` and must
// be a valid non-null pointer.
- let ptr = T::open(unsafe { &*arg }, fileref)?.into_foreign();
+ let ptr = T::open(fs_info, unsafe { &*arg }, fileref)?.into_foreign();
// SAFETY: The C contract guarantees that `private_data` is available
// for implementers of the file operations (no other C code accesses
// it), so we know that there are no concurrent threads/CPUs accessing
@@ -793,10 +798,19 @@ pub trait Operations {
/// The type of the context data passed to [`Operations::open`].
type OpenData: Sync = ();
+ /// Data associated with each file system instance.
+ // SAFETY: this is not safe because we don't enforce the same type as fs::Type::Data, so it's
+ // possible to store a Box in s_fs_info and retrieve an Arc when open is called, leading to UB
+ type FSData: ForeignOwnable + Send + Sync = ();
+
/// Creates a new instance of this file.
///
/// Corresponds to the `open` function pointer in `struct file_operations`.
- fn open(context: &Self::OpenData, file: &File) -> Result<Self::Data>;
+ fn open(
+ fs_info: <Self::FSData as ForeignOwnable>::Borrowed<'_>,
+ context: &Self::OpenData,
+ file: &File,
+ ) -> Result<Self::Data>;
/// Cleans up after the last reference to the file goes away.
///
diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
index b149af4e66ce..e454bce7dbc6 100644
--- a/samples/rust/puzzlefs.rs
+++ b/samples/rust/puzzlefs.rs
@@ -4,7 +4,7 @@
use kernel::module_fs;
use kernel::prelude::*;
-use kernel::{c_str, file, fs, io_buffer::IoBufferWriter};
+use kernel::{c_str, file, fs, io_buffer::IoBufferWriter, fmt, str::CString};
mod puzzle;
@@ -17,6 +17,11 @@
struct PuzzleFs;
+#[derive(Debug)]
+struct PuzzlefsInfo {
+ base_path: CString,
+}
+
#[vtable]
impl fs::Context<Self> for PuzzleFs {
type Data = ();
@@ -44,14 +49,17 @@ fn try_new() -> Result {
impl fs::Type for PuzzleFs {
type Context = Self;
type INodeData = &'static [u8];
+ type Data = Box<PuzzlefsInfo>;
const SUPER_TYPE: fs::Super = fs::Super::Independent;
const NAME: &'static CStr = c_str!("puzzlefs");
const FLAGS: i32 = fs::flags::USERNS_MOUNT;
const DCACHE_BASED: bool = true;
fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBlock<Self>> {
+ let base_path = CString::try_from_fmt(fmt!("hello world"))?;
+ pr_info!("base_path {:?}\n", base_path);
let sb = sb.init(
- (),
+ Box::try_new(PuzzlefsInfo { base_path })?,
&fs::SuperParams {
magic: 0x72757374,
..fs::SuperParams::DEFAULT
@@ -87,8 +95,15 @@ fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBl
#[vtable]
impl file::Operations for FsFile {
type OpenData = &'static [u8];
+ type FSData = Box<PuzzlefsInfo>;
+
+ fn open(
+ fs_info: &PuzzlefsInfo,
+ _context: &Self::OpenData,
+ _file: &file::File,
+ ) -> Result<Self::Data> {
+ pr_info!("got {:?}\n", fs_info);
- fn open(_context: &Self::OpenData, _file: &file::File) -> Result<Self::Data> {
Ok(())
}
diff --git a/samples/rust/rust_fs.rs b/samples/rust/rust_fs.rs
index 7527681ee024..36bca7da179f 100644
--- a/samples/rust/rust_fs.rs
+++ b/samples/rust/rust_fs.rs
@@ -86,7 +86,7 @@ fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBl
impl file::Operations for FsFile {
type OpenData = &'static [u8];
- fn open(_context: &Self::OpenData, _file: &file::File) -> Result<Self::Data> {
+ fn open(_fs_info: (), _context: &Self::OpenData, _file: &file::File) -> Result<Self::Data> {
Ok(())
}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 50/80] rust: kernel: add an abstraction over vfsmount to allow cloning a new private mount
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (43 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 49/80] rust: file: present the filesystem context to the open function Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 51/80] rust: file: add from_path, from_path_in_root_mnt and read_with_offset methods to File Ariel Miculas
` (32 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/bindings/bindings_helper.h | 1 +
rust/kernel/lib.rs | 1 +
rust/kernel/mount.rs | 66 +++++++++++++++++++++++++++++++++
3 files changed, 68 insertions(+)
create mode 100644 rust/kernel/mount.rs
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 6182174663ba..3ea8e3f610ee 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -17,6 +17,7 @@
#include <linux/uio.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
+#include <linux/namei.h>
/* `bindgen` gets confused at certain things. */
const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index a94fb784d576..5ad583ce6fb4 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -46,6 +46,7 @@
pub mod ioctl;
pub mod iov_iter;
pub mod mm;
+pub mod mount;
pub mod pages;
pub mod prelude;
pub mod print;
diff --git a/rust/kernel/mount.rs b/rust/kernel/mount.rs
new file mode 100644
index 000000000000..c8c517eb996a
--- /dev/null
+++ b/rust/kernel/mount.rs
@@ -0,0 +1,66 @@
+//! Mount interface
+//!
+//! C headers: [`include/linux/mount.h`](../../../../include/linux/mount.h)
+
+use kernel::bindings;
+use kernel::error::from_err_ptr;
+use kernel::pr_err;
+use kernel::prelude::*;
+use kernel::str::CStr;
+use kernel::types::Opaque;
+
+/// Wraps the kernel's `struct path`.
+#[repr(transparent)]
+pub struct Path(pub(crate) Opaque<bindings::path>);
+
+/// Wraps the kernel's `struct vfsmount`.
+#[repr(transparent)]
+#[derive(Debug)]
+pub struct Vfsmount {
+ vfsmount: *mut bindings::vfsmount,
+}
+
+// SAFETY: No one besides us has the raw pointer, so we can safely transfer Vfsmount to another thread
+unsafe impl Send for Vfsmount {}
+// SAFETY: It's OK to access `Vfsmount` through references from other threads because we're not
+// accessing any properties from the underlying raw pointer
+unsafe impl Sync for Vfsmount {}
+
+impl Vfsmount {
+ /// Create a new private mount clone based on a path name
+ pub fn new_private_mount(path_name: &CStr) -> Result<Self> {
+ let path: Path = Path(Opaque::uninit());
+ let err = unsafe {
+ bindings::kern_path(
+ path_name.as_ptr() as *const i8,
+ bindings::LOOKUP_FOLLOW | bindings::LOOKUP_DIRECTORY,
+ path.0.get(),
+ )
+ };
+ if err != 0 {
+ pr_err!("failed to resolve '{}': {}\n", path_name, err);
+ return Err(EINVAL);
+ }
+
+ let vfsmount = unsafe {
+ let mnt = from_err_ptr(bindings::clone_private_mount(path.0.get()))?;
+ // Don't inherit atime flags
+ (*mnt).mnt_flags &=
+ !(bindings::MNT_NOATIME | bindings::MNT_NODIRATIME | bindings::MNT_RELATIME) as i32;
+ mnt
+ };
+ Ok(Self { vfsmount })
+ }
+
+ /// Returns a raw pointer to vfsmount
+ pub fn get(&self) -> *mut bindings::vfsmount {
+ self.vfsmount
+ }
+}
+
+impl Drop for Vfsmount {
+ fn drop(&mut self) {
+ // SAFETY new_private_mount makes sure to return a valid pointer
+ unsafe { bindings::kern_unmount(self.vfsmount) };
+ }
+}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 51/80] rust: file: add from_path, from_path_in_root_mnt and read_with_offset methods to File
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (44 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 50/80] rust: kernel: add an abstraction over vfsmount to allow cloning a new private mount Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 52/80] samples: puzzlefs: pass the Vfsmount structure from open to read and return the contents of the data file inside /home/puzzlefs_oci Ariel Miculas
` (31 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/kernel/error.rs | 4 +--
rust/kernel/file.rs | 74 +++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 74 insertions(+), 4 deletions(-)
diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index 231f7dfd8005..6fff9a4f0672 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -234,9 +234,7 @@ pub fn to_result(err: core::ffi::c_int) -> Result {
/// }
/// }
/// ```
-// TODO: Remove `dead_code` marker once an in-kernel client is available.
-#[allow(dead_code)]
-pub(crate) fn from_err_ptr<T>(ptr: *mut T) -> Result<*mut T> {
+pub fn from_err_ptr<T>(ptr: *mut T) -> Result<*mut T> {
// CAST: Casting a pointer to `*const core::ffi::c_void` is always valid.
let const_ptr: *const core::ffi::c_void = ptr.cast();
// SAFETY: The FFI function does not deref the pointer.
diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs
index 0062d8b17990..a4926b316573 100644
--- a/rust/kernel/file.rs
+++ b/rust/kernel/file.rs
@@ -8,11 +8,13 @@
use crate::{
bindings,
cred::Credential,
- error::{code::*, from_kernel_result, Error, Result},
+ error::{code::*, from_err_ptr, from_kernel_result, Error, Result},
fs,
io_buffer::{IoBufferReader, IoBufferWriter},
iov_iter::IovIter,
mm,
+ mount::Vfsmount,
+ str::CStr,
sync::CondVar,
types::ARef,
types::AlwaysRefCounted,
@@ -131,6 +133,76 @@ pub fn from_fd(fd: u32) -> Result<ARef<Self>> {
Ok(unsafe { ARef::from_raw(ptr.cast()) })
}
+ /// Constructs a new [`struct file`] wrapper from a path.
+ pub fn from_path(filename: &CStr, flags: i32, mode: u16) -> Result<ARef<Self>> {
+ let file_ptr = unsafe {
+ from_err_ptr(bindings::filp_open(
+ filename.as_ptr() as *const i8,
+ flags,
+ mode,
+ ))?
+ };
+ let file_ptr = ptr::NonNull::new(file_ptr).ok_or(ENOENT)?;
+
+ // SAFETY: `filp_open` initializes the refcount with 1
+ Ok(unsafe { ARef::from_raw(file_ptr.cast()) })
+ }
+
+ /// Constructs a new [`struct file`] wrapper from a path and a vfsmount.
+ pub fn from_path_in_root_mnt(
+ mount: &Vfsmount,
+ filename: &CStr,
+ flags: i32,
+ mode: u16,
+ ) -> Result<ARef<Self>> {
+ let file_ptr = unsafe {
+ let mnt = mount.get();
+ let raw_path = bindings::path {
+ mnt,
+ dentry: (*mnt).mnt_root,
+ };
+ from_err_ptr(bindings::file_open_root(
+ &raw_path,
+ filename.as_ptr() as *const i8,
+ flags,
+ mode,
+ ))?
+ };
+ let file_ptr = ptr::NonNull::new(file_ptr).ok_or(ENOENT)?;
+
+ // SAFETY: `file_open_root` increments the refcount before returning. (TODO does it?)
+ Ok(unsafe { ARef::from_raw(file_ptr.cast()) })
+ }
+
+ /// Read from the file into the specified buffer
+ pub fn read_with_offset(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
+ Ok(unsafe {
+ // kernel_read_file expects a pointer to a "void *" buffer
+ let mut ptr_to_buf = buf.as_mut_ptr() as *mut core::ffi::c_void;
+ // Unless we give a non-null pointer to the file size:
+ // 1. we cannot give a non-zero value for the offset
+ // 2. we cannot have offset 0 and buffer_size > file_size
+ let mut file_size = 0;
+
+ // SAFETY: all the pointers to kernel_read_file are valid
+ let result = bindings::kernel_read_file(
+ self.0.get(),
+ offset.try_into()?,
+ &mut ptr_to_buf,
+ buf.len(),
+ &mut file_size,
+ bindings::kernel_read_file_id_READING_UNKNOWN,
+ );
+
+ // kernel_read_file returns the number of bytes read on success or negative on error.
+ if result < 0 {
+ return Err(Error::from_errno(result.try_into()?));
+ }
+
+ result.try_into()?
+ })
+ }
+
/// Creates a reference to a [`File`] from a valid pointer.
///
/// # Safety
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 52/80] samples: puzzlefs: pass the Vfsmount structure from open to read and return the contents of the data file inside /home/puzzlefs_oci
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (45 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 51/80] rust: file: add from_path, from_path_in_root_mnt and read_with_offset methods to File Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 53/80] rust: file: move from_path, from_path_in_root_mnt and read_with_offset methods to a RegularFile newtype Ariel Miculas
` (30 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
samples/rust/puzzlefs.rs | 44 +++++++++++++++++++++++++++++-----------
1 file changed, 32 insertions(+), 12 deletions(-)
diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
index e454bce7dbc6..db13a1d12466 100644
--- a/samples/rust/puzzlefs.rs
+++ b/samples/rust/puzzlefs.rs
@@ -3,8 +3,14 @@
//! Rust file system sample.
use kernel::module_fs;
+use kernel::mount::Vfsmount;
use kernel::prelude::*;
-use kernel::{c_str, file, fs, io_buffer::IoBufferWriter, fmt, str::CString};
+use kernel::{
+ c_str, file, fmt, fs,
+ io_buffer::IoBufferWriter,
+ str::CString,
+ sync::{Arc, ArcBorrow},
+};
mod puzzle;
@@ -20,6 +26,7 @@
#[derive(Debug)]
struct PuzzlefsInfo {
base_path: CString,
+ vfs_mount: Arc<Vfsmount>,
}
#[vtable]
@@ -58,8 +65,14 @@ impl fs::Type for PuzzleFs {
fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBlock<Self>> {
let base_path = CString::try_from_fmt(fmt!("hello world"))?;
pr_info!("base_path {:?}\n", base_path);
+ let vfs_mount = Vfsmount::new_private_mount(c_str!("/home/puzzlefs_oci"))?;
+ pr_info!("vfs_mount {:?}\n", vfs_mount);
+
let sb = sb.init(
- Box::try_new(PuzzlefsInfo { base_path })?,
+ Box::try_new(PuzzlefsInfo {
+ base_path,
+ vfs_mount: Arc::try_new(vfs_mount)?,
+ })?,
&fs::SuperParams {
magic: 0x72757374,
..fs::SuperParams::DEFAULT
@@ -94,29 +107,36 @@ fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBl
#[vtable]
impl file::Operations for FsFile {
+ // must be the same as INodeData
type OpenData = &'static [u8];
type FSData = Box<PuzzlefsInfo>;
+ // this is an Arc because Data must be ForeignOwnable and the only implementors of it are Box,
+ // Arc and (); we cannot pass a reference to read, so we share Vfsmount using and Arc
+ type Data = Arc<Vfsmount>;
fn open(
fs_info: &PuzzlefsInfo,
_context: &Self::OpenData,
_file: &file::File,
) -> Result<Self::Data> {
- pr_info!("got {:?}\n", fs_info);
-
- Ok(())
+ Ok(fs_info.vfs_mount.clone())
}
fn read(
- _data: (),
- file: &file::File,
+ data: ArcBorrow<'_, Vfsmount>,
+ _file: &file::File,
writer: &mut impl IoBufferWriter,
offset: u64,
) -> Result<usize> {
- file::read_from_slice(
- file.inode::<PuzzleFs>().ok_or(EINVAL)?.fs_data(),
- writer,
- offset,
- )
+ let mut buf = Vec::try_with_capacity(writer.len())?;
+ buf.try_resize(writer.len(), 0)?;
+ let file = file::File::from_path_in_root_mnt(
+ &data,
+ c_str!("data"),
+ file::flags::O_RDONLY.try_into().unwrap(),
+ 0,
+ )?;
+ let nr_bytes_read = file.read_with_offset(&mut buf[..], offset)?;
+ file::read_from_slice(&buf[..nr_bytes_read], writer, 0)
}
}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 53/80] rust: file: move from_path, from_path_in_root_mnt and read_with_offset methods to a RegularFile newtype
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (46 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 52/80] samples: puzzlefs: pass the Vfsmount structure from open to read and return the contents of the data file inside /home/puzzlefs_oci Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 54/80] rust: file: ensure RegularFile can only create regular files Ariel Miculas
` (29 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/kernel/file.rs | 144 ++++++++++++++++++++-------------------
samples/rust/puzzlefs.rs | 2 +-
2 files changed, 75 insertions(+), 71 deletions(-)
diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs
index a4926b316573..0d2771027150 100644
--- a/rust/kernel/file.rs
+++ b/rust/kernel/file.rs
@@ -133,76 +133,6 @@ pub fn from_fd(fd: u32) -> Result<ARef<Self>> {
Ok(unsafe { ARef::from_raw(ptr.cast()) })
}
- /// Constructs a new [`struct file`] wrapper from a path.
- pub fn from_path(filename: &CStr, flags: i32, mode: u16) -> Result<ARef<Self>> {
- let file_ptr = unsafe {
- from_err_ptr(bindings::filp_open(
- filename.as_ptr() as *const i8,
- flags,
- mode,
- ))?
- };
- let file_ptr = ptr::NonNull::new(file_ptr).ok_or(ENOENT)?;
-
- // SAFETY: `filp_open` initializes the refcount with 1
- Ok(unsafe { ARef::from_raw(file_ptr.cast()) })
- }
-
- /// Constructs a new [`struct file`] wrapper from a path and a vfsmount.
- pub fn from_path_in_root_mnt(
- mount: &Vfsmount,
- filename: &CStr,
- flags: i32,
- mode: u16,
- ) -> Result<ARef<Self>> {
- let file_ptr = unsafe {
- let mnt = mount.get();
- let raw_path = bindings::path {
- mnt,
- dentry: (*mnt).mnt_root,
- };
- from_err_ptr(bindings::file_open_root(
- &raw_path,
- filename.as_ptr() as *const i8,
- flags,
- mode,
- ))?
- };
- let file_ptr = ptr::NonNull::new(file_ptr).ok_or(ENOENT)?;
-
- // SAFETY: `file_open_root` increments the refcount before returning. (TODO does it?)
- Ok(unsafe { ARef::from_raw(file_ptr.cast()) })
- }
-
- /// Read from the file into the specified buffer
- pub fn read_with_offset(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
- Ok(unsafe {
- // kernel_read_file expects a pointer to a "void *" buffer
- let mut ptr_to_buf = buf.as_mut_ptr() as *mut core::ffi::c_void;
- // Unless we give a non-null pointer to the file size:
- // 1. we cannot give a non-zero value for the offset
- // 2. we cannot have offset 0 and buffer_size > file_size
- let mut file_size = 0;
-
- // SAFETY: all the pointers to kernel_read_file are valid
- let result = bindings::kernel_read_file(
- self.0.get(),
- offset.try_into()?,
- &mut ptr_to_buf,
- buf.len(),
- &mut file_size,
- bindings::kernel_read_file_id_READING_UNKNOWN,
- );
-
- // kernel_read_file returns the number of bytes read on success or negative on error.
- if result < 0 {
- return Err(Error::from_errno(result.try_into()?));
- }
-
- result.try_into()?
- })
- }
-
/// Creates a reference to a [`File`] from a valid pointer.
///
/// # Safety
@@ -273,6 +203,80 @@ unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
}
}
+/// A newtype over file, specific to regular files
+pub struct RegularFile(ARef<File>);
+impl RegularFile {
+ /// Constructs a new [`struct file`] wrapper from a path.
+ pub fn from_path(filename: &CStr, flags: i32, mode: u16) -> Result<Self> {
+ let file_ptr = unsafe {
+ from_err_ptr(bindings::filp_open(
+ filename.as_ptr() as *const i8,
+ flags,
+ mode,
+ ))?
+ };
+ let file_ptr = ptr::NonNull::new(file_ptr).ok_or(ENOENT)?;
+
+ // SAFETY: `filp_open` initializes the refcount with 1
+ Ok(RegularFile(unsafe { ARef::from_raw(file_ptr.cast()) }))
+ }
+
+ /// Constructs a new [`struct file`] wrapper from a path and a vfsmount.
+ pub fn from_path_in_root_mnt(
+ mount: &Vfsmount,
+ filename: &CStr,
+ flags: i32,
+ mode: u16,
+ ) -> Result<Self> {
+ let file_ptr = unsafe {
+ let mnt = mount.get();
+ let raw_path = bindings::path {
+ mnt,
+ dentry: (*mnt).mnt_root,
+ };
+ from_err_ptr(bindings::file_open_root(
+ &raw_path,
+ filename.as_ptr() as *const i8,
+ flags,
+ mode,
+ ))?
+ };
+ let file_ptr = ptr::NonNull::new(file_ptr).ok_or(ENOENT)?;
+
+ // SAFETY: `file_open_root` increments the refcount before returning. (TODO does it?)
+ Ok(RegularFile(unsafe { ARef::from_raw(file_ptr.cast()) }))
+ }
+
+ /// Read from the file into the specified buffer
+ pub fn read_with_offset(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
+ Ok(unsafe {
+ // kernel_read_file expects a pointer to a "void *" buffer
+ let mut ptr_to_buf = buf.as_mut_ptr() as *mut core::ffi::c_void;
+ // Unless we give a non-null pointer to the file size:
+ // 1. we cannot give a non-zero value for the offset
+ // 2. we cannot have offset 0 and buffer_size > file_size
+ let mut file_size = 0;
+
+ // SAFETY: all the pointers to kernel_read_file are valid
+ let result = bindings::kernel_read_file(
+ self.0 .0.get(),
+ offset.try_into()?,
+ &mut ptr_to_buf,
+ buf.len(),
+ &mut file_size,
+ bindings::kernel_read_file_id_READING_UNKNOWN,
+ );
+
+ // kernel_read_file returns the number of bytes read on success or negative on error.
+ if result < 0 {
+ return Err(Error::from_errno(result.try_into()?));
+ }
+
+ result.try_into()?
+ })
+ }
+}
+
/// A file descriptor reservation.
///
/// This allows the creation of a file descriptor in two steps: first, we reserve a slot for it,
diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
index db13a1d12466..50d62109a9c1 100644
--- a/samples/rust/puzzlefs.rs
+++ b/samples/rust/puzzlefs.rs
@@ -130,7 +130,7 @@ fn read(
) -> Result<usize> {
let mut buf = Vec::try_with_capacity(writer.len())?;
buf.try_resize(writer.len(), 0)?;
- let file = file::File::from_path_in_root_mnt(
+ let file = file::RegularFile::from_path_in_root_mnt(
&data,
c_str!("data"),
file::flags::O_RDONLY.try_into().unwrap(),
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 54/80] rust: file: ensure RegularFile can only create regular files
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (47 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 53/80] rust: file: move from_path, from_path_in_root_mnt and read_with_offset methods to a RegularFile newtype Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 55/80] rust: file: add get_pos method to RegularFile Ariel Miculas
` (28 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/kernel/file.rs | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs
index 0d2771027150..afb2084745de 100644
--- a/rust/kernel/file.rs
+++ b/rust/kernel/file.rs
@@ -206,8 +206,21 @@ unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
/// A newtype over file, specific to regular files
pub struct RegularFile(ARef<File>);
impl RegularFile {
+ fn create_if_regular(file_ptr: ptr::NonNull<bindings::file>) -> Result<RegularFile> {
+ // make sure the file is a regular file
+ // TODO: create a helper function
+ // SAFETY: we have a NonNull pointer
+ unsafe {
+ let inode = core::ptr::addr_of!((*file_ptr.as_ptr()).f_inode).read();
+ if bindings::S_IFMT & ((*inode).i_mode) as u32 != bindings::S_IFREG {
+ return Err(EINVAL);
+ }
+ }
+ Ok(RegularFile(unsafe { ARef::from_raw(file_ptr.cast()) }))
+ }
/// Constructs a new [`struct file`] wrapper from a path.
pub fn from_path(filename: &CStr, flags: i32, mode: u16) -> Result<Self> {
+ // SAFETY: `filp_open` initializes the refcount with 1
let file_ptr = unsafe {
from_err_ptr(bindings::filp_open(
filename.as_ptr() as *const i8,
@@ -217,8 +230,7 @@ pub fn from_path(filename: &CStr, flags: i32, mode: u16) -> Result<Self> {
};
let file_ptr = ptr::NonNull::new(file_ptr).ok_or(ENOENT)?;
- // SAFETY: `filp_open` initializes the refcount with 1
- Ok(RegularFile(unsafe { ARef::from_raw(file_ptr.cast()) }))
+ Self::create_if_regular(file_ptr)
}
/// Constructs a new [`struct file`] wrapper from a path and a vfsmount.
@@ -228,6 +240,7 @@ pub fn from_path_in_root_mnt(
flags: i32,
mode: u16,
) -> Result<Self> {
+ // SAFETY: `file_open_root` increments the refcount before returning. (TODO does it?)
let file_ptr = unsafe {
let mnt = mount.get();
let raw_path = bindings::path {
@@ -243,8 +256,7 @@ pub fn from_path_in_root_mnt(
};
let file_ptr = ptr::NonNull::new(file_ptr).ok_or(ENOENT)?;
- // SAFETY: `file_open_root` increments the refcount before returning. (TODO does it?)
- Ok(RegularFile(unsafe { ARef::from_raw(file_ptr.cast()) }))
+ Self::create_if_regular(file_ptr)
}
/// Read from the file into the specified buffer
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 55/80] rust: file: add get_pos method to RegularFile
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (48 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 54/80] rust: file: ensure RegularFile can only create regular files Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 56/80] rust: file: add methods read_to_end, get_file_size and update_pos " Ariel Miculas
` (27 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/kernel/file.rs | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs
index afb2084745de..5b119d6c4f72 100644
--- a/rust/kernel/file.rs
+++ b/rust/kernel/file.rs
@@ -287,6 +287,11 @@ pub fn read_with_offset(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
result.try_into()?
})
}
+
+ /// Get the current file position
+ pub fn get_pos(&self) -> u64 {
+ self.0.pos()
+ }
}
/// A file descriptor reservation.
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 56/80] rust: file: add methods read_to_end, get_file_size and update_pos to RegularFile
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (49 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 55/80] rust: file: add get_pos method to RegularFile Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 57/80] rust: file: define a minimal Read trait and implement it for RegularFile Ariel Miculas
` (26 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/kernel/file.rs | 52 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs
index 5b119d6c4f72..894ee573d147 100644
--- a/rust/kernel/file.rs
+++ b/rust/kernel/file.rs
@@ -22,6 +22,7 @@
types::Opaque,
user_ptr::{UserSlicePtr, UserSlicePtrReader, UserSlicePtrWriter},
};
+use alloc::vec::Vec;
use core::convert::{TryFrom, TryInto};
use core::{marker, mem, ptr};
use macros::vtable;
@@ -288,10 +289,61 @@ pub fn read_with_offset(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
})
}
+ /// Allocate and return a vector containing the contents of the entire file
+ pub fn read_to_end(&self) -> Result<Vec<u8>> {
+ let file_size = self.get_file_size()?;
+ let mut buffer = Vec::try_with_capacity(file_size)?;
+ buffer.try_resize(file_size, 0)?;
+ self.read_with_offset(&mut buffer, 0)?;
+ Ok(buffer)
+ }
+
/// Get the current file position
pub fn get_pos(&self) -> u64 {
self.0.pos()
}
+
+ fn get_file_size(&self) -> Result<usize> {
+ let mut buf = Vec::new();
+ unsafe {
+ // kernel_read_file expects a pointer to a "void *" buffer
+ let mut ptr_to_buf = buf.as_mut_ptr() as *mut core::ffi::c_void;
+ let mut file_size = 0;
+
+ // SAFETY: all the pointers to kernel_read_file are valid
+ let result = bindings::kernel_read_file(
+ self.0 .0.get(),
+ 0,
+ &mut ptr_to_buf,
+ buf.len(),
+ &mut file_size,
+ bindings::kernel_read_file_id_READING_UNKNOWN,
+ );
+
+ // kernel_read_file returns the number of bytes read on success or negative on error.
+ if result < 0 {
+ return Err(Error::from_errno(result.try_into()?));
+ }
+ Ok(file_size)
+ }
+ }
+
+ fn update_pos(&self, where_to: SeekFrom) -> Result<u64> {
+ let (offset, whence): (i64, _) = match where_to {
+ SeekFrom::Start(off) => (off.try_into()?, bindings::SEEK_SET),
+ SeekFrom::Current(off) => (off, bindings::SEEK_CUR),
+ SeekFrom::End(off) => (off, bindings::SEEK_END),
+ };
+
+ let offset =
+ unsafe { bindings::generic_file_llseek(self.0 .0.get(), offset, whence.try_into()?) };
+ if offset < 0 {
+ return Err(Error::from_errno(offset.try_into()?));
+ }
+
+ // we just checked that offset is non negative
+ Ok(offset as u64)
+ }
}
/// A file descriptor reservation.
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 57/80] rust: file: define a minimal Read trait and implement it for RegularFile
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (50 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 56/80] rust: file: add methods read_to_end, get_file_size and update_pos " Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 58/80] samples: puzzlefs: add cbor_get_array_size method Ariel Miculas
` (25 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/kernel/file.rs | 42 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs
index 894ee573d147..e51675823724 100644
--- a/rust/kernel/file.rs
+++ b/rust/kernel/file.rs
@@ -204,6 +204,40 @@ unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
}
}
+/// adapted from https://doc.rust-lang.org/std/io/trait.Read.html
+pub trait Read {
+ // Required method
+ /// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
+ /// See https://doc.rust-lang.org/std/io/trait.Read.html#tymethod.read for details
+ /// This is the equivalent of read from std::io::Read::read, but in kernel space instead of user space
+ /// The buffer is a kernel-allocated buffer
+ fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
+
+ // Provided methods
+
+ // Implementation adapted from https://github.com/rust-lang/rust/blob/80917360d350fb55aebf383e7ff99efea41f63fd/library/std/src/io/mod.rs#L465
+ /// Read the exact number of bytes required to fill buf.
+ /// See https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact for details
+ fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> {
+ while !buf.is_empty() {
+ match self.read(buf) {
+ Ok(0) => break,
+ Ok(n) => {
+ let tmp = buf;
+ buf = &mut tmp[n..];
+ }
+ Err(e) => return Err(e),
+ }
+ }
+
+ if !buf.is_empty() {
+ Err(EINVAL)
+ } else {
+ Ok(())
+ }
+ }
+}
+
/// A newtype over file, specific to regular files
pub struct RegularFile(ARef<File>);
impl RegularFile {
@@ -346,6 +380,14 @@ fn update_pos(&self, where_to: SeekFrom) -> Result<u64> {
}
}
+impl Read for &mut RegularFile {
+ fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
+ let nr_bytes_read = self.read_with_offset(buf, self.get_pos())?;
+ self.update_pos(SeekFrom::Current(nr_bytes_read.try_into()?))?;
+ Ok(nr_bytes_read)
+ }
+}
+
/// A file descriptor reservation.
///
/// This allows the creation of a file descriptor in two steps: first, we reserve a slot for it,
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 58/80] samples: puzzlefs: add cbor_get_array_size method
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (51 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 57/80] rust: file: define a minimal Read trait and implement it for RegularFile Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 59/80] samples: puzzlefs: add KernelError to WireFormatError and implement From conversion Ariel Miculas
` (24 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
samples/rust/puzzle/types/cbor_helpers.rs | 41 +++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/samples/rust/puzzle/types/cbor_helpers.rs b/samples/rust/puzzle/types/cbor_helpers.rs
index ae2aa4609428..b09c104a9286 100644
--- a/samples/rust/puzzle/types/cbor_helpers.rs
+++ b/samples/rust/puzzle/types/cbor_helpers.rs
@@ -1,3 +1,6 @@
+use crate::puzzle::types::{Result, WireFormatError};
+use kernel::file::Read;
+
pub(crate) const fn cbor_size_of_list_header(size: usize) -> usize {
match size {
0..=23 => 1,
@@ -7,3 +10,41 @@ pub(crate) const fn cbor_size_of_list_header(size: usize) -> usize {
_ => 8,
}
}
+
+fn parse_u8(mut reader: impl Read) -> Result<u8> {
+ let mut buf = [0; 1];
+ reader.read_exact(&mut buf)?;
+ Ok(u8::from_be_bytes(buf))
+}
+
+fn parse_u16(mut reader: impl Read) -> Result<u16> {
+ let mut buf = [0; 2];
+ reader.read_exact(&mut buf)?;
+ Ok(u16::from_be_bytes(buf))
+}
+
+fn parse_u32(mut reader: impl Read) -> Result<u32> {
+ let mut buf = [0; 4];
+ reader.read_exact(&mut buf)?;
+ Ok(u32::from_be_bytes(buf))
+}
+
+fn parse_u64(mut reader: impl Read) -> Result<u64> {
+ let mut buf = [0; 8];
+ reader.read_exact(&mut buf)?;
+ Ok(u64::from_be_bytes(buf))
+}
+
+pub(crate) fn cbor_get_array_size<R: Read>(mut reader: R) -> Result<u64> {
+ let mut buf = [0; 1];
+ reader.read_exact(&mut buf)?;
+
+ match buf[0] {
+ 0x80..=0x97 => Ok((buf[0] - 0x80) as u64),
+ 0x98 => parse_u8(reader).map(u64::from),
+ 0x99 => parse_u16(reader).map(u64::from),
+ 0x9a => parse_u32(reader).map(u64::from),
+ 0x9b => parse_u64(reader).map(u64::from),
+ _ => Err(WireFormatError::ValueMissing),
+ }
+}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 59/80] samples: puzzlefs: add KernelError to WireFormatError and implement From conversion
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (52 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 58/80] samples: puzzlefs: add cbor_get_array_size method Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 60/80] samples: puzzlefs: implement new for MetadataBlob Ariel Miculas
` (23 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
samples/rust/puzzle/error.rs | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/samples/rust/puzzle/error.rs b/samples/rust/puzzle/error.rs
index 3427c5d2f7e3..5fa9c1404df4 100644
--- a/samples/rust/puzzle/error.rs
+++ b/samples/rust/puzzle/error.rs
@@ -7,6 +7,7 @@ pub(crate) enum WireFormatError {
SeekOtherError,
ValueMissing,
CBORError(serde_cbor::Error),
+ KernelError(kernel::error::Error),
}
impl Display for WireFormatError {
@@ -16,6 +17,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
WireFormatError::SeekOtherError => f.write_str("cannot seek to other blob"),
WireFormatError::ValueMissing => f.write_str("no value present"),
WireFormatError::CBORError(_) => f.write_str("CBOR error"),
+ WireFormatError::KernelError(_) => f.write_str("Kernel error"),
}
}
}
@@ -30,3 +32,11 @@ fn from(source: serde_cbor::Error) -> Self {
WireFormatError::CBORError(source)
}
}
+
+#[allow(unused_qualifications)]
+impl core::convert::From<kernel::error::Error> for WireFormatError {
+ #[allow(deprecated)]
+ fn from(source: kernel::error::Error) -> Self {
+ WireFormatError::KernelError(source)
+ }
+}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 60/80] samples: puzzlefs: implement new for MetadataBlob
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (53 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 59/80] samples: puzzlefs: add KernelError to WireFormatError and implement From conversion Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:30 ` [PATCH 61/80] samples: puzzlefs: build puzzlefs into the kernel, thus avoiding the need to export rust symbols Ariel Miculas
` (22 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
samples/rust/puzzle/types.rs | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/samples/rust/puzzle/types.rs b/samples/rust/puzzle/types.rs
index 207e5c4f86fa..c6b69b94fa6d 100644
--- a/samples/rust/puzzle/types.rs
+++ b/samples/rust/puzzle/types.rs
@@ -1,5 +1,4 @@
use crate::puzzle::error::WireFormatError;
-use alloc::boxed::Box;
use alloc::vec::Vec;
use core::mem::size_of;
use serde::de::Error as SerdeError;
@@ -8,7 +7,8 @@
use serde_derive::Deserialize;
mod cbor_helpers;
use crate::puzzle::error::Result;
-pub(crate) use cbor_helpers::cbor_size_of_list_header;
+pub(crate) use cbor_helpers::{cbor_get_array_size, cbor_size_of_list_header};
+use kernel::file;
#[derive(Deserialize, Debug)]
pub(crate) struct InodeAdditional {
@@ -26,9 +26,10 @@ pub(crate) struct Xattr {
pub(crate) val: Vec<u8>,
}
+#[derive(Debug)]
pub(crate) struct MetadataBlob {
- mmapped_region: Box<[u8]>,
- inode_count: usize,
+ pub(crate) mmapped_region: Vec<u8>,
+ pub(crate) inode_count: usize,
}
fn read_one_from_slice<'a, T: Deserialize<'a>>(bytes: &'a [u8]) -> Result<T> {
@@ -116,6 +117,15 @@ fn visit_bytes<E>(self, v: &[u8]) -> kernel::error::Result<BlobRef, E>
}
impl MetadataBlob {
+ pub(crate) fn new(mut f: file::RegularFile) -> Result<MetadataBlob> {
+ let inodes_count = cbor_get_array_size(&mut f)? as usize;
+ let mmapped_region = f.read_to_end()?;
+ Ok(MetadataBlob {
+ mmapped_region,
+ inode_count: inodes_count,
+ })
+ }
+
pub(crate) fn seek_ref(&mut self, r: &BlobRef) -> Result<u64> {
match r.kind {
BlobRefKind::Other { .. } => Err(WireFormatError::SeekOtherError),
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 61/80] samples: puzzlefs: build puzzlefs into the kernel, thus avoiding the need to export rust symbols
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (54 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 60/80] samples: puzzlefs: implement new for MetadataBlob Ariel Miculas
@ 2023-06-09 6:30 ` Ariel Miculas
2023-06-09 6:31 ` [PATCH 62/80] rust: alloc: add try_clone for Vec<T> Ariel Miculas
` (21 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:30 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
---
kernel/configs/rust.config | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel/configs/rust.config b/kernel/configs/rust.config
index 5e29e66e8f7d..4b109055f990 100644
--- a/kernel/configs/rust.config
+++ b/kernel/configs/rust.config
@@ -6,7 +6,7 @@ CONFIG_SAMPLES_RUST=y
CONFIG_SAMPLE_RUST_MINIMAL=m
CONFIG_SAMPLE_RUST_PRINT=m
CONFIG_SAMPLE_RUST_FS=m
-CONFIG_SAMPLE_PUZZLEFS=m
+CONFIG_SAMPLE_PUZZLEFS=y
CONFIG_SAMPLE_RUST_HOSTPROGS=y
CONFIG_SAMPLE_RUST_SERDE=y
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 62/80] rust: alloc: add try_clone for Vec<T>
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (55 preceding siblings ...)
2023-06-09 6:30 ` [PATCH 61/80] samples: puzzlefs: build puzzlefs into the kernel, thus avoiding the need to export rust symbols Ariel Miculas
@ 2023-06-09 6:31 ` Ariel Miculas
2023-06-09 6:31 ` [PATCH 63/80] rust: alloc: add from_iter_fallible " Ariel Miculas
` (20 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:31 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/alloc/vec/mod.rs | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/rust/alloc/vec/mod.rs b/rust/alloc/vec/mod.rs
index 94995913566b..2ca945f7eb9b 100644
--- a/rust/alloc/vec/mod.rs
+++ b/rust/alloc/vec/mod.rs
@@ -2489,6 +2489,15 @@ unsafe fn split_at_spare_mut_with_len(
}
}
+impl<T: Clone> Vec<T> {
+ /// Try to clone the vector using the global allocator
+ #[inline]
+ #[stable(feature = "kernel", since = "1.0.0")]
+ pub fn try_clone(&self) -> Result<Self, TryReserveError> {
+ self.try_clone_in(Global)
+ }
+}
+
impl<T: Clone, A: Allocator> Vec<T, A> {
/// Resizes the `Vec` in-place so that `len` is equal to `new_len`.
///
@@ -2613,6 +2622,14 @@ pub fn try_extend_from_slice(&mut self, other: &[T]) -> Result<(), TryReserveErr
self.try_spec_extend(other.iter())
}
+ /// Tries to clone the vector using the given allocator
+ #[stable(feature = "kernel", since = "1.0.0")]
+ pub fn try_clone_in(&self, allocator: A) -> Result<Self, TryReserveError> {
+ let mut new_vec = Vec::try_with_capacity_in(self.len(), allocator)?;
+ new_vec.try_extend_from_slice(&self)?;
+ Ok(new_vec)
+ }
+
/// Copies elements from `src` range to the end of the vector.
///
/// # Panics
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 63/80] rust: alloc: add from_iter_fallible for Vec<T>
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (56 preceding siblings ...)
2023-06-09 6:31 ` [PATCH 62/80] rust: alloc: add try_clone for Vec<T> Ariel Miculas
@ 2023-06-09 6:31 ` Ariel Miculas
2023-06-09 10:06 ` Miguel Ojeda
2023-06-09 6:31 ` [PATCH 64/80] samples: puzzlefs: implement to_errno and from_errno for WireFormatError Ariel Miculas
` (19 subsequent siblings)
77 siblings, 1 reply; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:31 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/alloc/vec/mod.rs | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/rust/alloc/vec/mod.rs b/rust/alloc/vec/mod.rs
index 2ca945f7eb9b..b755cf0b936c 100644
--- a/rust/alloc/vec/mod.rs
+++ b/rust/alloc/vec/mod.rs
@@ -649,6 +649,16 @@ pub fn try_with_capacity(capacity: usize) -> Result<Self, TryReserveError> {
pub unsafe fn from_raw_parts(ptr: *mut T, length: usize, capacity: usize) -> Self {
unsafe { Self::from_raw_parts_in(ptr, length, capacity, Global) }
}
+
+ /// See https://doc.rust-lang.org/std/vec/struct.Vec.html#examples-31
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn from_iter_fallible(iter: impl Iterator<Item=T>) -> Result<Vec<T>, TryReserveError> {
+ let mut vec = Vec::new();
+ for value in iter {
+ vec.try_push(value)?;
+ }
+ Ok(vec)
+ }
}
impl<T, A: Allocator> Vec<T, A> {
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 64/80] samples: puzzlefs: implement to_errno and from_errno for WireFormatError
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (57 preceding siblings ...)
2023-06-09 6:31 ` [PATCH 63/80] rust: alloc: add from_iter_fallible " Ariel Miculas
@ 2023-06-09 6:31 ` Ariel Miculas
2023-06-09 6:31 ` [PATCH 65/80] samples: puzzlefs: add TryReserveError (and from conversion) to WireFormatError Ariel Miculas
` (18 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:31 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
samples/rust/puzzle/error.rs | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/samples/rust/puzzle/error.rs b/samples/rust/puzzle/error.rs
index 5fa9c1404df4..254926e6b4e2 100644
--- a/samples/rust/puzzle/error.rs
+++ b/samples/rust/puzzle/error.rs
@@ -1,4 +1,6 @@
+use core::ffi::c_int;
use core::fmt::{self, Display};
+use kernel::prelude::{EINVAL, ENOENT, ESPIPE};
// TODO use String in error types (when it's available from the kernel)
@@ -10,6 +12,23 @@ pub(crate) enum WireFormatError {
KernelError(kernel::error::Error),
}
+impl WireFormatError {
+ pub(crate) fn to_errno(&self) -> c_int {
+ match self {
+ WireFormatError::LocalRefError => kernel::error::Error::to_errno(EINVAL),
+ WireFormatError::SeekOtherError => kernel::error::Error::to_errno(ESPIPE),
+ WireFormatError::ValueMissing => kernel::error::Error::to_errno(ENOENT),
+ WireFormatError::CBORError(..) => kernel::error::Error::to_errno(EINVAL),
+ WireFormatError::KernelError(e) => kernel::error::Error::to_errno(*e),
+ WireFormatError::TryReserveError(e) => kernel::error::Error::to_errno(EINVAL),
+ }
+ }
+
+ pub(crate) fn from_errno(errno: kernel::error::Error) -> Self {
+ Self::KernelError(errno)
+ }
+}
+
impl Display for WireFormatError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 65/80] samples: puzzlefs: add TryReserveError (and from conversion) to WireFormatError
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (58 preceding siblings ...)
2023-06-09 6:31 ` [PATCH 64/80] samples: puzzlefs: implement to_errno and from_errno for WireFormatError Ariel Miculas
@ 2023-06-09 6:31 ` Ariel Miculas
2023-06-09 6:31 ` [PATCH 66/80] samples: puzzlefs: add higher level inode related functionality Ariel Miculas
` (17 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:31 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
samples/rust/puzzle/error.rs | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/samples/rust/puzzle/error.rs b/samples/rust/puzzle/error.rs
index 254926e6b4e2..e803c2177b72 100644
--- a/samples/rust/puzzle/error.rs
+++ b/samples/rust/puzzle/error.rs
@@ -1,3 +1,4 @@
+use alloc::collections::TryReserveError;
use core::ffi::c_int;
use core::fmt::{self, Display};
use kernel::prelude::{EINVAL, ENOENT, ESPIPE};
@@ -10,6 +11,7 @@ pub(crate) enum WireFormatError {
ValueMissing,
CBORError(serde_cbor::Error),
KernelError(kernel::error::Error),
+ TryReserveError(TryReserveError),
}
impl WireFormatError {
@@ -20,7 +22,7 @@ pub(crate) fn to_errno(&self) -> c_int {
WireFormatError::ValueMissing => kernel::error::Error::to_errno(ENOENT),
WireFormatError::CBORError(..) => kernel::error::Error::to_errno(EINVAL),
WireFormatError::KernelError(e) => kernel::error::Error::to_errno(*e),
- WireFormatError::TryReserveError(e) => kernel::error::Error::to_errno(EINVAL),
+ WireFormatError::TryReserveError(_) => kernel::error::Error::to_errno(EINVAL),
}
}
@@ -37,6 +39,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
WireFormatError::ValueMissing => f.write_str("no value present"),
WireFormatError::CBORError(_) => f.write_str("CBOR error"),
WireFormatError::KernelError(_) => f.write_str("Kernel error"),
+ WireFormatError::TryReserveError(_) => f.write_str("TryReserveError"),
}
}
}
@@ -59,3 +62,11 @@ fn from(source: kernel::error::Error) -> Self {
WireFormatError::KernelError(source)
}
}
+
+#[allow(unused_qualifications)]
+impl core::convert::From<TryReserveError> for WireFormatError {
+ #[allow(deprecated)]
+ fn from(source: TryReserveError) -> Self {
+ WireFormatError::TryReserveError(source)
+ }
+}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 66/80] samples: puzzlefs: add higher level inode related functionality
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (59 preceding siblings ...)
2023-06-09 6:31 ` [PATCH 65/80] samples: puzzlefs: add TryReserveError (and from conversion) to WireFormatError Ariel Miculas
@ 2023-06-09 6:31 ` Ariel Miculas
2023-06-09 6:31 ` [PATCH 67/80] samples: puzzlefs: populate the directory entries with the inodes from the puzzlefs metadata file Ariel Miculas
` (16 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:31 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
samples/rust/puzzle.rs | 3 +-
samples/rust/puzzle/inode.rs | 83 ++++++++++++++++++++++++++++++++++++
2 files changed, 85 insertions(+), 1 deletion(-)
create mode 100644 samples/rust/puzzle/inode.rs
diff --git a/samples/rust/puzzle.rs b/samples/rust/puzzle.rs
index 4d558561974d..1b989a2579c4 100644
--- a/samples/rust/puzzle.rs
+++ b/samples/rust/puzzle.rs
@@ -1,2 +1,3 @@
pub(crate) mod error;
-mod types;
+pub(crate) mod inode;
+pub(crate) mod types;
diff --git a/samples/rust/puzzle/inode.rs b/samples/rust/puzzle/inode.rs
new file mode 100644
index 000000000000..980aac9c4fd3
--- /dev/null
+++ b/samples/rust/puzzle/inode.rs
@@ -0,0 +1,83 @@
+// This contents of this file is taken from puzzlefs.rs (the userspace implementation)
+// It is named inode.rs instead puzzlefs.rs since the root of this kernel module already has that name
+
+use crate::puzzle::error::Result;
+use crate::puzzle::error::WireFormatError;
+use crate::puzzle::types as format;
+use crate::puzzle::types::{FileChunk, Ino, InodeAdditional, MetadataBlob};
+use alloc::vec::Vec;
+use kernel::prelude::{ENOENT, ENOTDIR};
+
+#[derive(Debug)]
+pub(crate) struct Inode {
+ pub(crate) inode: format::Inode,
+ pub(crate) mode: InodeMode,
+ #[allow(dead_code)]
+ pub(crate) additional: Option<InodeAdditional>,
+}
+
+pub(crate) struct PuzzleFS {
+ layers: Vec<format::MetadataBlob>,
+}
+
+impl PuzzleFS {
+ pub(crate) fn new(md: MetadataBlob) -> Result<Self> {
+ let mut v = Vec::new();
+ v.try_push(md)?;
+ Ok(PuzzleFS { layers: v })
+ }
+
+ // Temporary helper function used until PuzzleFs is integrated
+ pub(crate) fn find_inode(&mut self, ino: u64) -> Result<Inode> {
+ for layer in self.layers.iter_mut() {
+ if let Some(inode) = layer.find_inode(ino)? {
+ return Inode::new(layer, inode);
+ }
+ }
+ Err(WireFormatError::from_errno(ENOENT))
+ }
+}
+
+impl Inode {
+ fn new(layer: &mut MetadataBlob, inode: format::Inode) -> Result<Inode> {
+ let mode = match inode.mode {
+ format::InodeMode::Reg { offset } => {
+ let chunks = layer.read_file_chunks(offset)?;
+ InodeMode::File { chunks }
+ }
+ format::InodeMode::Dir { offset } => {
+ // TODO: implement something like collect_fallible (since try_collect already exists with another purpose)
+ let mut entries = Vec::from_iter_fallible(
+ layer
+ .read_dir_list(offset)?
+ .entries
+ .iter_mut()
+ .map(|de| (de.name.try_clone().unwrap(), de.ino)),
+ )?;
+ // Unstable sort is used because it avoids memory allocation
+ // There should not be two directories with the same name, so stable sort doesn't have any advantage
+ entries.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
+ InodeMode::Dir { entries }
+ }
+ _ => InodeMode::Other,
+ };
+
+ let additional = inode
+ .additional
+ .map(|additional_ref| layer.read_inode_additional(&additional_ref))
+ .transpose()?;
+
+ Ok(Inode {
+ inode,
+ mode,
+ additional,
+ })
+ }
+}
+
+#[derive(Debug)]
+pub(crate) enum InodeMode {
+ File { chunks: Vec<FileChunk> },
+ Dir { entries: Vec<(Vec<u8>, Ino)> },
+ Other,
+}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 67/80] samples: puzzlefs: populate the directory entries with the inodes from the puzzlefs metadata file
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (60 preceding siblings ...)
2023-06-09 6:31 ` [PATCH 66/80] samples: puzzlefs: add higher level inode related functionality Ariel Miculas
@ 2023-06-09 6:31 ` Ariel Miculas
2023-06-09 6:31 ` [PATCH 68/80] rust: hex: import crate Ariel Miculas
` (15 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:31 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/kernel/fs.rs | 4 +-
samples/rust/puzzlefs.rs | 125 ++++++++++++++++++++++++++++++---------
2 files changed, 100 insertions(+), 29 deletions(-)
diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs
index ba98ae7caf00..81c64faf717b 100644
--- a/rust/kernel/fs.rs
+++ b/rust/kernel/fs.rs
@@ -775,7 +775,9 @@ impl SuperParams {
///
/// The superblock is a newly-created one and this is the only active pointer to it.
pub struct NewSuperBlock<'a, T: Type + ?Sized, S = NeedsInit> {
- sb: &'a mut SuperBlock<T>,
+ /// Pointer to the superblock; this fields is public so puzzlefs can call
+ /// try_new_dcache_dir_inode when populating the directory hierarchy
+ pub sb: &'a mut SuperBlock<T>,
// This also forces `'a` to be invariant.
_p: PhantomData<&'a mut &'a S>,
diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
index 50d62109a9c1..c160b5e5f911 100644
--- a/samples/rust/puzzlefs.rs
+++ b/samples/rust/puzzlefs.rs
@@ -6,13 +6,16 @@
use kernel::mount::Vfsmount;
use kernel::prelude::*;
use kernel::{
- c_str, file, fmt, fs,
+ c_str, file, fs,
io_buffer::IoBufferWriter,
- str::CString,
sync::{Arc, ArcBorrow},
};
mod puzzle;
+use puzzle::inode::{Inode, InodeMode, PuzzleFS};
+use puzzle::types::MetadataBlob;
+
+use kernel::fs::{DEntry, INodeParams, NeedsRoot, NewSuperBlock, RootDEntry};
module_fs! {
type: PuzzleFs,
@@ -25,7 +28,6 @@
#[derive(Debug)]
struct PuzzlefsInfo {
- base_path: CString,
vfs_mount: Arc<Vfsmount>,
}
@@ -53,9 +55,81 @@ fn try_new() -> Result {
}
}
+fn puzzlefs_populate_dir(
+ sb: &NewSuperBlock<'_, PuzzleFs, NeedsRoot>,
+ pfs: &mut PuzzleFS,
+ parent: &DEntry<PuzzleFs>,
+ ino: u64,
+ name: &CStr,
+ recursion: usize,
+) -> Result {
+ if recursion == 0 {
+ return Err(E2BIG);
+ }
+
+ let inode = Arc::try_new(pfs.find_inode(ino).map_err(|_| EINVAL)?)?;
+ match &inode.mode {
+ InodeMode::File { chunks: _ } => {
+ let params = INodeParams {
+ mode: inode.inode.permissions,
+ ino: inode.inode.ino,
+ value: inode.clone(),
+ };
+ let creator = fs::file_creator::<_, FsFile>();
+ let inode = creator(sb, params)?;
+ sb.try_new_dentry(inode, parent, name)?;
+ }
+ InodeMode::Dir { entries } => {
+ let params = INodeParams {
+ mode: inode.inode.permissions,
+ ino: inode.inode.ino,
+ value: inode.clone(),
+ };
+
+ let new_dentry;
+ let new_parent = if name.as_bytes() != c_str!("").as_bytes() {
+ let dcache_inode = sb.sb.try_new_dcache_dir_inode(params)?;
+ new_dentry = sb.try_new_dentry(dcache_inode, parent, name)?;
+ &new_dentry
+ } else {
+ parent
+ };
+
+ for (name, new_inode) in entries {
+ let mut name = name.try_clone()?;
+ // append NUL terminator
+ name.try_push(0)?;
+ let name = CStr::from_bytes_with_nul(&name)?;
+ puzzlefs_populate_dir(sb, pfs, new_parent, *new_inode, name, recursion - 1)?;
+ }
+ }
+ InodeMode::Other => todo!(),
+ }
+
+ Ok(())
+}
+
+/// Creates a new root dentry populated with the given entries.
+fn try_new_populated_root_puzzlefs_dentry(
+ sb: &NewSuperBlock<'_, PuzzleFs, NeedsRoot>,
+ pfs: &mut PuzzleFS,
+ root_value: <PuzzleFs as fs::Type>::INodeData,
+) -> Result<RootDEntry<PuzzleFs>> {
+ let root_inode = sb.sb.try_new_dcache_dir_inode(INodeParams {
+ mode: 0o755,
+ ino: root_value.inode.ino,
+ value: root_value,
+ })?;
+ let root = sb.try_new_root_dentry(root_inode)?;
+ let ino = 1u64;
+ puzzlefs_populate_dir(sb, pfs, &root, ino, c_str!(""), 10)?;
+ Ok(root)
+}
+
impl fs::Type for PuzzleFs {
type Context = Self;
- type INodeData = &'static [u8];
+ // this is Arc so it can be cloned in puzzlefs_populate_dir
+ type INodeData = Arc<Inode>;
type Data = Box<PuzzlefsInfo>;
const SUPER_TYPE: fs::Super = fs::Super::Independent;
const NAME: &'static CStr = c_str!("puzzlefs");
@@ -63,41 +137,36 @@ impl fs::Type for PuzzleFs {
const DCACHE_BASED: bool = true;
fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBlock<Self>> {
- let base_path = CString::try_from_fmt(fmt!("hello world"))?;
- pr_info!("base_path {:?}\n", base_path);
let vfs_mount = Vfsmount::new_private_mount(c_str!("/home/puzzlefs_oci"))?;
pr_info!("vfs_mount {:?}\n", vfs_mount);
+ let arc_vfs_mount = Arc::try_new(vfs_mount)?;
+
let sb = sb.init(
Box::try_new(PuzzlefsInfo {
- base_path,
- vfs_mount: Arc::try_new(vfs_mount)?,
+ vfs_mount: arc_vfs_mount.clone(),
})?,
&fs::SuperParams {
magic: 0x72757374,
..fs::SuperParams::DEFAULT
},
)?;
- let root = sb.try_new_populated_root_dentry(
- &[],
- kernel::fs_entries![
- file("test1", 0o600, "abc\n".as_bytes(), FsFile),
- file("test2", 0o600, "def\n".as_bytes(), FsFile),
- char("test3", 0o600, [].as_slice(), (10, 125)),
- sock("test4", 0o755, [].as_slice()),
- fifo("test5", 0o755, [].as_slice()),
- block("test6", 0o755, [].as_slice(), (1, 1)),
- dir(
- "dir1",
- 0o755,
- [].as_slice(),
- [
- file("test1", 0o600, "abc\n".as_bytes(), FsFile),
- file("test2", 0o600, "def\n".as_bytes(), FsFile),
- ]
- ),
- ],
+
+ let file = file::RegularFile::from_path_in_root_mnt(
+ &arc_vfs_mount,
+ c_str!("997eed138af30d187e87d682dd2ae9f240fae78f668907a0519460b397c82467"),
+ file::flags::O_RDONLY.try_into().unwrap(),
+ 0,
)?;
+
+ // TODO: figure out how to go from WireFormatError to kernel::error::Error
+ let metadata = MetadataBlob::new(file).map_err(|_| EINVAL)?;
+ pr_info!("number of inodes {:?}\n", metadata.inode_count);
+
+ let mut puzzlefs = PuzzleFS::new(metadata).map_err(|_| EINVAL)?;
+ let root_inode = Arc::try_new(puzzlefs.find_inode(1).map_err(|_| EINVAL)?)?;
+
+ let root = try_new_populated_root_puzzlefs_dentry(&sb, &mut puzzlefs, root_inode)?;
let sb = sb.init_root(root)?;
Ok(sb)
}
@@ -108,7 +177,7 @@ fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBl
#[vtable]
impl file::Operations for FsFile {
// must be the same as INodeData
- type OpenData = &'static [u8];
+ type OpenData = Arc<Inode>;
type FSData = Box<PuzzlefsInfo>;
// this is an Arc because Data must be ForeignOwnable and the only implementors of it are Box,
// Arc and (); we cannot pass a reference to read, so we share Vfsmount using and Arc
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 68/80] rust: hex: import crate
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (61 preceding siblings ...)
2023-06-09 6:31 ` [PATCH 67/80] samples: puzzlefs: populate the directory entries with the inodes from the puzzlefs metadata file Ariel Miculas
@ 2023-06-09 6:31 ` Ariel Miculas
2023-06-09 6:31 ` [PATCH 69/80] rust: hex: add SPDX license identifiers Ariel Miculas
` (14 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:31 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
This is a subset of the Rust `hex` crate,
version v0.4.3, licensed under "Apache-2.0 OR MIT", from:
https://github.com/KokaKiwi/rust-hex/tree/v0.4.3/src
The files are copied as-is, with no modifications whatsoever
(not even adding the SPDX identifiers).
For copyright details, please see:
https://github.com/KokaKiwi/rust-hex/blob/v0.4.3/README.md#license
https://github.com/KokaKiwi/rust-hex/blob/v0.4.3/LICENSE-APACHE
https://github.com/KokaKiwi/rust-hex/blob/v0.4.3/LICENSE-MIT
The next three patches modify these files as needed for use within
the kernel. This patch split allows reviewers to double-check
the import and to clearly see the differences introduced.
The following script may be used to verify the contents:
for path in $(cd rust/hex/ && find . -type f -name '*.rs'); do
curl --silent --show-error --location \
https://github.com/KokaKiwi/rust-hex/raw/v0.4.3/src/$path \
| diff --unified rust/hex/$path - && echo $path: OK
done
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/hex/error.rs | 59 ++++++
rust/hex/lib.rs | 525 ++++++++++++++++++++++++++++++++++++++++++++++
rust/hex/serde.rs | 102 +++++++++
3 files changed, 686 insertions(+)
create mode 100644 rust/hex/error.rs
create mode 100644 rust/hex/lib.rs
create mode 100644 rust/hex/serde.rs
diff --git a/rust/hex/error.rs b/rust/hex/error.rs
new file mode 100644
index 000000000000..ff7a3b5c16bd
--- /dev/null
+++ b/rust/hex/error.rs
@@ -0,0 +1,59 @@
+use core::fmt;
+
+/// The error type for decoding a hex string into `Vec<u8>` or `[u8; N]`.
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum FromHexError {
+ /// An invalid character was found. Valid ones are: `0...9`, `a...f`
+ /// or `A...F`.
+ InvalidHexCharacter { c: char, index: usize },
+
+ /// A hex string's length needs to be even, as two digits correspond to
+ /// one byte.
+ OddLength,
+
+ /// If the hex string is decoded into a fixed sized container, such as an
+ /// array, the hex string's length * 2 has to match the container's
+ /// length.
+ InvalidStringLength,
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for FromHexError {}
+
+impl fmt::Display for FromHexError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ FromHexError::InvalidHexCharacter { c, index } => {
+ write!(f, "Invalid character {:?} at position {}", c, index)
+ }
+ FromHexError::OddLength => write!(f, "Odd number of digits"),
+ FromHexError::InvalidStringLength => write!(f, "Invalid string length"),
+ }
+ }
+}
+
+#[cfg(test)]
+// this feature flag is here to suppress unused
+// warnings of `super::*` and `pretty_assertions::assert_eq`
+#[cfg(feature = "alloc")]
+mod tests {
+ use super::*;
+ #[cfg(feature = "alloc")]
+ use alloc::string::ToString;
+ use pretty_assertions::assert_eq;
+
+ #[test]
+ #[cfg(feature = "alloc")]
+ fn test_display() {
+ assert_eq!(
+ FromHexError::InvalidHexCharacter { c: '\n', index: 5 }.to_string(),
+ "Invalid character '\\n' at position 5"
+ );
+
+ assert_eq!(FromHexError::OddLength.to_string(), "Odd number of digits");
+ assert_eq!(
+ FromHexError::InvalidStringLength.to_string(),
+ "Invalid string length"
+ );
+ }
+}
diff --git a/rust/hex/lib.rs b/rust/hex/lib.rs
new file mode 100644
index 000000000000..ec48961b9ffe
--- /dev/null
+++ b/rust/hex/lib.rs
@@ -0,0 +1,525 @@
+// Copyright (c) 2013-2014 The Rust Project Developers.
+// Copyright (c) 2015-2020 The rust-hex Developers.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//! Encoding and decoding hex strings.
+//!
+//! For most cases, you can simply use the [`decode`], [`encode`] and
+//! [`encode_upper`] functions. If you need a bit more control, use the traits
+//! [`ToHex`] and [`FromHex`] instead.
+//!
+//! # Example
+//!
+//! ```
+//! # #[cfg(not(feature = "alloc"))]
+//! # let mut output = [0; 0x18];
+//! #
+//! # #[cfg(not(feature = "alloc"))]
+//! # hex::encode_to_slice(b"Hello world!", &mut output).unwrap();
+//! #
+//! # #[cfg(not(feature = "alloc"))]
+//! # let hex_string = ::core::str::from_utf8(&output).unwrap();
+//! #
+//! # #[cfg(feature = "alloc")]
+//! let hex_string = hex::encode("Hello world!");
+//!
+//! println!("{}", hex_string); // Prints "48656c6c6f20776f726c6421"
+//!
+//! # assert_eq!(hex_string, "48656c6c6f20776f726c6421");
+//! ```
+
+#![doc(html_root_url = "https://docs.rs/hex/0.4.3")]
+#![cfg_attr(not(feature = "std"), no_std)]
+#![cfg_attr(docsrs, feature(doc_cfg))]
+#![allow(clippy::unreadable_literal)]
+
+#[cfg(feature = "alloc")]
+extern crate alloc;
+#[cfg(feature = "alloc")]
+use alloc::{string::String, vec::Vec};
+
+use core::iter;
+
+mod error;
+pub use crate::error::FromHexError;
+
+#[cfg(feature = "serde")]
+#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
+pub mod serde;
+#[cfg(feature = "serde")]
+pub use crate::serde::deserialize;
+#[cfg(all(feature = "alloc", feature = "serde"))]
+pub use crate::serde::{serialize, serialize_upper};
+
+/// Encoding values as hex string.
+///
+/// This trait is implemented for all `T` which implement `AsRef<[u8]>`. This
+/// includes `String`, `str`, `Vec<u8>` and `[u8]`.
+///
+/// # Example
+///
+/// ```
+/// use hex::ToHex;
+///
+/// println!("{}", "Hello world!".encode_hex::<String>());
+/// # assert_eq!("Hello world!".encode_hex::<String>(), "48656c6c6f20776f726c6421".to_string());
+/// ```
+///
+/// *Note*: instead of using this trait, you might want to use [`encode()`].
+pub trait ToHex {
+ /// Encode the hex strict representing `self` into the result. Lower case
+ /// letters are used (e.g. `f9b4ca`)
+ fn encode_hex<T: iter::FromIterator<char>>(&self) -> T;
+
+ /// Encode the hex strict representing `self` into the result. Upper case
+ /// letters are used (e.g. `F9B4CA`)
+ fn encode_hex_upper<T: iter::FromIterator<char>>(&self) -> T;
+}
+
+const HEX_CHARS_LOWER: &[u8; 16] = b"0123456789abcdef";
+const HEX_CHARS_UPPER: &[u8; 16] = b"0123456789ABCDEF";
+
+struct BytesToHexChars<'a> {
+ inner: ::core::slice::Iter<'a, u8>,
+ table: &'static [u8; 16],
+ next: Option<char>,
+}
+
+impl<'a> BytesToHexChars<'a> {
+ fn new(inner: &'a [u8], table: &'static [u8; 16]) -> BytesToHexChars<'a> {
+ BytesToHexChars {
+ inner: inner.iter(),
+ table,
+ next: None,
+ }
+ }
+}
+
+impl<'a> Iterator for BytesToHexChars<'a> {
+ type Item = char;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self.next.take() {
+ Some(current) => Some(current),
+ None => self.inner.next().map(|byte| {
+ let current = self.table[(byte >> 4) as usize] as char;
+ self.next = Some(self.table[(byte & 0x0F) as usize] as char);
+ current
+ }),
+ }
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ let length = self.len();
+ (length, Some(length))
+ }
+}
+
+impl<'a> iter::ExactSizeIterator for BytesToHexChars<'a> {
+ fn len(&self) -> usize {
+ let mut length = self.inner.len() * 2;
+ if self.next.is_some() {
+ length += 1;
+ }
+ length
+ }
+}
+
+#[inline]
+fn encode_to_iter<T: iter::FromIterator<char>>(table: &'static [u8; 16], source: &[u8]) -> T {
+ BytesToHexChars::new(source, table).collect()
+}
+
+impl<T: AsRef<[u8]>> ToHex for T {
+ fn encode_hex<U: iter::FromIterator<char>>(&self) -> U {
+ encode_to_iter(HEX_CHARS_LOWER, self.as_ref())
+ }
+
+ fn encode_hex_upper<U: iter::FromIterator<char>>(&self) -> U {
+ encode_to_iter(HEX_CHARS_UPPER, self.as_ref())
+ }
+}
+
+/// Types that can be decoded from a hex string.
+///
+/// This trait is implemented for `Vec<u8>` and small `u8`-arrays.
+///
+/// # Example
+///
+/// ```
+/// use core::str;
+/// use hex::FromHex;
+///
+/// let buffer = <[u8; 12]>::from_hex("48656c6c6f20776f726c6421")?;
+/// let string = str::from_utf8(&buffer).expect("invalid buffer length");
+///
+/// println!("{}", string); // prints "Hello world!"
+/// # assert_eq!("Hello world!", string);
+/// # Ok::<(), hex::FromHexError>(())
+/// ```
+pub trait FromHex: Sized {
+ type Error;
+
+ /// Creates an instance of type `Self` from the given hex string, or fails
+ /// with a custom error type.
+ ///
+ /// Both, upper and lower case characters are valid and can even be
+ /// mixed (e.g. `f9b4ca`, `F9B4CA` and `f9B4Ca` are all valid strings).
+ fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error>;
+}
+
+fn val(c: u8, idx: usize) -> Result<u8, FromHexError> {
+ match c {
+ b'A'..=b'F' => Ok(c - b'A' + 10),
+ b'a'..=b'f' => Ok(c - b'a' + 10),
+ b'0'..=b'9' => Ok(c - b'0'),
+ _ => Err(FromHexError::InvalidHexCharacter {
+ c: c as char,
+ index: idx,
+ }),
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl FromHex for Vec<u8> {
+ type Error = FromHexError;
+
+ fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
+ let hex = hex.as_ref();
+ if hex.len() % 2 != 0 {
+ return Err(FromHexError::OddLength);
+ }
+
+ hex.chunks(2)
+ .enumerate()
+ .map(|(i, pair)| Ok(val(pair[0], 2 * i)? << 4 | val(pair[1], 2 * i + 1)?))
+ .collect()
+ }
+}
+
+// Helper macro to implement the trait for a few fixed sized arrays. Once Rust
+// has type level integers, this should be removed.
+macro_rules! from_hex_array_impl {
+ ($($len:expr)+) => {$(
+ impl FromHex for [u8; $len] {
+ type Error = FromHexError;
+
+ fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
+ let mut out = [0_u8; $len];
+ decode_to_slice(hex, &mut out as &mut [u8])?;
+ Ok(out)
+ }
+ }
+ )+}
+}
+
+from_hex_array_impl! {
+ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+ 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
+ 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
+ 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
+ 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
+ 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
+ 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
+ 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
+ 160 192 200 224 256 384 512 768 1024 2048 4096 8192 16384 32768
+}
+
+#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
+from_hex_array_impl! {
+ 65536 131072 262144 524288 1048576 2097152 4194304 8388608
+ 16777216 33554432 67108864 134217728 268435456 536870912
+ 1073741824 2147483648
+}
+
+#[cfg(target_pointer_width = "64")]
+from_hex_array_impl! {
+ 4294967296
+}
+
+/// Encodes `data` as hex string using lowercase characters.
+///
+/// Lowercase characters are used (e.g. `f9b4ca`). The resulting string's
+/// length is always even, each byte in `data` is always encoded using two hex
+/// digits. Thus, the resulting string contains exactly twice as many bytes as
+/// the input data.
+///
+/// # Example
+///
+/// ```
+/// assert_eq!(hex::encode("Hello world!"), "48656c6c6f20776f726c6421");
+/// assert_eq!(hex::encode(vec![1, 2, 3, 15, 16]), "0102030f10");
+/// ```
+#[must_use]
+#[cfg(feature = "alloc")]
+pub fn encode<T: AsRef<[u8]>>(data: T) -> String {
+ data.encode_hex()
+}
+
+/// Encodes `data` as hex string using uppercase characters.
+///
+/// Apart from the characters' casing, this works exactly like `encode()`.
+///
+/// # Example
+///
+/// ```
+/// assert_eq!(hex::encode_upper("Hello world!"), "48656C6C6F20776F726C6421");
+/// assert_eq!(hex::encode_upper(vec![1, 2, 3, 15, 16]), "0102030F10");
+/// ```
+#[must_use]
+#[cfg(feature = "alloc")]
+pub fn encode_upper<T: AsRef<[u8]>>(data: T) -> String {
+ data.encode_hex_upper()
+}
+
+/// Decodes a hex string into raw bytes.
+///
+/// Both, upper and lower case characters are valid in the input string and can
+/// even be mixed (e.g. `f9b4ca`, `F9B4CA` and `f9B4Ca` are all valid strings).
+///
+/// # Example
+///
+/// ```
+/// assert_eq!(
+/// hex::decode("48656c6c6f20776f726c6421"),
+/// Ok("Hello world!".to_owned().into_bytes())
+/// );
+///
+/// assert_eq!(hex::decode("123"), Err(hex::FromHexError::OddLength));
+/// assert!(hex::decode("foo").is_err());
+/// ```
+#[cfg(feature = "alloc")]
+pub fn decode<T: AsRef<[u8]>>(data: T) -> Result<Vec<u8>, FromHexError> {
+ FromHex::from_hex(data)
+}
+
+/// Decode a hex string into a mutable bytes slice.
+///
+/// Both, upper and lower case characters are valid in the input string and can
+/// even be mixed (e.g. `f9b4ca`, `F9B4CA` and `f9B4Ca` are all valid strings).
+///
+/// # Example
+///
+/// ```
+/// let mut bytes = [0u8; 4];
+/// assert_eq!(hex::decode_to_slice("6b697769", &mut bytes as &mut [u8]), Ok(()));
+/// assert_eq!(&bytes, b"kiwi");
+/// ```
+pub fn decode_to_slice<T: AsRef<[u8]>>(data: T, out: &mut [u8]) -> Result<(), FromHexError> {
+ let data = data.as_ref();
+
+ if data.len() % 2 != 0 {
+ return Err(FromHexError::OddLength);
+ }
+ if data.len() / 2 != out.len() {
+ return Err(FromHexError::InvalidStringLength);
+ }
+
+ for (i, byte) in out.iter_mut().enumerate() {
+ *byte = val(data[2 * i], 2 * i)? << 4 | val(data[2 * i + 1], 2 * i + 1)?;
+ }
+
+ Ok(())
+}
+
+// generates an iterator like this
+// (0, 1)
+// (2, 3)
+// (4, 5)
+// (6, 7)
+// ...
+#[inline]
+fn generate_iter(len: usize) -> impl Iterator<Item = (usize, usize)> {
+ (0..len).step_by(2).zip((0..len).skip(1).step_by(2))
+}
+
+// the inverse of `val`.
+#[inline]
+#[must_use]
+fn byte2hex(byte: u8, table: &[u8; 16]) -> (u8, u8) {
+ let high = table[((byte & 0xf0) >> 4) as usize];
+ let low = table[(byte & 0x0f) as usize];
+
+ (high, low)
+}
+
+/// Encodes some bytes into a mutable slice of bytes.
+///
+/// The output buffer, has to be able to hold at least `input.len() * 2` bytes,
+/// otherwise this function will return an error.
+///
+/// # Example
+///
+/// ```
+/// # use hex::FromHexError;
+/// # fn main() -> Result<(), FromHexError> {
+/// let mut bytes = [0u8; 4 * 2];
+///
+/// hex::encode_to_slice(b"kiwi", &mut bytes)?;
+/// assert_eq!(&bytes, b"6b697769");
+/// # Ok(())
+/// # }
+/// ```
+pub fn encode_to_slice<T: AsRef<[u8]>>(input: T, output: &mut [u8]) -> Result<(), FromHexError> {
+ if input.as_ref().len() * 2 != output.len() {
+ return Err(FromHexError::InvalidStringLength);
+ }
+
+ for (byte, (i, j)) in input
+ .as_ref()
+ .iter()
+ .zip(generate_iter(input.as_ref().len() * 2))
+ {
+ let (high, low) = byte2hex(*byte, HEX_CHARS_LOWER);
+ output[i] = high;
+ output[j] = low;
+ }
+
+ Ok(())
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ #[cfg(feature = "alloc")]
+ use alloc::string::ToString;
+ use pretty_assertions::assert_eq;
+
+ #[test]
+ #[cfg(feature = "alloc")]
+ fn test_gen_iter() {
+ let result = vec![(0, 1), (2, 3)];
+
+ assert_eq!(generate_iter(5).collect::<Vec<_>>(), result);
+ }
+
+ #[test]
+ fn test_encode_to_slice() {
+ let mut output_1 = [0; 4 * 2];
+ encode_to_slice(b"kiwi", &mut output_1).unwrap();
+ assert_eq!(&output_1, b"6b697769");
+
+ let mut output_2 = [0; 5 * 2];
+ encode_to_slice(b"kiwis", &mut output_2).unwrap();
+ assert_eq!(&output_2, b"6b69776973");
+
+ let mut output_3 = [0; 100];
+
+ assert_eq!(
+ encode_to_slice(b"kiwis", &mut output_3),
+ Err(FromHexError::InvalidStringLength)
+ );
+ }
+
+ #[test]
+ fn test_decode_to_slice() {
+ let mut output_1 = [0; 4];
+ decode_to_slice(b"6b697769", &mut output_1).unwrap();
+ assert_eq!(&output_1, b"kiwi");
+
+ let mut output_2 = [0; 5];
+ decode_to_slice(b"6b69776973", &mut output_2).unwrap();
+ assert_eq!(&output_2, b"kiwis");
+
+ let mut output_3 = [0; 4];
+
+ assert_eq!(
+ decode_to_slice(b"6", &mut output_3),
+ Err(FromHexError::OddLength)
+ );
+ }
+
+ #[test]
+ #[cfg(feature = "alloc")]
+ fn test_encode() {
+ assert_eq!(encode("foobar"), "666f6f626172");
+ }
+
+ #[test]
+ #[cfg(feature = "alloc")]
+ fn test_decode() {
+ assert_eq!(
+ decode("666f6f626172"),
+ Ok(String::from("foobar").into_bytes())
+ );
+ }
+
+ #[test]
+ #[cfg(feature = "alloc")]
+ pub fn test_from_hex_okay_str() {
+ assert_eq!(Vec::from_hex("666f6f626172").unwrap(), b"foobar");
+ assert_eq!(Vec::from_hex("666F6F626172").unwrap(), b"foobar");
+ }
+
+ #[test]
+ #[cfg(feature = "alloc")]
+ pub fn test_from_hex_okay_bytes() {
+ assert_eq!(Vec::from_hex(b"666f6f626172").unwrap(), b"foobar");
+ assert_eq!(Vec::from_hex(b"666F6F626172").unwrap(), b"foobar");
+ }
+
+ #[test]
+ #[cfg(feature = "alloc")]
+ pub fn test_invalid_length() {
+ assert_eq!(Vec::from_hex("1").unwrap_err(), FromHexError::OddLength);
+ assert_eq!(
+ Vec::from_hex("666f6f6261721").unwrap_err(),
+ FromHexError::OddLength
+ );
+ }
+
+ #[test]
+ #[cfg(feature = "alloc")]
+ pub fn test_invalid_char() {
+ assert_eq!(
+ Vec::from_hex("66ag").unwrap_err(),
+ FromHexError::InvalidHexCharacter { c: 'g', index: 3 }
+ );
+ }
+
+ #[test]
+ #[cfg(feature = "alloc")]
+ pub fn test_empty() {
+ assert_eq!(Vec::from_hex("").unwrap(), b"");
+ }
+
+ #[test]
+ #[cfg(feature = "alloc")]
+ pub fn test_from_hex_whitespace() {
+ assert_eq!(
+ Vec::from_hex("666f 6f62617").unwrap_err(),
+ FromHexError::InvalidHexCharacter { c: ' ', index: 4 }
+ );
+ }
+
+ #[test]
+ pub fn test_from_hex_array() {
+ assert_eq!(
+ <[u8; 6] as FromHex>::from_hex("666f6f626172"),
+ Ok([0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72])
+ );
+
+ assert_eq!(
+ <[u8; 5] as FromHex>::from_hex("666f6f626172"),
+ Err(FromHexError::InvalidStringLength)
+ );
+ }
+
+ #[test]
+ #[cfg(feature = "alloc")]
+ fn test_to_hex() {
+ assert_eq!(
+ [0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72].encode_hex::<String>(),
+ "666f6f626172".to_string(),
+ );
+
+ assert_eq!(
+ [0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72].encode_hex_upper::<String>(),
+ "666F6F626172".to_string(),
+ );
+ }
+}
diff --git a/rust/hex/serde.rs b/rust/hex/serde.rs
new file mode 100644
index 000000000000..335a15132a27
--- /dev/null
+++ b/rust/hex/serde.rs
@@ -0,0 +1,102 @@
+//! Hex encoding with `serde`.
+#[cfg_attr(
+ all(feature = "alloc", feature = "serde"),
+ doc = r##"
+# Example
+
+```
+use serde::{Serialize, Deserialize};
+
+#[derive(Serialize, Deserialize)]
+struct Foo {
+ #[serde(with = "hex")]
+ bar: Vec<u8>,
+}
+```
+"##
+)]
+use serde::de::{Error, Visitor};
+use serde::Deserializer;
+#[cfg(feature = "alloc")]
+use serde::Serializer;
+
+#[cfg(feature = "alloc")]
+use alloc::string::String;
+
+use core::fmt;
+use core::marker::PhantomData;
+
+use crate::FromHex;
+
+#[cfg(feature = "alloc")]
+use crate::ToHex;
+
+/// Serializes `data` as hex string using uppercase characters.
+///
+/// Apart from the characters' casing, this works exactly like `serialize()`.
+#[cfg(feature = "alloc")]
+pub fn serialize_upper<S, T>(data: T, serializer: S) -> Result<S::Ok, S::Error>
+where
+ S: Serializer,
+ T: ToHex,
+{
+ let s = data.encode_hex_upper::<String>();
+ serializer.serialize_str(&s)
+}
+
+/// Serializes `data` as hex string using lowercase characters.
+///
+/// Lowercase characters are used (e.g. `f9b4ca`). The resulting string's length
+/// is always even, each byte in data is always encoded using two hex digits.
+/// Thus, the resulting string contains exactly twice as many bytes as the input
+/// data.
+#[cfg(feature = "alloc")]
+pub fn serialize<S, T>(data: T, serializer: S) -> Result<S::Ok, S::Error>
+where
+ S: Serializer,
+ T: ToHex,
+{
+ let s = data.encode_hex::<String>();
+ serializer.serialize_str(&s)
+}
+
+/// Deserializes a hex string into raw bytes.
+///
+/// Both, upper and lower case characters are valid in the input string and can
+/// even be mixed (e.g. `f9b4ca`, `F9B4CA` and `f9B4Ca` are all valid strings).
+pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
+where
+ D: Deserializer<'de>,
+ T: FromHex,
+ <T as FromHex>::Error: fmt::Display,
+{
+ struct HexStrVisitor<T>(PhantomData<T>);
+
+ impl<'de, T> Visitor<'de> for HexStrVisitor<T>
+ where
+ T: FromHex,
+ <T as FromHex>::Error: fmt::Display,
+ {
+ type Value = T;
+
+ fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "a hex encoded string")
+ }
+
+ fn visit_str<E>(self, data: &str) -> Result<Self::Value, E>
+ where
+ E: Error,
+ {
+ FromHex::from_hex(data).map_err(Error::custom)
+ }
+
+ fn visit_borrowed_str<E>(self, data: &'de str) -> Result<Self::Value, E>
+ where
+ E: Error,
+ {
+ FromHex::from_hex(data).map_err(Error::custom)
+ }
+ }
+
+ deserializer.deserialize_str(HexStrVisitor(PhantomData))
+}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 69/80] rust: hex: add SPDX license identifiers
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (62 preceding siblings ...)
2023-06-09 6:31 ` [PATCH 68/80] rust: hex: import crate Ariel Miculas
@ 2023-06-09 6:31 ` Ariel Miculas
2023-06-09 6:31 ` [PATCH 70/80] rust: Kbuild: enable `hex` Ariel Miculas
` (13 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:31 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/hex/error.rs | 2 ++
rust/hex/lib.rs | 2 ++
rust/hex/serde.rs | 2 ++
3 files changed, 6 insertions(+)
diff --git a/rust/hex/error.rs b/rust/hex/error.rs
index ff7a3b5c16bd..e553a046c9ed 100644
--- a/rust/hex/error.rs
+++ b/rust/hex/error.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
use core::fmt;
/// The error type for decoding a hex string into `Vec<u8>` or `[u8; N]`.
diff --git a/rust/hex/lib.rs b/rust/hex/lib.rs
index ec48961b9ffe..a23def3f80db 100644
--- a/rust/hex/lib.rs
+++ b/rust/hex/lib.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
// Copyright (c) 2013-2014 The Rust Project Developers.
// Copyright (c) 2015-2020 The rust-hex Developers.
//
diff --git a/rust/hex/serde.rs b/rust/hex/serde.rs
index 335a15132a27..40ab1edb2bc8 100644
--- a/rust/hex/serde.rs
+++ b/rust/hex/serde.rs
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
//! Hex encoding with `serde`.
#[cfg_attr(
all(feature = "alloc", feature = "serde"),
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 70/80] rust: Kbuild: enable `hex`
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (63 preceding siblings ...)
2023-06-09 6:31 ` [PATCH 69/80] rust: hex: add SPDX license identifiers Ariel Miculas
@ 2023-06-09 6:31 ` Ariel Miculas
2023-06-09 6:31 ` [PATCH 71/80] rust: hex: implement FromHex trait and hex::decode using a custom kernel_alloc feature Ariel Miculas
` (12 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:31 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
With all the new files in place from the hex crate, this patch
adds support for them in the build system.
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/Makefile | 21 ++++++++++++++++++---
scripts/Makefile.build | 2 +-
2 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/rust/Makefile b/rust/Makefile
index c9feb8e1afe1..ba7f911d6919 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -13,7 +13,7 @@ always-$(CONFIG_RUST) += libserde_derive.so libmacros.so
no-clean-files += libserde_derive.so libmacros.so
always-$(CONFIG_RUST) += bindings/bindings_generated.rs bindings/bindings_helpers_generated.rs
-obj-$(CONFIG_RUST) += alloc.o bindings.o serde.o serde_cbor.o kernel.o
+obj-$(CONFIG_RUST) += alloc.o bindings.o serde.o serde_cbor.o hex.o kernel.o
always-$(CONFIG_RUST) += exports_alloc_generated.h exports_bindings_generated.h \
exports_kernel_generated.h
@@ -137,6 +137,15 @@ serde_cbor-flags := \
--extern alloc \
--extern serde
+hex-skip_flags := \
+ --edition=2021 \
+ -Drust_2018_idioms \
+ -Dunreachable_pub
+
+hex-flags := \
+ --edition=2018 \
+ -Amissing_docs
+
quiet_cmd_rustdoc = RUSTDOC $(if $(rustdoc_host),H, ) $<
cmd_rustdoc = \
OBJTREE=$(abspath $(objtree)) \
@@ -589,12 +598,18 @@ $(obj)/serde_cbor.o: private rustc_target_flags = $(serde_cbor-flags)
$(obj)/serde_cbor.o: $(src)/serde_cbor/lib.rs $(obj)/compiler_builtins.o FORCE
$(call if_changed_dep,rustc_library)
+$(obj)/hex.o: private skip_clippy = 1
+$(obj)/hex.o: private skip_flags = $(hex-skip_flags)
+$(obj)/hex.o: private rustc_target_flags = $(hex-flags)
+$(obj)/hex.o: $(src)/hex/lib.rs $(obj)/compiler_builtins.o FORCE
+ $(call if_changed_dep,rustc_library)
+
$(obj)/kernel.o: private rustc_target_flags = --extern alloc \
--extern build_error --extern macros --extern bindings --extern uapi \
- --extern serde --extern serde_derive --extern serde_cbor
+ --extern serde --extern serde_derive --extern serde_cbor --extern hex
$(obj)/kernel.o: $(src)/kernel/lib.rs $(obj)/alloc.o $(obj)/build_error.o \
$(obj)/libmacros.so $(obj)/bindings.o $(obj)/uapi.o \
- $(obj)/serde.o $(obj)/libserde_derive.so $(obj)/serde_cbor.o FORCE
+ $(obj)/serde.o $(obj)/libserde_derive.so $(obj)/serde_cbor.o $(obj)/hex.o FORCE
$(call if_changed_dep,rustc_library)
endif # CONFIG_RUST
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 758b327fd54d..e88cfd953833 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -284,7 +284,7 @@ rust_common_cmd = \
-Zallow-features=$(rust_allowed_features) \
-Zcrate-attr=no_std \
-Zcrate-attr='feature($(rust_allowed_features))' \
- --extern alloc --extern kernel --extern serde --extern serde_derive --extern serde_cbor \
+ --extern alloc --extern kernel --extern serde --extern serde_derive --extern serde_cbor --extern hex \
--crate-type rlib -L $(objtree)/rust/ \
--crate-name $(basename $(notdir $@)) \
--emit=dep-info=$(depfile)
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 71/80] rust: hex: implement FromHex trait and hex::decode using a custom kernel_alloc feature
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (64 preceding siblings ...)
2023-06-09 6:31 ` [PATCH 70/80] rust: Kbuild: enable `hex` Ariel Miculas
@ 2023-06-09 6:31 ` Ariel Miculas
2023-06-09 6:31 ` [PATCH 72/80] rust: hex: add encode_hex_iter and encode_hex_upper_iter methods Ariel Miculas
` (11 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:31 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
This commit also adds a process_results function for Vec<Result<T,E>>
which transforms it into a Result<Vec<T,E>>, collecting all the values
inside the Result elements into a new vector which directly stores the
elements.
For this to work, the TryReserveError was added to the FromHexError
enum.
This is probably not the ideal design, but since collect cannot be used
directly, it's a good substitute.
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/Makefile | 4 +++-
rust/alloc/vec/mod.rs | 21 +++++++++++++++++++++
rust/hex/error.rs | 17 +++++++++++++++++
rust/hex/lib.rs | 19 +++++++++++++++----
4 files changed, 56 insertions(+), 5 deletions(-)
diff --git a/rust/Makefile b/rust/Makefile
index ba7f911d6919..9b3488764d68 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -144,7 +144,9 @@ hex-skip_flags := \
hex-flags := \
--edition=2018 \
- -Amissing_docs
+ -Amissing_docs \
+ --cfg 'feature="kernel_alloc"' \
+ --extern alloc
quiet_cmd_rustdoc = RUSTDOC $(if $(rustdoc_host),H, ) $<
cmd_rustdoc = \
diff --git a/rust/alloc/vec/mod.rs b/rust/alloc/vec/mod.rs
index b755cf0b936c..2761f8bf5a9e 100644
--- a/rust/alloc/vec/mod.rs
+++ b/rust/alloc/vec/mod.rs
@@ -661,6 +661,27 @@ pub fn from_iter_fallible(iter: impl Iterator<Item=T>) -> Result<Vec<T>, TryRese
}
}
+impl<T,E: core::convert::From<TryReserveError>> Vec<Result<T,E>> {
+ /// Iterates through the vector, consuming self, unwrapping the individual values in each
+ /// Result and creating a new vector with them. If there's any error value, it returns it
+ /// immediately
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn process_results(self) -> Result<Vec<T>,E> {
+ let mut vec = Vec::new();
+ for value in self {
+ match value {
+ Ok(v) => {
+ vec.try_push(v)?;
+ },
+ Err(e) => {
+ return Err(e);
+ }
+ }
+ }
+ Ok(vec)
+ }
+}
+
impl<T, A: Allocator> Vec<T, A> {
/// Constructs a new, empty `Vec<T, A>`.
///
diff --git a/rust/hex/error.rs b/rust/hex/error.rs
index e553a046c9ed..c0dc2f558c4d 100644
--- a/rust/hex/error.rs
+++ b/rust/hex/error.rs
@@ -1,5 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
+#[cfg(feature = "kernel_alloc")]
+use alloc::collections::TryReserveError;
use core::fmt;
/// The error type for decoding a hex string into `Vec<u8>` or `[u8; N]`.
@@ -17,6 +19,10 @@ pub enum FromHexError {
/// array, the hex string's length * 2 has to match the container's
/// length.
InvalidStringLength,
+
+ #[cfg(feature = "kernel_alloc")]
+ /// If an allocation fails, useful in the kernel context
+ TryReserveError,
}
#[cfg(feature = "std")]
@@ -30,10 +36,21 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
}
FromHexError::OddLength => write!(f, "Odd number of digits"),
FromHexError::InvalidStringLength => write!(f, "Invalid string length"),
+ #[cfg(feature = "kernel_alloc")]
+ FromHexError::TryReserveError => write!(f, "TryReserveError"),
}
}
}
+#[cfg(feature = "kernel_alloc")]
+#[allow(unused_qualifications)]
+impl core::convert::From<TryReserveError> for FromHexError {
+ #[allow(deprecated)]
+ fn from(_source: TryReserveError) -> Self {
+ FromHexError::TryReserveError
+ }
+}
+
#[cfg(test)]
// this feature flag is here to suppress unused
// warnings of `super::*` and `pretty_assertions::assert_eq`
diff --git a/rust/hex/lib.rs b/rust/hex/lib.rs
index a23def3f80db..5cd90d622d16 100644
--- a/rust/hex/lib.rs
+++ b/rust/hex/lib.rs
@@ -41,6 +41,8 @@
#[cfg(feature = "alloc")]
extern crate alloc;
+#[cfg(feature = "kernel_alloc")]
+use alloc::vec::Vec;
#[cfg(feature = "alloc")]
use alloc::{string::String, vec::Vec};
@@ -186,7 +188,7 @@ fn val(c: u8, idx: usize) -> Result<u8, FromHexError> {
}
}
-#[cfg(feature = "alloc")]
+#[cfg(any(feature = "alloc", feature = "kernel_alloc"))]
impl FromHex for Vec<u8> {
type Error = FromHexError;
@@ -196,10 +198,19 @@ fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
return Err(FromHexError::OddLength);
}
- hex.chunks(2)
+ #[cfg(not(feature = "kernel_alloc"))]
+ return hex
+ .chunks(2)
.enumerate()
.map(|(i, pair)| Ok(val(pair[0], 2 * i)? << 4 | val(pair[1], 2 * i + 1)?))
- .collect()
+ .collect();
+ #[cfg(feature = "kernel_alloc")]
+ return Vec::from_iter_fallible(
+ hex.chunks(2)
+ .enumerate()
+ .map(|(i, pair)| Ok(val(pair[0], 2 * i)? << 4 | val(pair[1], 2 * i + 1)?)),
+ )?
+ .process_results();
}
}
@@ -294,7 +305,7 @@ pub fn encode_upper<T: AsRef<[u8]>>(data: T) -> String {
/// assert_eq!(hex::decode("123"), Err(hex::FromHexError::OddLength));
/// assert!(hex::decode("foo").is_err());
/// ```
-#[cfg(feature = "alloc")]
+#[cfg(any(feature = "alloc", feature = "kernel_alloc"))]
pub fn decode<T: AsRef<[u8]>>(data: T) -> Result<Vec<u8>, FromHexError> {
FromHex::from_hex(data)
}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 72/80] rust: hex: add encode_hex_iter and encode_hex_upper_iter methods
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (65 preceding siblings ...)
2023-06-09 6:31 ` [PATCH 71/80] rust: hex: implement FromHex trait and hex::decode using a custom kernel_alloc feature Ariel Miculas
@ 2023-06-09 6:31 ` Ariel Miculas
2023-06-09 6:31 ` [PATCH 73/80] rust: puzzlefs: add HexError to WireFormatError and implement the From conversion Ariel Miculas
` (10 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:31 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Add encode_hex_iter and encode_hex_upper_iter which return an iterator,
so it can be used in conjunction with the Vec::from_iter_fallible
function to (try to) create a hex string.
Replace BytesToHexChars with BytesToHexByteSequence because String is
not available. We need to collect the values in a Vec<u8>, append a NUL
character and the create a CStr on top of it.
Also remove the ToHex trait because it uses the FromIterator trait which
cannot be implemented in the kernel due to the prohibition of infallible
allocations.
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/hex/lib.rs | 60 ++++++++++++-------------------------------------
1 file changed, 14 insertions(+), 46 deletions(-)
diff --git a/rust/hex/lib.rs b/rust/hex/lib.rs
index 5cd90d622d16..18f5c240c46b 100644
--- a/rust/hex/lib.rs
+++ b/rust/hex/lib.rs
@@ -59,43 +59,18 @@
#[cfg(all(feature = "alloc", feature = "serde"))]
pub use crate::serde::{serialize, serialize_upper};
-/// Encoding values as hex string.
-///
-/// This trait is implemented for all `T` which implement `AsRef<[u8]>`. This
-/// includes `String`, `str`, `Vec<u8>` and `[u8]`.
-///
-/// # Example
-///
-/// ```
-/// use hex::ToHex;
-///
-/// println!("{}", "Hello world!".encode_hex::<String>());
-/// # assert_eq!("Hello world!".encode_hex::<String>(), "48656c6c6f20776f726c6421".to_string());
-/// ```
-///
-/// *Note*: instead of using this trait, you might want to use [`encode()`].
-pub trait ToHex {
- /// Encode the hex strict representing `self` into the result. Lower case
- /// letters are used (e.g. `f9b4ca`)
- fn encode_hex<T: iter::FromIterator<char>>(&self) -> T;
-
- /// Encode the hex strict representing `self` into the result. Upper case
- /// letters are used (e.g. `F9B4CA`)
- fn encode_hex_upper<T: iter::FromIterator<char>>(&self) -> T;
-}
-
const HEX_CHARS_LOWER: &[u8; 16] = b"0123456789abcdef";
const HEX_CHARS_UPPER: &[u8; 16] = b"0123456789ABCDEF";
-struct BytesToHexChars<'a> {
+struct BytesToHexByteSequence<'a> {
inner: ::core::slice::Iter<'a, u8>,
table: &'static [u8; 16],
- next: Option<char>,
+ next: Option<u8>,
}
-impl<'a> BytesToHexChars<'a> {
- fn new(inner: &'a [u8], table: &'static [u8; 16]) -> BytesToHexChars<'a> {
- BytesToHexChars {
+impl<'a> BytesToHexByteSequence<'a> {
+ fn new(inner: &'a [u8], table: &'static [u8; 16]) -> BytesToHexByteSequence<'a> {
+ BytesToHexByteSequence {
inner: inner.iter(),
table,
next: None,
@@ -103,15 +78,15 @@ impl<'a> BytesToHexChars<'a> {
}
}
-impl<'a> Iterator for BytesToHexChars<'a> {
- type Item = char;
+impl<'a> Iterator for BytesToHexByteSequence<'a> {
+ type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
match self.next.take() {
Some(current) => Some(current),
None => self.inner.next().map(|byte| {
- let current = self.table[(byte >> 4) as usize] as char;
- self.next = Some(self.table[(byte & 0x0F) as usize] as char);
+ let current = self.table[(byte >> 4) as usize];
+ self.next = Some(self.table[(byte & 0x0F) as usize]);
current
}),
}
@@ -123,7 +98,7 @@ fn size_hint(&self) -> (usize, Option<usize>) {
}
}
-impl<'a> iter::ExactSizeIterator for BytesToHexChars<'a> {
+impl<'a> iter::ExactSizeIterator for BytesToHexByteSequence<'a> {
fn len(&self) -> usize {
let mut length = self.inner.len() * 2;
if self.next.is_some() {
@@ -133,19 +108,12 @@ fn len(&self) -> usize {
}
}
-#[inline]
-fn encode_to_iter<T: iter::FromIterator<char>>(table: &'static [u8; 16], source: &[u8]) -> T {
- BytesToHexChars::new(source, table).collect()
+pub fn encode_hex_iter<'a>(source: &'a [u8]) -> impl iter::Iterator<Item = u8> + 'a {
+ BytesToHexByteSequence::new(source, HEX_CHARS_LOWER).into_iter()
}
-impl<T: AsRef<[u8]>> ToHex for T {
- fn encode_hex<U: iter::FromIterator<char>>(&self) -> U {
- encode_to_iter(HEX_CHARS_LOWER, self.as_ref())
- }
-
- fn encode_hex_upper<U: iter::FromIterator<char>>(&self) -> U {
- encode_to_iter(HEX_CHARS_UPPER, self.as_ref())
- }
+pub fn encode_hex_upper_iter<'a>(source: &'a [u8]) -> impl iter::Iterator<Item = u8> + 'a {
+ BytesToHexByteSequence::new(source, HEX_CHARS_UPPER).into_iter()
}
/// Types that can be decoded from a hex string.
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 73/80] rust: puzzlefs: add HexError to WireFormatError and implement the From conversion
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (66 preceding siblings ...)
2023-06-09 6:31 ` [PATCH 72/80] rust: hex: add encode_hex_iter and encode_hex_upper_iter methods Ariel Miculas
@ 2023-06-09 6:31 ` Ariel Miculas
2023-06-09 6:31 ` [PATCH 74/80] rust: puzzlefs: display the error value for WireFormatError::KernelError Ariel Miculas
` (9 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:31 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
samples/rust/puzzle/error.rs | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/samples/rust/puzzle/error.rs b/samples/rust/puzzle/error.rs
index e803c2177b72..8554756be8d3 100644
--- a/samples/rust/puzzle/error.rs
+++ b/samples/rust/puzzle/error.rs
@@ -12,6 +12,7 @@ pub(crate) enum WireFormatError {
CBORError(serde_cbor::Error),
KernelError(kernel::error::Error),
TryReserveError(TryReserveError),
+ HexError(hex::FromHexError),
}
impl WireFormatError {
@@ -23,6 +24,7 @@ pub(crate) fn to_errno(&self) -> c_int {
WireFormatError::CBORError(..) => kernel::error::Error::to_errno(EINVAL),
WireFormatError::KernelError(e) => kernel::error::Error::to_errno(*e),
WireFormatError::TryReserveError(_) => kernel::error::Error::to_errno(EINVAL),
+ WireFormatError::HexError(_) => kernel::error::Error::to_errno(EINVAL),
}
}
@@ -40,6 +42,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
WireFormatError::CBORError(_) => f.write_str("CBOR error"),
WireFormatError::KernelError(_) => f.write_str("Kernel error"),
WireFormatError::TryReserveError(_) => f.write_str("TryReserveError"),
+ WireFormatError::HexError(_) => f.write_str("HexError"),
}
}
}
@@ -70,3 +73,11 @@ fn from(source: TryReserveError) -> Self {
WireFormatError::TryReserveError(source)
}
}
+
+#[allow(unused_qualifications)]
+impl core::convert::From<hex::FromHexError> for WireFormatError {
+ #[allow(deprecated)]
+ fn from(source: hex::FromHexError) -> Self {
+ WireFormatError::HexError(source)
+ }
+}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 74/80] rust: puzzlefs: display the error value for WireFormatError::KernelError
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (67 preceding siblings ...)
2023-06-09 6:31 ` [PATCH 73/80] rust: puzzlefs: add HexError to WireFormatError and implement the From conversion Ariel Miculas
@ 2023-06-09 6:31 ` Ariel Miculas
2023-06-09 6:31 ` [PATCH 75/80] samples: puzzlefs: add Rootfs and Digest structs to types.rs Ariel Miculas
` (8 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:31 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
samples/rust/puzzle/error.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/samples/rust/puzzle/error.rs b/samples/rust/puzzle/error.rs
index 8554756be8d3..8766d01ef932 100644
--- a/samples/rust/puzzle/error.rs
+++ b/samples/rust/puzzle/error.rs
@@ -40,7 +40,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
WireFormatError::SeekOtherError => f.write_str("cannot seek to other blob"),
WireFormatError::ValueMissing => f.write_str("no value present"),
WireFormatError::CBORError(_) => f.write_str("CBOR error"),
- WireFormatError::KernelError(_) => f.write_str("Kernel error"),
+ WireFormatError::KernelError(e) => write!(f, "Kernel error {:?}", e.to_errno()),
WireFormatError::TryReserveError(_) => f.write_str("TryReserveError"),
WireFormatError::HexError(_) => f.write_str("HexError"),
}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 75/80] samples: puzzlefs: add Rootfs and Digest structs to types.rs
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (68 preceding siblings ...)
2023-06-09 6:31 ` [PATCH 74/80] rust: puzzlefs: display the error value for WireFormatError::KernelError Ariel Miculas
@ 2023-06-09 6:31 ` Ariel Miculas
2023-06-09 6:31 ` [PATCH 76/80] samples: puzzlefs: implement the conversion from WireFormatError to kernel::error::Error Ariel Miculas
` (7 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:31 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
samples/rust/puzzle/types.rs | 75 ++++++++++++++++++++++++++++++++++++
1 file changed, 75 insertions(+)
diff --git a/samples/rust/puzzle/types.rs b/samples/rust/puzzle/types.rs
index c6b69b94fa6d..69d4f5dfa12c 100644
--- a/samples/rust/puzzle/types.rs
+++ b/samples/rust/puzzle/types.rs
@@ -8,7 +8,10 @@
mod cbor_helpers;
use crate::puzzle::error::Result;
pub(crate) use cbor_helpers::{cbor_get_array_size, cbor_size_of_list_header};
+use core::fmt;
+use hex::{encode_hex_iter, FromHexError};
use kernel::file;
+use kernel::str::CStr;
#[derive(Deserialize, Debug)]
pub(crate) struct InodeAdditional {
@@ -32,6 +35,8 @@ pub(crate) struct MetadataBlob {
pub(crate) inode_count: usize,
}
+pub(crate) const SHA256_BLOCK_SIZE: usize = 32;
+
fn read_one_from_slice<'a, T: Deserialize<'a>>(bytes: &'a [u8]) -> Result<T> {
// serde complains when we leave extra bytes on the wire, which we often want to do. as a
// hack, we create a streaming deserializer for the type we're about to read, and then only
@@ -41,6 +46,23 @@ fn read_one_from_slice<'a, T: Deserialize<'a>>(bytes: &'a [u8]) -> Result<T> {
v.ok_or(WireFormatError::ValueMissing)
}
+#[derive(Deserialize, Debug)]
+pub(crate) struct Rootfs {
+ pub(crate) metadatas: Vec<BlobRef>,
+ // TODO: deserialize fs_verity_data, for the moment BTreeMap is not supported
+ #[allow(dead_code)]
+ pub(crate) fs_verity_data: (),
+ #[allow(dead_code)]
+ pub(crate) manifest_version: u64,
+}
+
+impl Rootfs {
+ pub(crate) fn open(file: file::RegularFile) -> Result<Rootfs> {
+ let buffer = file.read_to_end()?;
+ read_one_from_slice(&buffer)
+ }
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum BlobRefKind {
Local,
@@ -176,6 +198,59 @@ pub(crate) fn find_inode(&mut self, ino: Ino) -> Result<Option<Inode>> {
}
}
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) struct Digest([u8; SHA256_BLOCK_SIZE]);
+
+impl Digest {
+ pub(crate) fn underlying(&self) -> [u8; SHA256_BLOCK_SIZE] {
+ let mut dest = [0_u8; SHA256_BLOCK_SIZE];
+ dest.copy_from_slice(&self.0);
+ dest
+ }
+}
+
+impl fmt::Display for Digest {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut hex_string =
+ Vec::from_iter_fallible(encode_hex_iter(&self.underlying())).map_err(|_| fmt::Error)?;
+ // append NUL character
+ hex_string.try_push(0).map_err(|_| fmt::Error)?;
+ let hex_string = CStr::from_bytes_with_nul(&hex_string).map_err(|_| fmt::Error)?;
+ write!(f, "{}", hex_string)
+ }
+}
+
+impl TryFrom<&CStr> for Digest {
+ type Error = FromHexError;
+ fn try_from(s: &CStr) -> kernel::error::Result<Self, Self::Error> {
+ let digest = hex::decode(s)?;
+ let digest: [u8; SHA256_BLOCK_SIZE] = digest
+ .try_into()
+ .map_err(|_| FromHexError::InvalidStringLength)?;
+ Ok(Digest(digest))
+ }
+}
+
+impl TryFrom<BlobRef> for Digest {
+ type Error = WireFormatError;
+ fn try_from(v: BlobRef) -> kernel::error::Result<Self, Self::Error> {
+ match v.kind {
+ BlobRefKind::Other { digest } => Ok(Digest(digest)),
+ BlobRefKind::Local => Err(WireFormatError::LocalRefError),
+ }
+ }
+}
+
+impl TryFrom<&BlobRef> for Digest {
+ type Error = WireFormatError;
+ fn try_from(v: &BlobRef) -> kernel::error::Result<Self, Self::Error> {
+ match v.kind {
+ BlobRefKind::Other { digest } => Ok(Digest(digest)),
+ BlobRefKind::Local => Err(WireFormatError::LocalRefError),
+ }
+ }
+}
+
#[derive(Deserialize, Debug)]
pub(crate) struct DirEnt {
pub(crate) ino: Ino,
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 76/80] samples: puzzlefs: implement the conversion from WireFormatError to kernel::error::Error
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (69 preceding siblings ...)
2023-06-09 6:31 ` [PATCH 75/80] samples: puzzlefs: add Rootfs and Digest structs to types.rs Ariel Miculas
@ 2023-06-09 6:31 ` Ariel Miculas
2023-06-09 6:31 ` [PATCH 77/80] rust: puzzlefs: read the puzzlefs image manifest instead of an individual metadata layer Ariel Miculas
` (6 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:31 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
rust/kernel/error.rs | 2 +-
samples/rust/puzzle/error.rs | 8 ++++++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index 6fff9a4f0672..8196a7462561 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -96,7 +96,7 @@ impl Error {
///
/// It is a bug to pass an out-of-range `errno`. `EINVAL` would
/// be returned in such a case.
- pub(crate) fn from_errno(errno: core::ffi::c_int) -> Error {
+ pub fn from_errno(errno: core::ffi::c_int) -> Error {
if errno < -(bindings::MAX_ERRNO as i32) || errno >= 0 {
// TODO: Make it a `WARN_ONCE` once available.
crate::pr_warn!(
diff --git a/samples/rust/puzzle/error.rs b/samples/rust/puzzle/error.rs
index 8766d01ef932..c9635a96f9e2 100644
--- a/samples/rust/puzzle/error.rs
+++ b/samples/rust/puzzle/error.rs
@@ -81,3 +81,11 @@ fn from(source: hex::FromHexError) -> Self {
WireFormatError::HexError(source)
}
}
+
+#[allow(unused_qualifications)]
+impl core::convert::From<WireFormatError> for kernel::error::Error {
+ #[allow(deprecated)]
+ fn from(source: WireFormatError) -> Self {
+ kernel::error::Error::from_errno(source.to_errno())
+ }
+}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 77/80] rust: puzzlefs: read the puzzlefs image manifest instead of an individual metadata layer
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (70 preceding siblings ...)
2023-06-09 6:31 ` [PATCH 76/80] samples: puzzlefs: implement the conversion from WireFormatError to kernel::error::Error Ariel Miculas
@ 2023-06-09 6:31 ` Ariel Miculas
2023-06-09 6:31 ` [PATCH 78/80] rust: puzzlefs: rename PuzzleFs to PuzzleFsModule to avoid confusion with the PuzzleFS struct Ariel Miculas
` (5 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:31 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
The puzzlefs image manifest is the file referenced by index.json and it
contains the array of metadata layers that describe the puzzlefs image.
This file represents the root of the puzzlefs filesystem since we can't
parse json files. When this filesystem is mounted, usermode will need to
read the tag from index.json, find the corresponding puzzlefs image
manifest and pass it to the kernel module.
Due to the lack of BTreeMap in the kernel, only image manifests without
fs verity information are supported for now.
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
samples/rust/puzzle.rs | 1 +
samples/rust/puzzle/inode.rs | 23 ++++++++++++----
samples/rust/puzzle/oci.rs | 51 ++++++++++++++++++++++++++++++++++++
samples/rust/puzzlefs.rs | 23 +++++++---------
4 files changed, 80 insertions(+), 18 deletions(-)
create mode 100644 samples/rust/puzzle/oci.rs
diff --git a/samples/rust/puzzle.rs b/samples/rust/puzzle.rs
index 1b989a2579c4..ae7c57efc92c 100644
--- a/samples/rust/puzzle.rs
+++ b/samples/rust/puzzle.rs
@@ -1,3 +1,4 @@
pub(crate) mod error;
pub(crate) mod inode;
+pub(crate) mod oci;
pub(crate) mod types;
diff --git a/samples/rust/puzzle/inode.rs b/samples/rust/puzzle/inode.rs
index 980aac9c4fd3..eaaa6d964db2 100644
--- a/samples/rust/puzzle/inode.rs
+++ b/samples/rust/puzzle/inode.rs
@@ -3,10 +3,15 @@
use crate::puzzle::error::Result;
use crate::puzzle::error::WireFormatError;
+use crate::puzzle::oci::Image;
use crate::puzzle::types as format;
+use crate::puzzle::types::Digest;
use crate::puzzle::types::{FileChunk, Ino, InodeAdditional, MetadataBlob};
use alloc::vec::Vec;
+use kernel::mount::Vfsmount;
use kernel::prelude::{ENOENT, ENOTDIR};
+use kernel::str::CStr;
+use kernel::sync::Arc;
#[derive(Debug)]
pub(crate) struct Inode {
@@ -17,17 +22,25 @@ pub(crate) struct Inode {
}
pub(crate) struct PuzzleFS {
+ pub(crate) oci: Image,
layers: Vec<format::MetadataBlob>,
}
impl PuzzleFS {
- pub(crate) fn new(md: MetadataBlob) -> Result<Self> {
- let mut v = Vec::new();
- v.try_push(md)?;
- Ok(PuzzleFS { layers: v })
+ pub(crate) fn open(vfsmount: Arc<Vfsmount>, rootfs_path: &CStr) -> Result<PuzzleFS> {
+ let oci = Image::open(vfsmount)?;
+ let rootfs = oci.open_rootfs_blob(rootfs_path)?;
+
+ let layers =
+ Vec::from_iter_fallible(rootfs.metadatas.iter().map(|md| -> Result<MetadataBlob> {
+ let digest = Digest::try_from(md)?;
+ oci.open_metadata_blob(&digest)
+ }))?
+ .process_results()?;
+
+ Ok(PuzzleFS { oci, layers })
}
- // Temporary helper function used until PuzzleFs is integrated
pub(crate) fn find_inode(&mut self, ino: u64) -> Result<Inode> {
for layer in self.layers.iter_mut() {
if let Some(inode) = layer.find_inode(ino)? {
diff --git a/samples/rust/puzzle/oci.rs b/samples/rust/puzzle/oci.rs
new file mode 100644
index 000000000000..becb2b868450
--- /dev/null
+++ b/samples/rust/puzzle/oci.rs
@@ -0,0 +1,51 @@
+use crate::puzzle::error::Result;
+use crate::puzzle::types::{Digest, MetadataBlob, Rootfs};
+use kernel::c_str;
+use kernel::file;
+use kernel::file::RegularFile;
+use kernel::mount::Vfsmount;
+use kernel::pr_info;
+use kernel::str::{CStr, CString};
+use kernel::sync::Arc;
+
+pub(crate) struct Image {
+ vfs_mount: Arc<Vfsmount>,
+}
+
+impl Image {
+ pub(crate) fn open(vfsmount: Arc<Vfsmount>) -> Result<Self> {
+ Ok(Image {
+ vfs_mount: vfsmount,
+ })
+ }
+
+ pub(crate) fn blob_path_relative(&self) -> &CStr {
+ c_str!("blobs/sha256")
+ }
+
+ fn open_raw_blob(&self, digest: &Digest) -> Result<RegularFile> {
+ let filename =
+ CString::try_from_fmt(format_args!("{}/{digest}", self.blob_path_relative()))?;
+ pr_info!("trying to open {:?}\n", &filename);
+
+ let file = RegularFile::from_path_in_root_mnt(
+ &self.vfs_mount,
+ &filename,
+ file::flags::O_RDONLY.try_into().unwrap(),
+ 0,
+ )?;
+
+ Ok(file)
+ }
+
+ pub(crate) fn open_metadata_blob(&self, digest: &Digest) -> Result<MetadataBlob> {
+ let f = self.open_raw_blob(digest)?;
+ MetadataBlob::new(f)
+ }
+
+ pub(crate) fn open_rootfs_blob(&self, path: &CStr) -> Result<Rootfs> {
+ let digest = Digest::try_from(path)?;
+ let rootfs = Rootfs::open(self.open_raw_blob(&digest)?)?;
+ Ok(rootfs)
+ }
+}
diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
index c160b5e5f911..d74e034538eb 100644
--- a/samples/rust/puzzlefs.rs
+++ b/samples/rust/puzzlefs.rs
@@ -13,7 +13,6 @@
mod puzzle;
use puzzle::inode::{Inode, InodeMode, PuzzleFS};
-use puzzle::types::MetadataBlob;
use kernel::fs::{DEntry, INodeParams, NeedsRoot, NewSuperBlock, RootDEntry};
@@ -67,7 +66,7 @@ fn puzzlefs_populate_dir(
return Err(E2BIG);
}
- let inode = Arc::try_new(pfs.find_inode(ino).map_err(|_| EINVAL)?)?;
+ let inode = Arc::try_new(pfs.find_inode(ino)?)?;
match &inode.mode {
InodeMode::File { chunks: _ } => {
let params = INodeParams {
@@ -152,19 +151,17 @@ fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBl
},
)?;
- let file = file::RegularFile::from_path_in_root_mnt(
- &arc_vfs_mount,
- c_str!("997eed138af30d187e87d682dd2ae9f240fae78f668907a0519460b397c82467"),
- file::flags::O_RDONLY.try_into().unwrap(),
- 0,
- )?;
+ let puzzlefs = PuzzleFS::open(
+ arc_vfs_mount,
+ c_str!("2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b"),
+ );
- // TODO: figure out how to go from WireFormatError to kernel::error::Error
- let metadata = MetadataBlob::new(file).map_err(|_| EINVAL)?;
- pr_info!("number of inodes {:?}\n", metadata.inode_count);
+ if let Err(ref e) = puzzlefs {
+ pr_info!("error opening puzzlefs {e}\n");
+ }
- let mut puzzlefs = PuzzleFS::new(metadata).map_err(|_| EINVAL)?;
- let root_inode = Arc::try_new(puzzlefs.find_inode(1).map_err(|_| EINVAL)?)?;
+ let mut puzzlefs = puzzlefs?;
+ let root_inode = Arc::try_new(puzzlefs.find_inode(1)?)?;
let root = try_new_populated_root_puzzlefs_dentry(&sb, &mut puzzlefs, root_inode)?;
let sb = sb.init_root(root)?;
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 78/80] rust: puzzlefs: rename PuzzleFs to PuzzleFsModule to avoid confusion with the PuzzleFS struct
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (71 preceding siblings ...)
2023-06-09 6:31 ` [PATCH 77/80] rust: puzzlefs: read the puzzlefs image manifest instead of an individual metadata layer Ariel Miculas
@ 2023-06-09 6:31 ` Ariel Miculas
2023-06-09 6:31 ` [PATCH 79/80] rust: puzzlefs: add support for reading files Ariel Miculas
` (4 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:31 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
samples/rust/puzzlefs.rs | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
index d74e034538eb..f04a2e5bcb04 100644
--- a/samples/rust/puzzlefs.rs
+++ b/samples/rust/puzzlefs.rs
@@ -17,13 +17,13 @@
use kernel::fs::{DEntry, INodeParams, NeedsRoot, NewSuperBlock, RootDEntry};
module_fs! {
- type: PuzzleFs,
+ type: PuzzleFsModule,
name: "puzzlefs",
author: "Ariel Miculas",
license: "GPL",
}
-struct PuzzleFs;
+struct PuzzleFsModule;
#[derive(Debug)]
struct PuzzlefsInfo {
@@ -31,7 +31,7 @@ struct PuzzlefsInfo {
}
#[vtable]
-impl fs::Context<Self> for PuzzleFs {
+impl fs::Context<Self> for PuzzleFsModule {
type Data = ();
kernel::define_fs_params! {(),
@@ -55,9 +55,9 @@ fn try_new() -> Result {
}
fn puzzlefs_populate_dir(
- sb: &NewSuperBlock<'_, PuzzleFs, NeedsRoot>,
+ sb: &NewSuperBlock<'_, PuzzleFsModule, NeedsRoot>,
pfs: &mut PuzzleFS,
- parent: &DEntry<PuzzleFs>,
+ parent: &DEntry<PuzzleFsModule>,
ino: u64,
name: &CStr,
recursion: usize,
@@ -110,10 +110,10 @@ fn puzzlefs_populate_dir(
/// Creates a new root dentry populated with the given entries.
fn try_new_populated_root_puzzlefs_dentry(
- sb: &NewSuperBlock<'_, PuzzleFs, NeedsRoot>,
+ sb: &NewSuperBlock<'_, PuzzleFsModule, NeedsRoot>,
pfs: &mut PuzzleFS,
- root_value: <PuzzleFs as fs::Type>::INodeData,
-) -> Result<RootDEntry<PuzzleFs>> {
+ root_value: <PuzzleFsModule as fs::Type>::INodeData,
+) -> Result<RootDEntry<PuzzleFsModule>> {
let root_inode = sb.sb.try_new_dcache_dir_inode(INodeParams {
mode: 0o755,
ino: root_value.inode.ino,
@@ -125,7 +125,7 @@ fn try_new_populated_root_puzzlefs_dentry(
Ok(root)
}
-impl fs::Type for PuzzleFs {
+impl fs::Type for PuzzleFsModule {
type Context = Self;
// this is Arc so it can be cloned in puzzlefs_populate_dir
type INodeData = Arc<Inode>;
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 79/80] rust: puzzlefs: add support for reading files
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (72 preceding siblings ...)
2023-06-09 6:31 ` [PATCH 78/80] rust: puzzlefs: rename PuzzleFs to PuzzleFsModule to avoid confusion with the PuzzleFS struct Ariel Miculas
@ 2023-06-09 6:31 ` Ariel Miculas
2023-06-09 6:31 ` [PATCH 80/80] rust: puzzlefs: add oci_root_dir and image_manifest filesystem parameters Ariel Miculas
` (3 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:31 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
samples/rust/puzzle/inode.rs | 68 ++++++++++++++++++++++++++++++++----
samples/rust/puzzle/oci.rs | 32 +++++++++++++----
samples/rust/puzzle/types.rs | 10 +++---
samples/rust/puzzlefs.rs | 59 ++++++++++++++-----------------
4 files changed, 118 insertions(+), 51 deletions(-)
diff --git a/samples/rust/puzzle/inode.rs b/samples/rust/puzzle/inode.rs
index eaaa6d964db2..678e9fe1af03 100644
--- a/samples/rust/puzzle/inode.rs
+++ b/samples/rust/puzzle/inode.rs
@@ -8,10 +8,10 @@
use crate::puzzle::types::Digest;
use crate::puzzle::types::{FileChunk, Ino, InodeAdditional, MetadataBlob};
use alloc::vec::Vec;
+use core::cmp::min;
use kernel::mount::Vfsmount;
use kernel::prelude::{ENOENT, ENOTDIR};
use kernel::str::CStr;
-use kernel::sync::Arc;
#[derive(Debug)]
pub(crate) struct Inode {
@@ -21,14 +21,16 @@ pub(crate) struct Inode {
pub(crate) additional: Option<InodeAdditional>,
}
+#[derive(Debug)]
pub(crate) struct PuzzleFS {
pub(crate) oci: Image,
layers: Vec<format::MetadataBlob>,
}
impl PuzzleFS {
- pub(crate) fn open(vfsmount: Arc<Vfsmount>, rootfs_path: &CStr) -> Result<PuzzleFS> {
- let oci = Image::open(vfsmount)?;
+ pub(crate) fn open(oci_root_dir: &CStr, rootfs_path: &CStr) -> Result<PuzzleFS> {
+ let vfs_mount = Vfsmount::new_private_mount(oci_root_dir)?;
+ let oci = Image::open(vfs_mount)?;
let rootfs = oci.open_rootfs_blob(rootfs_path)?;
let layers =
@@ -41,8 +43,8 @@ pub(crate) fn open(vfsmount: Arc<Vfsmount>, rootfs_path: &CStr) -> Result<Puzzle
Ok(PuzzleFS { oci, layers })
}
- pub(crate) fn find_inode(&mut self, ino: u64) -> Result<Inode> {
- for layer in self.layers.iter_mut() {
+ pub(crate) fn find_inode(&self, ino: u64) -> Result<Inode> {
+ for layer in self.layers.iter() {
if let Some(inode) = layer.find_inode(ino)? {
return Inode::new(layer, inode);
}
@@ -52,7 +54,7 @@ pub(crate) fn find_inode(&mut self, ino: u64) -> Result<Inode> {
}
impl Inode {
- fn new(layer: &mut MetadataBlob, inode: format::Inode) -> Result<Inode> {
+ fn new(layer: &MetadataBlob, inode: format::Inode) -> Result<Inode> {
let mode = match inode.mode {
format::InodeMode::Reg { offset } => {
let chunks = layer.read_file_chunks(offset)?;
@@ -64,7 +66,7 @@ fn new(layer: &mut MetadataBlob, inode: format::Inode) -> Result<Inode> {
layer
.read_dir_list(offset)?
.entries
- .iter_mut()
+ .iter()
.map(|de| (de.name.try_clone().unwrap(), de.ino)),
)?;
// Unstable sort is used because it avoids memory allocation
@@ -94,3 +96,55 @@ pub(crate) enum InodeMode {
Dir { entries: Vec<(Vec<u8>, Ino)> },
Other,
}
+
+pub(crate) fn file_read(
+ oci: &Image,
+ inode: &Inode,
+ offset: usize,
+ data: &mut [u8],
+) -> Result<usize> {
+ let chunks = match &inode.mode {
+ InodeMode::File { chunks } => chunks,
+ _ => return Err(WireFormatError::from_errno(ENOTDIR)),
+ };
+
+ // TODO: fix all this casting...
+ let end = offset + data.len();
+
+ let mut file_offset = 0;
+ let mut buf_offset = 0;
+ for chunk in chunks {
+ // have we read enough?
+ if file_offset > end {
+ break;
+ }
+
+ // should we skip this chunk?
+ if file_offset + (chunk.len as usize) < offset {
+ file_offset += chunk.len as usize;
+ continue;
+ }
+
+ let addl_offset = if offset > file_offset {
+ offset - file_offset
+ } else {
+ 0
+ };
+
+ // ok, need to read this chunk; how much?
+ let left_in_buf = data.len() - buf_offset;
+ let to_read = min(left_in_buf, chunk.len as usize - addl_offset);
+
+ let start = buf_offset;
+ let finish = start + to_read;
+ file_offset += addl_offset;
+
+ // how many did we actually read?
+ let n = oci.fill_from_chunk(chunk.blob, addl_offset as u64, &mut data[start..finish])?;
+ file_offset += n;
+ buf_offset += n;
+ }
+
+ // discard any extra if we hit EOF
+ Ok(buf_offset)
+}
diff --git a/samples/rust/puzzle/oci.rs b/samples/rust/puzzle/oci.rs
index becb2b868450..935beb168079 100644
--- a/samples/rust/puzzle/oci.rs
+++ b/samples/rust/puzzle/oci.rs
@@ -1,19 +1,21 @@
-use crate::puzzle::error::Result;
+use crate::puzzle::error::{Result, WireFormatError};
+use crate::puzzle::types as format;
use crate::puzzle::types::{Digest, MetadataBlob, Rootfs};
use kernel::c_str;
use kernel::file;
use kernel::file::RegularFile;
use kernel::mount::Vfsmount;
-use kernel::pr_info;
+use kernel::pr_debug;
+use kernel::prelude::ENOTSUPP;
use kernel::str::{CStr, CString};
-use kernel::sync::Arc;
+#[derive(Debug)]
pub(crate) struct Image {
- vfs_mount: Arc<Vfsmount>,
+ pub(crate) vfs_mount: Vfsmount,
}
impl Image {
- pub(crate) fn open(vfsmount: Arc<Vfsmount>) -> Result<Self> {
+ pub(crate) fn open(vfsmount: Vfsmount) -> Result<Self> {
Ok(Image {
vfs_mount: vfsmount,
})
@@ -26,7 +28,7 @@ pub(crate) fn blob_path_relative(&self) -> &CStr {
fn open_raw_blob(&self, digest: &Digest) -> Result<RegularFile> {
let filename =
CString::try_from_fmt(format_args!("{}/{digest}", self.blob_path_relative()))?;
- pr_info!("trying to open {:?}\n", &filename);
+ pr_debug!("trying to open {:?}\n", &filename);
let file = RegularFile::from_path_in_root_mnt(
&self.vfs_mount,
@@ -48,4 +50,22 @@ pub(crate) fn open_rootfs_blob(&self, path: &CStr) -> Result<Rootfs> {
let rootfs = Rootfs::open(self.open_raw_blob(&digest)?)?;
Ok(rootfs)
}
+
+ pub(crate) fn fill_from_chunk(
+ &self,
+ chunk: format::BlobRef,
+ addl_offset: u64,
+ buf: &mut [u8],
+ ) -> Result<usize> {
+ let digest = &<Digest>::try_from(chunk)?;
+
+ let blob = if chunk.compressed {
+ return Err(WireFormatError::KernelError(ENOTSUPP));
+ } else {
+ self.open_raw_blob(digest)?
+ };
+
+ let n = blob.read_with_offset(buf, chunk.offset + addl_offset)?;
+ Ok(n)
+ }
}
diff --git a/samples/rust/puzzle/types.rs b/samples/rust/puzzle/types.rs
index 69d4f5dfa12c..353323f73ab1 100644
--- a/samples/rust/puzzle/types.rs
+++ b/samples/rust/puzzle/types.rs
@@ -148,28 +148,28 @@ pub(crate) fn new(mut f: file::RegularFile) -> Result<MetadataBlob> {
})
}
- pub(crate) fn seek_ref(&mut self, r: &BlobRef) -> Result<u64> {
+ pub(crate) fn seek_ref(&self, r: &BlobRef) -> Result<u64> {
match r.kind {
BlobRefKind::Other { .. } => Err(WireFormatError::SeekOtherError),
BlobRefKind::Local => Ok(r.offset),
}
}
- pub(crate) fn read_file_chunks(&mut self, offset: u64) -> Result<Vec<FileChunk>> {
+ pub(crate) fn read_file_chunks(&self, offset: u64) -> Result<Vec<FileChunk>> {
read_one_from_slice::<FileChunkList>(&self.mmapped_region[offset as usize..])
.map(|cl| cl.chunks)
}
- pub(crate) fn read_dir_list(&mut self, offset: u64) -> Result<DirList> {
+ pub(crate) fn read_dir_list(&self, offset: u64) -> Result<DirList> {
read_one_from_slice(&self.mmapped_region[offset as usize..])
}
- pub(crate) fn read_inode_additional(&mut self, r: &BlobRef) -> Result<InodeAdditional> {
+ pub(crate) fn read_inode_additional(&self, r: &BlobRef) -> Result<InodeAdditional> {
let offset = self.seek_ref(r)? as usize;
read_one_from_slice(&self.mmapped_region[offset..])
}
- pub(crate) fn find_inode(&mut self, ino: Ino) -> Result<Option<Inode>> {
+ pub(crate) fn find_inode(&self, ino: Ino) -> Result<Option<Inode>> {
let mut left = 0;
let mut right = self.inode_count;
diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
index f04a2e5bcb04..897a996a6ab3 100644
--- a/samples/rust/puzzlefs.rs
+++ b/samples/rust/puzzlefs.rs
@@ -3,7 +3,6 @@
//! Rust file system sample.
use kernel::module_fs;
-use kernel::mount::Vfsmount;
use kernel::prelude::*;
use kernel::{
c_str, file, fs,
@@ -12,7 +11,7 @@
};
mod puzzle;
-use puzzle::inode::{Inode, InodeMode, PuzzleFS};
+use puzzle::inode::{file_read, Inode, InodeMode, PuzzleFS};
use kernel::fs::{DEntry, INodeParams, NeedsRoot, NewSuperBlock, RootDEntry};
@@ -27,7 +26,7 @@
#[derive(Debug)]
struct PuzzlefsInfo {
- vfs_mount: Arc<Vfsmount>,
+ puzzlefs: Arc<PuzzleFS>,
}
#[vtable]
@@ -56,7 +55,7 @@ fn try_new() -> Result {
fn puzzlefs_populate_dir(
sb: &NewSuperBlock<'_, PuzzleFsModule, NeedsRoot>,
- pfs: &mut PuzzleFS,
+ pfs: &PuzzleFS,
parent: &DEntry<PuzzleFsModule>,
ino: u64,
name: &CStr,
@@ -111,7 +110,7 @@ fn puzzlefs_populate_dir(
/// Creates a new root dentry populated with the given entries.
fn try_new_populated_root_puzzlefs_dentry(
sb: &NewSuperBlock<'_, PuzzleFsModule, NeedsRoot>,
- pfs: &mut PuzzleFS,
+ pfs: &PuzzleFS,
root_value: <PuzzleFsModule as fs::Type>::INodeData,
) -> Result<RootDEntry<PuzzleFsModule>> {
let root_inode = sb.sb.try_new_dcache_dir_inode(INodeParams {
@@ -136,14 +135,20 @@ impl fs::Type for PuzzleFsModule {
const DCACHE_BASED: bool = true;
fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBlock<Self>> {
- let vfs_mount = Vfsmount::new_private_mount(c_str!("/home/puzzlefs_oci"))?;
- pr_info!("vfs_mount {:?}\n", vfs_mount);
+ let puzzlefs = PuzzleFS::open(
+ c_str!("/home/puzzlefs_oci"),
+ c_str!("2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b"),
+ );
- let arc_vfs_mount = Arc::try_new(vfs_mount)?;
+ if let Err(ref e) = puzzlefs {
+ pr_info!("error opening puzzlefs {e}\n");
+ }
+
+ let puzzlefs = Arc::try_new(puzzlefs?)?;
let sb = sb.init(
Box::try_new(PuzzlefsInfo {
- vfs_mount: arc_vfs_mount.clone(),
+ puzzlefs: puzzlefs.clone(),
})?,
&fs::SuperParams {
magic: 0x72757374,
@@ -151,19 +156,9 @@ fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBl
},
)?;
- let puzzlefs = PuzzleFS::open(
- arc_vfs_mount,
- c_str!("2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b"),
- );
-
- if let Err(ref e) = puzzlefs {
- pr_info!("error opening puzzlefs {e}\n");
- }
-
- let mut puzzlefs = puzzlefs?;
let root_inode = Arc::try_new(puzzlefs.find_inode(1)?)?;
- let root = try_new_populated_root_puzzlefs_dentry(&sb, &mut puzzlefs, root_inode)?;
+ let root = try_new_populated_root_puzzlefs_dentry(&sb, &puzzlefs, root_inode)?;
let sb = sb.init_root(root)?;
Ok(sb)
}
@@ -175,34 +170,32 @@ fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBl
impl file::Operations for FsFile {
// must be the same as INodeData
type OpenData = Arc<Inode>;
+ // must be the same as fs::Type::Data
+ // TODO: find a way to enforce this
type FSData = Box<PuzzlefsInfo>;
// this is an Arc because Data must be ForeignOwnable and the only implementors of it are Box,
- // Arc and (); we cannot pass a reference to read, so we share Vfsmount using and Arc
- type Data = Arc<Vfsmount>;
+ // Arc and (); we cannot pass a reference to the read callback, so we share PuzzleFS using Arc
+ type Data = Arc<PuzzleFS>;
fn open(
fs_info: &PuzzlefsInfo,
_context: &Self::OpenData,
_file: &file::File,
) -> Result<Self::Data> {
- Ok(fs_info.vfs_mount.clone())
+ Ok(fs_info.puzzlefs.clone())
}
fn read(
- data: ArcBorrow<'_, Vfsmount>,
- _file: &file::File,
+ data: ArcBorrow<'_, PuzzleFS>,
+ file: &file::File,
writer: &mut impl IoBufferWriter,
offset: u64,
) -> Result<usize> {
+ let inode = file.inode::<PuzzleFsModule>().ok_or(EINVAL)?.fs_data();
let mut buf = Vec::try_with_capacity(writer.len())?;
buf.try_resize(writer.len(), 0)?;
- let file = file::RegularFile::from_path_in_root_mnt(
- &data,
- c_str!("data"),
- file::flags::O_RDONLY.try_into().unwrap(),
- 0,
- )?;
- let nr_bytes_read = file.read_with_offset(&mut buf[..], offset)?;
- file::read_from_slice(&buf[..nr_bytes_read], writer, 0)
+ let read = file_read(&data.oci, inode, offset as usize, &mut buf)?;
+ buf.truncate(read);
+ file::read_from_slice(&buf, writer, 0)
}
}
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* [PATCH 80/80] rust: puzzlefs: add oci_root_dir and image_manifest filesystem parameters
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (73 preceding siblings ...)
2023-06-09 6:31 ` [PATCH 79/80] rust: puzzlefs: add support for reading files Ariel Miculas
@ 2023-06-09 6:31 ` Ariel Miculas
2023-06-09 10:26 ` [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Miguel Ojeda
` (2 subsequent siblings)
77 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 6:31 UTC (permalink / raw)
To: rust-for-linux; +Cc: Ariel Miculas
These parameters are passed when mounting puzzlefs using '-o' option of
mount:
-o oci_root_dir="/path/to/oci/dir"
-o image_manifest="root_hash_of_image_manifest"
For a particular manifest in the manifests array in index.json (located
in the oci_root_dir), the root hash of the image manifest is found in
the digest field.
It would be nicer if we could pass the tag, but we don't support json
deserialization.
Example of mount:
mount -t puzzlefs -o oci_root_dir="/home/puzzlefs_oci" -o \
image_manifest="2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b" \
none /mnt
Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
samples/rust/puzzlefs.rs | 63 ++++++++++++++++++++++++++--------------
1 file changed, 41 insertions(+), 22 deletions(-)
diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
index 897a996a6ab3..fbd091d9d00d 100644
--- a/samples/rust/puzzlefs.rs
+++ b/samples/rust/puzzlefs.rs
@@ -7,6 +7,7 @@
use kernel::{
c_str, file, fs,
io_buffer::IoBufferWriter,
+ str::CString,
sync::{Arc, ArcBorrow},
};
@@ -29,27 +30,29 @@ struct PuzzlefsInfo {
puzzlefs: Arc<PuzzleFS>,
}
+#[derive(Default)]
+struct PuzzleFsParams {
+ oci_root_dir: Option<CString>,
+ image_manifest: Option<CString>,
+}
+
#[vtable]
impl fs::Context<Self> for PuzzleFsModule {
- type Data = ();
-
- kernel::define_fs_params! {(),
- {flag, "flag", |_, v| { pr_info!("flag passed-in: {v}\n"); Ok(()) } },
- {flag_no, "flagno", |_, v| { pr_info!("flagno passed-in: {v}\n"); Ok(()) } },
- {bool, "bool", |_, v| { pr_info!("bool passed-in: {v}\n"); Ok(()) } },
- {u32, "u32", |_, v| { pr_info!("u32 passed-in: {v}\n"); Ok(()) } },
- {u32oct, "u32oct", |_, v| { pr_info!("u32oct passed-in: {v}\n"); Ok(()) } },
- {u32hex, "u32hex", |_, v| { pr_info!("u32hex passed-in: {v}\n"); Ok(()) } },
- {s32, "s32", |_, v| { pr_info!("s32 passed-in: {v}\n"); Ok(()) } },
- {u64, "u64", |_, v| { pr_info!("u64 passed-in: {v}\n"); Ok(()) } },
- {string, "string", |_, v| { pr_info!("string passed-in: {v}\n"); Ok(()) } },
- {enum, "enum", [("first", 10), ("second", 20)], |_, v| {
- pr_info!("enum passed-in: {v}\n"); Ok(()) }
- },
+ type Data = Box<PuzzleFsParams>;
+
+ kernel::define_fs_params! {Box<PuzzleFsParams>,
+ {string, "oci_root_dir", |s, v| {
+ s.oci_root_dir = Some(CString::try_from_fmt(format_args!("{v}"))?);
+ Ok(())
+ }},
+ {string, "image_manifest", |s, v| {
+ s.image_manifest = Some(CString::try_from_fmt(format_args!("{v}"))?);
+ Ok(())
+ }},
}
- fn try_new() -> Result {
- Ok(())
+ fn try_new() -> Result<Self::Data> {
+ Ok(Box::try_new(PuzzleFsParams::default())?)
}
}
@@ -134,11 +137,27 @@ impl fs::Type for PuzzleFsModule {
const FLAGS: i32 = fs::flags::USERNS_MOUNT;
const DCACHE_BASED: bool = true;
- fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBlock<Self>> {
- let puzzlefs = PuzzleFS::open(
- c_str!("/home/puzzlefs_oci"),
- c_str!("2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b"),
- );
+ fn fill_super(
+ data: Box<PuzzleFsParams>,
+ sb: fs::NewSuperBlock<'_, Self>,
+ ) -> Result<&fs::SuperBlock<Self>> {
+ let oci_root_dir = match data.oci_root_dir {
+ Some(val) => val,
+ None => {
+ pr_err!("missing oci_root_dir parameter!\n");
+ return Err(ENOTSUPP);
+ }
+ };
+
+ let image_manifest = match data.image_manifest {
+ Some(val) => val,
+ None => {
+ pr_err!("missing image_manifest parameter!\n");
+ return Err(ENOTSUPP);
+ }
+ };
+
+ let puzzlefs = PuzzleFS::open(&oci_root_dir, &image_manifest);
if let Err(ref e) = puzzlefs {
pr_info!("error opening puzzlefs {e}\n");
--
2.40.1
^ permalink raw reply related [flat|nested] 134+ messages in thread
* Re: [PATCH 18/80] rust: kernel: format the rust code
2023-06-09 6:30 ` [PATCH 18/80] rust: kernel: format the rust code Ariel Miculas
@ 2023-06-09 9:21 ` Miguel Ojeda
0 siblings, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 9:21 UTC (permalink / raw)
To: Ariel Miculas; +Cc: rust-for-linux
On Fri, Jun 9, 2023 at 8:58 AM Ariel Miculas <amiculas@cisco.com> wrote:
>
> Signed-off-by: Ariel Miculas <amiculas@cisco.com>
This should be done by the commits that change the respective code.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 02/80] rust: add ability to register a file system
2023-06-09 6:30 ` [PATCH 02/80] rust: add ability to register a file system Ariel Miculas
@ 2023-06-09 9:23 ` Miguel Ojeda
0 siblings, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 9:23 UTC (permalink / raw)
To: Ariel Miculas; +Cc: rust-for-linux, Wedson Almeida Filho
On Fri, Jun 9, 2023 at 9:03 AM Ariel Miculas <amiculas@cisco.com> wrote:
>
> From: Wedson Almeida Filho <wedsonaf@google.com>
>
> Also add sample that uses the new public API. The registered file system
> cannot be mounted yet, but can be seen in /proc/filesystems.
>
> More functionality will be added in subsequent commits.
>
> Signed-off-by: Wedson Almeida Filho <wedsonaf@google.com>
Note that you need to add your SoB too in cases where you forward a
patch, even if you are just handling the patch; see
https://docs.kernel.org/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 20/80] kernel: configs: enable rust samples in rust.config
2023-06-09 6:30 ` [PATCH 20/80] kernel: configs: enable rust samples in rust.config Ariel Miculas
@ 2023-06-09 9:25 ` Miguel Ojeda
0 siblings, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 9:25 UTC (permalink / raw)
To: Ariel Miculas; +Cc: rust-for-linux
On Fri, Jun 9, 2023 at 9:03 AM Ariel Miculas <amiculas@cisco.com> wrote:
>
> Signed-off-by: Ariel Miculas <amiculas@cisco.com>
I think the intention of `rust.config` is to enable the Rust support,
not everything related to Rust.
So it would be better to have another file to enable samples, or one
to enable as much as possible (e.g. for testing/CI), etc.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 09/80] rust: kernel: backport the delay module from the rust branch
2023-06-09 6:30 ` [PATCH 09/80] rust: kernel: backport the delay module from the rust branch Ariel Miculas
@ 2023-06-09 9:29 ` Miguel Ojeda
0 siblings, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 9:29 UTC (permalink / raw)
To: Ariel Miculas; +Cc: rust-for-linux, John Baublitz
On Fri, Jun 9, 2023 at 9:01 AM Ariel Miculas <amiculas@cisco.com> wrote:
>
> Signed-off-by: Ariel Miculas <amiculas@cisco.com>
If we end up adding it as it was in the `rust` branch, then this would
need proper attribution (Cc'ing John).
We are also trying to have the original authors from the `rust` branch
to submit the code they wrote, if possible / they have the time, so
that they are involved in the discussion.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 12/80] drop: Add crate::pr_warn declaration
2023-06-09 6:30 ` [PATCH 12/80] drop: Add crate::pr_warn declaration Ariel Miculas
@ 2023-06-09 9:29 ` Miguel Ojeda
2023-06-09 10:46 ` Ariel Miculas (amiculas)
0 siblings, 1 reply; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 9:29 UTC (permalink / raw)
To: Ariel Miculas; +Cc: rust-for-linux
On Fri, Jun 9, 2023 at 9:05 AM Ariel Miculas <amiculas@cisco.com> wrote:
>
> ---
> rust/kernel/fs.rs | 1 -
> 1 file changed, 1 deletion(-)
Empty patch, probably due to rebasing fun? :)
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 37/80] rust: kernel: fix compile errors after rebase to rust-next
2023-06-09 6:30 ` [PATCH 37/80] rust: kernel: fix compile errors after rebase to rust-next Ariel Miculas
@ 2023-06-09 9:38 ` Miguel Ojeda
0 siblings, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 9:38 UTC (permalink / raw)
To: Ariel Miculas; +Cc: rust-for-linux
On Fri, Jun 9, 2023 at 9:03 AM Ariel Miculas <amiculas@cisco.com> wrote:
>
> Signed-off-by: Ariel Miculas <amiculas@cisco.com>
Not a big deal since it is an RFC, but please note that we want to
keep all commits building (and passing docs, tests etc.).
If the patch is from somebody else (which may be the reason why you
split it), then ideally the original author would sign off on your
changes (or, even better, they can send the updated patch themselves),
potentially having you as `Co-developed-by` if they are bigger
changes, or you can otherwise use the `[ ... ]` notation in the
message to indicate what you changed (e.g. for small notes I use this
style: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f431c5c581fa).
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 17/80] kernel: configs: add qemu-busybox-min.config
2023-06-09 6:30 ` [PATCH 17/80] kernel: configs: add qemu-busybox-min.config Ariel Miculas
@ 2023-06-09 9:39 ` Miguel Ojeda
0 siblings, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 9:39 UTC (permalink / raw)
To: Ariel Miculas; +Cc: rust-for-linux, Wedson Almeida Filho
On Fri, Jun 9, 2023 at 8:58 AM Ariel Miculas <amiculas@cisco.com> wrote:
>
> Signed-off-by: Ariel Miculas <amiculas@cisco.com>
These originally came from Wedson (Cc'd).
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 05/80] rust: kernel: add libraries required by the filesystem abstractions
2023-06-09 6:30 ` [PATCH 05/80] rust: kernel: add libraries required by the filesystem abstractions Ariel Miculas
@ 2023-06-09 9:46 ` Miguel Ojeda
0 siblings, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 9:46 UTC (permalink / raw)
To: Ariel Miculas; +Cc: rust-for-linux
On Fri, Jun 9, 2023 at 8:58 AM Ariel Miculas <amiculas@cisco.com> wrote:
>
> Add cred.rs, file.rs, io_buffer.rs, iov_iter.rs, mm.rs, pages.rs and
> user_ptr.rs from the rust branch.
>
> Signed-off-by: Ariel Miculas <amiculas@cisco.com>
I guess you added these in bulk for RFC purposes, so that is fine, but
eventually these will need to be submitted individually etc.
An alternative (for RFCs), if you want people to focus on another part
of the series, is to create a branch somewhere with the dependencies
applied and then base the patches on top (being clear that the
dependencies would be needed, of course). Some extra notes at [1] for
others that may be interested in that approach.
[1] https://rust-for-linux.com/contributing#submitting-new-abstractions-and-modules
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 43/80] rust: serde_cbor: add support for serde_cbor's from_slice method by using a custom alloc_kernel feature
2023-06-09 6:30 ` [PATCH 43/80] rust: serde_cbor: add support for serde_cbor's from_slice method by using a custom alloc_kernel feature Ariel Miculas
@ 2023-06-09 9:55 ` Miguel Ojeda
0 siblings, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 9:55 UTC (permalink / raw)
To: Ariel Miculas; +Cc: rust-for-linux
On Fri, Jun 9, 2023 at 9:05 AM Ariel Miculas <amiculas@cisco.com> wrote:
>
> let j = &[2, 0, 1, 0, 3];
> - let expected = Test {
> - a: (),
> - b: false,
> - };
> + let expected = Test { a: (), b: false };
It seems `rustfmt` was run within this commit, which makes it harder
to spot the changes. Please apply it on the commits that add the code
itself instead.
> impl<'a> SliceRead<'a> {
> /// Creates a CBOR input source to read from a slice of bytes.
> pub fn new(slice: &'a [u8]) -> SliceRead<'a> {
> SliceRead {
> slice,
> + #[cfg(not(feature = "kernel_alloc"))]
> scratch: vec![],
> + #[cfg(feature = "kernel_alloc")]
> + scratch: Vec::new(),
Can this be simply replaced, i.e. without the gate, or is there a reason for it?
Either that, or we go ahead and make `vec!` work, but originally I
thought it would be more confusing, see
https://github.com/rust-lang/rust/commit/8cec88ba76e1d6edc98f30101c40f9247c754898.
> @@ -371,7 +376,10 @@ fn clear_buffer(&mut self) {
> fn read_to_buffer(&mut self, n: usize) -> Result<()> {
> let end = self.end(n)?;
> let slice = &self.slice[self.index..end];
> + #[cfg(not(feature = "kernel_alloc"))]
> self.scratch.extend_from_slice(slice);
> + #[cfg(feature = "kernel_alloc")]
> + self.scratch.try_extend_from_slice(slice).unwrap();
Do we know this allocation would not fail? i.e. we need to be extra
careful on `unwrap()`s within the kernel. Especially for facilities
that may have more than one caller/user like libraries.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 13/80] rust: kernel: rename from_kernel_errno to from_errno
2023-06-09 6:30 ` [PATCH 13/80] rust: kernel: rename from_kernel_errno to from_errno Ariel Miculas
@ 2023-06-09 9:56 ` Miguel Ojeda
0 siblings, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 9:56 UTC (permalink / raw)
To: Ariel Miculas; +Cc: rust-for-linux
On Fri, Jun 9, 2023 at 9:06 AM Ariel Miculas <amiculas@cisco.com> wrote:
>
> Signed-off-by: Ariel Miculas <amiculas@cisco.com>
This one should also be applied to the original patch.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 16/80] rust: helpers: add missing rust helper 'alloc_pages'
2023-06-09 6:30 ` [PATCH 16/80] rust: helpers: add missing rust helper 'alloc_pages' Ariel Miculas
@ 2023-06-09 9:57 ` Miguel Ojeda
0 siblings, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 9:57 UTC (permalink / raw)
To: Ariel Miculas; +Cc: rust-for-linux
On Fri, Jun 9, 2023 at 9:03 AM Ariel Miculas <amiculas@cisco.com> wrote:
>
> Signed-off-by: Ariel Miculas <amiculas@cisco.com>
I think this could be added to the patch where the user/caller is.
Otherwise, the commit message should explain the need for it.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 14/80] rust: kernel: Rename from_pointer to from_foreing and into_pointer to into_foreign
2023-06-09 6:30 ` [PATCH 14/80] rust: kernel: Rename from_pointer to from_foreing and into_pointer to into_foreign Ariel Miculas
@ 2023-06-09 9:57 ` Miguel Ojeda
0 siblings, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 9:57 UTC (permalink / raw)
To: Ariel Miculas; +Cc: rust-for-linux
On Fri, Jun 9, 2023 at 9:01 AM Ariel Miculas <amiculas@cisco.com> wrote:
>
> Signed-off-by: Ariel Miculas <amiculas@cisco.com>
This one should be squashed into the patch that adds the code too.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 63/80] rust: alloc: add from_iter_fallible for Vec<T>
2023-06-09 6:31 ` [PATCH 63/80] rust: alloc: add from_iter_fallible " Ariel Miculas
@ 2023-06-09 10:06 ` Miguel Ojeda
0 siblings, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 10:06 UTC (permalink / raw)
To: Ariel Miculas; +Cc: rust-for-linux
On Fri, Jun 9, 2023 at 8:56 AM Ariel Miculas <amiculas@cisco.com> wrote:
>
> + /// See https://doc.rust-lang.org/std/vec/struct.Vec.html#examples-31
> + #[stable(feature = "rust1", since = "1.0.0")]
For additions to the `alloc` crate, please use `kernel` as the feature
name. In addition, we write (or rather, copy, if possible, from the
infallible ones :) docs.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 44/80] rust: serde: add support for deserializing Vec with kernel_alloc feature
2023-06-09 6:30 ` [PATCH 44/80] rust: serde: add support for deserializing Vec with kernel_alloc feature Ariel Miculas
@ 2023-06-09 10:10 ` Miguel Ojeda
0 siblings, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 10:10 UTC (permalink / raw)
To: Ariel Miculas; +Cc: rust-for-linux
On Fri, Jun 9, 2023 at 8:58 AM Ariel Miculas <amiculas@cisco.com> wrote:
>
> + #[cfg(not(feature = "kernel_alloc"))]
> let mut values = Vec::with_capacity(size_hint::cautious(seq.size_hint()));
> + #[cfg(feature = "kernel_alloc")]
> + let mut values = Vec::try_with_capacity(size_hint::cautious(seq.size_hint())).unwrap();
Same note on these `unwrap()`s as in the other patch.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 41/80] rust: Kbuild: enable serde_cbor
2023-06-09 6:30 ` [PATCH 41/80] rust: Kbuild: enable serde_cbor Ariel Miculas
@ 2023-06-09 10:21 ` Miguel Ojeda
0 siblings, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 10:21 UTC (permalink / raw)
To: Ariel Miculas; +Cc: rust-for-linux
On Fri, Jun 9, 2023 at 8:57 AM Ariel Miculas <amiculas@cisco.com> wrote:
>
> Signed-off-by: Ariel Miculas <amiculas@cisco.com>
Regarding `serde_cbor`: the repository upstream says it was archived
almost two years ago.
While it says it is widely used, it also says it is unmaintained and
recommends other two crates for new users.
Since this is an RFC, this is fine for the moment to have something
that works, but if eventually the kernel decides to merge puzzlefs and
its dependencies, we will need to consider who will maintain this, or
whether another crate would be a better fit (or a kernel-made one),
etc.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (74 preceding siblings ...)
2023-06-09 6:31 ` [PATCH 80/80] rust: puzzlefs: add oci_root_dir and image_manifest filesystem parameters Ariel Miculas
@ 2023-06-09 10:26 ` Miguel Ojeda
2023-06-09 10:36 ` Christian Brauner
2023-06-10 0:09 ` Kent Overstreet
77 siblings, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 10:26 UTC (permalink / raw)
To: Ariel Miculas; +Cc: rust-for-linux
On Fri, Jun 9, 2023 at 8:58 AM Ariel Miculas <amiculas@cisco.com> wrote:
>
> This is a proof of concept driver written for the PuzzleFS
> next-generation container filesystem [1]. I've included a short abstract
> about puzzlefs further below. This driver is based on the rust-next
Thanks for preparing this RFC Ariel!
I have left some procedural notes here and there.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (75 preceding siblings ...)
2023-06-09 10:26 ` [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Miguel Ojeda
@ 2023-06-09 10:36 ` Christian Brauner
2023-06-09 11:42 ` Miguel Ojeda
` (2 more replies)
2023-06-10 0:09 ` Kent Overstreet
77 siblings, 3 replies; 134+ messages in thread
From: Christian Brauner @ 2023-06-09 10:36 UTC (permalink / raw)
To: Ariel Miculas, linux-fsdevel; +Cc: rust-for-linux, linux-mm
On Fri, Jun 09, 2023 at 09:29:58AM +0300, Ariel Miculas wrote:
> Hi all!
>
> This is a proof of concept driver written for the PuzzleFS
Uhm, the PuzzleFS filesystem isn't actually sent to fsdevel? Yet I see
tons of patches in there that add wrappers to our core fs data
structures. I even see a ton of new files that all fall clearly into
fsdevel territory:
create mode 100644 rust/kernel/cred.rs
create mode 100644 rust/kernel/delay.rs
create mode 100644 rust/kernel/driver.rs
create mode 100644 rust/kernel/file.rs
create mode 100644 rust/kernel/fs.rs
create mode 100644 rust/kernel/fs/param.rs
create mode 100644 rust/kernel/io_buffer.rs
create mode 100644 rust/kernel/iov_iter.rs
create mode 100644 rust/kernel/mm.rs
create mode 100644 rust/kernel/mount.rs
create mode 100644 rust/kernel/pages.rs
There's also quite a lot of new mm/ in there, no?
Any wrappers and code for core fs should be maintained as part of fs.
Rust shouldn't become a way to avoid our reviews once you have a few
wrappers added somewhere.
> next-generation container filesystem [1]. I've included a short abstract
> about puzzlefs further below. This driver is based on the rust-next
> branch, on top of which I've backported the filesystem abstractions from
> Wedson Almeida Filho [2][3] and Miguel Ojeda's third-party crates
> support: proc-macro2, quote, syn, serde and serde_derive [4]. I've added
> the additional third-party crates serde_cbor[5] and hex [6]. Then I've
> adapted the user space puzzlefs code [1] so that the puzzlefs kernel
> module could present the directory hierarchy and implement the basic
> read functionality.
> For some additional context, puzzlefs was started by Tycho Andersen and
> it's the successor of atomfs. This FOSDEM presentation from 2019 [12]
> covers the rationale for a new oci image format and presents a higher
> level overview of our goals with puzzlefs.
> I've split the rest of the cover letter in following sections (using a
> markdown style):
> * Example: it describes a practical example of what was achieved
> * Limitations: it presents the existing limitations of this POC
> * Upstreaming steps: it describes the steps needed for upstreaming this
> driver
> * Setup: it shows how to setup the necessary environment for testing the
> puzzlefs driver
> * Puzzlefs abstract: it provides a short overview of puzzlefs
>
> # Example
> An example is provided below:
>
> ```
> ~ # cat /proc/filesystems | grep puzzlefs
> nodev puzzlefs
> ~ # mount -t puzzlefs -o oci_root_dir="/home/puzzlefs_oci" -o image_manifest="2d
> 6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b" none /mnt
> ~ # ls -lR /mnt/
> /mnt/:
> total 0
> drwxr-xr-x 2 0 0 0 Jun 8 12:26 dir-1
> drwxr-xr-x 2 0 0 0 Jun 8 12:26 dir-2
> drwxr-xr-x 2 0 0 0 Jun 8 12:26 dir-3
> drwxr-xr-x 2 0 0 0 Jun 8 12:26 dir-4
> -rw-r--r-- 1 0 0 0 Jun 8 12:26 file1
> -rw-r--r-- 1 0 0 0 Jun 8 12:26 file2
>
> /mnt/dir-1:
> total 0
>
> /mnt/dir-2:
> total 0
>
> /mnt/dir-3:
> total 0
>
> /mnt/dir-4:
> total 0
> ~ # cat /mnt/file2
> ana are mere bla bla bla
> ~ # wc /mnt/file1
> 202 202 5454 /mnt/file1
> ```
>
> In this example, /home/puzzlefs_oci is the puzzlefs oci directory:
> ```
> ~ # ls -lR /home/puzzlefs_oci/
> /home/puzzlefs_oci/:
> total 8
> drwxr-xr-x 3 1000 1000 0 Jun 8 14:33 blobs
> -rw-r--r-- 1 1000 1000 266 Jun 8 14:33 index.json
> -rw-r--r-- 1 1000 1000 37 Jun 8 14:33 oci-layout
>
> /home/puzzlefs_oci/blobs:
> total 0
> drwxr-xr-x 2 1000 1000 0 Jun 8 14:33 sha256
>
> /home/puzzlefs_oci/blobs/sha256:
> total 16
> -rw------- 1 1000 1000 89 Jun 8 14:33 2d6602d678140540dc7e96de652a76a8b16eb
> -rw------- 1 1000 1000 925 Jun 8 14:33 4df03518eea406343dbb55046720f6a478881
> -rw------- 1 1000 1000 5479 Jun 8 14:33 d86a87b19bd9a2fec0d31687c1d669cdb59eb
> ```
>
> `2d6602d678140540dc7e96de652a76a8b16eb` is the puzzlefs image manifest
> hash for the first_try tag:
> ```
> $ cat /tmp/oci-simple/index.json | jq .
> {
> "schemaVersion": -1,
> "manifests": [
> {
> "digest": "sha256:2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b",
> "size": 89,
> "media_type": "application/vnd.puzzlefs.image.rootfs.v1",
> "annotations": {
> "org.opencontainers.image.ref.name": "first_try"
> }
> }
> ],
> "annotations": {}
> }
> ```
>
> I will describe how to build a puzzlefs image in the `Setup` section, at
> step 5.
>
> # Limitations
> One limitation is that the puzzlefs driver doesn't implement any lookup
> functionality and instead it inserts every directory entry into the
> dcache during init (see the `DCACHE_BASED` constant). This is similar to
> how the sample `rust_fs` driver works, but the goal is to implement
> proper lookup functions. However, more filesystem abstractions need to
> be implemented before this can be achieved.
>
> # Upstreaming steps
> Before the puzzlefs driver can be upstreamed, the following need to be
> merged:
> * Wedson's filesystem abstractions [3]
> * the necessary third-party crates [4] (with the preliminary discussion
> about whether this is desirable)
>
> # Setup
> My setup is based on Wedson's tutorial [8]. Next, I will describe the
> necessary steps to build an initrd and run a custom kernel under qemu.
>
> 1. Get the linux rust-next branch [9] and apply this patchset
>
> 2. Follow the rust quickstart guide [10]
>
> 3. configure and build the kernel
> ```
> $ make LLVM=1 allnoconfig qemu-busybox-min.config rust.config
> $ make LLVM=1 -j$(nproc)
> ```
>
> 4. setup busybox
> ```
> git clone git://git.busybox.net/busybox
> cd busybox
> make menuconfig # enable 'Build static binary' config
> make
> make install
> ```
> This will create the `_install` directory with the rootfs inside it.
>
> 5. create a home directory in the rootfs and copy a puzzlefs oci
> directory in home/puzzlefs_oci
> To create a puzzlefs oci directory:
> * download this custom puzzlefs repository [11] (it's custom because we
> want to build an image without verity data)
> * run `make release`
> * create a simple filesystem structure with a few directories and files
> (I've created one at ../test-puzzlefs/simple_rootfs)
> * build a puzzlefs oci image at
> `~/work/busybox/_install/home/puzzlefs_oci` (replace this path with
> your busybox path) with the tag `first_try`:
> ```
> $ target/release/puzzlefs build --omit-verity \
> ../test-puzzlefs/simple_rootfs ~/work/busybox/_install/home/puzzlefs_oci \
> first_try
> ```
> * get first_try's image manifest from index.json (inside `puzzlefs_oci`)
> ```
> $ cat index.json | jq . | grep digest
> "digest": "sha256:2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b",
> ```
>
> 6. add the following 'init' script in the busybox rootfs (rootfs path
> defaults to `./_install'):
> ```
> #!/bin/sh
> mount -t devtmpfs none /dev
> mkdir -p /proc
> mount -t proc none /proc
>
> ifconfig lo up
> udhcpc -i eth0
>
> mkdir /mnt
> mount -t puzzlefs -o oci_root_dir="/home/puzzlefs_oci" -o \
> image_manifest="2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b" \
> none /mnt
>
> setsid sh -c 'exec sh -l </dev/ttyS0 >/dev/ttyS0 2>&1'
> ```
>
> Make sure to replace the `image_manifest` with your own digest. This
> init script will be passed to rdinit in the kernel command line.
>
> 7. generate the initramfs
>
> Assuming busybox is in `~/work/busybox`:
> ```
> cd ~/work/busybox/_install && find . | cpio -H newc -o | gzip > ../ramdisk.img
> ```
> This will generate a compressed ramdisk image in
> `~/work/busybox/ramdisk.img`.
>
> 8. run with qemu (this assumes the linux tree is at '../linux' and busybox
> is at '../busybox'):
> ```
> qemu-system-x86_64 \
> -accel kvm \
> -cpu host \
> -m 4G \
> -initrd ../busybox/ramdisk.img \
> -kernel ../linux/arch/x86/boot/bzImage \
> -nographic \
> -append 'console=ttyS0 nokaslr debug rdinit=/init' \
> -nic user,model=rtl8139 \
> -no-reboot
> ```
>
> 9. Check whether puzzlefs has been successfully mounted
> ```
> ~ # mount | grep puzzlefs
> none on /mnt type puzzlefs (rw,relatime)
> ~ # ls /mnt/
> dir-1 dir-2 dir-3 dir-4 file1 file2
> ```
>
>
> # Puzzlefs abstract
> Puzzlefs [1] is a container filesystem designed to address the
> limitations of the existing OCI format. The main goals of the project
> are reduced duplication, reproducible image builds, direct mounting
> support and memory safety guarantees, some inspired by the OCIv2 design
> document [7].
>
> Reduced duplication is achieved using the content defined chunking
> algorithm FastCDC. This implementation allows chunks to be shared among
> layers. Building a new layer starting from an existing one allows
> reusing most of the chunks.
>
> Another goal of the project is reproducible image builds, which is
> achieved by defining a canonical representation of the image format.
>
> Direct mounting support is a key feature of puzzlefs and, together with
> fs-verity, it provides data integrity. Currently, puzzlefs is
> implemented as a userspace filesystem (FUSE). A read-only kernel
> filesystem driver is underway.
>
> Lastly, memory safety is critical to puzzlefs, leading to the decision
> to implement it in Rust. Another goal is to share the same code between
> user space and kernel space in order to provide one secure
> implementation.
>
>
> [1] https://github.com/anuvu/puzzlefs
> [2] https://github.com/wedsonaf/linux/tree/fs
> [3] https://github.com/Rust-for-Linux/linux/issues/1004
> [4] https://github.com/Rust-for-Linux/linux/pull/1007
> [5] https://docs.rs/serde_cbor/latest/serde_cbor/
> [6] https://docs.rs/hex/0.4.3/hex/
> [7] https://hackmd.io/@cyphar/ociv2-brainstorm
> [8] https://www.youtube.com/watch?v=tPs1uRqOnlk
> [9] https://github.com/Rust-for-Linux/linux/tree/rust-next
> [10] https://docs.kernel.org/rust/quick-start.html
> [11] https://github.com/ariel-miculas/puzzlefs/tree/support-no-verity-data
> [12] https://archive.fosdem.org/2019/schedule/event/containers_atomfs/
>
> Ariel Miculas (58):
> rust: kernel: add libraries required by the filesystem abstractions
> rust: kernel: backport the delay module from the rust branch
> rust: kernel: add container_of macro
> rust: kernel: add offset_of macro
> drop: Add crate::pr_warn declaration
> rust: kernel: rename from_kernel_errno to from_errno
> rust: kernel: Rename from_pointer to from_foreing and into_pointer to
> into_foreign
> rust: kernel: add count_paren_items macro, needed by define_fs_params
> macro
> rust: helpers: add missing rust helper 'alloc_pages'
> kernel: configs: add qemu-busybox-min.config
> rust: kernel: format the rust code
> samples: puzzlefs: add initial puzzlefs sample, copied from rust_fs.rs
> kernel: configs: enable rust samples in rust.config
> Add SAMPLE_RUST_SERDE in rust.config
> rust: kernel: fix compile errors after rebase to rust-next
> rust: serde_cbor: import crate
> rust: serde_cbor: add SPDX License Identifiers
> rust: serde_cbor: add no_fp_fmt_parse support
> rust: Kbuild: enable serde_cbor
> samples: rust: add cbor serialize/deserialize example
> rust: serde_cbor: add support for serde_cbor's from_slice method by
> using a custom alloc_kernel feature
> rust: serde: add support for deserializing Vec with kernel_alloc
> feature
> rust: file: Replace UnsafeCell with Opaque for File
> rust: kernel: implement fmt::Debug for CString
> samples: puzzlefs: rename RustFs to PuzzleFs
> samples: puzzlefs: add basic deserializing support for the puzzlefs
> metadata
> rust: file: present the filesystem context to the open function
> rust: kernel: add an abstraction over vfsmount to allow cloning a new
> private mount
> rust: file: add from_path, from_path_in_root_mnt and read_with_offset
> methods to File
> samples: puzzlefs: pass the Vfsmount structure from open to read and
> return the contents of the data file inside /home/puzzlefs_oci
> rust: file: move from_path, from_path_in_root_mnt and read_with_offset
> methods to a RegularFile newtype
> rust: file: ensure RegularFile can only create regular files
> rust: file: add get_pos method to RegularFile
> rust: file: add methods read_to_end, get_file_size and update_pos to
> RegularFile
> rust: file: define a minimal Read trait and implement it for
> RegularFile
> samples: puzzlefs: add cbor_get_array_size method
> samples: puzzlefs: add KernelError to WireFormatError and implement
> From conversion
> samples: puzzlefs: implement new for MetadataBlob
> samples: puzzlefs: build puzzlefs into the kernel, thus avoiding the
> need to export rust symbols
> rust: alloc: add try_clone for Vec<T>
> rust: alloc: add from_iter_fallible for Vec<T>
> samples: puzzlefs: implement to_errno and from_errno for
> WireFormatError
> samples: puzzlefs: add TryReserveError (and from conversion) to
> WireFormatError
> samples: puzzlefs: add higher level inode related functionality
> samples: puzzlefs: populate the directory entries with the inodes from
> the puzzlefs metadata file
> rust: hex: import crate
> rust: hex: add SPDX license identifiers
> rust: Kbuild: enable `hex`
> rust: hex: implement FromHex trait and hex::decode using a custom
> kernel_alloc feature
> rust: hex: add encode_hex_iter and encode_hex_upper_iter methods
> rust: puzzlefs: add HexError to WireFormatError and implement the From
> conversion
> rust: puzzlefs: display the error value for
> WireFormatError::KernelError
> samples: puzzlefs: add Rootfs and Digest structs to types.rs
> samples: puzzlefs: implement the conversion from WireFormatError to
> kernel::error::Error
> rust: puzzlefs: read the puzzlefs image manifest instead of an
> individual metadata layer
> rust: puzzlefs: rename PuzzleFs to PuzzleFsModule to avoid confusion
> with the PuzzleFS struct
> rust: puzzlefs: add support for reading files
> rust: puzzlefs: add oci_root_dir and image_manifest filesystem
> parameters
>
> Miguel Ojeda (15):
> rust: proc-macro2: import crate
> rust: proc-macro2: add SPDX License Identifiers
> rust: proc-macro2: remove `unicode_ident` dependency
> rust: quote: import crate
> rust: quote: add SPDX License Identifiers
> rust: syn: import crate
> rust: syn: add SPDX License Identifiers
> rust: syn: remove `unicode-ident` dependency
> rust: serde: import crate
> rust: serde: add `no_fp_fmt_parse` support
> rust: serde: add SPDX License Identifiers
> rust: serde_derive: import crate
> rust: serde_derive: add SPDX License Identifiers
> rust: Kbuild: enable `proc-macro2`, `quote`, `syn`, `serde` and
> `serde_derive`
> rust: test `serde` support
>
> Wedson Almeida Filho (7):
> rust: add definitions for ref-counted inodes and dentries
> rust: add ability to register a file system
> rust: define fs context
> rust: add support for file system parameters
> rust: allow fs driver to initialise new superblocks
> rust: add `module_fs` macro
> WIP: rust: allow fs to be populated
>
> Makefile | 14 +-
> arch/x86/configs/qemu-busybox-min.config | 11 +
> kernel/configs/qemu-busybox-min.config | 56 +
> kernel/configs/rust.config | 11 +
> rust/.gitignore | 1 +
> rust/Makefile | 232 +-
> rust/alloc/vec/mod.rs | 48 +
> rust/bindings/bindings_helper.h | 14 +
> rust/bindings/lib.rs | 5 +
> rust/helpers.c | 76 +
> rust/hex/error.rs | 78 +
> rust/hex/lib.rs | 506 +++
> rust/hex/serde.rs | 104 +
> rust/kernel/cred.rs | 46 +
> rust/kernel/delay.rs | 104 +
> rust/kernel/driver.rs | 28 +
> rust/kernel/error.rs | 52 +-
> rust/kernel/file.rs | 1117 ++++++
> rust/kernel/fs.rs | 1478 ++++++++
> rust/kernel/fs/param.rs | 558 +++
> rust/kernel/io_buffer.rs | 153 +
> rust/kernel/iov_iter.rs | 81 +
> rust/kernel/lib.rs | 83 +
> rust/kernel/mm.rs | 149 +
> rust/kernel/mount.rs | 66 +
> rust/kernel/pages.rs | 144 +
> rust/kernel/str.rs | 6 +
> rust/kernel/test_serde.rs | 26 +
> rust/kernel/test_serde/de.rs | 439 +++
> rust/kernel/test_serde/error.rs | 73 +
> rust/kernel/test_serde/ser.rs | 466 +++
> rust/kernel/user_ptr.rs | 175 +
> rust/proc-macro2/detection.rs | 77 +
> rust/proc-macro2/fallback.rs | 1004 ++++++
> rust/proc-macro2/lib.rs | 1341 ++++++++
> rust/proc-macro2/marker.rs | 20 +
> rust/proc-macro2/parse.rs | 874 +++++
> rust/proc-macro2/rcvec.rs | 144 +
> rust/proc-macro2/wrapper.rs | 996 ++++++
> rust/quote/ext.rs | 112 +
> rust/quote/format.rs | 170 +
> rust/quote/ident_fragment.rs | 88 +
> rust/quote/lib.rs | 1436 ++++++++
> rust/quote/runtime.rs | 440 +++
> rust/quote/spanned.rs | 45 +
> rust/quote/to_tokens.rs | 211 ++
> rust/serde/de/format.rs | 32 +
> rust/serde/de/ignored_any.rs | 246 ++
> rust/serde/de/impls.rs | 2755 +++++++++++++++
> rust/serde/de/mod.rs | 2313 +++++++++++++
> rust/serde/de/seed.rs | 21 +
> rust/serde/de/utf8.rs | 48 +
> rust/serde/de/value.rs | 1718 ++++++++++
> rust/serde/integer128.rs | 84 +
> rust/serde/lib.rs | 351 ++
> rust/serde/macros.rs | 238 ++
> rust/serde/private/de.rs | 2997 ++++++++++++++++
> rust/serde/private/doc.rs | 161 +
> rust/serde/private/mod.rs | 52 +
> rust/serde/private/ser.rs | 1316 +++++++
> rust/serde/private/size_hint.rs | 23 +
> rust/serde/ser/fmt.rs | 180 +
> rust/serde/ser/impls.rs | 987 ++++++
> rust/serde/ser/impossible.rs | 218 ++
> rust/serde/ser/mod.rs | 1992 +++++++++++
> rust/serde/std_error.rs | 50 +
> rust/serde_cbor/de.rs | 1370 ++++++++
> rust/serde_cbor/error.rs | 320 ++
> rust/serde_cbor/lib.rs | 371 ++
> rust/serde_cbor/read.rs | 647 ++++
> rust/serde_cbor/ser.rs | 748 ++++
> rust/serde_cbor/tags.rs | 224 ++
> rust/serde_cbor/value/de.rs | 168 +
> rust/serde_cbor/value/mod.rs | 158 +
> rust/serde_cbor/value/ser.rs | 447 +++
> rust/serde_cbor/write.rs | 177 +
> rust/serde_derive/bound.rs | 408 +++
> rust/serde_derive/de.rs | 3148 +++++++++++++++++
> rust/serde_derive/dummy.rs | 46 +
> rust/serde_derive/fragment.rs | 76 +
> rust/serde_derive/internals/ast.rs | 204 ++
> rust/serde_derive/internals/attr.rs | 1908 +++++++++++
> rust/serde_derive/internals/case.rs | 199 ++
> rust/serde_derive/internals/check.rs | 445 +++
> rust/serde_derive/internals/ctxt.rs | 64 +
> rust/serde_derive/internals/mod.rs | 30 +
> rust/serde_derive/internals/receiver.rs | 287 ++
> rust/serde_derive/internals/respan.rs | 18 +
> rust/serde_derive/internals/symbol.rs | 71 +
> rust/serde_derive/lib.rs | 112 +
> rust/serde_derive/pretend.rs | 203 ++
> rust/serde_derive/ser.rs | 1342 ++++++++
> rust/serde_derive/this.rs | 34 +
> rust/serde_derive/try.rs | 26 +
> rust/syn/attr.rs | 664 ++++
> rust/syn/await.rs | 4 +
> rust/syn/bigint.rs | 68 +
> rust/syn/buffer.rs | 400 +++
> rust/syn/custom_keyword.rs | 255 ++
> rust/syn/custom_punctuation.rs | 302 ++
> rust/syn/data.rs | 495 +++
> rust/syn/derive.rs | 276 ++
> rust/syn/discouraged.rs | 196 ++
> rust/syn/error.rs | 430 +++
> rust/syn/export.rs | 41 +
> rust/syn/expr.rs | 3560 +++++++++++++++++++
> rust/syn/ext.rs | 141 +
> rust/syn/file.rs | 127 +
> rust/syn/gen/clone.rs | 2243 ++++++++++++
> rust/syn/gen/debug.rs | 3044 +++++++++++++++++
> rust/syn/gen/eq.rs | 2197 ++++++++++++
> rust/syn/gen/fold.rs | 3343 ++++++++++++++++++
> rust/syn/gen/hash.rs | 2871 ++++++++++++++++
> rust/syn/gen/visit.rs | 3788 +++++++++++++++++++++
> rust/syn/gen/visit_mut.rs | 3788 +++++++++++++++++++++
> rust/syn/gen_helper.rs | 156 +
> rust/syn/generics.rs | 1339 ++++++++
> rust/syn/group.rs | 284 ++
> rust/syn/ident.rs | 103 +
> rust/syn/item.rs | 3315 ++++++++++++++++++
> rust/syn/lib.rs | 985 ++++++
> rust/syn/lifetime.rs | 156 +
> rust/syn/lit.rs | 1602 +++++++++
> rust/syn/lookahead.rs | 171 +
> rust/syn/mac.rs | 221 ++
> rust/syn/macros.rs | 179 +
> rust/syn/op.rs | 236 ++
> rust/syn/parse.rs | 1316 +++++++
> rust/syn/parse_macro_input.rs | 181 +
> rust/syn/parse_quote.rs | 169 +
> rust/syn/pat.rs | 929 +++++
> rust/syn/path.rs | 856 +++++
> rust/syn/print.rs | 18 +
> rust/syn/punctuated.rs | 1070 ++++++
> rust/syn/reserved.rs | 46 +
> rust/syn/sealed.rs | 6 +
> rust/syn/span.rs | 69 +
> rust/syn/spanned.rs | 116 +
> rust/syn/stmt.rs | 351 ++
> rust/syn/thread.rs | 43 +
> rust/syn/token.rs | 1015 ++++++
> rust/syn/tt.rs | 109 +
> rust/syn/ty.rs | 1288 +++++++
> rust/syn/verbatim.rs | 35 +
> rust/syn/whitespace.rs | 67 +
> samples/rust/Kconfig | 28 +
> samples/rust/Makefile | 3 +
> samples/rust/local_data_format/de.rs | 422 +++
> samples/rust/local_data_format/error.rs | 73 +
> samples/rust/local_data_format/ser.rs | 443 +++
> samples/rust/puzzle.rs | 4 +
> samples/rust/puzzle/error.rs | 91 +
> samples/rust/puzzle/inode.rs | 150 +
> samples/rust/puzzle/oci.rs | 71 +
> samples/rust/puzzle/types.rs | 389 +++
> samples/rust/puzzle/types/cbor_helpers.rs | 50 +
> samples/rust/puzzlefs.rs | 220 ++
> samples/rust/rust_fs.rs | 105 +
> samples/rust/rust_serde.rs | 125 +
> scripts/Makefile.build | 4 +-
> 160 files changed, 89204 insertions(+), 29 deletions(-)
> create mode 100644 arch/x86/configs/qemu-busybox-min.config
> create mode 100644 kernel/configs/qemu-busybox-min.config
> create mode 100644 rust/hex/error.rs
> create mode 100644 rust/hex/lib.rs
> create mode 100644 rust/hex/serde.rs
> create mode 100644 rust/kernel/cred.rs
> create mode 100644 rust/kernel/delay.rs
> create mode 100644 rust/kernel/driver.rs
> create mode 100644 rust/kernel/file.rs
> create mode 100644 rust/kernel/fs.rs
> create mode 100644 rust/kernel/fs/param.rs
> create mode 100644 rust/kernel/io_buffer.rs
> create mode 100644 rust/kernel/iov_iter.rs
> create mode 100644 rust/kernel/mm.rs
> create mode 100644 rust/kernel/mount.rs
> create mode 100644 rust/kernel/pages.rs
> create mode 100644 rust/kernel/test_serde.rs
> create mode 100644 rust/kernel/test_serde/de.rs
> create mode 100644 rust/kernel/test_serde/error.rs
> create mode 100644 rust/kernel/test_serde/ser.rs
> create mode 100644 rust/kernel/user_ptr.rs
> create mode 100644 rust/proc-macro2/detection.rs
> create mode 100644 rust/proc-macro2/fallback.rs
> create mode 100644 rust/proc-macro2/lib.rs
> create mode 100644 rust/proc-macro2/marker.rs
> create mode 100644 rust/proc-macro2/parse.rs
> create mode 100644 rust/proc-macro2/rcvec.rs
> create mode 100644 rust/proc-macro2/wrapper.rs
> create mode 100644 rust/quote/ext.rs
> create mode 100644 rust/quote/format.rs
> create mode 100644 rust/quote/ident_fragment.rs
> create mode 100644 rust/quote/lib.rs
> create mode 100644 rust/quote/runtime.rs
> create mode 100644 rust/quote/spanned.rs
> create mode 100644 rust/quote/to_tokens.rs
> create mode 100644 rust/serde/de/format.rs
> create mode 100644 rust/serde/de/ignored_any.rs
> create mode 100644 rust/serde/de/impls.rs
> create mode 100644 rust/serde/de/mod.rs
> create mode 100644 rust/serde/de/seed.rs
> create mode 100644 rust/serde/de/utf8.rs
> create mode 100644 rust/serde/de/value.rs
> create mode 100644 rust/serde/integer128.rs
> create mode 100644 rust/serde/lib.rs
> create mode 100644 rust/serde/macros.rs
> create mode 100644 rust/serde/private/de.rs
> create mode 100644 rust/serde/private/doc.rs
> create mode 100644 rust/serde/private/mod.rs
> create mode 100644 rust/serde/private/ser.rs
> create mode 100644 rust/serde/private/size_hint.rs
> create mode 100644 rust/serde/ser/fmt.rs
> create mode 100644 rust/serde/ser/impls.rs
> create mode 100644 rust/serde/ser/impossible.rs
> create mode 100644 rust/serde/ser/mod.rs
> create mode 100644 rust/serde/std_error.rs
> create mode 100644 rust/serde_cbor/de.rs
> create mode 100644 rust/serde_cbor/error.rs
> create mode 100644 rust/serde_cbor/lib.rs
> create mode 100644 rust/serde_cbor/read.rs
> create mode 100644 rust/serde_cbor/ser.rs
> create mode 100644 rust/serde_cbor/tags.rs
> create mode 100644 rust/serde_cbor/value/de.rs
> create mode 100644 rust/serde_cbor/value/mod.rs
> create mode 100644 rust/serde_cbor/value/ser.rs
> create mode 100644 rust/serde_cbor/write.rs
> create mode 100644 rust/serde_derive/bound.rs
> create mode 100644 rust/serde_derive/de.rs
> create mode 100644 rust/serde_derive/dummy.rs
> create mode 100644 rust/serde_derive/fragment.rs
> create mode 100644 rust/serde_derive/internals/ast.rs
> create mode 100644 rust/serde_derive/internals/attr.rs
> create mode 100644 rust/serde_derive/internals/case.rs
> create mode 100644 rust/serde_derive/internals/check.rs
> create mode 100644 rust/serde_derive/internals/ctxt.rs
> create mode 100644 rust/serde_derive/internals/mod.rs
> create mode 100644 rust/serde_derive/internals/receiver.rs
> create mode 100644 rust/serde_derive/internals/respan.rs
> create mode 100644 rust/serde_derive/internals/symbol.rs
> create mode 100644 rust/serde_derive/lib.rs
> create mode 100644 rust/serde_derive/pretend.rs
> create mode 100644 rust/serde_derive/ser.rs
> create mode 100644 rust/serde_derive/this.rs
> create mode 100644 rust/serde_derive/try.rs
> create mode 100644 rust/syn/attr.rs
> create mode 100644 rust/syn/await.rs
> create mode 100644 rust/syn/bigint.rs
> create mode 100644 rust/syn/buffer.rs
> create mode 100644 rust/syn/custom_keyword.rs
> create mode 100644 rust/syn/custom_punctuation.rs
> create mode 100644 rust/syn/data.rs
> create mode 100644 rust/syn/derive.rs
> create mode 100644 rust/syn/discouraged.rs
> create mode 100644 rust/syn/error.rs
> create mode 100644 rust/syn/export.rs
> create mode 100644 rust/syn/expr.rs
> create mode 100644 rust/syn/ext.rs
> create mode 100644 rust/syn/file.rs
> create mode 100644 rust/syn/gen/clone.rs
> create mode 100644 rust/syn/gen/debug.rs
> create mode 100644 rust/syn/gen/eq.rs
> create mode 100644 rust/syn/gen/fold.rs
> create mode 100644 rust/syn/gen/hash.rs
> create mode 100644 rust/syn/gen/visit.rs
> create mode 100644 rust/syn/gen/visit_mut.rs
> create mode 100644 rust/syn/gen_helper.rs
> create mode 100644 rust/syn/generics.rs
> create mode 100644 rust/syn/group.rs
> create mode 100644 rust/syn/ident.rs
> create mode 100644 rust/syn/item.rs
> create mode 100644 rust/syn/lib.rs
> create mode 100644 rust/syn/lifetime.rs
> create mode 100644 rust/syn/lit.rs
> create mode 100644 rust/syn/lookahead.rs
> create mode 100644 rust/syn/mac.rs
> create mode 100644 rust/syn/macros.rs
> create mode 100644 rust/syn/op.rs
> create mode 100644 rust/syn/parse.rs
> create mode 100644 rust/syn/parse_macro_input.rs
> create mode 100644 rust/syn/parse_quote.rs
> create mode 100644 rust/syn/pat.rs
> create mode 100644 rust/syn/path.rs
> create mode 100644 rust/syn/print.rs
> create mode 100644 rust/syn/punctuated.rs
> create mode 100644 rust/syn/reserved.rs
> create mode 100644 rust/syn/sealed.rs
> create mode 100644 rust/syn/span.rs
> create mode 100644 rust/syn/spanned.rs
> create mode 100644 rust/syn/stmt.rs
> create mode 100644 rust/syn/thread.rs
> create mode 100644 rust/syn/token.rs
> create mode 100644 rust/syn/tt.rs
> create mode 100644 rust/syn/ty.rs
> create mode 100644 rust/syn/verbatim.rs
> create mode 100644 rust/syn/whitespace.rs
> create mode 100644 samples/rust/local_data_format/de.rs
> create mode 100644 samples/rust/local_data_format/error.rs
> create mode 100644 samples/rust/local_data_format/ser.rs
> create mode 100644 samples/rust/puzzle.rs
> create mode 100644 samples/rust/puzzle/error.rs
> create mode 100644 samples/rust/puzzle/inode.rs
> create mode 100644 samples/rust/puzzle/oci.rs
> create mode 100644 samples/rust/puzzle/types.rs
> create mode 100644 samples/rust/puzzle/types/cbor_helpers.rs
> create mode 100644 samples/rust/puzzlefs.rs
> create mode 100644 samples/rust/rust_fs.rs
> create mode 100644 samples/rust/rust_serde.rs
>
> --
> 2.40.1
>
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 12/80] drop: Add crate::pr_warn declaration
2023-06-09 9:29 ` Miguel Ojeda
@ 2023-06-09 10:46 ` Ariel Miculas (amiculas)
0 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas (amiculas) @ 2023-06-09 10:46 UTC (permalink / raw)
To: Miguel Ojeda; +Cc: rust-for-linux@vger.kernel.org
Yes, I should have dropped this commit, that's why it says "drop" in the message.
Regards,
Ariel
From: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
Sent: Friday, June 9, 2023 12:29 PM
To: Ariel Miculas (amiculas) <amiculas@cisco.com>
Cc: rust-for-linux@vger.kernel.org <rust-for-linux@vger.kernel.org>
Subject: Re: [PATCH 12/80] drop: Add crate::pr_warn declaration
On Fri, Jun 9, 2023 at 9:05 AM Ariel Miculas <amiculas@cisco.com> wrote:
>
> ---
> rust/kernel/fs.rs | 1 -
> 1 file changed, 1 deletion(-)
Empty patch, probably due to rebasing fun? :)
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 10:36 ` Christian Brauner
@ 2023-06-09 11:42 ` Miguel Ojeda
[not found] ` <CH0PR11MB529981313ED5A1F815350E41CD51A@CH0PR11MB5299.namprd11.prod.outlook.com>
2023-06-09 23:52 ` Kent Overstreet
2 siblings, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 11:42 UTC (permalink / raw)
To: Christian Brauner; +Cc: Ariel Miculas, linux-fsdevel, rust-for-linux, linux-mm
On Fri, Jun 9, 2023 at 1:06 PM Christian Brauner <brauner@kernel.org> wrote:
>
> Any wrappers and code for core fs should be maintained as part of fs.
> Rust shouldn't become a way to avoid our reviews once you have a few
> wrappers added somewhere.
Definitely and, to be clear, we are strict about it (e.g.
https://rust-for-linux.com/contributing#the-rust-subsystem).
In fact, we appreciate maintainers that are willing to take patches
through their tree and take ownership of code too (e.g. KUnit and DRM
are doing so already).
I imagine Ariel sent the RFC as a way to announce his work early on,
especially given how some patches were split, the lack of commit
messages and tags, etc.
In other cases, we have reviewed patches privately first to iron out
this sort of thing, but I wasn't aware of this series coming (I knew
Ariel was working on puzzlefs and that he wanted to submit it
eventually, but not that an RFC was ready).
Also, some of the code here comes from the `rust` branch, but some of
it may not be ready yet for upstreaming.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
[not found] ` <CH0PR11MB529981313ED5A1F815350E41CD51A@CH0PR11MB5299.namprd11.prod.outlook.com>
@ 2023-06-09 11:45 ` Christian Brauner
2023-06-09 12:03 ` Ariel Miculas (amiculas)
` (2 more replies)
0 siblings, 3 replies; 134+ messages in thread
From: Christian Brauner @ 2023-06-09 11:45 UTC (permalink / raw)
To: Ariel Miculas (amiculas)
Cc: linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-mm@kvack.org
On Fri, Jun 09, 2023 at 11:22:12AM +0000, Ariel Miculas (amiculas) wrote:
> Hello Christian,
>
> I didn't send these patches to a wider audience because this is an
> initial prototype of the PuzzleFS driver, and it has a few
> prerequisites before it could be even considered for merging. First of
> all, the rust filesystem abstractions and their dependencies need to
> be upstreamed, then there needs to be a discussion regarding the
Yes.
> inclusion of third-party crates in the linux kernel.
>
> My plan was to send these patches to the rust-for-linux mailing list and then start a discussion with Miguel Ojeda regarding the upstreaming approach.
> There are a lot of new files added in this patch series because I've included all the dependencies required so that my patches could be applied to the rust-next branch, but these dependencies will most likely need to be upstreamed separately.
>
> It was never my intention to avoid your reviews, should I also send
> subsequent patches to linux-fsdevel, even if they're in the early
> stages of development?
Yeah, I think that would be great.
Because the series you sent here touches on a lot of things in terms of
infrastructure alone. That work could very well be rather interesting
independent of PuzzleFS. We might just want to get enough infrastructure
to start porting a tiny existing fs (binderfs or something similar
small) to Rust to see how feasible this is and to wet our appetite for
bigger changes such as accepting a new filesystem driver completely
written in Rust.
But aside from the infrastructure discussion:
This is yet another filesystem for solving the container image problem
in the kernel with the addition of yet another filesystem. We just went
through this excercise with another filesystem. So I'd expect some
reluctance here. Tbh, the container world keeps sending us filesystems
at an alarming rate. That's two within a few months and that leaves a
rather disorganized impression.
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 11:45 ` Christian Brauner
@ 2023-06-09 12:03 ` Ariel Miculas (amiculas)
2023-06-09 12:56 ` Gao Xiang
2023-06-09 12:07 ` Miguel Ojeda
2023-06-09 12:20 ` Colin Walters
2 siblings, 1 reply; 134+ messages in thread
From: Ariel Miculas (amiculas) @ 2023-06-09 12:03 UTC (permalink / raw)
To: Christian Brauner
Cc: linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-mm@kvack.org, Serge Hallyn (shallyn)
Adding Serge Hallyn for visibility.
Regards,
Ariel
From: Christian Brauner <brauner@kernel.org>
Sent: Friday, June 9, 2023 2:45 PM
To: Ariel Miculas (amiculas) <amiculas@cisco.com>
Cc: linux-fsdevel@vger.kernel.org <linux-fsdevel@vger.kernel.org>; rust-for-linux@vger.kernel.org <rust-for-linux@vger.kernel.org>; linux-mm@kvack.org <linux-mm@kvack.org>
Subject: Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
On Fri, Jun 09, 2023 at 11:22:12AM +0000, Ariel Miculas (amiculas) wrote:
> Hello Christian,
>
> I didn't send these patches to a wider audience because this is an
> initial prototype of the PuzzleFS driver, and it has a few
> prerequisites before it could be even considered for merging. First of
> all, the rust filesystem abstractions and their dependencies need to
> be upstreamed, then there needs to be a discussion regarding the
Yes.
> inclusion of third-party crates in the linux kernel.
>
> My plan was to send these patches to the rust-for-linux mailing list and then start a discussion with Miguel Ojeda regarding the upstreaming approach.
> There are a lot of new files added in this patch series because I've included all the dependencies required so that my patches could be applied to the rust-next branch, but these dependencies will most likely need to be upstreamed separately.
>
> It was never my intention to avoid your reviews, should I also send
> subsequent patches to linux-fsdevel, even if they're in the early
> stages of development?
Yeah, I think that would be great.
Because the series you sent here touches on a lot of things in terms of
infrastructure alone. That work could very well be rather interesting
independent of PuzzleFS. We might just want to get enough infrastructure
to start porting a tiny existing fs (binderfs or something similar
small) to Rust to see how feasible this is and to wet our appetite for
bigger changes such as accepting a new filesystem driver completely
written in Rust.
But aside from the infrastructure discussion:
This is yet another filesystem for solving the container image problem
in the kernel with the addition of yet another filesystem. We just went
through this excercise with another filesystem. So I'd expect some
reluctance here. Tbh, the container world keeps sending us filesystems
at an alarming rate. That's two within a few months and that leaves a
rather disorganized impression.
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 11:45 ` Christian Brauner
2023-06-09 12:03 ` Ariel Miculas (amiculas)
@ 2023-06-09 12:07 ` Miguel Ojeda
2023-06-09 12:11 ` Ariel Miculas (amiculas)
2023-06-09 13:05 ` Alice Ryhl
2023-06-09 12:20 ` Colin Walters
2 siblings, 2 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 12:07 UTC (permalink / raw)
To: Christian Brauner
Cc: Ariel Miculas (amiculas), linux-fsdevel@vger.kernel.org,
rust-for-linux@vger.kernel.org, linux-mm@kvack.org, Alice Ryhl
On Fri, Jun 9, 2023 at 1:48 PM Christian Brauner <brauner@kernel.org> wrote:
>
> Because the series you sent here touches on a lot of things in terms of
> infrastructure alone. That work could very well be rather interesting
> independent of PuzzleFS. We might just want to get enough infrastructure
> to start porting a tiny existing fs (binderfs or something similar
> small) to Rust to see how feasible this is and to wet our appetite for
> bigger changes such as accepting a new filesystem driver completely
> written in Rust.
That would be great, thanks Christian! (Cc'ing Alice for binderfs -- I
think Rust Binder is keeping binderfs in C for the moment, but if you
are willing to try things, they are probably interested :)
Ariel: sorry, we crossed messages; I didn't receive your message at
[1], the rust-for-linux list probably dropped it due to the included
HTML.
[1] https://lore.kernel.org/linux-mm/CH0PR11MB529981313ED5A1F815350E41CD51A@CH0PR11MB5299.namprd11.prod.outlook.com/
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 12:07 ` Miguel Ojeda
@ 2023-06-09 12:11 ` Ariel Miculas (amiculas)
2023-06-09 12:21 ` Greg KH
2023-06-09 13:05 ` Alice Ryhl
1 sibling, 1 reply; 134+ messages in thread
From: Ariel Miculas (amiculas) @ 2023-06-09 12:11 UTC (permalink / raw)
To: Miguel Ojeda, Christian Brauner
Cc: linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-mm@kvack.org, Alice Ryhl
Sorry about that, it seems like I need to switch to plain text mode for every reply in outlook, which is annoying.
Regards,
Ariel
From: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
Sent: Friday, June 9, 2023 3:07 PM
To: Christian Brauner <brauner@kernel.org>
Cc: Ariel Miculas (amiculas) <amiculas@cisco.com>; linux-fsdevel@vger.kernel.org <linux-fsdevel@vger.kernel.org>; rust-for-linux@vger.kernel.org <rust-for-linux@vger.kernel.org>; linux-mm@kvack.org <linux-mm@kvack.org>; Alice Ryhl <aliceryhl@google.com>
Subject: Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
On Fri, Jun 9, 2023 at 1:48 PM Christian Brauner <brauner@kernel.org> wrote:
>
> Because the series you sent here touches on a lot of things in terms of
> infrastructure alone. That work could very well be rather interesting
> independent of PuzzleFS. We might just want to get enough infrastructure
> to start porting a tiny existing fs (binderfs or something similar
> small) to Rust to see how feasible this is and to wet our appetite for
> bigger changes such as accepting a new filesystem driver completely
> written in Rust.
That would be great, thanks Christian! (Cc'ing Alice for binderfs -- I
think Rust Binder is keeping binderfs in C for the moment, but if you
are willing to try things, they are probably interested :)
Ariel: sorry, we crossed messages; I didn't receive your message at
[1], the rust-for-linux list probably dropped it due to the included
HTML.
[1] https://lore.kernel.org/linux-mm/CH0PR11MB529981313ED5A1F815350E41CD51A@CH0PR11MB5299.namprd11.prod.outlook.com/
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 11:45 ` Christian Brauner
2023-06-09 12:03 ` Ariel Miculas (amiculas)
2023-06-09 12:07 ` Miguel Ojeda
@ 2023-06-09 12:20 ` Colin Walters
2023-06-09 12:42 ` Christian Brauner
2023-06-09 13:45 ` Ariel Miculas (amiculas)
2 siblings, 2 replies; 134+ messages in thread
From: Colin Walters @ 2023-06-09 12:20 UTC (permalink / raw)
To: Christian Brauner, Ariel Miculas (amiculas)
Cc: linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-mm@kvack.org
On Fri, Jun 9, 2023, at 7:45 AM, Christian Brauner wrote:
>
> Because the series you sent here touches on a lot of things in terms of
> infrastructure alone. That work could very well be rather interesting
> independent of PuzzleFS. We might just want to get enough infrastructure
> to start porting a tiny existing fs (binderfs or something similar
> small) to Rust to see how feasible this is and to wet our appetite for
> bigger changes such as accepting a new filesystem driver completely
> written in Rust.
(Not a kernel developer, but this argument makes sense to me)
> But aside from the infrastructure discussion:
>
> This is yet another filesystem for solving the container image problem
> in the kernel with the addition of yet another filesystem. We just went
> through this excercise with another filesystem. So I'd expect some
> reluctance here. Tbh, the container world keeps sending us filesystems
> at an alarming rate. That's two within a few months and that leaves a
> rather disorganized impression.
I am sure you are aware there's not some "container world" monoculture, there are many organizations, people and companies here with some healthy co-opetition but also some duplication inherent from that.
That said at a practical level, Ariel in the https://github.com/containers GH organization we're kind of a "big tent" place. A subset of the organization is very heavily Rust oriented now (certainly the parts I touch) and briefly skimming the puzzlefs code, there are definitely some bits of code we could consider sharing in userspace. Actually though since this isn't releated to the in-kernel discussion I'll file an issue on Github and we can discuss there.
But there is definitely a subset of the discussion that Christian is referring to here that is about the intersections/overlap with the composefs approach that is relevant for this list. Maybe we could try to collaborate on an unbiased "puzzlefs vs composefs" document? (What's in https://github.com/anuvu/puzzlefs/tree/master/doc is a bit sparse right now)
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 12:11 ` Ariel Miculas (amiculas)
@ 2023-06-09 12:21 ` Greg KH
0 siblings, 0 replies; 134+ messages in thread
From: Greg KH @ 2023-06-09 12:21 UTC (permalink / raw)
To: Ariel Miculas (amiculas)
Cc: Miguel Ojeda, Christian Brauner, linux-fsdevel@vger.kernel.org,
rust-for-linux@vger.kernel.org, linux-mm@kvack.org, Alice Ryhl
On Fri, Jun 09, 2023 at 12:11:14PM +0000, Ariel Miculas (amiculas) wrote:
> Sorry about that, it seems like I need to switch to plain text mode for every reply in outlook, which is annoying.
You should also turn off the top-posting mode, as that's not good
etiquette on the kernel mailing lists for obvious reasons :)
thanks,
greg k-h
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 12:20 ` Colin Walters
@ 2023-06-09 12:42 ` Christian Brauner
2023-06-09 17:28 ` Serge Hallyn
2023-06-09 13:45 ` Ariel Miculas (amiculas)
1 sibling, 1 reply; 134+ messages in thread
From: Christian Brauner @ 2023-06-09 12:42 UTC (permalink / raw)
To: Colin Walters
Cc: Ariel Miculas (amiculas), linux-fsdevel@vger.kernel.org,
rust-for-linux@vger.kernel.org, linux-mm@kvack.org
On Fri, Jun 09, 2023 at 08:20:30AM -0400, Colin Walters wrote:
>
>
> On Fri, Jun 9, 2023, at 7:45 AM, Christian Brauner wrote:
> >
> > Because the series you sent here touches on a lot of things in terms of
> > infrastructure alone. That work could very well be rather interesting
> > independent of PuzzleFS. We might just want to get enough infrastructure
> > to start porting a tiny existing fs (binderfs or something similar
> > small) to Rust to see how feasible this is and to wet our appetite for
> > bigger changes such as accepting a new filesystem driver completely
> > written in Rust.
>
> (Not a kernel developer, but this argument makes sense to me)
>
> > But aside from the infrastructure discussion:
> >
> > This is yet another filesystem for solving the container image problem
> > in the kernel with the addition of yet another filesystem. We just went
> > through this excercise with another filesystem. So I'd expect some
> > reluctance here. Tbh, the container world keeps sending us filesystems
> > at an alarming rate. That's two within a few months and that leaves a
> > rather disorganized impression.
>
> I am sure you are aware there's not some "container world"
> monoculture, there are many organizations, people and companies here
That submission here explicitly references OCI v2. Composefs explicitly
advertises 100% compatibility with OCI. So, there's a set of OCI specs
including runtime and image. As far as I'm concerned you're all one
container world under the OCI umbrella.
We're not going to push multiple filesystems into the kernel that all do
slightly different things but all serve the OCI container world and use
some spec as an argument to move stuff into the kernel.
The OCI initiative is hailed as unifying the container ecosystem. Hence,
we can expect coordination.
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 12:03 ` Ariel Miculas (amiculas)
@ 2023-06-09 12:56 ` Gao Xiang
0 siblings, 0 replies; 134+ messages in thread
From: Gao Xiang @ 2023-06-09 12:56 UTC (permalink / raw)
To: Ariel Miculas (amiculas), Christian Brauner
Cc: linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-mm@kvack.org, Serge Hallyn (shallyn), Colin Walters
On 2023/6/9 20:03, Ariel Miculas (amiculas) wrote:
...
>
> But aside from the infrastructure discussion:
>
> This is yet another filesystem for solving the container image problem
> in the kernel with the addition of yet another filesystem. We just went
> through this excercise with another filesystem. So I'd expect some
> reluctance here. Tbh, the container world keeps sending us filesystems
> at an alarming rate. That's two within a few months and that leaves a
> rather disorganized impression.
Just a head up. Since Rust kernel infrastructure is too premature,
it's impossible to handle page cache / iomap and many useful stuffs.
In the long term, at least (someday) after Rust infrastructure is
mature, I will implement EROFS ino Rust as a try as well.
As for chunk CDC, I don't see it's hard (since we already have some
CDC approach since Linux v6.1) but as an effective disk filesystem
for performance, EROFS on-disk data is all block-aligned to match
storage and page cache alignment. If it's really needed, I could
update a more complete (but ineffective and slow) index version to
implement unaligned extents (both for decoded and encoded sides).
Yet I really think the main purpose of a kernel filesystem is to
make full use of kernel infrastructure for performance (like page
cache handling) otherwise a FUSE approach is enough.
Finally, as for OCI container image stuffs, I'd like to avoid
saying this topic anymore on the list (too tired about this). I've
seen _three_ in-kernel approaches already before this one and I tend
to avoid listing the complete names (including FUSE alternatives)
here. I really suggest if you guys could sit down and plan at least
a complete OCI standard for the next image format (even you don't
want to reuse any exist filesystem for whatever reasons).
Thanks,
Gao Xiang
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 12:07 ` Miguel Ojeda
2023-06-09 12:11 ` Ariel Miculas (amiculas)
@ 2023-06-09 13:05 ` Alice Ryhl
1 sibling, 0 replies; 134+ messages in thread
From: Alice Ryhl @ 2023-06-09 13:05 UTC (permalink / raw)
To: miguel.ojeda.sandonis, brauner
Cc: amiculas, linux-fsdevel, linux-mm, rust-for-linux, aliceryhl
Miguel Ojeda writes:
> That would be great, thanks Christian! (Cc'ing Alice for binderfs -- I
> think Rust Binder is keeping binderfs in C for the moment, but if you
> are willing to try things, they are probably interested :)
Yeah, Rust binder already needs bindings to a lot of other parts of the
kernel, so I decided to not rewrite the binderfs part for now to cut
down on the number of subsystems I would need to upstream bindings for.
Upstreaming bindings has proved to be a lot of work.
If someone else wants to upstream the filesystem bindings that binderfs
would need, then we can certainly look into using them in Rust binder.
Alice
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 12:20 ` Colin Walters
2023-06-09 12:42 ` Christian Brauner
@ 2023-06-09 13:45 ` Ariel Miculas (amiculas)
2023-06-09 17:10 ` Trilok Soni
1 sibling, 1 reply; 134+ messages in thread
From: Ariel Miculas (amiculas) @ 2023-06-09 13:45 UTC (permalink / raw)
To: Colin Walters, Christian Brauner
Cc: linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-mm@kvack.org
A "puzzlefs vs composefs" document sounds like a good idea. The documentation in puzzlefs is a little outdated and could be improved.
Feel free to create a github issue and tag me in there.
PS: as soon as I figure out how to turn off the top-posting mode, I'll do it.
Regards,
Ariel
________________________________________
From: Colin Walters <walters@verbum.org>
Sent: Friday, June 9, 2023 3:20 PM
To: Christian Brauner; Ariel Miculas (amiculas)
Cc: linux-fsdevel@vger.kernel.org; rust-for-linux@vger.kernel.org; linux-mm@kvack.org
Subject: Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
On Fri, Jun 9, 2023, at 7:45 AM, Christian Brauner wrote:
>
> Because the series you sent here touches on a lot of things in terms of
> infrastructure alone. That work could very well be rather interesting
> independent of PuzzleFS. We might just want to get enough infrastructure
> to start porting a tiny existing fs (binderfs or something similar
> small) to Rust to see how feasible this is and to wet our appetite for
> bigger changes such as accepting a new filesystem driver completely
> written in Rust.
(Not a kernel developer, but this argument makes sense to me)
> But aside from the infrastructure discussion:
>
> This is yet another filesystem for solving the container image problem
> in the kernel with the addition of yet another filesystem. We just went
> through this excercise with another filesystem. So I'd expect some
> reluctance here. Tbh, the container world keeps sending us filesystems
> at an alarming rate. That's two within a few months and that leaves a
> rather disorganized impression.
I am sure you are aware there's not some "container world" monoculture, there are many organizations, people and companies here with some healthy co-opetition but also some duplication inherent from that.
That said at a practical level, Ariel in the https://github.com/containers GH organization we're kind of a "big tent" place. A subset of the organization is very heavily Rust oriented now (certainly the parts I touch) and briefly skimming the puzzlefs code, there are definitely some bits of code we could consider sharing in userspace. Actually though since this isn't releated to the in-kernel discussion I'll file an issue on Github and we can discuss there.
But there is definitely a subset of the discussion that Christian is referring to here that is about the intersections/overlap with the composefs approach that is relevant for this list. Maybe we could try to collaborate on an unbiased "puzzlefs vs composefs" document? (What's in https://github.com/anuvu/puzzlefs/tree/master/doc is a bit sparse right now)
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 13:45 ` Ariel Miculas (amiculas)
@ 2023-06-09 17:10 ` Trilok Soni
2023-06-09 17:16 ` Ariel Miculas (amiculas)
0 siblings, 1 reply; 134+ messages in thread
From: Trilok Soni @ 2023-06-09 17:10 UTC (permalink / raw)
To: Ariel Miculas (amiculas), Colin Walters, Christian Brauner
Cc: linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-mm@kvack.org
On 6/9/2023 6:45 AM, Ariel Miculas (amiculas) wrote:
> A "puzzlefs vs composefs" document sounds like a good idea. The documentation in puzzlefs is a little outdated and could be improved.
> Feel free to create a github issue and tag me in there.
>
> PS: as soon as I figure out how to turn off the top-posting mode, I'll do it.
>
Let me know as well if you could do w/ Outlook :). Switch to other email
clients if possible.
---Trilok Soni
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 17:10 ` Trilok Soni
@ 2023-06-09 17:16 ` Ariel Miculas (amiculas)
2023-06-09 17:41 ` Miguel Ojeda
2023-06-09 18:43 ` James Bottomley
0 siblings, 2 replies; 134+ messages in thread
From: Ariel Miculas (amiculas) @ 2023-06-09 17:16 UTC (permalink / raw)
To: Trilok Soni, Colin Walters, Christian Brauner
Cc: linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-mm@kvack.org
I could switch to my personal gmail, but last time Miguel Ojeda asked me to use my cisco email when I send commits signed off by amiculas@cisco.com.
If this is not a hard requirement, then I could switch.
Regards,
Ariel
________________________________________
From: Trilok Soni <quic_tsoni@quicinc.com>
Sent: Friday, June 9, 2023 8:10 PM
To: Ariel Miculas (amiculas); Colin Walters; Christian Brauner
Cc: linux-fsdevel@vger.kernel.org; rust-for-linux@vger.kernel.org; linux-mm@kvack.org
Subject: Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
On 6/9/2023 6:45 AM, Ariel Miculas (amiculas) wrote:
> A "puzzlefs vs composefs" document sounds like a good idea. The documentation in puzzlefs is a little outdated and could be improved.
> Feel free to create a github issue and tag me in there.
>
> PS: as soon as I figure out how to turn off the top-posting mode, I'll do it.
>
Let me know as well if you could do w/ Outlook :). Switch to other email
clients if possible.
---Trilok Soni
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 12:42 ` Christian Brauner
@ 2023-06-09 17:28 ` Serge Hallyn
0 siblings, 0 replies; 134+ messages in thread
From: Serge Hallyn @ 2023-06-09 17:28 UTC (permalink / raw)
To: Christian Brauner
Cc: Colin Walters, Ariel Miculas (amiculas),
linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-mm@kvack.org
On Fri, Jun 09, 2023 at 02:42:47PM +0200, Christian Brauner wrote:
> On Fri, Jun 09, 2023 at 08:20:30AM -0400, Colin Walters wrote:
> >
> >
> > On Fri, Jun 9, 2023, at 7:45 AM, Christian Brauner wrote:
> > >
> > > Because the series you sent here touches on a lot of things in terms of
> > > infrastructure alone. That work could very well be rather interesting
> > > independent of PuzzleFS. We might just want to get enough infrastructure
> > > to start porting a tiny existing fs (binderfs or something similar
> > > small) to Rust to see how feasible this is and to wet our appetite for
> > > bigger changes such as accepting a new filesystem driver completely
> > > written in Rust.
> >
> > (Not a kernel developer, but this argument makes sense to me)
> >
> > > But aside from the infrastructure discussion:
> > >
> > > This is yet another filesystem for solving the container image problem
> > > in the kernel with the addition of yet another filesystem. We just went
> > > through this excercise with another filesystem. So I'd expect some
> > > reluctance here. Tbh, the container world keeps sending us filesystems
> > > at an alarming rate. That's two within a few months and that leaves a
> > > rather disorganized impression.
> >
> > I am sure you are aware there's not some "container world"
> > monoculture, there are many organizations, people and companies here
>
> That submission here explicitly references OCI v2. Composefs explicitly
> advertises 100% compatibility with OCI. So, there's a set of OCI specs
OCI v2 doesn't currently exist :) There were many design goals for
it, and near as I can tell, after everyone discussed those together,
everyone went off to work on implementing the bits the needed - which
is a right and proper step before coming back and comparing notes
about what went well, etc.
The two main things where puzzlefs is experimenting are the use of
content defined chunking (CDC), and being written in rust (and,
especially, to have the same rust code base be used for the in kernel
driver, the fuse mounter, the builder, and the userspace extractor).
(Well, and reproducible images through a canonical representation,
but...)
It requires a POC in order to really determine whether the CDC will
be worth it, or will have pitfalls. So far, it looks very promising.
Adding that functionality to composefs one day could be cool.
Likewise, it requires a user in order to push on the infrastructure
required to support a full filesystem in rust in the kernel. But that
really isn't something we can "add to composefs". :)
The main goal of this posting, then, was to show the infrastructure
pieces and work on that with the community. We're definitely not
(currently :) asking for puzzlefs to be included. However, it is
our (business and personal) justification for the rest of the work.
> including runtime and image. As far as I'm concerned you're all one
> container world under the OCI umbrella.
One big happy family!
> We're not going to push multiple filesystems into the kernel that all do
> slightly different things but all serve the OCI container world and use
> some spec as an argument to move stuff into the kernel.
>
> The OCI initiative is hailed as unifying the container ecosystem. Hence,
> we can expect coordination.
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 17:16 ` Ariel Miculas (amiculas)
@ 2023-06-09 17:41 ` Miguel Ojeda
2023-06-09 18:49 ` James Bottomley
2023-06-09 18:43 ` James Bottomley
1 sibling, 1 reply; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 17:41 UTC (permalink / raw)
To: Ariel Miculas (amiculas)
Cc: Trilok Soni, Colin Walters, Christian Brauner,
linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-mm@kvack.org
On Fri, Jun 9, 2023 at 7:25 PM Ariel Miculas (amiculas)
<amiculas@cisco.com> wrote:
>
> I could switch to my personal gmail, but last time Miguel Ojeda asked me to use my cisco email when I send commits signed off by amiculas@cisco.com.
> If this is not a hard requirement, then I could switch.
For patches, yeah, that is ideal, so that it matches the Git author / `From:`.
But for the other emails, you could use your personal address, if that
makes things easier.
Hope that helps!
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 17:16 ` Ariel Miculas (amiculas)
2023-06-09 17:41 ` Miguel Ojeda
@ 2023-06-09 18:43 ` James Bottomley
2023-06-09 18:59 ` Ariel Miculas (amiculas)
1 sibling, 1 reply; 134+ messages in thread
From: James Bottomley @ 2023-06-09 18:43 UTC (permalink / raw)
To: Ariel Miculas (amiculas), Trilok Soni, Colin Walters,
Christian Brauner
Cc: linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-mm@kvack.org
On Fri, 2023-06-09 at 17:16 +0000, Ariel Miculas (amiculas) wrote:
> I could switch to my personal gmail, but last time Miguel Ojeda asked
> me to use my cisco email when I send commits signed off by
> amiculas@cisco.com.
> If this is not a hard requirement, then I could switch.
For sending patches, you can simply use git-send-email. All you need
to point it at is the outgoing email server (which should be a config
setting in whatever tool you are using now). We have a (reasonably) up
to date document with some recommendations:
https://www.kernel.org/doc/html/latest/process/email-clients.html
I've successfully used evolution with an exchange server for many
years, but the interface isn't to everyone's taste and Mozilla
Thunderbird is also known to connect to it. Basic outlook has proven
impossible to configure correctly (which is why it doesn't have an
entry).
Regards,
James
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 17:41 ` Miguel Ojeda
@ 2023-06-09 18:49 ` James Bottomley
2023-06-09 19:08 ` Miguel Ojeda
2023-06-09 19:11 ` Ariel Miculas
0 siblings, 2 replies; 134+ messages in thread
From: James Bottomley @ 2023-06-09 18:49 UTC (permalink / raw)
To: Miguel Ojeda, Ariel Miculas (amiculas)
Cc: Trilok Soni, Colin Walters, Christian Brauner,
linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-mm@kvack.org
On Fri, 2023-06-09 at 19:41 +0200, Miguel Ojeda wrote:
> On Fri, Jun 9, 2023 at 7:25 PM Ariel Miculas (amiculas)
> <amiculas@cisco.com> wrote:
> >
> > I could switch to my personal gmail, but last time Miguel Ojeda
> > asked me to use my cisco email when I send commits signed off by
> > amiculas@cisco.com. If this is not a hard requirement, then I could
> > switch.
>
> For patches, yeah, that is ideal, so that it matches the Git author /
> `From:`.
>
> But for the other emails, you could use your personal address, if
> that makes things easier.
It's still not a requirement, though. You can send from your gmail
account and still have
From: Ariel Miculas <amiculas@cisco.com>
As the first line (separated from the commit message by a blank line),
which git am (or b4) will pick up as the author email. This behaviour
is specifically for people who want the author to be their corporate
email address, but have failed to persuade corporate IT to make it
possible.
Regards,
James
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 18:43 ` James Bottomley
@ 2023-06-09 18:59 ` Ariel Miculas (amiculas)
2023-06-09 19:20 ` Ariel Miculas
2023-06-09 19:53 ` Alice Ryhl
0 siblings, 2 replies; 134+ messages in thread
From: Ariel Miculas (amiculas) @ 2023-06-09 18:59 UTC (permalink / raw)
To: James Bottomley, Trilok Soni, Colin Walters, Christian Brauner
Cc: linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-mm@kvack.org
I did use git send-email for sending this patch series, but I cannot find any setting in the Outlook web client for disabling "top posting" when replying to emails:
https://answers.microsoft.com/en-us/outlook_com/forum/all/eliminate-top-posting/5e1e5729-30f8-41e9-84cb-fb5e81229c7c
Regards,
Ariel
________________________________________
From: James Bottomley <James.Bottomley@HansenPartnership.com>
Sent: Friday, June 9, 2023 9:43 PM
To: Ariel Miculas (amiculas); Trilok Soni; Colin Walters; Christian Brauner
Cc: linux-fsdevel@vger.kernel.org; rust-for-linux@vger.kernel.org; linux-mm@kvack.org
Subject: Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
On Fri, 2023-06-09 at 17:16 +0000, Ariel Miculas (amiculas) wrote:
> I could switch to my personal gmail, but last time Miguel Ojeda asked
> me to use my cisco email when I send commits signed off by
> amiculas@cisco.com.
> If this is not a hard requirement, then I could switch.
For sending patches, you can simply use git-send-email. All you need
to point it at is the outgoing email server (which should be a config
setting in whatever tool you are using now). We have a (reasonably) up
to date document with some recommendations:
https://www.kernel.org/doc/html/latest/process/email-clients.html
I've successfully used evolution with an exchange server for many
years, but the interface isn't to everyone's taste and Mozilla
Thunderbird is also known to connect to it. Basic outlook has proven
impossible to configure correctly (which is why it doesn't have an
entry).
Regards,
James
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 18:49 ` James Bottomley
@ 2023-06-09 19:08 ` Miguel Ojeda
2023-06-09 19:11 ` Ariel Miculas
1 sibling, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-09 19:08 UTC (permalink / raw)
To: James Bottomley
Cc: Ariel Miculas (amiculas), Trilok Soni, Colin Walters,
Christian Brauner, linux-fsdevel@vger.kernel.org,
rust-for-linux@vger.kernel.org, linux-mm@kvack.org
On Fri, Jun 9, 2023 at 8:49 PM James Bottomley
<James.Bottomley@hansenpartnership.com> wrote:
>
> On Fri, 2023-06-09 at 19:41 +0200, Miguel Ojeda wrote:
> > For patches, yeah, that is ideal, so that it matches the Git author /
> > `From:`.
>
> It's still not a requirement, though. You can send from your gmail
> account and still have
Yeah, that is what I said "ideal". When Ariel sent the first patch, he
didn't use the `From:` tag within the email body, but attributed it to
Cisco, and I had not seen any commit/email from his Cisco address yet,
so I asked him if he could use his corporate address.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 18:49 ` James Bottomley
2023-06-09 19:08 ` Miguel Ojeda
@ 2023-06-09 19:11 ` Ariel Miculas
2023-06-09 20:01 ` James Bottomley
2023-06-10 9:34 ` Miguel Ojeda
1 sibling, 2 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 19:11 UTC (permalink / raw)
To: James Bottomley
Cc: Miguel Ojeda, Ariel Miculas (amiculas), Trilok Soni,
Colin Walters, Christian Brauner, linux-fsdevel@vger.kernel.org,
rust-for-linux@vger.kernel.org, linux-mm@kvack.org
Yes, but then how can you be sure that amiculas@cisco.com is the real
author of the commit? I think that's why Miguel Ojeda asked me to send
them from my business email, otherwise some random gmail account could
claim that he is "Ariel Miculas", so he's entitled to sign-off as
amiculas@cisco.com.
Regards,
Ariel
On Fri, Jun 9, 2023 at 10:03 PM James Bottomley
<James.Bottomley@hansenpartnership.com> wrote:
>
> On Fri, 2023-06-09 at 19:41 +0200, Miguel Ojeda wrote:
> > On Fri, Jun 9, 2023 at 7:25 PM Ariel Miculas (amiculas)
> > <amiculas@cisco.com> wrote:
> > >
> > > I could switch to my personal gmail, but last time Miguel Ojeda
> > > asked me to use my cisco email when I send commits signed off by
> > > amiculas@cisco.com. If this is not a hard requirement, then I could
> > > switch.
> >
> > For patches, yeah, that is ideal, so that it matches the Git author /
> > `From:`.
> >
> > But for the other emails, you could use your personal address, if
> > that makes things easier.
>
> It's still not a requirement, though. You can send from your gmail
> account and still have
>
> From: Ariel Miculas <amiculas@cisco.com>
>
> As the first line (separated from the commit message by a blank line),
> which git am (or b4) will pick up as the author email. This behaviour
> is specifically for people who want the author to be their corporate
> email address, but have failed to persuade corporate IT to make it
> possible.
>
> Regards,
>
> James
>
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 18:59 ` Ariel Miculas (amiculas)
@ 2023-06-09 19:20 ` Ariel Miculas
2023-06-09 19:45 ` Trilok Soni
2023-06-09 19:53 ` Alice Ryhl
1 sibling, 1 reply; 134+ messages in thread
From: Ariel Miculas @ 2023-06-09 19:20 UTC (permalink / raw)
To: Ariel Miculas (amiculas)
Cc: James Bottomley, Trilok Soni, Colin Walters, Christian Brauner,
linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-mm@kvack.org
Now if I could figure out how to disable "top posting" in gmail...
Regards,
Ariel
On Fri, Jun 9, 2023 at 10:06 PM Ariel Miculas (amiculas)
<amiculas@cisco.com> wrote:
>
> I did use git send-email for sending this patch series, but I cannot find any setting in the Outlook web client for disabling "top posting" when replying to emails:
> https://answers.microsoft.com/en-us/outlook_com/forum/all/eliminate-top-posting/5e1e5729-30f8-41e9-84cb-fb5e81229c7c
>
> Regards,
> Ariel
>
> ________________________________________
> From: James Bottomley <James.Bottomley@HansenPartnership.com>
> Sent: Friday, June 9, 2023 9:43 PM
> To: Ariel Miculas (amiculas); Trilok Soni; Colin Walters; Christian Brauner
> Cc: linux-fsdevel@vger.kernel.org; rust-for-linux@vger.kernel.org; linux-mm@kvack.org
> Subject: Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
>
> On Fri, 2023-06-09 at 17:16 +0000, Ariel Miculas (amiculas) wrote:
> > I could switch to my personal gmail, but last time Miguel Ojeda asked
> > me to use my cisco email when I send commits signed off by
> > amiculas@cisco.com.
> > If this is not a hard requirement, then I could switch.
>
> For sending patches, you can simply use git-send-email. All you need
> to point it at is the outgoing email server (which should be a config
> setting in whatever tool you are using now). We have a (reasonably) up
> to date document with some recommendations:
>
> https://www.kernel.org/doc/html/latest/process/email-clients.html
>
> I've successfully used evolution with an exchange server for many
> years, but the interface isn't to everyone's taste and Mozilla
> Thunderbird is also known to connect to it. Basic outlook has proven
> impossible to configure correctly (which is why it doesn't have an
> entry).
>
> Regards,
>
> James
>
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 19:20 ` Ariel Miculas
@ 2023-06-09 19:45 ` Trilok Soni
0 siblings, 0 replies; 134+ messages in thread
From: Trilok Soni @ 2023-06-09 19:45 UTC (permalink / raw)
To: Ariel Miculas, Ariel Miculas (amiculas)
Cc: James Bottomley, Colin Walters, Christian Brauner,
linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-mm@kvack.org
On 6/9/2023 12:20 PM, Ariel Miculas wrote:
> Now if I could figure out how to disable "top posting" in gmail...
>
> Regards,
> Ariel
>
> On Fri, Jun 9, 2023 at 10:06 PM Ariel Miculas (amiculas)
> <amiculas@cisco.com> wrote:
>>
>> I did use git send-email for sending this patch series, but I cannot find any setting in the Outlook web client for disabling "top posting" when replying to emails:
>> https://answers.microsoft.com/en-us/outlook_com/forum/all/eliminate-top-posting/5e1e5729-30f8-41e9-84cb-fb5e81229c7c
>>
>> Regards,
>> Ariel
>>
>> ________________________________________
>> From: James Bottomley <James.Bottomley@HansenPartnership.com>
>> Sent: Friday, June 9, 2023 9:43 PM
>> To: Ariel Miculas (amiculas); Trilok Soni; Colin Walters; Christian Brauner
>> Cc: linux-fsdevel@vger.kernel.org; rust-for-linux@vger.kernel.org; linux-mm@kvack.org
>> Subject: Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
>>
>> On Fri, 2023-06-09 at 17:16 +0000, Ariel Miculas (amiculas) wrote:
>>> I could switch to my personal gmail, but last time Miguel Ojeda asked
>>> me to use my cisco email when I send commits signed off by
>>> amiculas@cisco.com.
>>> If this is not a hard requirement, then I could switch.
>>
>> For sending patches, you can simply use git-send-email. All you need
>> to point it at is the outgoing email server (which should be a config
>> setting in whatever tool you are using now). We have a (reasonably) up
>> to date document with some recommendations:
>>
>> https://www.kernel.org/doc/html/latest/process/email-clients.html
>>
>> I've successfully used evolution with an exchange server for many
>> years, but the interface isn't to everyone's taste and Mozilla
>> Thunderbird is also known to connect to it. Basic outlook has proven
>> impossible to configure correctly (which is why it doesn't have an
>> entry).
One of the big reasons why I have quic_tsoni at quicinc dot com
(Thunderbird/Mutt/whateveryoulike except Outlook) on top of tsoni at
quicinc dot com (Outlook) to make sure that we comply w/ upstream
guidelines. Two email IDs are hard to manage but it gives
the good separation and freedom.
---Trilok Soni
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 18:59 ` Ariel Miculas (amiculas)
2023-06-09 19:20 ` Ariel Miculas
@ 2023-06-09 19:53 ` Alice Ryhl
1 sibling, 0 replies; 134+ messages in thread
From: Alice Ryhl @ 2023-06-09 19:53 UTC (permalink / raw)
To: Ariel Miculas (amiculas), James Bottomley, Trilok Soni,
Colin Walters, Christian Brauner
Cc: linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-mm@kvack.org
On 6/9/23 20:59, Ariel Miculas (amiculas) wrote:
> I did use git send-email for sending this patch series, but I cannot find any setting in the Outlook web client for disabling "top posting" when replying to emails:
> https://answers.microsoft.com/en-us/outlook_com/forum/all/eliminate-top-posting/5e1e5729-30f8-41e9-84cb-fb5e81229c7c
>
> Regards,
> Ariel
You can also use git-send-email for sending replies. Just make a .txt
file with this format:
Subject: <subject goes here>
<your reply after an empty line>
The lore.kernel.org site generates the appropriate git-send-email
command for sending the reply, so you can copy-paste that to send it.
I send most of my replies like that.
> From: James Bottomley <James.Bottomley@HansenPartnership.com>
> Sent: Friday, June 9, 2023 9:43 PM
> To: Ariel Miculas (amiculas); Trilok Soni; Colin Walters; Christian Brauner
> Cc: linux-fsdevel@vger.kernel.org; rust-for-linux@vger.kernel.org; linux-mm@kvack.org
> Subject: Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
>
> On Fri, 2023-06-09 at 17:16 +0000, Ariel Miculas (amiculas) wrote:
>> I could switch to my personal gmail, but last time Miguel Ojeda asked
>> me to use my cisco email when I send commits signed off by
>> amiculas@cisco.com.
>> If this is not a hard requirement, then I could switch.
>
> For sending patches, you can simply use git-send-email. All you need
> to point it at is the outgoing email server (which should be a config
> setting in whatever tool you are using now). We have a (reasonably) up
> to date document with some recommendations:
>
> https://www.kernel.org/doc/html/latest/process/email-clients.html
>
> I've successfully used evolution with an exchange server for many
> years, but the interface isn't to everyone's taste and Mozilla
> Thunderbird is also known to connect to it. Basic outlook has proven
> impossible to configure correctly (which is why it doesn't have an
> entry).
>
> Regards,
>
> James
>
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 19:11 ` Ariel Miculas
@ 2023-06-09 20:01 ` James Bottomley
2023-06-10 9:34 ` Miguel Ojeda
1 sibling, 0 replies; 134+ messages in thread
From: James Bottomley @ 2023-06-09 20:01 UTC (permalink / raw)
To: Ariel Miculas
Cc: Miguel Ojeda, Ariel Miculas (amiculas), Trilok Soni,
Colin Walters, Christian Brauner, linux-fsdevel@vger.kernel.org,
rust-for-linux@vger.kernel.org, linux-mm@kvack.org
On Fri, 2023-06-09 at 22:11 +0300, Ariel Miculas wrote:
> Yes, but then how can you be sure that amiculas@cisco.com is the real
> author of the commit? I think that's why Miguel Ojeda asked me to
> send them from my business email, otherwise some random gmail account
> could claim that he is "Ariel Miculas", so he's entitled to sign-off
> as amiculas@cisco.com.
Because it's a public list and the real one would observe and repudiate
...
The DCO is an attestation which we basically believe absent any proof
to the contrary. It's also exhausting and an incredible amount of
pointless admin not to believe anything until it's proved, which is why
we don't.
James
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 10:36 ` Christian Brauner
2023-06-09 11:42 ` Miguel Ojeda
[not found] ` <CH0PR11MB529981313ED5A1F815350E41CD51A@CH0PR11MB5299.namprd11.prod.outlook.com>
@ 2023-06-09 23:52 ` Kent Overstreet
2023-06-10 9:40 ` Miguel Ojeda
2 siblings, 1 reply; 134+ messages in thread
From: Kent Overstreet @ 2023-06-09 23:52 UTC (permalink / raw)
To: Christian Brauner; +Cc: Ariel Miculas, linux-fsdevel, rust-for-linux, linux-mm
On Fri, Jun 09, 2023 at 12:36:29PM +0200, Christian Brauner wrote:
> On Fri, Jun 09, 2023 at 09:29:58AM +0300, Ariel Miculas wrote:
> > Hi all!
> >
> > This is a proof of concept driver written for the PuzzleFS
>
> Uhm, the PuzzleFS filesystem isn't actually sent to fsdevel? Yet I see
> tons of patches in there that add wrappers to our core fs data
> structures. I even see a ton of new files that all fall clearly into
> fsdevel territory:
>
> create mode 100644 rust/kernel/cred.rs
> create mode 100644 rust/kernel/delay.rs
> create mode 100644 rust/kernel/driver.rs
> create mode 100644 rust/kernel/file.rs
> create mode 100644 rust/kernel/fs.rs
> create mode 100644 rust/kernel/fs/param.rs
> create mode 100644 rust/kernel/io_buffer.rs
> create mode 100644 rust/kernel/iov_iter.rs
> create mode 100644 rust/kernel/mm.rs
> create mode 100644 rust/kernel/mount.rs
> create mode 100644 rust/kernel/pages.rs
>
> There's also quite a lot of new mm/ in there, no?
>
> Any wrappers and code for core fs should be maintained as part of fs.
> Rust shouldn't become a way to avoid our reviews once you have a few
> wrappers added somewhere.
I'm of the opinion that Rust wrappers should generally live in the same
directories as the code they're wrapping. The Rust wrappers are unsafe
code that is intimately coupled with the C code they're creating safe
interfaces for, and the organizational structure should reflect that.
This'll be particularly important when mixing Rust and C in the same
module, as I intend to do in bcachefs in the not-too-distant future.
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
` (76 preceding siblings ...)
2023-06-09 10:36 ` Christian Brauner
@ 2023-06-10 0:09 ` Kent Overstreet
77 siblings, 0 replies; 134+ messages in thread
From: Kent Overstreet @ 2023-06-10 0:09 UTC (permalink / raw)
To: Ariel Miculas; +Cc: rust-for-linux
On Fri, Jun 09, 2023 at 09:29:58AM +0300, Ariel Miculas wrote:
> Hi all!
>
> This is a proof of concept driver written for the PuzzleFS
> next-generation container filesystem [1].
I can't find the link to the git tree that corresponds to the patch
series you just posted. Could you make sure that is front and center
when submitting large patch series? I find it a lot easier to get my
head around a large body of code by fetching into my local repository.
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 31/80] rust: serde: add SPDX License Identifiers
2023-06-09 6:30 ` [PATCH 31/80] rust: serde: add SPDX License Identifiers Ariel Miculas
@ 2023-06-10 0:19 ` Kent Overstreet
2023-06-10 6:43 ` Greg KH
2023-06-10 0:25 ` Kent Overstreet
1 sibling, 1 reply; 134+ messages in thread
From: Kent Overstreet @ 2023-06-10 0:19 UTC (permalink / raw)
To: Ariel Miculas; +Cc: rust-for-linux, Miguel Ojeda, jejb, gregkh
On Fri, Jun 09, 2023 at 09:30:29AM +0300, Ariel Miculas wrote:
> From: Miguel Ojeda <ojeda@kernel.org>
>
> Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
> ---
> rust/serde/de/format.rs | 2 ++
> rust/serde/de/ignored_any.rs | 2 ++
> rust/serde/de/impls.rs | 2 ++
> rust/serde/de/mod.rs | 2 ++
> rust/serde/de/seed.rs | 2 ++
> rust/serde/de/utf8.rs | 2 ++
> rust/serde/de/value.rs | 2 ++
> rust/serde/integer128.rs | 2 ++
> rust/serde/lib.rs | 2 ++
> rust/serde/macros.rs | 2 ++
> rust/serde/private/de.rs | 2 ++
> rust/serde/private/doc.rs | 2 ++
> rust/serde/private/mod.rs | 2 ++
> rust/serde/private/ser.rs | 2 ++
> rust/serde/private/size_hint.rs | 2 ++
> rust/serde/ser/fmt.rs | 2 ++
> rust/serde/ser/impls.rs | 2 ++
> rust/serde/ser/impossible.rs | 2 ++
> rust/serde/ser/mod.rs | 2 ++
> rust/serde/std_error.rs | 2 ++
> 20 files changed, 40 insertions(+)
>
> diff --git a/rust/serde/de/format.rs b/rust/serde/de/format.rs
> index f14580b8d162..33233766fc27 100644
> --- a/rust/serde/de/format.rs
> +++ b/rust/serde/de/format.rs
> @@ -1,3 +1,5 @@
> +// SPDX-License-Identifier: Apache-2.0 OR MIT
> +
> use lib::fmt::{self, Write};
> use lib::str;
Hang on, are we applying our own local modifications to code vendored
from an outside repository?
We gotta stop doing this - this turns our version into an unsupported
fork. What do we then do when we need to merge in security updates from
the upstream repository?
The only sane thing to do is keep the vendored code in lockstep with the
upstream repository - i.e. always a direct copy of a specific commit.
Any changes for the kernel need to be applied to the upstream repository
(which could be an intermediate repository, not necessarly _the_
upstream repository; the point is, that way there's a proper revision
history for _that_ repo, and changes can get merged upstream).
Otherwise, we're setting ourselves up for a maintainence nightmare...
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 31/80] rust: serde: add SPDX License Identifiers
2023-06-09 6:30 ` [PATCH 31/80] rust: serde: add SPDX License Identifiers Ariel Miculas
2023-06-10 0:19 ` Kent Overstreet
@ 2023-06-10 0:25 ` Kent Overstreet
2023-06-10 9:04 ` Andreas Hindborg (Samsung)
` (3 more replies)
1 sibling, 4 replies; 134+ messages in thread
From: Kent Overstreet @ 2023-06-10 0:25 UTC (permalink / raw)
To: Ariel Miculas; +Cc: rust-for-linux, Miguel Ojeda
On Fri, Jun 09, 2023 at 09:30:29AM +0300, Ariel Miculas wrote:
> From: Miguel Ojeda <ojeda@kernel.org>
>
> Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
> ---
> rust/serde/de/format.rs | 2 ++
> rust/serde/de/ignored_any.rs | 2 ++
> rust/serde/de/impls.rs | 2 ++
> rust/serde/de/mod.rs | 2 ++
> rust/serde/de/seed.rs | 2 ++
> rust/serde/de/utf8.rs | 2 ++
> rust/serde/de/value.rs | 2 ++
> rust/serde/integer128.rs | 2 ++
> rust/serde/lib.rs | 2 ++
> rust/serde/macros.rs | 2 ++
> rust/serde/private/de.rs | 2 ++
> rust/serde/private/doc.rs | 2 ++
> rust/serde/private/mod.rs | 2 ++
> rust/serde/private/ser.rs | 2 ++
> rust/serde/private/size_hint.rs | 2 ++
> rust/serde/ser/fmt.rs | 2 ++
> rust/serde/ser/impls.rs | 2 ++
> rust/serde/ser/impossible.rs | 2 ++
> rust/serde/ser/mod.rs | 2 ++
> rust/serde/std_error.rs | 2 ++
> 20 files changed, 40 insertions(+)
Separately from that - are we sure we want to be pulling Serde into the
kernel?
Don't get me wrong, Serde is amazing - it's highly ergonomic, and makes
a lot of compatibility issues just completely go away. It's
impresssively well done.
But it's a full json parser (are you using it in json mode here? it
supports multiple formats), and object size is going to be a
consideration. In particular, the way it works is by generating (via
procedural macros) encode/decode functions for every type you use
#[derive] an encoder/decoder for, so depending on how that was
implemented that's going to add up.
I would need to see text size numbers - the bloaty tool might be
useful here: https://github.com/google/bloaty
I'd give serious consideration to Cap'n proto instead, it's not as
ergonomic as Serde (the get/set interface is not ideal, ask me for more
details on how this could be fixed) but it's lower overhead than Serde.
https://github.com/capnproto/capnproto-rust
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 31/80] rust: serde: add SPDX License Identifiers
2023-06-10 0:19 ` Kent Overstreet
@ 2023-06-10 6:43 ` Greg KH
2023-06-10 13:18 ` Kent Overstreet
0 siblings, 1 reply; 134+ messages in thread
From: Greg KH @ 2023-06-10 6:43 UTC (permalink / raw)
To: Kent Overstreet; +Cc: Ariel Miculas, rust-for-linux, Miguel Ojeda, jejb
On Fri, Jun 09, 2023 at 08:19:56PM -0400, Kent Overstreet wrote:
> Hang on, are we applying our own local modifications to code vendored
> from an outside repository?
For adding a single license line, I think it's going to be ok, those are
trivial to handle :)
> We gotta stop doing this - this turns our version into an unsupported
> fork. What do we then do when we need to merge in security updates from
> the upstream repository?
Same as we do for other parts of the kernel that are copies of other
codebases, we merge them in as needed.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 31/80] rust: serde: add SPDX License Identifiers
2023-06-10 0:25 ` Kent Overstreet
@ 2023-06-10 9:04 ` Andreas Hindborg (Samsung)
2023-06-10 13:20 ` Kent Overstreet
2023-06-10 9:33 ` Miguel Ojeda
` (2 subsequent siblings)
3 siblings, 1 reply; 134+ messages in thread
From: Andreas Hindborg (Samsung) @ 2023-06-10 9:04 UTC (permalink / raw)
To: Kent Overstreet; +Cc: Ariel Miculas, rust-for-linux, Miguel Ojeda
Kent Overstreet <kent.overstreet@linux.dev> writes:
> On Fri, Jun 09, 2023 at 09:30:29AM +0300, Ariel Miculas wrote:
>> From: Miguel Ojeda <ojeda@kernel.org>
>>
>> Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
>> ---
>> rust/serde/de/format.rs | 2 ++
>> rust/serde/de/ignored_any.rs | 2 ++
>> rust/serde/de/impls.rs | 2 ++
>> rust/serde/de/mod.rs | 2 ++
>> rust/serde/de/seed.rs | 2 ++
>> rust/serde/de/utf8.rs | 2 ++
>> rust/serde/de/value.rs | 2 ++
>> rust/serde/integer128.rs | 2 ++
>> rust/serde/lib.rs | 2 ++
>> rust/serde/macros.rs | 2 ++
>> rust/serde/private/de.rs | 2 ++
>> rust/serde/private/doc.rs | 2 ++
>> rust/serde/private/mod.rs | 2 ++
>> rust/serde/private/ser.rs | 2 ++
>> rust/serde/private/size_hint.rs | 2 ++
>> rust/serde/ser/fmt.rs | 2 ++
>> rust/serde/ser/impls.rs | 2 ++
>> rust/serde/ser/impossible.rs | 2 ++
>> rust/serde/ser/mod.rs | 2 ++
>> rust/serde/std_error.rs | 2 ++
>> 20 files changed, 40 insertions(+)
>
> Separately from that - are we sure we want to be pulling Serde into the
> kernel?
>
> Don't get me wrong, Serde is amazing - it's highly ergonomic, and makes
> a lot of compatibility issues just completely go away. It's
> impresssively well done.
>
> But it's a full json parser (are you using it in json mode here? it
I think the json capability lives in serde_json, so this is actually not
a full json parser.
BR Andreas
> supports multiple formats), and object size is going to be a
> consideration. In particular, the way it works is by generating (via
> procedural macros) encode/decode functions for every type you use
> #[derive] an encoder/decoder for, so depending on how that was
> implemented that's going to add up.
>
> I would need to see text size numbers - the bloaty tool might be
> useful here: https://github.com/google/bloaty
>
> I'd give serious consideration to Cap'n proto instead, it's not as
> ergonomic as Serde (the get/set interface is not ideal, ask me for more
> details on how this could be fixed) but it's lower overhead than Serde.
>
> https://github.com/capnproto/capnproto-rust
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 31/80] rust: serde: add SPDX License Identifiers
2023-06-10 0:25 ` Kent Overstreet
2023-06-10 9:04 ` Andreas Hindborg (Samsung)
@ 2023-06-10 9:33 ` Miguel Ojeda
2023-06-12 11:58 ` Ariel Miculas
2023-06-15 15:05 ` Ariel Miculas
3 siblings, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-10 9:33 UTC (permalink / raw)
To: Kent Overstreet; +Cc: Ariel Miculas, rust-for-linux, Miguel Ojeda
On Sat, Jun 10, 2023 at 2:25 AM Kent Overstreet
<kent.overstreet@linux.dev> wrote:
>
> Separately from that - are we sure we want to be pulling Serde into the
> kernel?
No, we are not -- see https://rust-for-linux.com/third-party-crates
(these have my SoBs because I provide an experiment branch at
https://github.com/Rust-for-Linux/linux/pull/1007, since some people
wanted to use them, but it is not decided which ones to have).
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 19:11 ` Ariel Miculas
2023-06-09 20:01 ` James Bottomley
@ 2023-06-10 9:34 ` Miguel Ojeda
1 sibling, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-10 9:34 UTC (permalink / raw)
To: Ariel Miculas
Cc: James Bottomley, Ariel Miculas (amiculas), Trilok Soni,
Colin Walters, Christian Brauner, linux-fsdevel@vger.kernel.org,
rust-for-linux@vger.kernel.org, linux-mm@kvack.org
On Fri, Jun 9, 2023 at 9:11 PM Ariel Miculas <ariel.miculas@gmail.com> wrote:
>
> Yes, but then how can you be sure that amiculas@cisco.com is the real
> author of the commit? I think that's why Miguel Ojeda asked me to send
> them from my business email, otherwise some random gmail account could
> claim that he is "Ariel Miculas", so he's entitled to sign-off as
> amiculas@cisco.com.
Yeah, that was the main thing; and by asking that, if you managed to
send that original patch from the corporate one, then there was no
need for the `From:` to begin with :)
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [RFC PATCH 00/80] Rust PuzzleFS filesystem driver
2023-06-09 23:52 ` Kent Overstreet
@ 2023-06-10 9:40 ` Miguel Ojeda
0 siblings, 0 replies; 134+ messages in thread
From: Miguel Ojeda @ 2023-06-10 9:40 UTC (permalink / raw)
To: Kent Overstreet
Cc: Christian Brauner, Ariel Miculas, linux-fsdevel, rust-for-linux,
linux-mm
On Sat, Jun 10, 2023 at 2:12 AM Kent Overstreet
<kent.overstreet@linux.dev> wrote:
>
> I'm of the opinion that Rust wrappers should generally live in the same
> directories as the code they're wrapping. The Rust wrappers are unsafe
> code that is intimately coupled with the C code they're creating safe
> interfaces for, and the organizational structure should reflect that.
+1, that is the plan, which also means `MAINTAINERS` does not need
extra `F` fields etc.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 31/80] rust: serde: add SPDX License Identifiers
2023-06-10 6:43 ` Greg KH
@ 2023-06-10 13:18 ` Kent Overstreet
2023-06-10 15:28 ` Greg KH
0 siblings, 1 reply; 134+ messages in thread
From: Kent Overstreet @ 2023-06-10 13:18 UTC (permalink / raw)
To: Greg KH; +Cc: Ariel Miculas, rust-for-linux, Miguel Ojeda, jejb
On Sat, Jun 10, 2023 at 08:43:10AM +0200, Greg KH wrote:
> On Fri, Jun 09, 2023 at 08:19:56PM -0400, Kent Overstreet wrote:
> > Hang on, are we applying our own local modifications to code vendored
> > from an outside repository?
>
> For adding a single license line, I think it's going to be ok, those are
> trivial to handle :)
They'll still be lost when the package is updated from upstream. Just do
a proper upstream patch - I see no reason why SPDX identifiers wouldn't
be accepted by the host project.
>
> > We gotta stop doing this - this turns our version into an unsupported
> > fork. What do we then do when we need to merge in security updates from
> > the upstream repository?
>
> Same as we do for other parts of the kernel that are copies of other
> codebases, we merge them in as needed.
Yeah, and that workflow still leaves something to be desired...
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 31/80] rust: serde: add SPDX License Identifiers
2023-06-10 9:04 ` Andreas Hindborg (Samsung)
@ 2023-06-10 13:20 ` Kent Overstreet
2023-06-12 8:56 ` Ariel Miculas
0 siblings, 1 reply; 134+ messages in thread
From: Kent Overstreet @ 2023-06-10 13:20 UTC (permalink / raw)
To: Andreas Hindborg (Samsung); +Cc: Ariel Miculas, rust-for-linux, Miguel Ojeda
On Sat, Jun 10, 2023 at 11:04:24AM +0200, Andreas Hindborg (Samsung) wrote:
>
> Kent Overstreet <kent.overstreet@linux.dev> writes:
>
> > On Fri, Jun 09, 2023 at 09:30:29AM +0300, Ariel Miculas wrote:
> >> From: Miguel Ojeda <ojeda@kernel.org>
> >>
> >> Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
> >> ---
> >> rust/serde/de/format.rs | 2 ++
> >> rust/serde/de/ignored_any.rs | 2 ++
> >> rust/serde/de/impls.rs | 2 ++
> >> rust/serde/de/mod.rs | 2 ++
> >> rust/serde/de/seed.rs | 2 ++
> >> rust/serde/de/utf8.rs | 2 ++
> >> rust/serde/de/value.rs | 2 ++
> >> rust/serde/integer128.rs | 2 ++
> >> rust/serde/lib.rs | 2 ++
> >> rust/serde/macros.rs | 2 ++
> >> rust/serde/private/de.rs | 2 ++
> >> rust/serde/private/doc.rs | 2 ++
> >> rust/serde/private/mod.rs | 2 ++
> >> rust/serde/private/ser.rs | 2 ++
> >> rust/serde/private/size_hint.rs | 2 ++
> >> rust/serde/ser/fmt.rs | 2 ++
> >> rust/serde/ser/impls.rs | 2 ++
> >> rust/serde/ser/impossible.rs | 2 ++
> >> rust/serde/ser/mod.rs | 2 ++
> >> rust/serde/std_error.rs | 2 ++
> >> 20 files changed, 40 insertions(+)
> >
> > Separately from that - are we sure we want to be pulling Serde into the
> > kernel?
> >
> > Don't get me wrong, Serde is amazing - it's highly ergonomic, and makes
> > a lot of compatibility issues just completely go away. It's
> > impresssively well done.
> >
> > But it's a full json parser (are you using it in json mode here? it
>
> I think the json capability lives in serde_json, so this is actually not
> a full json parser.
Good to know
I don't think I'm the only one who's curious though - what's serde being
used for here? On disk data structures of some kind?
I'm probably not the only filesystem author eagerly waiting for the day
when I can stop using carefully packed C structs for this, with all the
sharp edges that entails...
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 31/80] rust: serde: add SPDX License Identifiers
2023-06-10 13:18 ` Kent Overstreet
@ 2023-06-10 15:28 ` Greg KH
0 siblings, 0 replies; 134+ messages in thread
From: Greg KH @ 2023-06-10 15:28 UTC (permalink / raw)
To: Kent Overstreet; +Cc: Ariel Miculas, rust-for-linux, Miguel Ojeda, jejb
On Sat, Jun 10, 2023 at 09:18:20AM -0400, Kent Overstreet wrote:
> On Sat, Jun 10, 2023 at 08:43:10AM +0200, Greg KH wrote:
> > On Fri, Jun 09, 2023 at 08:19:56PM -0400, Kent Overstreet wrote:
> > > Hang on, are we applying our own local modifications to code vendored
> > > from an outside repository?
> >
> > For adding a single license line, I think it's going to be ok, those are
> > trivial to handle :)
>
> They'll still be lost when the package is updated from upstream. Just do
> a proper upstream patch - I see no reason why SPDX identifiers wouldn't
> be accepted by the host project.
Great, then submit them upstream also, but for now, when added to the
kernel tree, they need to be present.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 31/80] rust: serde: add SPDX License Identifiers
2023-06-10 13:20 ` Kent Overstreet
@ 2023-06-12 8:56 ` Ariel Miculas
0 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-12 8:56 UTC (permalink / raw)
To: Kent Overstreet
Cc: Andreas Hindborg (Samsung), Ariel Miculas, rust-for-linux,
Miguel Ojeda
On Sat, Jun 10, 2023 at 4:30 PM Kent Overstreet
<kent.overstreet@linux.dev> wrote:
>
> On Sat, Jun 10, 2023 at 11:04:24AM +0200, Andreas Hindborg (Samsung) wrote:
> >
> > Kent Overstreet <kent.overstreet@linux.dev> writes:
> >
> > > On Fri, Jun 09, 2023 at 09:30:29AM +0300, Ariel Miculas wrote:
> > >> From: Miguel Ojeda <ojeda@kernel.org>
> > >>
> > >> Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
> > >> ---
> > >> rust/serde/de/format.rs | 2 ++
> > >> rust/serde/de/ignored_any.rs | 2 ++
> > >> rust/serde/de/impls.rs | 2 ++
> > >> rust/serde/de/mod.rs | 2 ++
> > >> rust/serde/de/seed.rs | 2 ++
> > >> rust/serde/de/utf8.rs | 2 ++
> > >> rust/serde/de/value.rs | 2 ++
> > >> rust/serde/integer128.rs | 2 ++
> > >> rust/serde/lib.rs | 2 ++
> > >> rust/serde/macros.rs | 2 ++
> > >> rust/serde/private/de.rs | 2 ++
> > >> rust/serde/private/doc.rs | 2 ++
> > >> rust/serde/private/mod.rs | 2 ++
> > >> rust/serde/private/ser.rs | 2 ++
> > >> rust/serde/private/size_hint.rs | 2 ++
> > >> rust/serde/ser/fmt.rs | 2 ++
> > >> rust/serde/ser/impls.rs | 2 ++
> > >> rust/serde/ser/impossible.rs | 2 ++
> > >> rust/serde/ser/mod.rs | 2 ++
> > >> rust/serde/std_error.rs | 2 ++
> > >> 20 files changed, 40 insertions(+)
> > >
> > > Separately from that - are we sure we want to be pulling Serde into the
> > > kernel?
> > >
> > > Don't get me wrong, Serde is amazing - it's highly ergonomic, and makes
> > > a lot of compatibility issues just completely go away. It's
> > > impresssively well done.
> > >
> > > But it's a full json parser (are you using it in json mode here? it
> >
> > I think the json capability lives in serde_json, so this is actually not
> > a full json parser.
>
> Good to know
>
> I don't think I'm the only one who's curious though - what's serde being
> used for here? On disk data structures of some kind?
>
> I'm probably not the only filesystem author eagerly waiting for the day
> when I can stop using carefully packed C structs for this, with all the
> sharp edges that entails...
serde, together with serde_cbor (added in another commit [3]), is used
to decode the puzzlefs metadata (check the "format" section for the
initial design [1]).
Sorry for the github links, but the patch series is incomplete because
I didn't send them to patches@lists.linux.dev, the entire "pull
request" is available here [2].
https://github.com/anuvu/puzzlefs/pull/6#issue-801680988 [1]
https://github.com/Rust-for-Linux/linux/compare/rust-next...ariel-miculas:puzzlefs_rfc_v1
[2]
https://github.com/Rust-for-Linux/linux/commit/3fe9d58fbd89b0ed488d3150620eb07be17f1f16
[3]
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 31/80] rust: serde: add SPDX License Identifiers
2023-06-10 0:25 ` Kent Overstreet
2023-06-10 9:04 ` Andreas Hindborg (Samsung)
2023-06-10 9:33 ` Miguel Ojeda
@ 2023-06-12 11:58 ` Ariel Miculas
2023-06-15 15:05 ` Ariel Miculas
3 siblings, 0 replies; 134+ messages in thread
From: Ariel Miculas @ 2023-06-12 11:58 UTC (permalink / raw)
To: Kent Overstreet; +Cc: Ariel Miculas, rust-for-linux, Miguel Ojeda
On Sat, Jun 10, 2023 at 3:29 AM Kent Overstreet
<kent.overstreet@linux.dev> wrote:
>
> On Fri, Jun 09, 2023 at 09:30:29AM +0300, Ariel Miculas wrote:
> > From: Miguel Ojeda <ojeda@kernel.org>
> >
> > Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
> > ---
> > rust/serde/de/format.rs | 2 ++
> > rust/serde/de/ignored_any.rs | 2 ++
> > rust/serde/de/impls.rs | 2 ++
> > rust/serde/de/mod.rs | 2 ++
> > rust/serde/de/seed.rs | 2 ++
> > rust/serde/de/utf8.rs | 2 ++
> > rust/serde/de/value.rs | 2 ++
> > rust/serde/integer128.rs | 2 ++
> > rust/serde/lib.rs | 2 ++
> > rust/serde/macros.rs | 2 ++
> > rust/serde/private/de.rs | 2 ++
> > rust/serde/private/doc.rs | 2 ++
> > rust/serde/private/mod.rs | 2 ++
> > rust/serde/private/ser.rs | 2 ++
> > rust/serde/private/size_hint.rs | 2 ++
> > rust/serde/ser/fmt.rs | 2 ++
> > rust/serde/ser/impls.rs | 2 ++
> > rust/serde/ser/impossible.rs | 2 ++
> > rust/serde/ser/mod.rs | 2 ++
> > rust/serde/std_error.rs | 2 ++
> > 20 files changed, 40 insertions(+)
>
> Separately from that - are we sure we want to be pulling Serde into the
> kernel?
>
> Don't get me wrong, Serde is amazing - it's highly ergonomic, and makes
> a lot of compatibility issues just completely go away. It's
> impresssively well done.
>
> But it's a full json parser (are you using it in json mode here? it
> supports multiple formats), and object size is going to be a
> consideration. In particular, the way it works is by generating (via
> procedural macros) encode/decode functions for every type you use
> #[derive] an encoder/decoder for, so depending on how that was
> implemented that's going to add up.
>
> I would need to see text size numbers - the bloaty tool might be
> useful here: https://github.com/google/bloaty
>
> I'd give serious consideration to Cap'n proto instead, it's not as
> ergonomic as Serde (the get/set interface is not ideal, ask me for more
> details on how this could be fixed) but it's lower overhead than Serde.
>
> https://github.com/capnproto/capnproto-rust
Unfortunately bloaty seems broken [1], but here's the output:
```
$ bloaty puzzlefs_vmlinux -- vmlinux
FILE SIZE VM SIZE
-------------- --------------
+102% +921Ki [ = ] 0 .debug_info
+139% +849Ki [ = ] 0 .debug_str
+251% +574Ki [ = ] 0 .debug_loc
+116% +246Ki [ = ] 0 .strtab
+292% +226Ki [ = ] 0 .debug_pubtypes
+297% +200Ki [ = ] 0 .debug_pubnames
+115% +176Ki +115% +176Ki .rodata
+103% +160Ki [ = ] 0 .debug_line
+287% +152Ki [ = ] 0 .debug_ranges
+104% +44.9Ki [ = ] 0 .debug_frame
+103% +43.2Ki [ = ] 0 .symtab
+112% +24.0Ki +112% +24.0Ki __ksymtab_strings
+100% +5.31Ki [ = ] 0 .debug_abbrev
+100% +4.20Ki [ = ] 0 .debug_str_offsets
+108% +2.89Ki +108% +2.89Ki __ksymtab_gpl
+100% +2.13Ki [ = ] 0 .debug_addr
+100% +452 [ = ] 0 .debug_loclists
+129% +416 [ = ] 0 .debug_aranges
+0.0% +60 +0.0% +68 [8 Others]
+99% -64 +99% -64 [LOAD #3 [RWX]]
+96% -202Ki [ = ] 0 [Unmapped]
+104% +90.2Mi +101% +13.8Mi TOTAL
```
where puzzlefs_vmlinux is the kernel built with my puzzlefs changes
and vmlinux is the kernel built on the rust-next branch.
```
$ ls -lah puzzlefs_vmlinux
-rwxr-xr-x 1 amiculas amiculas 91M Jun 12 14:38 puzzlefs_vmlinux
~/work/linux rust-next*
$ ls -lah vmlinux
-rwxr-xr-x 1 amiculas amiculas 87M Jun 12 14:38 vmlinux
```
It's a +4M difference between the two versions (3.35 M to be exact),
but they are both debug builds (with CONFIG_DEBUG_KERNEL and
CONFIG_KALLSYMS set).
If I should do additional comparisons, please let me know.
https://github.com/google/bloaty/issues/335 [1]
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 31/80] rust: serde: add SPDX License Identifiers
2023-06-10 0:25 ` Kent Overstreet
` (2 preceding siblings ...)
2023-06-12 11:58 ` Ariel Miculas
@ 2023-06-15 15:05 ` Ariel Miculas
2023-06-17 16:04 ` Kent Overstreet
3 siblings, 1 reply; 134+ messages in thread
From: Ariel Miculas @ 2023-06-15 15:05 UTC (permalink / raw)
To: Kent Overstreet; +Cc: Ariel Miculas, rust-for-linux, Miguel Ojeda
On Sat, Jun 10, 2023 at 3:29 AM Kent Overstreet
<kent.overstreet@linux.dev> wrote:
>
> On Fri, Jun 09, 2023 at 09:30:29AM +0300, Ariel Miculas wrote:
> > From: Miguel Ojeda <ojeda@kernel.org>
> >
> > Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
> > ---
> > rust/serde/de/format.rs | 2 ++
> > rust/serde/de/ignored_any.rs | 2 ++
> > rust/serde/de/impls.rs | 2 ++
> > rust/serde/de/mod.rs | 2 ++
> > rust/serde/de/seed.rs | 2 ++
> > rust/serde/de/utf8.rs | 2 ++
> > rust/serde/de/value.rs | 2 ++
> > rust/serde/integer128.rs | 2 ++
> > rust/serde/lib.rs | 2 ++
> > rust/serde/macros.rs | 2 ++
> > rust/serde/private/de.rs | 2 ++
> > rust/serde/private/doc.rs | 2 ++
> > rust/serde/private/mod.rs | 2 ++
> > rust/serde/private/ser.rs | 2 ++
> > rust/serde/private/size_hint.rs | 2 ++
> > rust/serde/ser/fmt.rs | 2 ++
> > rust/serde/ser/impls.rs | 2 ++
> > rust/serde/ser/impossible.rs | 2 ++
> > rust/serde/ser/mod.rs | 2 ++
> > rust/serde/std_error.rs | 2 ++
> > 20 files changed, 40 insertions(+)
>
> Separately from that - are we sure we want to be pulling Serde into the
> kernel?
>
> Don't get me wrong, Serde is amazing - it's highly ergonomic, and makes
> a lot of compatibility issues just completely go away. It's
> impresssively well done.
>
> But it's a full json parser (are you using it in json mode here? it
> supports multiple formats), and object size is going to be a
> consideration. In particular, the way it works is by generating (via
> procedural macros) encode/decode functions for every type you use
> #[derive] an encoder/decoder for, so depending on how that was
> implemented that's going to add up.
>
> I would need to see text size numbers - the bloaty tool might be
> useful here: https://github.com/google/bloaty
>
> I'd give serious consideration to Cap'n proto instead, it's not as
> ergonomic as Serde (the get/set interface is not ideal, ask me for more
> details on how this could be fixed) but it's lower overhead than Serde.
I've been checking out Cap'n proto and indeed that get/set interface
seems cumbersome, so how do you fix it?
>
> https://github.com/capnproto/capnproto-rust
Regards,
Ariel
^ permalink raw reply [flat|nested] 134+ messages in thread
* Re: [PATCH 31/80] rust: serde: add SPDX License Identifiers
2023-06-15 15:05 ` Ariel Miculas
@ 2023-06-17 16:04 ` Kent Overstreet
0 siblings, 0 replies; 134+ messages in thread
From: Kent Overstreet @ 2023-06-17 16:04 UTC (permalink / raw)
To: Ariel Miculas
Cc: Ariel Miculas, rust-for-linux, Miguel Ojeda, sviatoslavpestov,
dwrenshaw, temporal
On Thu, Jun 15, 2023 at 06:05:47PM +0300, Ariel Miculas wrote:
> On Sat, Jun 10, 2023 at 3:29 AM Kent Overstreet
> <kent.overstreet@linux.dev> wrote:
> >
> > On Fri, Jun 09, 2023 at 09:30:29AM +0300, Ariel Miculas wrote:
> > > From: Miguel Ojeda <ojeda@kernel.org>
> > >
> > > Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
> > > ---
> > > rust/serde/de/format.rs | 2 ++
> > > rust/serde/de/ignored_any.rs | 2 ++
> > > rust/serde/de/impls.rs | 2 ++
> > > rust/serde/de/mod.rs | 2 ++
> > > rust/serde/de/seed.rs | 2 ++
> > > rust/serde/de/utf8.rs | 2 ++
> > > rust/serde/de/value.rs | 2 ++
> > > rust/serde/integer128.rs | 2 ++
> > > rust/serde/lib.rs | 2 ++
> > > rust/serde/macros.rs | 2 ++
> > > rust/serde/private/de.rs | 2 ++
> > > rust/serde/private/doc.rs | 2 ++
> > > rust/serde/private/mod.rs | 2 ++
> > > rust/serde/private/ser.rs | 2 ++
> > > rust/serde/private/size_hint.rs | 2 ++
> > > rust/serde/ser/fmt.rs | 2 ++
> > > rust/serde/ser/impls.rs | 2 ++
> > > rust/serde/ser/impossible.rs | 2 ++
> > > rust/serde/ser/mod.rs | 2 ++
> > > rust/serde/std_error.rs | 2 ++
> > > 20 files changed, 40 insertions(+)
> >
> > Separately from that - are we sure we want to be pulling Serde into the
> > kernel?
> >
> > Don't get me wrong, Serde is amazing - it's highly ergonomic, and makes
> > a lot of compatibility issues just completely go away. It's
> > impresssively well done.
> >
> > But it's a full json parser (are you using it in json mode here? it
> > supports multiple formats), and object size is going to be a
> > consideration. In particular, the way it works is by generating (via
> > procedural macros) encode/decode functions for every type you use
> > #[derive] an encoder/decoder for, so depending on how that was
> > implemented that's going to add up.
> >
> > I would need to see text size numbers - the bloaty tool might be
> > useful here: https://github.com/google/bloaty
> >
> > I'd give serious consideration to Cap'n proto instead, it's not as
> > ergonomic as Serde (the get/set interface is not ideal, ask me for more
> > details on how this could be fixed) but it's lower overhead than Serde.
> I've been checking out Cap'n proto and indeed that get/set interface
> seems cumbersome, so how do you fix it?
One thing that makes it cumbersome is that any get can return an error:
a straightforward change would be to add a method that does all
validation/bounds checking up front, and returns a new type with methods
that can't return errors.
Kenton did it that way to avoid any O(n) parsing overhead up front;
with messages that can be arbitrarily sized it's definitely an option
that you want to have, especially when you won't necessarily be reading
the entire thing. But in my experience most messages are short, and
there's a reading-off-the-disk step or reading-from-the-network step,
and you probably want to do other validation, so doing all validation up
front simplifies error handling.
For the next step, what we really what we'd want is for cap'n proto to
generate normal Rust types but with a #[repr] that matches the cap'n
proto type layout.
Having the cap'n proto compiler emit #[repr(c)] types with the
appropriate padding isn't enough because:
- integers are stored little endian, and need byte swabbing to access
- booleans are stored as bitfields, and Rust doesn't have native
bitfields
Doing this right would require importing computed properties from Swift:
a computed property is something that looks like a struct field but
requires get/set calls to access it, the compiler just knows how to emit
them.
Miguel might be able to point us towards rustc people to talk to about
this :) Having native language-level types which are directly
serializable is amazing, as we've already seen with Serde. If we could
skip the encode/decode step and just mmap a file and start walking
objects - this could really change how we write and design filesystems,
databases - even compilers. Debug info (e.g. dwarf) is all about
serializing compiler internals, and a lot of the problems compiler
people are tackling right now basically require doing this in a more
principled way.
^ permalink raw reply [flat|nested] 134+ messages in thread
end of thread, other threads:[~2023-06-17 16:04 UTC | newest]
Thread overview: 134+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-06-09 6:29 [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Ariel Miculas
2023-06-09 6:29 ` [PATCH 01/80] rust: add definitions for ref-counted inodes and dentries Ariel Miculas
2023-06-09 6:30 ` [PATCH 02/80] rust: add ability to register a file system Ariel Miculas
2023-06-09 9:23 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 03/80] rust: define fs context Ariel Miculas
2023-06-09 6:30 ` [PATCH 04/80] rust: add support for file system parameters Ariel Miculas
2023-06-09 6:30 ` [PATCH 05/80] rust: kernel: add libraries required by the filesystem abstractions Ariel Miculas
2023-06-09 9:46 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 06/80] rust: allow fs driver to initialise new superblocks Ariel Miculas
2023-06-09 6:30 ` [PATCH 07/80] rust: add `module_fs` macro Ariel Miculas
2023-06-09 6:30 ` [PATCH 08/80] WIP: rust: allow fs to be populated Ariel Miculas
2023-06-09 6:30 ` [PATCH 09/80] rust: kernel: backport the delay module from the rust branch Ariel Miculas
2023-06-09 9:29 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 10/80] rust: kernel: add container_of macro Ariel Miculas
2023-06-09 6:30 ` [PATCH 11/80] rust: kernel: add offset_of macro Ariel Miculas
2023-06-09 6:30 ` [PATCH 12/80] drop: Add crate::pr_warn declaration Ariel Miculas
2023-06-09 9:29 ` Miguel Ojeda
2023-06-09 10:46 ` Ariel Miculas (amiculas)
2023-06-09 6:30 ` [PATCH 13/80] rust: kernel: rename from_kernel_errno to from_errno Ariel Miculas
2023-06-09 9:56 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 14/80] rust: kernel: Rename from_pointer to from_foreing and into_pointer to into_foreign Ariel Miculas
2023-06-09 9:57 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 15/80] rust: kernel: add count_paren_items macro, needed by define_fs_params macro Ariel Miculas
2023-06-09 6:30 ` [PATCH 16/80] rust: helpers: add missing rust helper 'alloc_pages' Ariel Miculas
2023-06-09 9:57 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 17/80] kernel: configs: add qemu-busybox-min.config Ariel Miculas
2023-06-09 9:39 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 18/80] rust: kernel: format the rust code Ariel Miculas
2023-06-09 9:21 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 19/80] samples: puzzlefs: add initial puzzlefs sample, copied from rust_fs.rs Ariel Miculas
2023-06-09 6:30 ` [PATCH 20/80] kernel: configs: enable rust samples in rust.config Ariel Miculas
2023-06-09 9:25 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 22/80] rust: proc-macro2: add SPDX License Identifiers Ariel Miculas
2023-06-09 6:30 ` [PATCH 23/80] rust: proc-macro2: remove `unicode_ident` dependency Ariel Miculas
2023-06-09 6:30 ` [PATCH 24/80] rust: quote: import crate Ariel Miculas
2023-06-09 6:30 ` [PATCH 25/80] rust: quote: add SPDX License Identifiers Ariel Miculas
2023-06-09 6:30 ` [PATCH 27/80] rust: syn: " Ariel Miculas
2023-06-09 6:30 ` [PATCH 28/80] rust: syn: remove `unicode-ident` dependency Ariel Miculas
2023-06-09 6:30 ` [PATCH 30/80] rust: serde: add `no_fp_fmt_parse` support Ariel Miculas
2023-06-09 6:30 ` [PATCH 31/80] rust: serde: add SPDX License Identifiers Ariel Miculas
2023-06-10 0:19 ` Kent Overstreet
2023-06-10 6:43 ` Greg KH
2023-06-10 13:18 ` Kent Overstreet
2023-06-10 15:28 ` Greg KH
2023-06-10 0:25 ` Kent Overstreet
2023-06-10 9:04 ` Andreas Hindborg (Samsung)
2023-06-10 13:20 ` Kent Overstreet
2023-06-12 8:56 ` Ariel Miculas
2023-06-10 9:33 ` Miguel Ojeda
2023-06-12 11:58 ` Ariel Miculas
2023-06-15 15:05 ` Ariel Miculas
2023-06-17 16:04 ` Kent Overstreet
2023-06-09 6:30 ` [PATCH 33/80] rust: serde_derive: " Ariel Miculas
2023-06-09 6:30 ` [PATCH 34/80] rust: Kbuild: enable `proc-macro2`, `quote`, `syn`, `serde` and `serde_derive` Ariel Miculas
2023-06-09 6:30 ` [PATCH 35/80] rust: test `serde` support Ariel Miculas
2023-06-09 6:30 ` [PATCH 36/80] Add SAMPLE_RUST_SERDE in rust.config Ariel Miculas
2023-06-09 6:30 ` [PATCH 37/80] rust: kernel: fix compile errors after rebase to rust-next Ariel Miculas
2023-06-09 9:38 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 39/80] rust: serde_cbor: add SPDX License Identifiers Ariel Miculas
2023-06-09 6:30 ` [PATCH 40/80] rust: serde_cbor: add no_fp_fmt_parse support Ariel Miculas
2023-06-09 6:30 ` [PATCH 41/80] rust: Kbuild: enable serde_cbor Ariel Miculas
2023-06-09 10:21 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 42/80] samples: rust: add cbor serialize/deserialize example Ariel Miculas
2023-06-09 6:30 ` [PATCH 43/80] rust: serde_cbor: add support for serde_cbor's from_slice method by using a custom alloc_kernel feature Ariel Miculas
2023-06-09 9:55 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 44/80] rust: serde: add support for deserializing Vec with kernel_alloc feature Ariel Miculas
2023-06-09 10:10 ` Miguel Ojeda
2023-06-09 6:30 ` [PATCH 45/80] rust: file: Replace UnsafeCell with Opaque for File Ariel Miculas
2023-06-09 6:30 ` [PATCH 46/80] rust: kernel: implement fmt::Debug for CString Ariel Miculas
2023-06-09 6:30 ` [PATCH 47/80] samples: puzzlefs: rename RustFs to PuzzleFs Ariel Miculas
2023-06-09 6:30 ` [PATCH 48/80] samples: puzzlefs: add basic deserializing support for the puzzlefs metadata Ariel Miculas
2023-06-09 6:30 ` [PATCH 49/80] rust: file: present the filesystem context to the open function Ariel Miculas
2023-06-09 6:30 ` [PATCH 50/80] rust: kernel: add an abstraction over vfsmount to allow cloning a new private mount Ariel Miculas
2023-06-09 6:30 ` [PATCH 51/80] rust: file: add from_path, from_path_in_root_mnt and read_with_offset methods to File Ariel Miculas
2023-06-09 6:30 ` [PATCH 52/80] samples: puzzlefs: pass the Vfsmount structure from open to read and return the contents of the data file inside /home/puzzlefs_oci Ariel Miculas
2023-06-09 6:30 ` [PATCH 53/80] rust: file: move from_path, from_path_in_root_mnt and read_with_offset methods to a RegularFile newtype Ariel Miculas
2023-06-09 6:30 ` [PATCH 54/80] rust: file: ensure RegularFile can only create regular files Ariel Miculas
2023-06-09 6:30 ` [PATCH 55/80] rust: file: add get_pos method to RegularFile Ariel Miculas
2023-06-09 6:30 ` [PATCH 56/80] rust: file: add methods read_to_end, get_file_size and update_pos " Ariel Miculas
2023-06-09 6:30 ` [PATCH 57/80] rust: file: define a minimal Read trait and implement it for RegularFile Ariel Miculas
2023-06-09 6:30 ` [PATCH 58/80] samples: puzzlefs: add cbor_get_array_size method Ariel Miculas
2023-06-09 6:30 ` [PATCH 59/80] samples: puzzlefs: add KernelError to WireFormatError and implement From conversion Ariel Miculas
2023-06-09 6:30 ` [PATCH 60/80] samples: puzzlefs: implement new for MetadataBlob Ariel Miculas
2023-06-09 6:30 ` [PATCH 61/80] samples: puzzlefs: build puzzlefs into the kernel, thus avoiding the need to export rust symbols Ariel Miculas
2023-06-09 6:31 ` [PATCH 62/80] rust: alloc: add try_clone for Vec<T> Ariel Miculas
2023-06-09 6:31 ` [PATCH 63/80] rust: alloc: add from_iter_fallible " Ariel Miculas
2023-06-09 10:06 ` Miguel Ojeda
2023-06-09 6:31 ` [PATCH 64/80] samples: puzzlefs: implement to_errno and from_errno for WireFormatError Ariel Miculas
2023-06-09 6:31 ` [PATCH 65/80] samples: puzzlefs: add TryReserveError (and from conversion) to WireFormatError Ariel Miculas
2023-06-09 6:31 ` [PATCH 66/80] samples: puzzlefs: add higher level inode related functionality Ariel Miculas
2023-06-09 6:31 ` [PATCH 67/80] samples: puzzlefs: populate the directory entries with the inodes from the puzzlefs metadata file Ariel Miculas
2023-06-09 6:31 ` [PATCH 68/80] rust: hex: import crate Ariel Miculas
2023-06-09 6:31 ` [PATCH 69/80] rust: hex: add SPDX license identifiers Ariel Miculas
2023-06-09 6:31 ` [PATCH 70/80] rust: Kbuild: enable `hex` Ariel Miculas
2023-06-09 6:31 ` [PATCH 71/80] rust: hex: implement FromHex trait and hex::decode using a custom kernel_alloc feature Ariel Miculas
2023-06-09 6:31 ` [PATCH 72/80] rust: hex: add encode_hex_iter and encode_hex_upper_iter methods Ariel Miculas
2023-06-09 6:31 ` [PATCH 73/80] rust: puzzlefs: add HexError to WireFormatError and implement the From conversion Ariel Miculas
2023-06-09 6:31 ` [PATCH 74/80] rust: puzzlefs: display the error value for WireFormatError::KernelError Ariel Miculas
2023-06-09 6:31 ` [PATCH 75/80] samples: puzzlefs: add Rootfs and Digest structs to types.rs Ariel Miculas
2023-06-09 6:31 ` [PATCH 76/80] samples: puzzlefs: implement the conversion from WireFormatError to kernel::error::Error Ariel Miculas
2023-06-09 6:31 ` [PATCH 77/80] rust: puzzlefs: read the puzzlefs image manifest instead of an individual metadata layer Ariel Miculas
2023-06-09 6:31 ` [PATCH 78/80] rust: puzzlefs: rename PuzzleFs to PuzzleFsModule to avoid confusion with the PuzzleFS struct Ariel Miculas
2023-06-09 6:31 ` [PATCH 79/80] rust: puzzlefs: add support for reading files Ariel Miculas
2023-06-09 6:31 ` [PATCH 80/80] rust: puzzlefs: add oci_root_dir and image_manifest filesystem parameters Ariel Miculas
2023-06-09 10:26 ` [RFC PATCH 00/80] Rust PuzzleFS filesystem driver Miguel Ojeda
2023-06-09 10:36 ` Christian Brauner
2023-06-09 11:42 ` Miguel Ojeda
[not found] ` <CH0PR11MB529981313ED5A1F815350E41CD51A@CH0PR11MB5299.namprd11.prod.outlook.com>
2023-06-09 11:45 ` Christian Brauner
2023-06-09 12:03 ` Ariel Miculas (amiculas)
2023-06-09 12:56 ` Gao Xiang
2023-06-09 12:07 ` Miguel Ojeda
2023-06-09 12:11 ` Ariel Miculas (amiculas)
2023-06-09 12:21 ` Greg KH
2023-06-09 13:05 ` Alice Ryhl
2023-06-09 12:20 ` Colin Walters
2023-06-09 12:42 ` Christian Brauner
2023-06-09 17:28 ` Serge Hallyn
2023-06-09 13:45 ` Ariel Miculas (amiculas)
2023-06-09 17:10 ` Trilok Soni
2023-06-09 17:16 ` Ariel Miculas (amiculas)
2023-06-09 17:41 ` Miguel Ojeda
2023-06-09 18:49 ` James Bottomley
2023-06-09 19:08 ` Miguel Ojeda
2023-06-09 19:11 ` Ariel Miculas
2023-06-09 20:01 ` James Bottomley
2023-06-10 9:34 ` Miguel Ojeda
2023-06-09 18:43 ` James Bottomley
2023-06-09 18:59 ` Ariel Miculas (amiculas)
2023-06-09 19:20 ` Ariel Miculas
2023-06-09 19:45 ` Trilok Soni
2023-06-09 19:53 ` Alice Ryhl
2023-06-09 23:52 ` Kent Overstreet
2023-06-10 9:40 ` Miguel Ojeda
2023-06-10 0:09 ` Kent Overstreet
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).