lfs-setdirstripe.1 \
lfs-setstripe.1 \
lfs-setquota.1 \
+ lfs-project.1 \
l_getidentity.8 \
lgss_sk.8 \
lhbadm.8 \
--- /dev/null
+.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)
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)
#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
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
change_project()
{
- echo "chattr $*"
- chattr $* || error "chattr $* failed"
+ echo "lfs project $*"
+ lfs project $* || error "lfs project $* failed"
}
RUNAS="runas -u $TSTID -g $TSTID"
[ $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)
# 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 ||
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)) ||
$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 ||
$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
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"
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"
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
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"
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"
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"
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.
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"
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
#include <dirent.h>
#include <time.h>
#include <ctype.h>
+#include "lfs_project.h"
#include <libcfs/util/string.h>
#include <libcfs/util/ioctl.h>
#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);
"\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.
*/
"<ost_idx>]\n"
" [<-u|-g|-p> <uname>|<uid>|<gname>|<gid>|<projid>] <filesystem>\n"
" quota [-o <obd_uuid>|-i <mdt_idx>|-I <ost_idx>] -t <-u|-g|-p> <filesystem>"},
+ {"project", lfs_project, 0,
+ "Change or list project attribute for specified file or directory.\n"
+ "usage: project [-d|-r] <file|directory...>\n"
+ " list project ID and flags on file(s) or directories\n"
+ " project [-p id] [-s] [-r] <file|directory...>\n"
+ " set project ID and/or inherit flag for specified file(s) or directories\n"
+ " project -c [-d|-r [-p id] [-0]] <file|directory...>\n"
+ " check project ID and flags on file(s) or directories, print outliers\n"
+ " project -C [-r] [-k] <file|directory...>\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...]"},
}
+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;
--- /dev/null
+/*
+ * 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 <wshilong@ddn.com>
+ * Author: Fan Yong <fan.yong@intel.com>
+ */
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <stddef.h>
+#include <libintl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <string.h>
+#include <libcfs/util/list.h>
+#include <libcfs/util/ioctl.h>
+#include <sys/ioctl.h>
+
+#include "lfs_project.h"
+#include <lustre/lustreapi.h>
+
+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);
+}
--- /dev/null
+/*
+ * 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 <wshilong@ddn.com>
+ * Author: Fan Yong <fan.yong@intel.com>
+ */
+#ifndef _LFS_PROJECT_H
+#define _LFS_PROJECT_H
+#include <stdbool.h>
+#include <linux/types.h>
+
+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 */