From 022c5a9a77d4612dce28b76a7691b7af1cefd058 Mon Sep 17 00:00:00 2001 From: Wang Shilong Date: Mon, 16 Oct 2017 14:58:04 +0800 Subject: [PATCH] LU-10030 utils: add lfs tool to change/list project of file Currently, we are using chattr/lsattr for project quota interface, this have some problems: 1)Client side need patched e2fsprogs or latest upstream e2fsprogs. 2)Project quota will be no longer osd-ldiskfs based, ZFS too, zfs guys might dislike ldiskfs tool dependency for them. 3)customers argue chattr might be a little dangerous. So this patch add native lfs tools for project quota. usage: project [-p id] [-s] [-r] set project ID and/or inherit flag for specified file(s) or directory. project [-d|-r [-0]] list project ID and flags on file(s) or directory, print outliers project -c [-d|-r [-p id] [-0]] check project ID and flags on file(s) or directory, print outliers project -C [-r] [-k] clear the project inherit flag and ID on the file or directory Test-Parameters: testlist=sanity-quota,sanity-quota,sanity-quota,\ sanity-quota clientdistro=el7 serverdistro=el7 \ ostfilesystemtype=ldiskfs mdtfilesystemtype=ldiskfs Signed-off-by: Wang Shilong Change-Id: I45960fb8fbd12e22a654792fba517896c0447447 Reviewed-on: https://review.whamcloud.com/29190 Tested-by: Jenkins Reviewed-by: Andreas Dilger Tested-by: Maloo Reviewed-by: Fan Yong Reviewed-by: Oleg Drokin --- lustre/doc/Makefile.am | 1 + lustre/doc/lfs-project.1 | 88 +++++++ lustre/doc/lfs.1 | 13 +- lustre/include/uapi/linux/lustre/lustre_user.h | 1 + lustre/tests/sanity-quota.sh | 115 ++++++--- lustre/utils/Makefile.am | 2 +- lustre/utils/lfs.c | 169 ++++++++++++- lustre/utils/lfs_project.c | 337 +++++++++++++++++++++++++ lustre/utils/lfs_project.h | 68 +++++ 9 files changed, 752 insertions(+), 42 deletions(-) create mode 100644 lustre/doc/lfs-project.1 create mode 100644 lustre/utils/lfs_project.c create mode 100644 lustre/utils/lfs_project.h diff --git a/lustre/doc/Makefile.am b/lustre/doc/Makefile.am index 8d63f45..33ad4a3 100644 --- a/lustre/doc/Makefile.am +++ b/lustre/doc/Makefile.am @@ -53,6 +53,7 @@ MANFILES = \ lfs-setdirstripe.1 \ lfs-setstripe.1 \ lfs-setquota.1 \ + lfs-project.1 \ l_getidentity.8 \ lgss_sk.8 \ lhbadm.8 \ diff --git a/lustre/doc/lfs-project.1 b/lustre/doc/lfs-project.1 new file mode 100644 index 0000000..cabd831 --- /dev/null +++ b/lustre/doc/lfs-project.1 @@ -0,0 +1,88 @@ +.TH LFS-PROJECT 1 2017-10-26 "Lustre" "Lustre Utilities" +.SH NAME +lfs project \- Change or list project attribute for specified file or directory. +.SH SYNOPSIS +.BR "lfs project" " [" -d | -r ] " "< \fI file | directory...\fR> +.br +.BR "lfs project" " {" -p " "\fIID " |" -s } " "[ -r ] " "<\fI file | directory...\fR> +.br +.BR "lfs project" " -c" " [" -d | -r " [" -p " "\fIID ] " [" -0 ] ] " <" file | directory...> +.br +.BR "lfs project" " -C" " [" -r | -k ] " <" file | directory...> +.br +.SH DESCRIPTION +.TP +.BR "lfs project" " [" -d | -r ] +.RI < file | directory...> +.TP +List project ID and flags on file(s) or directories. +.TP +.B -d +Show the directory's own project ID and flags, override \fB-r\fR option. +.TP +.B -r +Recursively list all descendants'(of the directory) project attribute. +.TP +.BR "lfs project" " {" -p " "\fIID " |" -s } " "[ -r ] +.RI < file | directory...> +.TP +Set project ID and/or inherit flag for specified file(s) or directories. +.TP +.B -p <\fIID\fR> +Set project \fIID\fR with given value for the specified file or directory +.TP +.B -s +Set the +.B PROJID_INHERIT +attribute on directories, so that new files and subdirectories created +therein will inherit the project ID and attribute from the parent. +.TP +.B -r +Set project \fIID\fR with the directory's project ID for all +its descendants (with \fB-p\fR specified). For descendant directories, also set +inherit flag (if \fI-s\fR specified). +.TP +.BR "lfs project" " -c" " [" -d|-r " [" -p " "\fIID ] " [" -0 ] ] +.RI < file | directory...> +.TP +Check project ID and flags on file(s) or directories, print outliers. +.TP +.B -c +Check project ID and inherit flag on specified file(s) or directory. If +.B -p +is not given, then use the project ID on the top-level directory +, otherwise use the ID specified with +.BR -p . +if checking a directory and or recursively, print only files that do not match. +.TP +.B -0 +Print pathnames found by -c with a trailing NUL for use by +.BR \' xargs "(1) " -0 \'. +.TP +.BR "lfs project" " -C [" -r | -k ] +.RI < file | directory...> +.TP +Clear the project inherit flag and ID on the file(s) or directories +.TP +.B -C +Clear inherit attribute, project ID will be reset to be 0 in default +.TP +.B -k +keep the project ID unchanged. +.TP +.SH EXAMPLES +.TP +.B $ lfs project -srp 1000 /mnt/lustre/dir1 +set directory quota on +.BR /mnt/lustre/dir1, +all descendants' project ID and inherit attribute are set. +.TP +.B $ lfs project -cr -p 1000 /mnt/lustre/dir1 +check directory +.BR /mnt/lustre/dir1, +whether all files and directories ID are 1000, inherit attribute +is properly set for all directories, print mismatch +if any are found. +.SH SEE ALSO +.BR lfs (1) +.BR xargs (1) diff --git a/lustre/doc/lfs.1 b/lustre/doc/lfs.1 index cf59e78..2ef08a5 100644 --- a/lustre/doc/lfs.1 +++ b/lustre/doc/lfs.1 @@ -364,20 +364,21 @@ Turn quotas of user and group on Turn quotas of user and group off .SH NOTES The usage of \fBlfs hsm_*\fR, \fBlfs setstripe\fR, \fBlfs migrate\fR, \fBlfs setdirstripe\fR, -\fBlfs getdirstripe\fR and \fBlfs mkdir\fR are explained in separated man pages. +\fBlfs getdirstripe\fR, \fBlfs mkdir\fR and \fBlfs project\fR are explained in separate +man pages. .SH BUGS The \fBlfs find\fR command isn't as comprehensive as \fBfind\fR(1). .SH AUTHOR The lfs command is part of the Lustre filesystem. .SH SEE ALSO +.BR lctl (8), .BR lfs-df (1), -.BR lfs-hsm (1), -.BR lfs-setdirstripe (1), .BR lfs-getdirstripe (1), +.BR lfs-hsm (1), .BR lfs-mkdir (1), -.BR lfs_migrate (1), -.BR lfs-setstripe (1), .BR lfs-migrate (1), +.BR lfs-project (1), +.BR lfs-setdirstripe (1), .BR lfs-setquota (1), -.BR lctl (8), +.BR lfs-setstripe (1), .BR lustre (7) diff --git a/lustre/include/uapi/linux/lustre/lustre_user.h b/lustre/include/uapi/linux/lustre/lustre_user.h index d840295..a07d74c 100644 --- a/lustre/include/uapi/linux/lustre/lustre_user.h +++ b/lustre/include/uapi/linux/lustre/lustre_user.h @@ -427,6 +427,7 @@ struct fsxattr { #endif #define LL_IOC_FSGETXATTR FS_IOC_FSGETXATTR #define LL_IOC_FSSETXATTR FS_IOC_FSSETXATTR +#define LL_PROJINHERIT_FL 0x20000000 #define LL_STATFS_LMV 1 diff --git a/lustre/tests/sanity-quota.sh b/lustre/tests/sanity-quota.sh index 6293ad9..b27702d 100755 --- a/lustre/tests/sanity-quota.sh +++ b/lustre/tests/sanity-quota.sh @@ -72,11 +72,10 @@ export QUOTA_AUTO=0 check_and_setup_lustre is_project_quota_supported() { - lsattr -dp > /dev/null 2>&1 || return 1 - [ "$(facet_fstype $SINGLEMDS)" == "ldiskfs" ] && [ $(lustre_version_code $SINGLEMDS) -gt \ $(version_code 2.9.55) ] && + lfs --help | grep project >&/dev/null && egrep -q "7." /etc/redhat-release && return 0 if [ "$(facet_fstype $SINGLEMDS)" == "zfs" ]; then @@ -117,8 +116,8 @@ lustre_fail() { change_project() { - echo "chattr $*" - chattr $* || error "chattr $* failed" + echo "lfs project $*" + lfs project $* || error "lfs project $* failed" } RUNAS="runas -u $TSTID -g $TSTID" @@ -696,16 +695,14 @@ test_2() { [ $USED -ne 0 ] && error "Used inodes($USED) for project $TSTPRJID isn't 0" - change_project +P $DIR/$tdir/ - change_project -p $TSTPRJID -d $DIR/$tdir + change_project -sp $TSTPRJID $DIR/$tdir log "Create $LIMIT files ..." $RUNAS createmany -m ${TESTFILE} $((LIMIT-1)) || quota_error p \ $TSTPRJID "project create fail, but expect success" log "Create out of file quota ..." $RUNAS touch ${TESTFILE}_xxx && quota_error p $TSTPRJID \ "project create success, but expect EDQUOT" - change_project -P $DIR/$tdir - change_project -p 0 -d $DIR/$tdir + change_project -C $DIR/$tdir cleanup_quota_test USED=$(getquota -p $TSTPRJID global curinodes) @@ -846,7 +843,7 @@ test_3() { # make sure the system is clean USED=$(getquota -p $TSTPRJID global curspace) [ $USED -ne 0 ] && error \ - "Used space($USED) for project $TSTPROJID isn't 0." + "Used space($USED) for project $TSTPRJID isn't 0." $LFS setquota -t -p --block-grace $GRACE --inode-grace \ $MAX_IQ_TIME $DIR || @@ -877,8 +874,7 @@ test_file_soft() { setup_quota_test trap cleanup_quota_test EXIT - is_project_quota_supported && change_project +P $DIR/$tdir/ && - change_project -p $TSTPRJID -d $DIR/$tdir + is_project_quota_supported && change_project -sp $TSTPRJID $DIR/$tdir echo "Create files to exceed soft limit" $RUNAS createmany -m ${TESTFILE}_ $((LIMIT + 1)) || @@ -1528,8 +1524,7 @@ test_8() { $LFS setquota -g $TSTUSR -b 0 -B $BLK_LIMIT -i 0 -I $FILE_LIMIT $DIR || error "set group quota failed" if is_project_quota_supported; then - change_project +P $DIR/$tdir && change_project -p \ - $TSTPRJID -d $DIR/$tdir + change_project -sp $TSTPRJID $DIR/$tdir echo "Set enough high limit for project: $TSTPRJID" $LFS setquota -p $TSTPRJID -b 0 \ -B $BLK_LIMIT -i 0 -I $FILE_LIMIT $DIR || @@ -1541,8 +1536,7 @@ test_8() { $RUNAS bash rundbench -D $DIR/$tdir 3 $duration || quota_error a $TSTUSR "dbench failed!" - is_project_quota_supported && change_project -P $DIR/$tdir && - change_project -dp 0 $DIR/$tdir + is_project_quota_supported && change_project -C $DIR/$tdir cleanup_quota_test resetquota -u $TSTUSR resetquota -g $TSTUSR @@ -2762,18 +2756,18 @@ test_39() { setup_quota_test || error "setup quota failed with $?" touch $TESTFILE - projectid=$(lsattr -p $TESTFILE | awk '{print $1}') + projectid=$(lfs project $TESTFILE | awk '{print $1}') [ $projectid -ne 0 ] && error "Project id should be 0 not $projectid" change_project -p 1024 $TESTFILE - projectid=$(lsattr -p $TESTFILE | awk '{print $1}') + projectid=$(lfs project $TESTFILE | awk '{print $1}') [ $projectid -ne 1024 ] && error "Project id should be 1024 not $projectid" stopall || error "failed to stopall (1)" mount setupall - projectid=$(lsattr -p $TESTFILE | awk '{print $1}') + projectid=$(lfs project $TESTFILE | awk '{print $1}') [ $projectid -ne 1024 ] && error "Project id should be 1024 not $projectid" @@ -2790,8 +2784,8 @@ test_40a() { setup_quota_test || error "setup quota failed with $?" mkdir -p $dir1 $dir2 - change_project +P $dir1 && change_project -p 1 -d $dir1 && touch $dir1/1 - change_project +P $dir2 && change_project -p 2 -d $dir2 + change_project -sp 1 $dir1 && touch $dir1/1 + change_project -sp 2 $dir2 ln $dir1/1 $dir2/1_link && error "Hard link across different project quota should fail" @@ -2809,11 +2803,11 @@ test_40b() { setup_quota_test || error "setup quota failed with $?" mkdir -p $dir1 $dir2 - change_project +P $dir1 && change_project -p 1 -d $dir1 && touch $dir1/1 - change_project +P $dir2 && change_project -p 2 -d $dir2 + change_project -sp 1 $dir1 && touch $dir1/1 + change_project -sp 2 $dir2 mv $dir1/1 $dir2/2 || error "mv failed $?" - local projid=$(lsattr -p $dir2/2 | awk '{print $1}') + local projid=$(lfs project $dir2/2 | awk '{print $1}') if [ "$projid" != "2" ]; then error "project id expected 2 not $projid" fi @@ -2830,13 +2824,13 @@ test_40c() { setup_quota_test || error "setup quota failed with $?" local dir="$DIR/$tdir/dir" - mkdir -p $dir && change_project +P $dir && change_project -dp 1 $dir + mkdir -p $dir && change_project -sp 1 $dir $LFS mkdir -i 1 $dir/remote_dir || error "create remote dir failed" - local projid=$(lsattr -dp $dir/remote_dir | awk '{print $1}') + local projid=$(lfs project -d $dir/remote_dir | awk '{print $1}') [ "$projid" != "1" ] && error "projid id expected 1 not $projid" touch $dir/remote_dir/file #verify inherit works file for remote dir. - local projid=$(lsattr -dp $dir/remote_dir/file | awk '{print $1}') + local projid=$(lfs project -d $dir/remote_dir/file | awk '{print $1}') [ "$projid" != "1" ] && error "file under remote dir expected 1 not $projid" @@ -2858,7 +2852,7 @@ test_50() { setup_quota_test || error "setup quota failed with $?" local dir="$DIR/$tdir/dir" - mkdir $dir && change_project -dp 1 $dir + mkdir $dir && change_project -p 1 $dir count=$($LFS find --projid 1 $DIR | wc -l) [ "$count" != 1 ] && error "expected 1 but got $count" @@ -2873,7 +2867,7 @@ test_51() { setup_quota_test || error "setup quota failed with $?" local dir="$DIR/$tdir/dir" - mkdir $dir && change_project -dp 1 $dir && change_project +P $dir + mkdir $dir && change_project -sp 1 $dir local used=$(getquota -p 1 global curinodes) [ $used != "1" ] && error "expected 1 got $used" @@ -2904,7 +2898,7 @@ test_52() { skip "Project quota is not supported" && return 0 setup_quota_test || error "setup quota failed with $?" local dir="$DIR/$tdir/dir" - mkdir $dir && change_project -dp 1 $dir && change_project +P $dir + mkdir $dir && change_project -sp 1 $dir touch $DIR/$tdir/file #Try renaming a file into the project. This should fail. @@ -2922,17 +2916,72 @@ test_53() { skip "Project quota is not supported" && return 0 setup_quota_test || error "setup quota failed with $?" local dir="$DIR/$tdir/dir" - mkdir $dir && change_project +P $dir - lsattr -pd $dir | grep P || error "inherit attribute should be set" + mkdir $dir && change_project -s $dir + lfs project -d $dir | grep P || error "inherit attribute should be set" - change_project -Pd $dir - lsattr -pd $dir | grep P && error "inherit attribute should be cleared" + change_project -C $dir + lfs project -d $dir | grep P && + error "inherit attribute should be cleared" rm -rf $dir cleanup_quota_test } run_test 53 "Project inherit attribute could be cleared" +test_54() { + ! is_project_quota_supported && + skip "Project quota is not supported" && return 0 + setup_quota_test || error "setup quota failed with $?" + trap cleanup_quota_test EXIT + local testfile="$DIR/$tdir/$tfile-0" + + #set project ID/inherit attribute + change_project -sp $TSTPRJID $DIR/$tdir + $RUNAS createmany -m ${testfile} 100 || + error "create many files failed" + + local proj_count=$(lfs project -r $DIR/$tdir | wc -l) + # one more count for directory itself */ + ((proj_count++)) + + #check project + local proj_count1=$(lfs project -rcp $TSTPRJID $DIR/$tdir | wc -l) + [ $proj_count1 -eq 0 ] || error "c1: expected 0 got $proj_count1" + + proj_count1=$(lfs project -rcp $((TSTPRJID+1)) $DIR/$tdir | wc -l) + [ $proj_count1 -eq $proj_count ] || + error "c2: expected $proj_count got $proj_count1" + + #clear project but with kept projid + change_project -rCk $DIR/$tdir + proj_count1=$(lfs project -rcp $TSTPRJID $DIR/$tdir | wc -l) + [ $proj_count1 -eq $proj_count ] || + error "c3: expected $proj_count got $proj_count1" + + #verify projid untouched. + proj_count1=$(lfs project -r $DIR/$tdir | grep -c $TSTPRJID) + ((proj_count1++)) + [ $proj_count1 -eq $proj_count ] || + error "c4: expected $proj_count got $proj_count1" + + # test -0 option + lfs project $DIR/$tdir -cr -0 | xargs -0 lfs project -s + proj_count1=$(lfs project -rcp $TSTPRJID $DIR/$tdir | wc -l) + [ $proj_count1 -eq 0 ] || error "c5: expected 0 got $proj_count1" + + #this time clear all + change_project -rC $DIR/$tdir + proj_count1=$(lfs project -r $DIR/$tdir | grep -c $TSTPRJID) + [ $proj_count1 -eq 0 ] || + error "c6: expected 0 got $proj_count1" + #cleanup + unlinkmany ${testfile} 100 || + error "unlink many files failed" + + cleanup_quota_test +} +run_test 54 "basic lfs project interface test" + quota_fini() { do_nodes $(comma_list $(nodes_list)) "lctl set_param debug=-quota" diff --git a/lustre/utils/Makefile.am b/lustre/utils/Makefile.am index c58743b..56aba33 100644 --- a/lustre/utils/Makefile.am +++ b/lustre/utils/Makefile.am @@ -58,7 +58,7 @@ endif lctl_LDADD := liblustreapi.a $(LIBCFS) $(LIBREADLINE) $(PTHREAD_LIBS) lctl_DEPENDENCIES := $(LIBCFS) liblustreapi.a -lfs_SOURCES = lfs.c +lfs_SOURCES = lfs.c lfs_project.c lfs_project.h lfs_LDADD := liblustreapi.a $(LIBCFS) $(LIBREADLINE) lfs_DEPENDENCIES := $(LIBCFS) liblustreapi.a diff --git a/lustre/utils/lfs.c b/lustre/utils/lfs.c index 9d3fd32..9f168aa 100644 --- a/lustre/utils/lfs.c +++ b/lustre/utils/lfs.c @@ -61,6 +61,7 @@ #include #include #include +#include "lfs_project.h" #include #include @@ -87,6 +88,7 @@ static int lfs_check(int argc, char **argv); #ifdef HAVE_SYS_QUOTA_H static int lfs_setquota(int argc, char **argv); static int lfs_quota(int argc, char **argv); +static int lfs_project(int argc, char **argv); #endif static int lfs_flushctx(int argc, char **argv); static int lfs_cp(int argc, char **argv); @@ -226,8 +228,6 @@ static inline int lfs_mirror_extend(int argc, char **argv) "\tdefault_stripe: set default dirstripe of the directory\n" \ "\tmode: the mode of the directory\n" -static const char *progname; - /** * command_t mirror_cmdlist - lfs mirror commands. */ @@ -390,6 +390,17 @@ command_t cmdlist[] = { "]\n" " [<-u|-g|-p> ||||] \n" " quota [-o |-i |-I ] -t <-u|-g|-p> "}, + {"project", lfs_project, 0, + "Change or list project attribute for specified file or directory.\n" + "usage: project [-d|-r] \n" + " list project ID and flags on file(s) or directories\n" + " project [-p id] [-s] [-r] \n" + " set project ID and/or inherit flag for specified file(s) or directories\n" + " project -c [-d|-r [-p id] [-0]] \n" + " check project ID and flags on file(s) or directories, print outliers\n" + " project -C [-r] [-k] \n" + " clear the project inherit flag and ID on the file or directory\n" + }, #endif {"flushctx", lfs_flushctx, 0, "Flush security context for current user.\n" "usage: flushctx [-k] [mountpoint...]"}, @@ -4916,6 +4927,160 @@ out: } +static int lfs_project(int argc, char **argv) +{ + int ret = 0, err = 0, c, i; + struct project_handle_control phc = { 0 }; + enum lfs_project_ops_t op; + + phc.newline = true; + phc.assign_projid = false; + /* default action */ + op = LFS_PROJECT_LIST; + + while ((c = getopt(argc, argv, "p:cCsdkr0")) != -1) { + switch (c) { + case 'c': + if (op != LFS_PROJECT_LIST) { + fprintf(stderr, + "%s: cannot specify '-c' '-C' '-s' together\n", + progname); + return CMD_HELP; + } + + op = LFS_PROJECT_CHECK; + break; + case 'C': + if (op != LFS_PROJECT_LIST) { + fprintf(stderr, + "%s: cannot specify '-c' '-C' '-s' together\n", + progname); + return CMD_HELP; + } + + op = LFS_PROJECT_CLEAR; + break; + case 's': + if (op != LFS_PROJECT_LIST) { + fprintf(stderr, + "%s: cannot specify '-c' '-C' '-s' together\n", + progname); + return CMD_HELP; + } + + phc.set_inherit = true; + op = LFS_PROJECT_SET; + break; + case 'd': + phc.dironly = true; + break; + case 'k': + phc.keep_projid = true; + break; + case 'r': + phc.recursive = true; + break; + case 'p': + phc.projid = strtoul(optarg, NULL, 0); + phc.assign_projid = true; + + break; + case '0': + phc.newline = false; + break; + default: + fprintf(stderr, "%s: invalid option '%c'\n", + progname, optopt); + return CMD_HELP; + } + } + + if (phc.assign_projid && op == LFS_PROJECT_LIST) { + op = LFS_PROJECT_SET; + phc.set_projid = true; + } else if (phc.assign_projid && op == LFS_PROJECT_SET) { + phc.set_projid = true; + } + + switch (op) { + case LFS_PROJECT_CHECK: + if (phc.keep_projid) { + fprintf(stderr, + "%s: '-k' is useless together with '-c'\n", + progname); + return CMD_HELP; + } + break; + case LFS_PROJECT_CLEAR: + if (!phc.newline) { + fprintf(stderr, + "%s: '-0' is useless together with '-C'\n", + progname); + return CMD_HELP; + } + if (phc.assign_projid) { + fprintf(stderr, + "%s: '-p' is useless together with '-C'\n", + progname); + return CMD_HELP; + } + break; + case LFS_PROJECT_SET: + if (!phc.newline) { + fprintf(stderr, + "%s: '-0' is useless together with '-s'\n", + progname); + return CMD_HELP; + } + if (phc.keep_projid) { + fprintf(stderr, + "%s: '-k' is useless together with '-s'\n", + progname); + return CMD_HELP; + } + break; + default: + if (!phc.newline) { + fprintf(stderr, + "%s: '-0' is useless for list operations\n", + progname); + return CMD_HELP; + } + break; + } + + argv += optind; + argc -= optind; + if (argc == 0) { + fprintf(stderr, "%s: missing file or directory target(s)\n", + progname); + return CMD_HELP; + } + + for (i = 0; i < argc; i++) { + switch (op) { + case LFS_PROJECT_CHECK: + err = lfs_project_check(argv[i], &phc); + break; + case LFS_PROJECT_LIST: + err = lfs_project_list(argv[i], &phc); + break; + case LFS_PROJECT_CLEAR: + err = lfs_project_clear(argv[i], &phc); + break; + case LFS_PROJECT_SET: + err = lfs_project_set(argv[i], &phc); + break; + default: + break; + } + if (err && !ret) + ret = err; + } + + return ret; +} + static int lfs_quota(int argc, char **argv) { int c; diff --git a/lustre/utils/lfs_project.c b/lustre/utils/lfs_project.c new file mode 100644 index 0000000..180447a --- /dev/null +++ b/lustre/utils/lfs_project.c @@ -0,0 +1,337 @@ +/* + * GPL HEADER START + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 for more details (a copy is included + * in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; If not, see + * http://www.gnu.org/licenses/gpl-2.0.html + * + * GPL HEADER END + */ +/* + * Copyright (c) 2017, DataDirect Networks Storage. + * Copyright (c) 2017, Intel Corporation. + */ +/* + * This file is part of Lustre, http://www.lustre.org/ + * + * lustre/utils/lfs_project.c + * + * Author: Wang Shilong + * Author: Fan Yong + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lfs_project.h" +#include + +struct lfs_project_item { + struct list_head lpi_list; + char lpi_pathname[PATH_MAX]; +}; + +static int +lfs_project_item_alloc(struct list_head *head, const char *pathname) +{ + struct lfs_project_item *lpi; + + lpi = malloc(sizeof(struct lfs_project_item)); + if (lpi == NULL) { + fprintf(stderr, + "%s: cannot allocate project item for '%s': %s\n", + progname, pathname, strerror(ENOMEM)); + return -ENOMEM; + } + + strncpy(lpi->lpi_pathname, pathname, sizeof(lpi->lpi_pathname)); + list_add_tail(&lpi->lpi_list, head); + + return 0; +} + +static int project_get_xattr(const char *pathname, struct fsxattr *fsx) +{ + int ret, fd; + + fd = open(pathname, O_RDONLY | O_NOCTTY); + if (fd < 0) { + fprintf(stderr, "%s: failed to open '%s': %s\n", + progname, pathname, strerror(errno)); + return -errno; + } + + ret = ioctl(fd, LL_IOC_FSGETXATTR, fsx); + if (ret) { + fprintf(stderr, "%s: failed to get xattr for '%s': %s\n", + progname, pathname, strerror(errno)); + return -errno; + } + return fd; +} + +static int +project_check_one(const char *pathname, struct project_handle_control *phc) +{ + struct fsxattr fsx; + struct stat st; + int ret; + + ret = stat(pathname, &st); + if (ret) { + fprintf(stderr, "%s: failed to stat '%s': %s\n", + progname, pathname, strerror(errno)); + return -errno; + } + + ret = project_get_xattr(pathname, &fsx); + if (ret < 0) + return ret; + + /* use top directory's project ID if not specified */ + if (!phc->assign_projid) { + phc->assign_projid = true; + phc->projid = fsx.fsx_projid; + } + + if (!(fsx.fsx_xflags & LL_PROJINHERIT_FL)) { + if (!phc->newline) { + printf("%s%c", pathname, '\0'); + goto out; + } + printf("%s - project inheritance flag is not set\n", + pathname); + } + + if (fsx.fsx_projid != phc->projid) { + if (!phc->newline) { + printf("%s%c", pathname, '\0'); + goto out; + } + printf("%s - project identifier is not set (inode=%u, tree=%u)\n", + pathname, fsx.fsx_projid, phc->projid); + } +out: + close(ret); + return 0; +} + +static int +project_list_one(const char *pathname, struct project_handle_control *phc) +{ + struct fsxattr fsx; + int ret; + + ret = project_get_xattr(pathname, &fsx); + if (ret < 0) + return ret; + + printf("%5u %c %s\n", fsx.fsx_projid, + (fsx.fsx_xflags & LL_PROJINHERIT_FL) ? + 'P' : '-', pathname); + + close(ret); + return 0; +} + +static int +project_set_one(const char *pathname, struct project_handle_control *phc) +{ + struct fsxattr fsx; + int fd, ret = 0; + + fd = project_get_xattr(pathname, &fsx); + if (fd < 0) + return fd; + + if ((!phc->set_projid || fsx.fsx_projid == phc->projid) && + (!phc->set_inherit || (fsx.fsx_xflags & LL_PROJINHERIT_FL))) + goto out; + + if (phc->set_inherit) + fsx.fsx_xflags |= LL_PROJINHERIT_FL; + if (phc->set_projid) + fsx.fsx_projid = phc->projid; + + ret = ioctl(fd, LL_IOC_FSSETXATTR, &fsx); + if (ret) + fprintf(stderr, "%s: failed to set xattr for '%s': %s\n", + progname, pathname, strerror(errno)); +out: + close(fd); + return ret; +} + +static int +project_clear_one(const char *pathname, struct project_handle_control *phc) +{ + struct fsxattr fsx; + int ret = 0, fd; + + fd = project_get_xattr(pathname, &fsx); + if (fd < 0) + return fd; + + if ((!(fsx.fsx_xflags & LL_PROJINHERIT_FL)) && + (fsx.fsx_projid == 0 || phc->keep_projid)) + goto out; + + fsx.fsx_xflags &= ~LL_PROJINHERIT_FL; + if (!phc->keep_projid) + fsx.fsx_projid = 0; + + ret = ioctl(fd, LL_IOC_FSSETXATTR, &fsx); + if (ret) + fprintf(stderr, "%s: failed to set xattr for '%s': %s\n", + progname, pathname, strerror(errno)); +out: + close(fd); + return ret; +} + +static int +lfs_project_handle_dir(struct list_head *head, const char *pathname, + struct project_handle_control *phc, + int (*func)(const char *, + struct project_handle_control *)) +{ + char fullname[PATH_MAX]; + struct dirent *ent; + DIR *dir; + int ret = 0; + + dir = opendir(pathname); + if (dir == NULL) { + ret = -errno; + fprintf(stderr, "%s: failed to opendir '%s': %s\n", + progname, pathname, strerror(-ret)); + return ret; + } + + while (ret == 0 && (ent = readdir(dir)) != NULL) { + /* skip "." and ".." */ + if (strcmp(ent->d_name, ".") == 0 || + strcmp(ent->d_name, "..") == 0) + continue; + + if (strlen(ent->d_name) + strlen(pathname) >= + sizeof(fullname) + 1) { + ret = -ENAMETOOLONG; + errno = ENAMETOOLONG; + break; + } + snprintf(fullname, PATH_MAX, "%s/%s", pathname, + ent->d_name); + + ret = func(fullname, phc); + if (phc->recursive && ret == 0 && ent->d_type == DT_DIR) + ret = lfs_project_item_alloc(head, fullname); + } + + if (ret) + fprintf(stderr, "%s: failed to handle dir '%s': %s\n", + progname, pathname, strerror(errno)); + + closedir(dir); + return ret; +} + +static int lfs_project_iterate(const char *pathname, + struct project_handle_control *phc, + int (*func)(const char *, + struct project_handle_control *)) +{ + struct lfs_project_item *lpi; + struct list_head head; + struct stat st; + int ret = 0; + bool top_dir = true; + + ret = stat(pathname, &st); + if (ret) { + fprintf(stderr, "%s: failed to stat '%s': %s\n", + progname, pathname, strerror(errno)); + return ret; + } + + /* list opeation will skip top directory in default */ + if (!S_ISDIR(st.st_mode) || phc->dironly || + project_list_one != func) + ret = func(pathname, phc); + + /* dironly first, recursive will be ignored */ + if (!S_ISDIR(st.st_mode) || phc->dironly || ret) + return ret; + + INIT_LIST_HEAD(&head); + ret = lfs_project_item_alloc(&head, pathname); + if (ret) + return ret; + + while (!list_empty(&head)) { + lpi = list_entry(head.next, struct lfs_project_item, lpi_list); + list_del(&lpi->lpi_list); + if (ret == 0) { + ret = lfs_project_handle_dir(&head, lpi->lpi_pathname, + phc, func); + /* only ignore ENOENT error if this is + * not top directory. */ + if (ret == -ENOENT && !top_dir) + ret = 0; + } + free(lpi); + top_dir = false; + } + + return ret; +} + + +inline int lfs_project_check(const char *pathname, + struct project_handle_control *phc) +{ + return lfs_project_iterate(pathname, phc, project_check_one); +} + +inline int lfs_project_clear(const char *pathname, + struct project_handle_control *phc) +{ + return lfs_project_iterate(pathname, phc, project_clear_one); +} + +inline int lfs_project_set(const char *pathname, + struct project_handle_control *phc) +{ + return lfs_project_iterate(pathname, phc, project_set_one); +} + +inline int lfs_project_list(const char *pathname, + struct project_handle_control *phc) +{ + return lfs_project_iterate(pathname, phc, project_list_one); +} diff --git a/lustre/utils/lfs_project.h b/lustre/utils/lfs_project.h new file mode 100644 index 0000000..66404ca --- /dev/null +++ b/lustre/utils/lfs_project.h @@ -0,0 +1,68 @@ +/* + * GPL HEADER START + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 for more details (a copy is included + * in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; If not, see + * http://www.gnu.org/licenses/gpl-2.0.html + * + * GPL HEADER END + */ +/* + * Copyright (c) 2017, DataDirect Networks Storage. + * Copyright (c) 2017, Intel Corporation. + */ +/* + * This file is part of Lustre, http://www.lustre.org/ + * + * lustre/utils/lfs_project.h + * + * Author: Wang Shilong + * Author: Fan Yong + */ +#ifndef _LFS_PROJECT_H +#define _LFS_PROJECT_H +#include +#include + +const char *progname; + +enum lfs_project_ops_t { + LFS_PROJECT_CHECK = 0, + LFS_PROJECT_CLEAR = 1, + LFS_PROJECT_SET = 2, + LFS_PROJECT_LIST = 3, + LFS_PROJECT_MAX = 4, +}; + +struct project_handle_control { + __u32 projid; + bool assign_projid; + bool set_inherit; + bool set_projid; + bool newline; + bool keep_projid; + bool recursive; + bool dironly; +}; + +int lfs_project_list(const char *pathname, + struct project_handle_control *phc); +int lfs_project_check(const char *pathname, + struct project_handle_control *phc); +int lfs_project_clear(const char *pathname, + struct project_handle_control *phc); +int lfs_project_set(const char *pathname, + struct project_handle_control *phc); +#endif /* _LFS_PROJECT_H */ -- 1.8.3.1