This change stores the jobid of the process that creates a file in an
extended attribute in the file's MDT inode, at file creation time.
The name of the xattr is determined by a new sysfs parameter
"mdt.*.job_xattr" so that the admin can choose a name that does
not conflict with other uses they may have for a given xattr.
The default value is "user.job". A value of "NONE" means that
the jobid will not be stored in the inode.
If the name is in the user namespace "user.", then the name portion
can be up to 7 alphanumeric characters long. The admin can choose
the trusted namespace to prevent users from modifying the value,
but only "trusted.job" is allowed in this namespace.
Allowing users to modify the contents of the xattr is helpful so
that the jobid can be preserved even when files are moved with tools
like `cp` or `rsync`, and when copied from one filesystem to another.
Lustre-change: https://review.whamcloud.com/50982
Lustre-commit:
23a2db28dcf1422a6a6da575e907fd257106d402
LU-13031 tests: skip sanity/test_205h,205i in interop
Skip sanity tests 205h and 205i when the MDS version is too old
to have the jobid xattr changes. Fix test 103a to not try to set
the job_xattr parameter when it does not exist.
Lustre-change: https://review.whamcloud.com/52095
Lustre-commit:
a6df532162556028c2ab9b974989fc0cca68d4fe
Test-Parameters: testlist=sanity clientdistro=el8.8 clientjob=lustre-b_es-reviews clientbuildno=12634 env=ONLY=103
Signed-off-by: Thomas Bertschinger <bertschinger@lanl.gov>
Change-Id: Iad78a5ec6fbc4b761ff481141763bdd0cdcd0128
Reviewed-by: Patrick Farrell <pfarrell@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-on: https://review.whamcloud.com/c/ex/lustre-release/+/52195
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
/** to create directory */
const struct dt_index_features *sp_feat;
+
+ /* name of xattr used to store jobid in inode, or empty if disabled */
+ char sp_cr_job_xattr[XATTR_JOB_MAX_LEN];
};
enum md_layout_opc {
#define XATTR_NAME_LFSCK_NAMESPACE "trusted.lfsck_ns"
#define XATTR_NAME_MAX_LEN 32 /* increase this, if there is longer name. */
+#define XATTR_NAME_JOB_DEFAULT "user.job"
+/* longest allowed jobid xattr name is "user." + 7 chars + null terminator */
+#define XATTR_JOB_MAX_LEN 13
+
struct lov_mds_md_v3 { /* LOV EA mds/wire data (little-endian) */
__u32 lmm_magic; /* magic number = LOV_MAGIC_V3 */
__u32 lmm_pattern; /* LOV_PATTERN_RAID0, LOV_PATTERN_RAID1 */
struct lu_buf *def_acl_buf,
struct lu_buf *hsm_buf,
struct dt_allocation_hint *hint,
- struct thandle *handle, bool initsecctx)
+ struct thandle *handle, bool initial_create)
{
+ const struct lu_fid *son_fid = mdd_object_fid(son);
+ const struct lu_ucred *uc = lu_ucred(env);
+ const char *jobid = uc->uc_jobid;
const struct lu_buf *buf;
+ size_t jobid_len;
int rc;
mdd_write_lock(env, son, DT_TGT_CHILD);
GOTO(err_initlized, rc = -EFAULT);
}
- if (initsecctx && spec->sp_cr_file_secctx_name != NULL) {
+ if (initial_create && spec->sp_cr_file_secctx_name != NULL) {
buf = mdd_buf_get_const(env, spec->sp_cr_file_secctx,
spec->sp_cr_file_secctx_size);
rc = mdo_xattr_set(env, son, buf, spec->sp_cr_file_secctx_name,
GOTO(err_initlized, rc);
}
+ if (initial_create &&
+ spec->sp_cr_job_xattr[0] != '\0' &&
+ jobid[0] != '\0' &&
+ (S_ISREG(attr->la_mode) || S_ISDIR(attr->la_mode))) {
+ jobid_len = strnlen(jobid, LUSTRE_JOBID_SIZE);
+ buf = mdd_buf_get_const(env, jobid, jobid_len);
+
+ rc = mdo_xattr_set(env, son, buf, spec->sp_cr_job_xattr,
+ LU_XATTR_CREATE, handle);
+
+ /* this xattr is nonessential, so ignore errors. */
+ if (rc != 0) {
+ CDEBUG(D_INODE,
+ DFID" failed to set xattr '%s': rc = %d\n",
+ PFID(son_fid), spec->sp_cr_job_xattr, rc);
+ rc = 0;
+ }
+ }
+
err_initlized:
if (unlikely(rc != 0)) {
int rc2;
/* Set this lu_device to obd for error handling purposes. */
obd->obd_lu_dev = &m->mdt_lu_dev;
+ strncpy(m->mdt_job_xattr, XATTR_NAME_JOB_DEFAULT, XATTR_JOB_MAX_LEN);
+
/* init the stack */
rc = mdt_stack_init((struct lu_env *)env, m, cfg);
if (rc) {
struct mdt_object *mdt_md_root;
struct mdt_dir_restriper mdt_restriper;
+
+ /* name of xattr used to store jobid in mdt inode */
+ char mdt_job_xattr[XATTR_JOB_MAX_LEN];
};
#define MDT_SERVICE_WATCHDOG_FACTOR (2)
return 0;
}
+ssize_t job_xattr_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ struct obd_device *obd = container_of(kobj, struct obd_device,
+ obd_kset.kobj);
+ struct mdt_device *mdt = mdt_dev(obd->obd_lu_dev);
+
+ if (mdt->mdt_job_xattr[0] == '\0')
+ return scnprintf(buf, PAGE_SIZE, "NONE\n");
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n", mdt->mdt_job_xattr);
+}
+
+/**
+ * Read in a name for the jobid xattr and validate it.
+ * The only valid names are "trusted.job" or "user.*" where the name portion
+ * is <= 7 bytes in the user namespace. Only alphanumeric characters are
+ * allowed, aside from the namespace separator '.'.
+ *
+ * "none" is a valid value to turn this feature off.
+ *
+ * @return -EINVAL if the name is invalid, else count
+ */
+ssize_t job_xattr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buffer, size_t count)
+{
+ struct obd_device *obd = container_of(kobj, struct obd_device,
+ obd_kset.kobj);
+ struct mdt_device *mdt = mdt_dev(obd->obd_lu_dev);
+ char name[XATTR_JOB_MAX_LEN] = { 0 };
+ char *p;
+
+
+ /* writing "none" turns this off by leaving the name empty */
+ if (!strncmp(buffer, "none", 4) ||
+ !strncmp(buffer, "NONE", 4)) {
+ memset(mdt->mdt_job_xattr, 0, sizeof(mdt->mdt_job_xattr));
+ return count;
+ }
+
+ /* account for stripping \n before rejecting name for being too long */
+ if (count > XATTR_JOB_MAX_LEN - 1 &&
+ buffer[XATTR_JOB_MAX_LEN - 1] != '\n')
+ return -EINVAL;
+
+ strncpy(name, buffer, XATTR_JOB_MAX_LEN - 1);
+
+ /* reject if not in namespace.name format */
+ p = strchr(name, '.');
+ if (p == NULL)
+ return -EINVAL;
+
+ p++;
+ for (; *p != '\0'; p++) {
+ /*
+ * if there are any non-alphanumeric characters, the name is
+ * invalid unless it's a newline, in which case overwrite it
+ * with '\0' and that's the end of the name.
+ */
+ if (!isalnum(*p)) {
+ if (*p != '\n')
+ return -EINVAL;
+ *p = '\0';
+ }
+ }
+
+ /* trusted.job is only valid name in trusted namespace */
+ if (!strncmp(name, "trusted.job", 12)) {
+ strncpy(mdt->mdt_job_xattr, name, XATTR_JOB_MAX_LEN);
+ return count;
+ }
+
+ /* only other valid namespace is user */
+ if (strncmp(name, XATTR_USER_PREFIX, sizeof(XATTR_USER_PREFIX) - 1))
+ return -EINVAL;
+
+ /* ensure that a name was specified */
+ if (name[sizeof(XATTR_USER_PREFIX) - 1] == '\0')
+ return -EINVAL;
+
+ strncpy(mdt->mdt_job_xattr, name, XATTR_JOB_MAX_LEN);
+
+ return count;
+}
+
LPROC_SEQ_FOPS_RO(mdt_checksum_type);
LPROC_SEQ_FOPS_RO_TYPE(mdt, hash);
LPROC_SEQ_FOPS_WR_ONLY(mdt, mds_evict_client);
LPROC_SEQ_FOPS_RW_TYPE(mdt, checksum_dump);
LUSTRE_RW_ATTR(job_cleanup_interval);
+LUSTRE_RW_ATTR(job_xattr);
LPROC_SEQ_FOPS_RW_TYPE(mdt, nid_stats_clear);
LUSTRE_RW_ATTR(hsm_control);
&lustre_attr_migrate_hsm_allowed.attr,
&lustre_attr_hsm_control.attr,
&lustre_attr_job_cleanup_interval.attr,
+ &lustre_attr_job_xattr.attr,
&lustre_attr_readonly.attr,
&lustre_attr_dir_split_count.attr,
&lustre_attr_dir_split_delta.attr,
info->mti_spec.sp_cr_lookup = 0;
info->mti_spec.sp_feat = &dt_directory_features;
+ /* set jobid xattr name from sysfs parameter */
+ strncpy(info->mti_spec.sp_cr_job_xattr, mdt->mdt_job_xattr,
+ XATTR_JOB_MAX_LEN);
+
result = mdo_create(info->mti_env, mdt_object_child(parent),
&rr->rr_name, mdt_object_child(child),
&info->mti_spec, &info->mti_attr);
info->mti_spec.sp_cr_lookup = 1;
info->mti_spec.sp_feat = &dt_directory_features;
+ /* set jobid xattr name from sysfs parameter */
+ strncpy(info->mti_spec.sp_cr_job_xattr, mdt->mdt_job_xattr,
+ XATTR_JOB_MAX_LEN);
+
rc = mdo_create(info->mti_env, mdt_object_child(parent), &rr->rr_name,
mdt_object_child(child), &info->mti_spec, ma);
if (rc == 0)
which setfacl || skip_env "could not find setfacl"
remote_mds_nodsh && skip "remote MDS with nodsh"
+ local mdts=$(comma_list $(mdts_nodes))
+ local saved=$(do_facet mds1 $LCTL get_param -n mdt.$FSNAME-MDT0000.job_xattr)
+
+ [[ -z "$saved" ]] || do_nodes $mdts $LCTL set_param mdt.*.job_xattr=NONE
+ stack_trap "[[ -z \"$saved\" ]] || \
+ do_nodes $mdts $LCTL set_param mdt.*.job_xattr=$saved" EXIT
+
ACLBIN=${ACLBIN:-"bin"}
ACLDMN=${ACLDMN:-"daemon"}
ACLGRP=${ACLGRP:-"users"}
}
run_test 205g "stress test for job_stats procfile"
+test_205h() {
+ (( $MDS1_VERSION >= $(version_code 2.14.0.99) )) ||
+ skip "Need MDS >= 2.14.0.99 for jobid xattr"
+ which getfattr > /dev/null 2>&1 || skip_env "no getfattr command"
+
+ local dir=$DIR/$tdir
+ local f=$dir/$tfile
+ local f2=$dir/$tfile-2
+ local f3=$dir/$tfile-3
+ local subdir=$DIR/dir
+ local val
+
+ local mdts=$(comma_list $(mdts_nodes))
+ local mds_saved=$(do_facet mds1 $LCTL get_param -n mdt.$FSNAME-MDT0000.job_xattr)
+ local client_saved=$($LCTL get_param -n jobid_var)
+
+ stack_trap "do_nodes $mdts $LCTL set_param mdt.*.job_xattr=$mds_saved" EXIT
+ stack_trap "$LCTL set_param jobid_var=$client_saved" EXIT
+
+ do_nodes $mdts $LCTL set_param mdt.*.job_xattr=user.job ||
+ error "failed to set job_xattr parameter to user.job"
+ $LCTL set_param jobid_var=procname.uid ||
+ error "failed to set jobid_var parameter"
+
+ test_mkdir $dir
+
+ touch $f
+ val=$(getfattr -n user.job $f | grep user.job)
+ [[ $val = user.job=\"touch.0\" ]] ||
+ error "expected user.job=\"touch.0\", got '$val'"
+
+ mkdir $subdir
+ val=$(getfattr -n user.job $subdir | grep user.job)
+ [[ $val = user.job=\"mkdir.0\" ]] ||
+ error "expected user.job=\"mkdir.0\", got '$val'"
+
+ do_nodes $mdts $LCTL set_param mdt.*.job_xattr=NONE ||
+ error "failed to set job_xattr parameter to NONE"
+
+ touch $f2
+ val=$(getfattr -d $f2)
+ [[ -z $val ]] ||
+ error "expected no user xattr, got '$val'"
+
+ do_nodes $mdts $LCTL set_param mdt.*.job_xattr=trusted.job ||
+ error "failed to set job_xattr parameter to trusted.job"
+
+ touch $f3
+ val=$(getfattr -n trusted.job $f3 | grep trusted.job)
+ [[ $val = trusted.job=\"touch.0\" ]] ||
+ error "expected trusted.job=\"touch.0\", got '$val'"
+}
+run_test 205h "check jobid xattr is stored correctly"
+
+test_205i() {
+ (( $MDS1_VERSION >= $(version_code 2.14.0.99) )) ||
+ skip "Need MDS >= 2.14.0.99 for jobid xattr"
+
+ local mdts=$(comma_list $(mdts_nodes))
+ local mds_saved=$(do_facet mds1 $LCTL get_param -n mdt.$FSNAME-MDT0000.job_xattr)
+
+ stack_trap "do_nodes $mdts $LCTL set_param mdt.*.job_xattr=$mds_saved" EXIT
+
+ do_nodes $mdts $LCTL set_param mdt.*.job_xattr=user.1234567 ||
+ error "failed to set mdt.*.job_xattr to user.1234567"
+
+ do_nodes $mdts $LCTL set_param mdt.*.job_xattr=user.12345678 &&
+ error "failed to reject too long job_xattr name"
+
+ do_nodes $mdts $LCTL set_param mdt.*.job_xattr=userjob &&
+ error "failed to reject job_xattr name in bad format"
+
+ do_nodes $mdts $LCTL set_param mdt.*.job_xattr=user.job/ &&
+ error "failed to reject job_xattr name with invalid character"
+
+ do_nodes $mdts "printf 'mdt.*.job_xattr=user.job\x80' |
+ xargs $LCTL set_param" &&
+ error "failed to reject job_xattr name with non-ascii character"
+
+ return 0
+}
+run_test 205i "check job_xattr parameter accepts and rejects values correctly"
+
# LU-1480, LU-1773 and LU-1657
test_206() {
mkdir -p $DIR/$tdir