From: Artem Blagodarenko EXT4 provides the EXT4_IOC_SET_LUFID ioctl, which allows setting or replacing the LUFID dirdata field for an existing directory entry. The set_lufid utility uses this ioctl and accepts a directory path, directory entry name, and LUFID value as arguments. This utility is used by subsequent dirdata-related tests. Signed-off-by: Artem Blagodarenko --- common/config | 1 + src/Makefile | 2 +- src/set_lufid.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+), 1 deletion(-) diff --git a/common/config b/common/config index 8468a600..2ca23207 100644 --- a/common/config +++ b/common/config @@ -210,6 +210,7 @@ export LVM_PROG="$(type -P lvm)" export LSATTR_PROG="$(type -P lsattr)" export CHATTR_PROG="$(type -P chattr)" export DEBUGFS_PROG="$(type -P debugfs)" +export SET_LUFID_PROG="$(type -P set_lufid || echo $here/src/set_lufid)" export UUIDGEN_PROG="$(type -P uuidgen)" export KEYCTL_PROG="$(type -P keyctl)" export XZ_PROG="$(type -P xz)" diff --git a/src/Makefile b/src/Makefile index 31ac43b2..a1f161b0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -36,7 +36,7 @@ LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \ fscrypt-crypt-util bulkstat_null_ocount splice-test chprojid_fail \ detached_mounts_propagation ext4_resize t_readdir_3 splice2pipe \ uuid_ioctl t_snapshot_deleted_subvolume fiemap-fault min_dio_alignment \ - rw_hint fs-monitor + rw_hint fs-monitor set_lufid EXTRA_EXECS = dmerror fill2attr fill2fs fill2fs_check scaleread.sh \ btrfs_crc32c_forged_name.py popdir.pl popattr.py \ diff --git a/src/set_lufid.c b/src/set_lufid.c new file mode 100644 index 00000000..2e75939a --- /dev/null +++ b/src/set_lufid.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * set_lufid.c --- Set LUFID on a directory entry using IOCTL + * + * Copyright (C) 2026 The Lustre Collective. All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Structure for EXT4_IOC_SET_LUFID - should match kernel definition */ +struct ext4_dirdata_fid { + __u8 edf_data_len; + char edf_data[255]; +} __attribute__((packed)); + +struct ext4_set_lufid { + __u8 esl_name_len; + char esl_name[255 + 1]; + union { + char esl_data[1 + 255]; + struct ext4_dirdata_fid esl_edf; + }; +} __attribute__((packed)); + +#ifndef EXT4_IOC_SET_LUFID +#define EXT4_IOC_SET_LUFID _IOW('f', 47, struct ext4_set_lufid) +#endif + +static void usage(const char *prog) +{ + fprintf(stderr, "Usage: %s DIRECTORY FILENAME [LUFID]\n", prog); + fprintf(stderr, " DIRECTORY: path to the directory containing the FILENAME\n"); + fprintf(stderr, " FILENAME: name of the file to set LUFID on\n"); + fprintf(stderr, " LUFID: data to attach (generated if not passed)\n"); + exit(1); +} + +static void dump_lufid_payload(const char *data, int len) +{ + int i; + + printf("LUFID payload length: %d bytes\n", len); + printf("LUFID payload hex:"); + for (i = 0; i < len; i++) + printf(" %02x", (unsigned char)data[i]); + printf("\n"); +} + +static int build_default_lufid(int dir_fd, const char *dir_path, const char *filename, + uint32_t fid[5]) +{ + int file_fd; + unsigned long ver = 0; + struct stat st; + + /* Build an IGIF-style default payload from inode + version. */ + file_fd = openat(dir_fd, filename, O_RDONLY | O_CLOEXEC); + if (file_fd < 0) { + fprintf(stderr, "Error opening %s/%s: %s\n", + dir_path, filename, strerror(errno)); + return -1; + } + + if (fstat(file_fd, &st) < 0) { + fprintf(stderr, "Error stating %s/%s: %s\n", + dir_path, filename, strerror(errno)); + close(file_fd); + return -1; + } + + fid[0] = (uint32_t)(st.st_ino >> 32); + fid[1] = (uint32_t)st.st_ino; + + if (ioctl(file_fd, FS_IOC_GETVERSION, &ver) < 0) { + fprintf(stderr, "Error calling EXT4_IOC_GETVERSION for %s/%s: %s\n", + dir_path, filename, strerror(errno)); + close(file_fd); + return -1; + } + + fid[2] = (uint32_t)ver; + fid[3] = (uint32_t)(ver >> 32); + + close(file_fd); + return 0; +} + +int main(int argc, char *argv[]) +{ + const char *dir_path; + const char *filename; + const char *lufid_data; + DIR *dirp; + int fd; + int name_len, data_len; + struct ext4_set_lufid lufid_args; + struct stat st; + uint32_t fid[4]; + int rc; + + if (argc < 2) { + usage(argv[0]); + } + + dir_path = argv[1]; + filename = argv[2]; + name_len = strlen(filename) + 1; /* +1 for NUL terminator */ + + if (name_len == 0 || name_len > 255) { + fprintf(stderr, "Error: Invalid filename length: %d (must be 1-256 with NUL)\n", + name_len); + return 1; + } + + /* Check if directory exists and is a directory */ + if (stat(dir_path, &st) < 0) { + fprintf(stderr, "Error accessing %s: %s\n", dir_path, strerror(errno)); + return 1; + } + + if (!S_ISDIR(st.st_mode)) { + fprintf(stderr, "Error: %s is not a directory\n", dir_path); + return 1; + } + + /* Open the directory */ + dirp = opendir(dir_path); + if (!dirp) { + fprintf(stderr, "Error opening directory %s: %s\n", dir_path, strerror(errno)); + return 1; + } + + fd = dirfd(dirp); + if (fd < 0) { + fprintf(stderr, "Error getting directory fd: %s\n", strerror(errno)); + closedir(dirp); + return 1; + } + + if (argc > 3) { + lufid_data = argv[3]; + data_len = strlen(lufid_data) + 1; + } else { + rc = build_default_lufid(fd, dir_path, filename, fid); + if (rc) { + fprintf(stderr, "Error getting lufid for %s/%s\n", + dir_path, filename); + closedir(dirp); + return 1; + } + lufid_data = (char *)fid; + data_len = sizeof(fid) + 1; + } + + if (data_len == 0 || data_len > 255) { + fprintf(stderr, "Error: Invalid LUFID data length: %d (must be 1-256 with NUL)\n", + data_len); + closedir(dirp); + return 1; + } + + /* Prepare LUFID data */ + memset(&lufid_args, 0, sizeof(lufid_args)); + lufid_args.esl_name_len = name_len; + lufid_args.esl_edf.edf_data_len = data_len; + /* Ensure filename is properly NUL-terminated at the correct position */ + strncpy(lufid_args.esl_name, filename, name_len - 1); + lufid_args.esl_name[name_len - 1] = '\0'; + memcpy(lufid_args.esl_edf.edf_data, lufid_data, data_len); + + /* Call the ioctl */ + if (ioctl(fd, EXT4_IOC_SET_LUFID, &lufid_args)) { + fprintf(stderr, "Error calling EXT4_IOC_SET_LUFID for %s/%s: %s\n", + dir_path, filename, strerror(errno)); + closedir(dirp); + return 1; + } + + closedir(dirp); + printf("Successfully set LUFID on %s in directory %s\n", filename, dir_path); + dump_lufid_payload(lufid_args.esl_edf.edf_data, data_len); + + return 0; +} -- 2.43.7