aboutsummaryrefslogtreecommitdiff
path: root/fs/btrfs/ino_lookup
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mssola@mssola.com>2026-07-01 13:40:16 +0200
committerMiquel Sabaté Solà <mssola@mssola.com>2026-07-01 13:40:16 +0200
commit7440c52ff48e957c7b4077d81cd5bc81b3153fd8 (patch)
treef926871dfff0173a2f808ac94e0bcdf48285fa5e /fs/btrfs/ino_lookup
parentf55cde81689a1a1a29666a3626b069a883be430d (diff)
downloadfarga-7440c52ff48e957c7b4077d81cd5bc81b3153fd8.tar.gz
farga-7440c52ff48e957c7b4077d81cd5bc81b3153fd8.zip
btrfs: add ino_lookup example
Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
Diffstat (limited to 'fs/btrfs/ino_lookup')
-rw-r--r--fs/btrfs/ino_lookup/Makefile56
-rw-r--r--fs/btrfs/ino_lookup/README.md117
-rw-r--r--fs/btrfs/ino_lookup/ino_lookup.c57
3 files changed, 230 insertions, 0 deletions
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 <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <linux/btrfs.h>
+#include <linux/btrfs_tree.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+ if (argc < 3) {
+ fprintf(stderr, "usage: %s <mount point> <tree ID> <inode number | --tree-id>\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;
+}