[[\fB!\fR] \fB--gid\fR|\fB-g\fR|\fB--group\fR|\fB-G\fR \fIGNAME\fR|\fIGID\fR>]
[\fB--help\fR|\fB-h\fR]
[[\fB!\fR] \fB--layout\fR|\fB-L mdt\fR,\fBraid0\fR,\fBreleased\fR]
-[\fB--lazy\fR]
- [\fB--maxdepth\fR|\fB-D\fI n\fR]
-[[\fB!\fR] \fB--mdt\fR|\fB--mdt-index\fR|\fB-m\fR \fIUUID\fR|\fIINDEX\fR,...]
+[\fB--lazy|-l\fR]
+ [[\fB!\fR] \fB--links\fR [\fB+-\fR]\fIn\fR] [\fB--maxdepth\fR|\fB-D\fI n\fR]
+ [[\fB!\fR] \fB--mdt\fR|\fB--mdt-index\fR|\fB-m\fR \fIUUID\fR|\fIINDEX\fR,...]
[[\fB!\fR] \fB--mdt-count\fR|\fB-T\fR [\fB+-\fR]\fIn\fR]
[[\fB!\fR] \fB--mdt-hash\fR|\fB-H \fR<[^]\fIHASHFLAG\fR,[^]\fIHASHTYPE\fR,...>]
[[\fB!\fR] \fB--mirror-count|\fB-N\fR [\fB+-\fR]\fIn\fR]
Files that have the first data component on an MDT.
.RE
.TP
-.BR --lazy
+.BR --lazy|-l
Use file size and blocks from MDT, if available, to avoid extra RPCs.
.TP
+.BR --links
+File has \fIn\fR links.
+.TP
.BR --maxdepth
Limits find to decend at most \fIn\fR levels of directory tree.
.TP
.B %m
File permission bits (in octal).
.TP
+.B %n
+Number of hard links to file.
+.TP
.B %p
File's name.
.TP
LFS_FIND_PERM_ALL = 1,
};
+/*
+ * new fields should be added to the end of this struct (unless filling a hole
+ * such as in a bitfield), to preserve the ABI
+ */
struct find_param {
unsigned int fp_max_depth;
dev_t fp_dev;
fp_blocks_sign:2,
fp_ext_size_sign:2,
fp_perm_sign:2,
- fp_unused2_sign:2, /* Once used we must add */
+ fp_nlink_sign:2, /* Once used we must add */
fp_unused3_sign:2, /* a separate flag field */
fp_unused4_sign:2; /* at end of the struct. */
unsigned long long fp_size;
fp_exclude_btime:1,
fp_exclude_perm:1,
fp_stop_on_error:1, /* stop iteration on error */
- fp_unused_bit5:1, /* are used we need to add */
+ fp_exclude_nlink:1, /* Once used, we must add*/
fp_unused_bit6:1, /* a separate flag field at*/
fp_unused_bit7:1; /* the end of the struct. */
unsigned int fp_hash_exflags;
/* Print all information (lfs find only) */
char *fp_format_printf_str;
+ nlink_t fp_nlink;
};
int llapi_ostlist(char *path, struct find_param *param);
touch $path/$tfile || error "touch $path/$tfile failed"
# Compare basic file attributes from -printf and stat
- local attr_printf=$($LFS find $path/$tfile -printf "%A@ %T@ %C@ %U %G")
- local attr_stat=$(stat -c "%X %Y %Z %u %g" $path/$tfile)
+ local attr_printf=$($LFS find $path/$tfile -printf "%A@ %T@ %C@ %U %G %n")
+ local attr_stat=$(stat -c "%X %Y %Z %u %g %h" $path/$tfile)
[[ "${attr_printf}" == "${attr_stat}" ]] ||
error "Attrs from lfs find and stat don't match"
}
run_test 56ec "check lfs getstripe,setstripe --hex --yaml"
+test_56eda() {
+ local dir=$DIR/$tdir
+ local subdir=$dir/subdir
+ local file1=$dir/$tfile
+ local file2=$dir/$tfile\2
+ local link=$dir/$tfile-link
+ local nfiles
+
+ test_mkdir -p $dir
+ $LFS setdirstripe -c1 $subdir
+ touch $file1
+ touch $file2
+ ln $file2 $link
+
+ nfiles=$($LFS find --links 1 $dir | wc -l)
+ (( $nfiles == 1 )) ||
+ error "lfs find --links expected 1 file, got $nfiles"
+
+ nfiles=$($LFS find --type f --links 2 $dir | wc -l)
+ (( $nfiles == 2 )) ||
+ error "lfs find --links expected 2 files, got $nfiles"
+
+ nfiles=$($LFS find --type d --links 2 $dir | wc -l)
+ (( $nfiles == 1 )) ||
+ error "lfs find --links expected 1 directory, got $nfiles"
+}
+run_test 56eda "check lfs find --links"
+
+test_56edb() {
+ [[ $MDSCOUNT -lt 2 ]] && skip_env "needs >= 2 MDTs"
+
+ local dir=$DIR/$tdir
+ local stripedir=$dir/stripedir
+ local nfiles
+
+ test_mkdir -p $dir
+
+ $LFS setdirstripe -c2 $stripedir
+
+ $LFS getdirstripe $stripedir
+
+ nfiles=$($LFS find --type d --links 2 $stripedir | wc -l)
+ (( $nfiles == 1 )) ||
+ error "lfs find --links expected 1 directory, got $nfiles"
+}
+run_test 56edb "check lfs find --links for directory striped on multiple MDTs"
+
test_57a() {
[ $PARALLEL == "yes" ] && skip "skip parallel run"
# note test will not do anything if MDS is not local
" [[!] --extension-size|--ext-size|-z [+-]N[kMGT]]\n"
" [[!] --foreign[=<foreign_type>]]\n"
" [[!] --gid|-g|--group|-G <gid>|<gname>] [--help|-h]\n"
- " [[!] --layout|-L released,raid0,mdt] [--lazy]\n"
+ " [[!] --layout|-L released,raid0,mdt] [--lazy|-l] [[!] --links [+-]n]\n"
" [--maxdepth|-D N] [[!] --mdt-count|-T [+-]<stripes>]\n"
" [[!] --mdt-hash|-H <[^][blm],[^]fnv_1a_64,all_char,crush,...>\n"
" [[!] --mdt-index|--mdt|-m <uuid|index,...>]\n"
LFS_NO_FOLLOW_OPT,
LFS_HEX_IDX_OPT,
LFS_STATS_OPT,
- LFS_STATS_INTERVAL_OPT
+ LFS_STATS_INTERVAL_OPT,
+ LFS_LINKS_OPT
};
#ifndef LCME_USER_MIRROR_FLAGS
/* getstripe { .val = 'I', .name = "comp-id", .has_arg = required_argument }*/
{ .val = 'l', .name = "lazy", .has_arg = no_argument },
{ .val = 'L', .name = "layout", .has_arg = required_argument },
+ { .val = LFS_LINKS_OPT,
+ .name = "links", .has_arg = required_argument },
{ .val = 'm', .name = "mdt", .has_arg = required_argument },
{ .val = 'm', .name = "mdt-index", .has_arg = required_argument },
{ .val = 'm', .name = "mdt_index", .has_arg = required_argument },
/* when getopt_long_only() hits '!' it returns 1, puts "!" in optarg */
while ((c = getopt_long_only(argc, argv,
- "-0A:b:B:c:C:D:E:g:G:hH:i:L:m:M:n:N:O:Ppqrs:S:t:T:u:U:z:",
+ "-0A:b:B:c:C:D:E:g:G:hH:i:lL:m:M:n:N:O:Ppqrs:S:t:T:u:U:z:",
long_opts, &optidx)) >= 0) {
xtime = NULL;
xsign = NULL;
param.fp_exclude_layout = !!neg_opt;
param.fp_check_layout = 1;
break;
+ case LFS_LINKS_OPT:
+ if (optarg[0] == '+') {
+ param.fp_nlink_sign = -1;
+ optarg++;
+ } else if (optarg[0] == '-') {
+ param.fp_nlink_sign = 1;
+ optarg++;
+ }
+ errno = 0;
+ param.fp_nlink = strtoul(optarg, &endptr, 0);
+ if (errno != 0 || *endptr != '\0' || !param.fp_nlink) {
+ fprintf(stderr, "error: bad link count '%s'\n",
+ optarg);
+ ret = -1;
+ goto err;
+ }
+ param.fp_exclude_nlink = !!neg_opt;
+ break;
case 'u':
case 'U':
rc = name2uid(¶m.fp_uid, optarg);
case 'm': /* file mode in octal */
*wrote = snprintf(buffer, size, "%#o", (mode & (~S_IFMT)));
break;
+ case 'n': /* number of links */
+ *wrote = snprintf(buffer, size, "%u",
+ param->fp_lmd->lmd_stx.stx_nlink);
+ break;
case 'p': /* Path name of file */
*wrote = snprintf(buffer, size, "%s", path);
break;
find_check_lmm_info(param) ||
param->fp_check_mdt_count || param->fp_hash_type ||
param->fp_check_hash_flag || param->fp_perm_sign ||
+ param->fp_nlink ||
gather_all)
decision = 0;
if (d != -1 &&
(param->fp_check_mdt_count || param->fp_hash_type ||
param->fp_check_hash_flag || param->fp_check_foreign ||
- gather_all)) {
+ /*
+ * cb_get_dirstripe is needed when checking nlink because
+ * nlink is handled differently for multi-stripe directory
+ * vs. single-stripe directory
+ */
+ param->fp_nlink || gather_all)) {
param->fp_get_lmv = 1;
ret = cb_get_dirstripe(path, &d, param);
if (ret != 0) {
decision = 0;
/*
+ * When checking nlink, stat(2) is needed for multi-striped directories
+ * because the nlink value retrieved from the MDS above comes from
+ * the number of stripes for the dir.
+ * The posix stat call below fills in the correct number of links.
+ * Single-stripe directories and regular files already have the
+ * correct nlink value.
+ */
+ if (param->fp_nlink && S_ISDIR(lmd->lmd_stx.stx_mode) &&
+ (param->fp_lmv_md->lum_stripe_count != 0))
+ decision = 0;
+
+ /*
* If file still fits the request, ask ost for updated info.
* The regular stat is almost of the same speed as some new
* 'glimpse-size-ioctl'.
}
}
+ if (param->fp_nlink) {
+ decision = find_value_cmp(lmd->lmd_stx.stx_nlink,
+ param->fp_nlink, param->fp_nlink_sign,
+ param->fp_exclude_nlink, 1, 0);
+ if (decision == -1)
+ goto decided;
+ }
+
if (param->fp_check_size) {
decision = find_value_cmp(lmd->lmd_stx.stx_size,
param->fp_size,
*/
int validate_printf_fmt(char *c)
{
- char *valid_fmt_single = "abcGkmpstUwy%";
+ char *valid_fmt_single = "abcGkmnpstUwy%";
char *valid_fmt_double = "ACTW";
char *valid_fmt_lustre = "cFhioPpS";
char curr = *c, next;