From 501e5b2c8a477e9cb5d9afc060044bfb1f4f9f2c Mon Sep 17 00:00:00 2001 From: Caleb Carlson Date: Tue, 2 Apr 2024 10:29:48 -0600 Subject: [PATCH] LU-18027 lfs: lfs find handling special files This replaces fget_projid() with get_projid(), a newer function that is able to get project ids on regular files, directories, symbolic links, and special file types. This adopts the same approach to getting file attributes that lfs_project.c uses in project_get_fsxattr() by getting a special file's attributes by the containing directory. This patch also replaces the exclusion logic for the --projid flag, reducing the depth count of if/else clauses by checking a boolean equivalence statement. Boolean algebra table for why this works (skip means we don't print, include means we do): matches | exclude_projid | matches == exclude_projid | ------------------------------------------------------ 1 | 1 | 1 (skip) | 1 | 0 | 0 (include) | 0 | 1 | 0 (include) | 0 | 0 | 1 (skip) | Finally, this patch replaces the INVALID_PROJID = -1 definition with DEFAULT_PROJID = 0. A major reason for this is that projid is defined as an unsigned, 32-bit integer, so storing a negative value in it doesn't make much sense. With this patch we should be able to get the project id for every file type (special or not), so we no longer need a case for invalid projid. .gitignore .vscode/settings.json files. Add test_56ei in sanity.sh that sets a project id on some special files and checks that lfs find --printf picks up the file, and prints the correct project id. Updates error messages in test_56rd to not assume we can't get projid from special file types. Fix bug where we were only printing file types. HPE-bug-id: LUS-12195 Signed-off-by: Caleb Carlson Test-Parameters: testlist=sanity Change-Id: I8b10044ea62d662d6f9388725c0e93d55a43b431 Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/55709 Tested-by: jenkins Tested-by: Maloo Reviewed-by: corey tesdahl Reviewed-by: Shaun Tancheff Reviewed-by: Sakib Samar Reviewed-by: Petros Koutoupis Reviewed-by: Oleg Drokin --- lustre/tests/sanity.sh | 36 ++++++++++++- lustre/utils/liblustreapi.c | 126 ++++++++++++++++++++++++++++++++------------ 2 files changed, 126 insertions(+), 36 deletions(-) diff --git a/lustre/tests/sanity.sh b/lustre/tests/sanity.sh index d82258e..064e7d6 100755 --- a/lustre/tests/sanity.sh +++ b/lustre/tests/sanity.sh @@ -7524,14 +7524,14 @@ test_56rd() { mkfifo $dir/fifo || error "failed to create fifo file" $LFS find $dir -t p --printf "%p %y %LP\n" || - error "should not fail even cannot get projid from pipe file" + error "should not fail when getting projid from pipe file" found=$($LFS find $dir -t p --printf "%y") [[ "p" == $found ]] || error "found $found, expect p" mknod $dir/chardev c 1 5 || error "failed to create character device file" $LFS find $dir -t c --printf "%p %y %LP\n" || - error "should not fail even cannot get projid from chardev file" + error "should not fail when getting projid from chardev file" found=$($LFS find $dir -t c --printf "%y") [[ "c" == $found ]] || error "found $found, expect c" @@ -9518,6 +9518,38 @@ test_56eh() { } run_test 56eh "check lfs find --skip" +test_56ei() { + local path=$DIR/$tdir + local projid=1234 + local expected_count=3 + + # Create test dir containing: + # - regular file, with default projid + # - regular file, with unique projid + # - symbolic link, with unique projid + # - special char dev file, with unique projid + test_mkdir $path || error "mkdir $path failed" + touch $path/file0 $path/file$projid || error "touch failed" + ln -s $path/file$projid $path/link$projid + mknod $path/char$projid c 1 3 + $LFS project -p $projid -s $path/file$projid + $LFS project -p $projid -s $path/link$projid + $LFS project -p $projid -s $path/char$projid + stack_trap "rm -rf $path" EXIT + + $LFS project -r $path/ + echo -e "Actual output:\n$($LFS find $path --printf '%LP %p\n')" + + # Find all files and print their projids along with their path; count + found_count=$($LFS find $path --printf "%LP %p\n" | + grep -E "$projid $path/(link|char|file)$projid" | wc -l) + echo -e "found_count: $found_count" + [[ $found_count == $expected_count ]] || + error "Did not find any entries with expected projid $projid" + +} +run_test 56ei "test lfs find --printf prints correct projid for special files" + test_57a() { [ $PARALLEL == "yes" ] && skip "skip parallel run" # note test will not do anything if MDS is not local diff --git a/lustre/utils/liblustreapi.c b/lustre/utils/liblustreapi.c index addaa14..3930a3a 100644 --- a/lustre/utils/liblustreapi.c +++ b/lustre/utils/liblustreapi.c @@ -85,8 +85,8 @@ #define FORMATTED_BUF_LEN 1024 -#ifndef INVALID_PROJID -#define INVALID_PROJID -1 +#ifndef DEFAULT_PROJID +#define DEFAULT_PROJID 0 #endif static int llapi_msg_level = LLAPI_MSG_MAX; @@ -5246,10 +5246,7 @@ static int printf_format_lustre(char *seq, char *buffer, size_t size, *wrote = snprintf(buffer, size, DFID_NOBRACE, PFID(&fid)); goto format_done; case 'P': - if (projid == INVALID_PROJID) - *wrote = snprintf(buffer, size, "-1"); - else - *wrote = snprintf(buffer, size, "%u", projid); + *wrote = snprintf(buffer, size, "%u", projid); goto format_done; case 'a': /* file attributes */ longopt = false; @@ -5562,22 +5559,81 @@ static void printf_format_string(struct find_param *param, char *path, } /* - * Get file/directory project id. - * by the open fd resides on. - * Return 0 and project id on success, or -ve errno. + * Gets the project id of a file, directory, or special file, + * and stores it at the projid memory address passed in. + * Returns 0 on success, or -errno for failure. + * + * @param[in] path The full path of the file or directory we're trying + * to retrieve the project id for. + * @param[in] fd A reference to the file descriptor of either the file + * or directory we're inspecting. The file/dir may or may + * not have been already opened, but if not, we'll open + * it here (for regular files/directories). + * @param[in] mode The mode type of the file. This will tell us if the file + * is a regular file/dir or if it's a special file type. + * @param[out] projid A reference to where to store the projid of the file/dir */ -static int fget_projid(int fd, __u32 *projid) +static int get_projid(const char *path, int *fd, mode_t mode, __u32 *projid) { - struct fsxattr fsx; - int rc; + struct fsxattr fsx = { 0 }; + struct lu_project lu_project = { 0 }; + int ret = 0; - rc = ioctl(fd, FS_IOC_FSGETXATTR, &fsx); - if (rc) { - *projid = INVALID_PROJID; - return -errno; + /* Check the mode of the file */ + if (S_ISREG(mode) || S_ISDIR(mode)) { + /* This is a regular file type or directory */ + if (*fd < 0) { + /* If we haven't yet opened the file, + * open it in read-only mode + */ + *fd = open(path, O_RDONLY | O_NOCTTY | O_NDELAY); + if (*fd <= 0) { + llapi_error(LLAPI_MSG_ERROR, -ENOENT, + "warning: %s: unable to open file \"%s\"to get project id", + __func__, path); + return -ENOENT; + } + } + ret = ioctl(*fd, FS_IOC_FSGETXATTR, &fsx); + if (ret) + return -errno; + + *projid = fsx.fsx_projid; + } else { + /* This is a special file type, like a symbolic link, block or + * character device file. We'll have to open its parent + * directory and get metadata about the file through that. + */ + char dir_path[PATH_MAX + 1] = { 0 }; + char base_path[PATH_MAX + 1] = { 0 }; + + strncpy(dir_path, path, PATH_MAX); + strncpy(base_path, path, PATH_MAX); + char *dir_name = dirname(dir_path); + char *base_name = basename(base_path); + int dir_fd = open(dir_name, O_RDONLY | O_NOCTTY | O_NDELAY); + + if (dir_fd < 0) { + llapi_error(LLAPI_MSG_ERROR, -ENOENT, + "warning: %s: unable to open dir \"%s\"to get project id", + __func__, path); + return -errno; + } + lu_project.project_type = LU_PROJECT_GET; + if (base_name) + strncpy(lu_project.project_name, base_name, NAME_MAX); + + ret = ioctl(dir_fd, LL_IOC_PROJECT, &lu_project); + if (ret) { + llapi_error(LLAPI_MSG_ERROR, -ENOENT, + "warning: %s: failed to get xattr for '%s': %s", + __func__, path, strerror(errno)); + return -errno; + } + *projid = lu_project.project_id; + close(dir_fd); } - *projid = fsx.fsx_projid; return 0; } @@ -5623,7 +5679,7 @@ static int cb_find_init(char *path, int p, int *dp, __u32 stripe_count = 0; __u64 flags; int fd = -2; - __u32 projid = INVALID_PROJID; + __u32 projid = DEFAULT_PROJID; bool gather_all = false; if (p == -1 && d == -1) @@ -5962,24 +6018,26 @@ obd_matches: } } + /* Retrieve project id from file/dir */ if (param->fp_check_projid || gather_all) { - if (fd == -2) - fd = open(path, O_RDONLY | O_NONBLOCK); - - if (fd > 0) - ret = fget_projid(fd, &projid); - else - ret = -errno; + ret = get_projid(path, &fd, lmd->lmd_stx.stx_mode, &projid); + if (ret) { + llapi_error(LLAPI_MSG_ERROR, -ENOENT, + "warning: %s: failed to get project id from file \"%s\"", + __func__, path); + goto out; + } if (param->fp_check_projid) { - if (ret) - goto out; - if (projid == param->fp_projid) { - if (param->fp_exclude_projid) - goto decided; - } else { - if (!param->fp_exclude_projid) - goto decided; - } + /* Conditionally filter this result based on --projid + * param, and whether or not we're including or + * excluding matching results. + * fp_exclude_projid = 0 means only include exact match. + * fp_exclude_projid = 1 means exclude exact match. + */ + bool matches = projid == param->fp_projid; + + if (matches == param->fp_exclude_projid) + goto decided; } } -- 1.8.3.1