From 7440c52ff48e957c7b4077d81cd5bc81b3153fd8 Mon Sep 17 00:00:00 2001 From: Miquel Sabaté Solà Date: Wed, 1 Jul 2026 13:40:16 +0200 Subject: btrfs: add ino_lookup example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Miquel Sabaté Solà --- fs/btrfs/ino_lookup/Makefile | 56 +++++++++++++++++++ fs/btrfs/ino_lookup/README.md | 117 +++++++++++++++++++++++++++++++++++++++ fs/btrfs/ino_lookup/ino_lookup.c | 57 +++++++++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 fs/btrfs/ino_lookup/Makefile create mode 100644 fs/btrfs/ino_lookup/README.md create mode 100644 fs/btrfs/ino_lookup/ino_lookup.c (limited to 'fs/btrfs/ino_lookup') diff --git a/fs/btrfs/ino_lookup/Makefile b/fs/btrfs/ino_lookup/Makefile new file mode 100644 index 0000000..4979e7a --- /dev/null +++ b/fs/btrfs/ino_lookup/Makefile @@ -0,0 +1,56 @@ +## +# By default everything is silent. If you want to change this behavior, simply +# assign V=1 when calling make. + +V = +ifeq ($(strip $(V)),) + E = @echo + Q = @ +else + E = @\# + Q = +endif + +## +# Compile options. You can use CROSS_COMPILE just like on the Linux Kernel. + +CC = $(CROSS_COMPILE)gcc +LD = $(CROSS_COMPILE)ld +CCFLAGS = -Werror -Wpedantic -Wall -Wextra -Wcast-align -Wcast-qual -Winit-self \ + -Wmissing-include-dirs -Wredundant-decls -Wshadow -Wsign-conversion \ + -Wswitch-default -Wundef -Wunreachable-code -Wmissing-noreturn \ + -D_FORTIFY_SOURCE=3 -fstrict-flex-arrays=3 -fsanitize=bounds-strict + # NOTE: last line: special flags for the examples :) +LDFLAGS = + + +# You can pass an optional `DEBUG` variable to manipulate the build type. +DEBUG = +ifeq ($(strip $(DEBUG)),) + CCFLAGS += -O3 +else + CCFLAGS += -g +endif + +## +# Paths. + +SRC = $(wildcard *.c) +EXES = $(SRC:.c=) + +## +# Targets + +.PHONY: all +all: clean $(EXES) + +.PHONY: build +build: $(EXES) + +.c: + $(E) " CC " $(*F) + $(Q) $(CC) $(CCFLAGS) $< -o $@ + +.PHONY: clean +clean: + $(Q) rm -f $(EXES) diff --git a/fs/btrfs/ino_lookup/README.md b/fs/btrfs/ino_lookup/README.md new file mode 100644 index 0000000..b4cfb15 --- /dev/null +++ b/fs/btrfs/ino_lookup/README.md @@ -0,0 +1,117 @@ +The ["inode lookup" +ioctl](https://btrfs.readthedocs.io/en/latest/btrfs-ioctl.html#btrfs-ioc-ino-lookup) +allows you to request certain information for a given inode. This ioctl is a bit +special because it has two modes of operation. + +First, you can provide both an inode and a tree ID. Doing this will allow +callers to retrieve the name that identifies the given inode on the tree +ID. Doing this requires `CAP_SYS_ADMIN` privileges. If you don't know which tree +ID to pick, you can also pick `0`, which selects the current subvolume you are +currently working with. + +I have written a small example in the form of `ino_lookup.c`, which performs a +call to this ioctl. I do this with a mount point with four subvolumes like so: + +``` +# btrfs subvolume list mnt/ +ID 256 gen 138 top level 5 path a +ID 257 gen 138 top level 5 path b +ID 258 gen 138 top level 5 path c +ID 259 gen 138 top level 5 path d + +# tree --inodes --device mnt/ +[ 256 55] mnt/ +├── [ 256 46] a +│   ├── [ 257 46] a.txt +│   └── [ 258 46] b.txt +├── [ 257 55] a.txt +├── [ 256 47] b +│   └── [ 257 47] b.txt +├── [ 256 56] c +│   └── [ 257 56] c.txt +└── [ 256 57] d + └── [ 257 57] d.txt +``` + +The first argument to pass to this small program is the tree ID, and the second +is the inode. With this into account, I have written a small shell script that +runs this program with different arguments like so (output with `set -x`): + +``` +# ./lookup_test.sh ++ ./ino_lookup mnt 256 258 +tree ID: 256 +inode: 258 +path: b.txt/ ++ ./ino_lookup mnt 0 257 +tree ID: 5 +inode: 257 +path: a.txt/ ++ ./ino_lookup mnt 256 257 +tree ID: 256 +inode: 257 +path: a.txt/ ++ ./ino_lookup mnt 257 257 +tree ID: 257 +inode: 257 +path: b.txt/ ++ ./ino_lookup mnt/a 0 257 +tree ID: 256 +inode: 257 +path: a.txt/ ++ ./ino_lookup mnt/a 256 257 +tree ID: 256 +inode: 257 +path: a.txt/ ++ ./ino_lookup mnt/a 257 257 +tree ID: 257 +inode: 257 +path: b.txt/ +``` + +But, as I briefly mentioned earlier, this ioctl also has another mode of +operation. This mode of operation does not require `CAP_SYS_ADMIN` and you can +call it by setting the tree ID to 0, and passing the special +`BTRFS_FIRST_FREE_OBJECTID` constant as a value for the inode. With all of this +you will be able to fetch the tree ID for the targetted mount point. Hence: + +``` +$ ./ino_lookup mnt/ 0 --tree-id +tree ID: 5 +inode: 256 +path: +$ ./ino_lookup mnt/a 0 --tree-id +tree ID: 256 +inode: 256 +path: +$ ./ino_lookup mnt/b 0 --tree-id +tree ID: 257 +inode: 256 +path: +$ ./ino_lookup mnt/c 0 --tree-id +tree ID: 258 +inode: 256 +path: +$ ./ino_lookup mnt/d 0 --tree-id +tree ID: 259 +inode: 256 +path: +``` + +You will also notice that: + +1. The inode is always set to `256`. This ioctl doesn't change the value, it + just happens to be the value of the `BTRFS_FIRST_FREE_OBJECTID` constant we + set for this mode of operation. +2. The path will be blank out on purpose. + +## Patches sent to the Linux kernel as a result + +I arrived to this ioctl pretty much out of nowhere as I was cruising through +another patch set I was working on. After looking into it later, I saw that a +bit of cleanup could be done on the kernel side. Then I worked on the patch and +pretty much forgot about it when I went into paternity leave :) + +Once I got back and saw the patch as something to be sent, I rebased the patch +and submitted it. Read the discussion in +[lore.kernel.org](https://lore.kernel.org/all/20260629142056.309300-1-mssola@mssola.com/). diff --git a/fs/btrfs/ino_lookup/ino_lookup.c b/fs/btrfs/ino_lookup/ino_lookup.c new file mode 100644 index 0000000..2233c9b --- /dev/null +++ b/fs/btrfs/ino_lookup/ino_lookup.c @@ -0,0 +1,57 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + if (argc < 3) { + fprintf(stderr, "usage: %s \n", argv[0]); + exit(EXIT_FAILURE); + } + + const char *const path = argv[1]; + unsigned long long treeid = strtoull(argv[2], NULL, 10); + + unsigned long long ino; + if (strcmp(argv[3], "--tree-id") == 0) { + ino = BTRFS_FIRST_FREE_OBJECTID; + if (treeid > 0) { + printf("warning: the given tree ID value will be discarded.\n"); + } + treeid = 0; + } else { + ino = strtoull(argv[3], NULL, 10); + } + + int fd = open(path, O_RDONLY | O_DIRECTORY); + if (fd < 0) { + perror("open"); + return 1; + } + + // Check the README.md file on the args. + struct btrfs_ioctl_ino_lookup_args args; + memset(&args, 0, sizeof(args)); + args.treeid = treeid; + args.objectid = ino; + + if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0) { + perror("BTRFS_IOC_INO_LOOKUP"); + close(fd); + exit(EXIT_FAILURE); + } + + printf("tree ID: %llu\n", args.treeid); + printf("inode: %llu\n", ino); + printf("path: %s\n", args.name); + + close(fd); + return 0; +} -- cgit v1.2.3