Allow "mdt.*.enable_cap_mask" param set and print symbolic names,
similar to the "debug" and "subsystem_debug" parameters. The
allowed parameter names are in the capabilities(7) man page, in
either upper or lowercase, like cap_chown, cap_dac_read_search,
etc. along with "all" to enable all capabilities if clients are
trusted. For example:
lctl set_param -P mdt.lfs-*.enable_cap_mask=+cap_dac_read_search
Since kernel_cap_t is a 64-bit value, enhance cfs_str2mask() to
take u64 mask arguments. The calling libcfs_debug_str2mask()
sticks with "int mask" for now.
Split the core out from libcfs_debug_mask2str() into a new helper
function cfs_mask2str() so it can be called directly.
Fixes:
54f677651b ("LU-13791 mdt: parameter to tune capabilities")
Signed-off-by: Andreas Dilger <adilger@whamcloud.com>
Change-Id: I3f71f61a17d4d3614e46a526c60e709d9eb825b3
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/54118
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Alexander Zarochentsev <alexander.zarochentsev@hpe.com>
Reviewed-by: Sebastien Buisson <sbuisson@ddn.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
/* libcfs_string.c */
/* Convert a text string to a bitmask */
int cfs_str2mask(const char *str, const char *(*bit2str)(int bit),
- int *oldmask, int minmask, int allmask, int defmask);
+ u64 *oldmask, u64 minmask, u64 allmask, u64 defmask);
+int cfs_mask2str(char *str, int size, u64 mask, const char *(*bit2str)(int),
+ char sep);
/*
* Structure to represent \<range_expr\> token of the syntax.
return libcfs_debug_masks[debug];
}
-int
-libcfs_debug_mask2str(char *str, int size, int mask, int is_subsys)
+int libcfs_debug_mask2str(char *str, int size, int mask, int is_subsys)
{
- const char *(*fn)(int bit) = is_subsys ? libcfs_debug_subsys2str :
- libcfs_debug_dbg2str;
- int len = 0;
- const char *token;
- int i;
-
- if (mask == 0) { /* "0" */
- if (size > 0)
- str[0] = '0';
- len = 1;
- } else { /* space-separated tokens */
- for (i = 0; i < 32; i++) {
- if ((mask & BIT(i)) == 0)
- continue;
-
- token = fn(i);
- if (!token) /* unused bit */
- continue;
-
- if (len > 0) { /* separator? */
- if (len < size)
- str[len] = ' ';
- len++;
- }
-
- while (*token != 0) {
- if (len < size)
- str[len] = *token;
- token++;
- len++;
- }
- }
- }
-
- /* terminate 'str' */
- if (len < size)
- str[len] = 0;
- else
- str[size - 1] = 0;
+ const char *(*bit2str)(int bit) = is_subsys ? libcfs_debug_subsys2str :
+ libcfs_debug_dbg2str;
- return len;
+ return cfs_mask2str(str, size, mask, bit2str, ' ');
}
-int
-libcfs_debug_str2mask(int *mask, const char *str, int is_subsys)
+int libcfs_debug_str2mask(int *mask, const char *str, int is_subsys)
{
- const char *(*fn)(int bit) = is_subsys ? libcfs_debug_subsys2str :
- libcfs_debug_dbg2str;
+ const char *(*bit2str)(int bit) = is_subsys ? libcfs_debug_subsys2str :
+ libcfs_debug_dbg2str;
+ u64 newmask = *mask;
int m = 0;
int matched;
- int n;
- int t;
+ int n, t;
+ int rc;
/* Allow a number for backwards compatibility */
for (n = strlen(str); n > 0; n--)
- if (!isspace(str[n-1]))
+ if (!isspace(str[n - 1]))
break;
matched = n;
t = sscanf(str, "%i%n", &m, &matched);
if (t >= 1 && matched == n) {
/* don't print warning for lctl set_param debug=0 or -1 */
if (m != 0 && m != -1)
- CWARN("You are trying to use a numerical value for the mask - this will be deprecated in a future release.\n");
+ CWARN("using a numerical debug mask is deprecated\n");
*mask = m;
return 0;
}
- return cfs_str2mask(str, fn, mask, is_subsys ? 0 : D_CANTMASK, ~0,
- is_subsys ? LIBCFS_S_DEFAULT : LIBCFS_D_DEFAULT);
+ rc = cfs_str2mask(str, bit2str, &newmask, is_subsys ? 0 : D_CANTMASK,
+ ~0, is_subsys ? LIBCFS_S_DEFAULT : LIBCFS_D_DEFAULT);
+
+ *mask = newmask;
+
+ return rc;
}
char lnet_debug_log_upcall[1024] = "/usr/lib/lustre/lnet_debug_log_upcall";
#include <libcfs/libcfs.h>
#include <libcfs/libcfs_string.h>
+
+/* convert a binary mask to a string of bit names */
+int cfs_mask2str(char *str, int size, u64 mask, const char *(*bit2str)(int bit),
+ char sep)
+{
+ int len = 0;
+ const char *token;
+ int i;
+
+ if (mask == 0) { /* "0" */
+ if (size > 0)
+ str[0] = '0';
+ len = 1;
+ } else { /* space-separated tokens */
+ for (i = 0; i < 64; i++) {
+ if ((mask & BIT(i)) == 0)
+ continue;
+
+ token = bit2str(i);
+ if (!token) /* unused bit */
+ continue;
+
+ if (len > 0) { /* separator? */
+ if (len < size)
+ str[len] = sep;
+ len++;
+ }
+
+ while (*token != 0) {
+ if (len < size)
+ str[len] = *token;
+ token++;
+ len++;
+ }
+ }
+ }
+
+ /* terminate 'str' */
+ if (len < size)
+ str[len++] = '\n';
+ if (len < size)
+ str[len] = '\0';
+ else
+ str[size - 1] = '\0';
+
+ return len;
+}
+EXPORT_SYMBOL(cfs_mask2str);
+
/* Convert a text string to a bitmask */
int cfs_str2mask(const char *str, const char *(*bit2str)(int bit),
- int *oldmask, int minmask, int allmask, int defmask)
+ u64 *oldmask, u64 minmask, u64 allmask, u64 defmask)
{
const char *debugstr;
- char op = 0;
- int newmask = minmask, i, len, found = 0;
+ u64 newmask = minmask, found = 0;
ENTRY;
/* <str> must be a list of tokens separated by whitespace or comma,
* applies to all following tokens up to the next operator.
*/
while (*str != 0) {
+ int i, len;
+ char op = 0;
+
while (isspace(*str) || *str == ',')
str++;
if (*str == 0)
rc = 0;
} else {
rc = cfs_trace_copyout_string(buffer, nob,
- tmpstr + pos, "\n");
+ tmpstr + pos, NULL);
}
} else {
tmpstr = memdup_user_nul(buffer, nob);
spin_unlock(&mdd->mdd_cl.mc_lock);
if (mask) {
+ u64 newmask = CHANGELOG_DEFMASK;
+
/* if user will use relative mask apply it on default one */
- rec->cur_mask = CHANGELOG_DEFMASK;
- rc = cfs_str2mask(mask, changelog_type2str, &rec->cur_mask,
+ rc = cfs_str2mask(mask, changelog_type2str, &newmask,
CHANGELOG_MINMASK, CHANGELOG_ALLMASK,
CHANGELOG_DEFMASK);
if (rc)
GOTO(out_users, rc);
+ rec->cur_mask = newmask;
} else if (mdd->mdd_cl.mc_proc_mask == CHANGELOG_MINMASK) {
/* a maskless users means default mask but only if server has
* no specific mask set
char *kernbuf;
int rc;
int oldmask = mdd->mdd_cl.mc_proc_mask;
- int newmask = oldmask;
+ u64 newmask = oldmask;
ENTRY;
return is_admin;
}
+/* convert a capability into an integer to print or manage more easily */
+static inline u64 mdt_cap2num(kernel_cap_t cap)
+{
+#ifdef CAP_FOR_EACH_U32
+ /* kernels before v6.2-13111-gf122a08b197d had a more complex
+ * kernel_cap_t structure with an array of __u32 values, but this
+ * was then fixed to have a single __u64 value. There are accessor
+ * functions for the old kernel_cap_t but since that is now dead code
+ * it isn't worthwhile to jump through hoops for compatibility for it.
+ */
+ return ((u64)cap.cap[1] << 32) | cap.cap[0];
+#else
+ return cap.val;
+#endif
+}
+
+/* convert an integer into a capabilityt */
+static inline kernel_cap_t mdt_num2cap(u64 num)
+{
+ kernel_cap_t cap;
+
+#ifdef CAP_FOR_EACH_U32
+ cap.cap[0] = num;
+ cap.cap[1] = (num >> 32);
+#else
+ cap.val = num;
+#endif
+
+ return cap;
+}
+
/* We forbid operations from encryption-unaware clients if they try to
* manipulate encrypted files/directories.
*/
}
LPROC_SEQ_FOPS(mdt_nosquash_nids);
+static const char *mdt_cap2str(int cap)
+{
+ /* We don't allow using all capabilities, but the fields must exist.
+ * The supported capabilities are CAP_FS_SET and CAP_NFSD_SET, plus
+ * CAP_SYS_ADMIN for a bunch of HSM operations (that should be fixed).
+ */
+ static const char *const capability_names[] = {
+ "cap_chown", /* 0 */
+ "cap_dac_override", /* 1 */
+ "cap_dac_read_search", /* 2 */
+ "cap_fowner", /* 3 */
+ "cap_fsetid", /* 4 */
+ NULL, /* 5 */
+ NULL, /* 6 */
+ NULL, /* 7 */
+ NULL, /* 8 */
+ "cap_linux_immutable", /* 9 */
+ NULL, /* 10 */
+ NULL, /* 11 */
+ NULL, /* 12 */
+ NULL, /* 13 */
+ NULL, /* 14 */
+ NULL, /* 15 */
+ NULL, /* 16 */
+ NULL, /* 17 */
+ NULL, /* 18 */
+ NULL, /* 19 */
+ NULL, /* 20 */
+ /* we should use more precise capabilities than this */
+ "cap_sys_admin", /* 21 */
+ NULL, /* 22 */
+ NULL, /* 23 */
+ "cap_sys_resource", /* 24 */
+ NULL, /* 25 */
+ NULL, /* 26 */
+ "cap_mknod", /* 27 */
+ NULL, /* 28 */
+ NULL, /* 29 */
+ NULL, /* 30 */
+ NULL, /* 31 */
+ "cap_mac_override", /* 32 */
+ };
+
+ if (cap >= ARRAY_SIZE(capability_names))
+ return NULL;
+
+ return capability_names[cap];
+}
+
static ssize_t enable_cap_mask_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);
- u64 cap;
-
- BUILD_BUG_ON(_KERNEL_CAP_T_SIZE != sizeof(u64));
+ u64 mask = mdt_cap2num(mdt->mdt_enable_cap_mask);
-#ifdef CAP_FOR_EACH_U32 /* kernels before v6.2-13111-gf122a08b197d */
- cap = ((u64)mdt->mdt_enable_cap_mask.cap[1] << 32) |
- mdt->mdt_enable_cap_mask.cap[0];
-#else
- cap = mdt->mdt_enable_cap_mask.val;
-#endif
- return scnprintf(buf, PAGE_SIZE, "%#0llx\n", cap);
+ return cfs_mask2str(buf, PAGE_SIZE, mask, mdt_cap2str, ',');
}
static ssize_t enable_cap_mask_store(struct kobject *kobj,
struct obd_device *obd = container_of(kobj, struct obd_device,
obd_kset.kobj);
struct mdt_device *mdt = mdt_dev(obd->obd_lu_dev);
+ static kernel_cap_t allowed_cap = CAP_EMPTY_SET;
unsigned long long val;
int rc;
rc = kstrtoull(buffer, 0, &val);
+ if (rc == -EINVAL) {
+ u64 cap = mdt_cap2num(mdt->mdt_enable_cap_mask);
+
+ /* the "allmask" is filtered by allowed_mask below */
+ rc = cfs_str2mask(buffer, mdt_cap2str, &cap, 0, ~0ULL, 0);
+ val = cap;
+ }
if (rc)
- /* should also accept symbolic names via cfs_str2mask() */
return rc;
-#ifdef CAP_FOR_EACH_U32
- mdt->mdt_enable_cap_mask.cap[0] = val &
- (CAP_FS_MASK_B0 | CAP_TO_MASK(CAP_SYS_RESOURCE) |
- CAP_TO_MASK(CAP_LINUX_IMMUTABLE));
- mdt->mdt_enable_cap_mask.cap[1] = (val >> 32) & CAP_FS_MASK_B1;
-#else
- mdt->mdt_enable_cap_mask.val = val &
- (CAP_FS_MASK | BIT_ULL(CAP_SYS_RESOURCE) |
- BIT_ULL(CAP_LINUX_IMMUTABLE));
-#endif
+ /* All of the capabilities that we currently allow/check */
+ if (unlikely(cap_isclear(allowed_cap))) {
+ allowed_cap = CAP_FS_SET;
+ cap_raise(allowed_cap, CAP_SYS_RESOURCE);
+ }
+
+ mdt->mdt_enable_cap_mask = cap_intersect(mdt_num2cap(val), allowed_cap);
return count;
}
old_cap=($(do_nodes $mdts $LCTL get_param -n $cap_param 2>/dev/null))
if [[ -n "$old_cap" ]]; then
- do_nodes $mdts $LCTL set_param $cap_param=0xf
+ local new_cap="+cap_chown+cap_fowner+cap_dac_override+cap_dac_read_search"
+ (( $MDS1_VERSION > $(version_code 2.14.0.135) )) || new_cap=0xf
+ echo "old_cap: $old_cap new_cap: $new_cap"
+ do_nodes $mdts $LCTL set_param $cap_param=$new_cap
stack_trap "do_nodes $mdts $LCTL set_param $cap_param=$old_cap"
fi