Other possible values (multiple can be specified, comma separated) are uid to
map UIDs, gid to map GIDs, both to map UIDs and GIDs, and projid to map PROJIDs.
.RE
+.PP
+readonly_mount
+.RS 4
+Defaults to off, which lets clients mount in read-write mode. If set to 1,
+clients are only allowed to mount in read-only mount, and get -EROFS otherwise.
+.RE
.RE
.I value
nmf_deny_unknown:1,
nmf_allow_root_access:1,
nmf_enable_audit:1,
- nmf_forbid_encryption:1;
+ nmf_forbid_encryption:1,
+ nmf_readonly_mount:1;
/* bitmap for mapping type */
enum nodemap_mapping_modes
nmf_map_mode;
int nodemap_set_squash_projid(const char *name, projid_t projid);
int nodemap_set_audit_mode(const char *name, bool enable_audit);
int nodemap_set_forbid_encryption(const char *name, bool forbid_encryption);
+int nodemap_set_readonly_mount(const char *name, bool readonly_mount);
bool nodemap_can_setquota(struct lu_nodemap *nodemap, __u32 qc_type, __u32 id);
int nodemap_add_idmap(const char *name, enum nodemap_id_type id_type,
const __u32 map[2]);
LCFG_NODEMAP_SET_SEPOL = 0x00ce05b, /**< set SELinux policy */
LCFG_NODEMAP_FORBID_ENCRYPT = 0x00ce05c, /**< forbid encryption */
LCFG_NODEMAP_SQUASH_PROJID = 0x00ce05d, /**< default map projid */
+ LCFG_NODEMAP_READONLY_MOUNT = 0x00ce05e, /**< read-only mount */
};
struct lustre_cfg_bufs {
SELINUX_POLICY_VER_LEN + \
SELINUX_POLICY_HASH_LEN + 3)
+/* lu_nodemap flags */
+enum nm_flag_bits {
+ NM_FL_ALLOW_ROOT_ACCESS = 0x1,
+ NM_FL_TRUST_CLIENT_IDS = 0x2,
+ NM_FL_DENY_UNKNOWN = 0x4,
+ NM_FL_MAP_UID = 0x8,
+ NM_FL_MAP_GID = 0x10,
+ NM_FL_ENABLE_AUDIT = 0x20,
+ NM_FL_FORBID_ENCRYPT = 0x40,
+ NM_FL_MAP_PROJID = 0x80,
+};
+enum nm_flag2_bits {
+ NM_FL2_READONLY_MOUNT = 0x1,
+};
+
/* nodemap records, uses 32 byte record length */
#define LUSTRE_NODEMAP_NAME_LENGTH 16
struct nodemap_cluster_rec {
- char ncr_name[LUSTRE_NODEMAP_NAME_LENGTH + 1];
- __u8 ncr_flags;
- __u16 ncr_padding1;
- __u32 ncr_squash_projid;
- __u32 ncr_squash_uid;
- __u32 ncr_squash_gid;
+ char ncr_name[LUSTRE_NODEMAP_NAME_LENGTH + 1];
+ enum nm_flag_bits ncr_flags:8;
+ enum nm_flag2_bits ncr_flags2:8;
+ __u8 ncr_padding1;
+ __u32 ncr_squash_projid;
+ __u32 ncr_squash_uid;
+ __u32 ncr_squash_gid;
};
/* lnet_nid_t is 8 bytes */
case LCFG_NODEMAP_MAP_MODE:
case LCFG_NODEMAP_AUDIT_MODE:
case LCFG_NODEMAP_FORBID_ENCRYPT:
+ case LCFG_NODEMAP_READONLY_MOUNT:
if (lcfg->lcfg_bufcount != 4)
GOTO(out_lcfg, rc = -EINVAL);
nodemap_name = lustre_cfg_string(lcfg, 1);
rc = nodemap_set_forbid_encryption(nodemap_name,
bool_switch);
break;
+ case LCFG_NODEMAP_READONLY_MOUNT:
+ rc = kstrtobool(param, &bool_switch);
+ if (rc == 0)
+ rc = nodemap_set_readonly_mount(nodemap_name,
+ bool_switch);
+ break;
case LCFG_NODEMAP_MAP_MODE:
{
char *p;
time64_t next_connect;
import_set_state_nolock(imp, LUSTRE_IMP_DISCON);
- if (rc == -EACCES) {
+ if (rc == -EACCES || rc == -EROFS) {
/*
* Give up trying to reconnect
* EACCES means client has no permission for connection
+ * EROFS means client must mount read-only
*/
imp->imp_obd->obd_no_recov = 1;
ptlrpc_deactivate_import_nolock(imp);
if (req->rq_status != -ENOSPC && req->rq_status != -EACCES &&
req->rq_status != -EPERM && req->rq_status != -ENOENT &&
- req->rq_status != -EINPROGRESS && req->rq_status != -EDQUOT)
+ req->rq_status != -EINPROGRESS && req->rq_status != -EDQUOT &&
+ req->rq_status != -EROFS)
req->rq_type = PTL_RPC_MSG_ERR;
rc = ptlrpc_send_reply(req, may_be_difficult);
nodemap->nmf_map_mode = NODEMAP_MAP_ALL;
nodemap->nmf_enable_audit = 1;
nodemap->nmf_forbid_encryption = 0;
+ nodemap->nmf_readonly_mount = 0;
nodemap->nm_squash_uid = NODEMAP_NOBODY_UID;
nodemap->nm_squash_gid = NODEMAP_NOBODY_GID;
nodemap->nmf_enable_audit = default_nodemap->nmf_enable_audit;
nodemap->nmf_forbid_encryption =
default_nodemap->nmf_forbid_encryption;
+ nodemap->nmf_readonly_mount =
+ default_nodemap->nmf_readonly_mount;
nodemap->nm_squash_uid = default_nodemap->nm_squash_uid;
nodemap->nm_squash_gid = default_nodemap->nm_squash_gid;
}
EXPORT_SYMBOL(nodemap_set_forbid_encryption);
+/**
+ * Set the nmf_readonly_mount flag to true or false.
+ * \param name nodemap name
+ * \param readonly_mount if true, forbid rw mount
+ * \retval 0 on success
+ *
+ */
+int nodemap_set_readonly_mount(const char *name, bool readonly_mount)
+{
+ struct lu_nodemap *nodemap = NULL;
+ int rc = 0;
+
+ mutex_lock(&active_config_lock);
+ nodemap = nodemap_lookup(name);
+ mutex_unlock(&active_config_lock);
+ if (IS_ERR(nodemap))
+ GOTO(out, rc = PTR_ERR(nodemap));
+
+ nodemap->nmf_readonly_mount = readonly_mount;
+ rc = nodemap_idx_nodemap_update(nodemap);
+
+ nm_member_revoke_locks(nodemap);
+ nodemap_putref(nodemap);
+out:
+ return rc;
+}
+EXPORT_SYMBOL(nodemap_set_readonly_mount);
/**
* Add a nodemap
return 0;
}
+/**
+ * Reads and prints the readonly_mount flag for the given nodemap.
+ *
+ * \param m seq file in proc fs
+ * \param data unused
+ * \retval 0 success
+ */
+static int nodemap_readonly_mount_seq_show(struct seq_file *m, void *data)
+{
+ struct lu_nodemap *nodemap;
+ int rc;
+
+ mutex_lock(&active_config_lock);
+ nodemap = nodemap_lookup(m->private);
+ mutex_unlock(&active_config_lock);
+ if (IS_ERR(nodemap)) {
+ rc = PTR_ERR(nodemap);
+ CERROR("cannot find nodemap '%s': rc = %d\n",
+ (char *)m->private, rc);
+ return rc;
+ }
+
+ seq_printf(m, "%d\n", (int)nodemap->nmf_readonly_mount);
+ nodemap_putref(nodemap);
+ return 0;
+}
+
static struct lprocfs_vars lprocfs_nm_module_vars[] = {
{
.name = "active",
LPROC_SEQ_FOPS_RO(nodemap_map_mode);
LPROC_SEQ_FOPS_RO(nodemap_audit_mode);
LPROC_SEQ_FOPS_RO(nodemap_forbid_encryption);
+LPROC_SEQ_FOPS_RO(nodemap_readonly_mount);
static const struct proc_ops nodemap_ranges_fops = {
.proc_open = nodemap_ranges_open,
.fops = &nodemap_forbid_encryption_fops,
},
{
+ .name = "readonly_mount",
+ .fops = &nodemap_readonly_mount_fops,
+ },
+ {
.name = "squash_uid",
.fops = &nodemap_squash_uid_fops,
},
.fops = &nodemap_forbid_encryption_fops,
},
{
+ .name = "readonly_mount",
+ .fops = &nodemap_readonly_mount_fops,
+ },
+ {
NULL
}
};
/* MGS index is different than others, others are listeners to MGS idx */
static struct nm_config_file *nodemap_mgs_ncf;
-/* lu_nodemap flags */
-enum nm_flag_shifts {
- NM_FL_ALLOW_ROOT_ACCESS = 0x1,
- NM_FL_TRUST_CLIENT_IDS = 0x2,
- NM_FL_DENY_UNKNOWN = 0x4,
- NM_FL_MAP_UID = 0x8,
- NM_FL_MAP_GID = 0x10,
- NM_FL_ENABLE_AUDIT = 0x20,
- NM_FL_FORBID_ENCRYPT = 0x40,
- NM_FL_MAP_PROJID = 0x80,
-};
-
static void nodemap_cluster_key_init(struct nodemap_key *nk, unsigned int nm_id)
{
nk->nk_nodemap_id = cpu_to_le32(nm_idx_set_type(nm_id,
nr->ncr.ncr_squash_uid = cpu_to_le32(nodemap->nm_squash_uid);
nr->ncr.ncr_squash_gid = cpu_to_le32(nodemap->nm_squash_gid);
nr->ncr.ncr_squash_projid = cpu_to_le32(nodemap->nm_squash_projid);
- nr->ncr.ncr_flags = cpu_to_le32(
+ nr->ncr.ncr_flags =
(nodemap->nmf_trust_client_ids ?
NM_FL_TRUST_CLIENT_IDS : 0) |
(nodemap->nmf_allow_root_access ?
(nodemap->nmf_enable_audit ?
NM_FL_ENABLE_AUDIT : 0) |
(nodemap->nmf_forbid_encryption ?
- NM_FL_FORBID_ENCRYPT : 0));
+ NM_FL_FORBID_ENCRYPT : 0);
+ nr->ncr.ncr_flags2 =
+ (nodemap->nmf_readonly_mount ?
+ NM_FL2_READONLY_MOUNT : 0);
}
static void nodemap_idmap_key_init(struct nodemap_key *nk, unsigned int nm_id,
struct lu_nodemap *nodemap = NULL;
enum nodemap_idx_type type;
enum nodemap_id_type id_type;
- u8 flags;
+ enum nm_flag_bits flags;
+ enum nm_flag2_bits flags2;
u32 nodemap_id;
lnet_nid_t nid[2];
u32 map[2];
nodemap->nm_squash_projid =
le32_to_cpu(rec->ncr.ncr_squash_projid);
- flags = le32_to_cpu(rec->ncr.ncr_flags);
+ flags = rec->ncr.ncr_flags;
nodemap->nmf_allow_root_access =
flags & NM_FL_ALLOW_ROOT_ACCESS;
nodemap->nmf_trust_client_ids =
flags & NM_FL_ENABLE_AUDIT;
nodemap->nmf_forbid_encryption =
flags & NM_FL_FORBID_ENCRYPT;
+ flags2 = rec->ncr.ncr_flags2;
+ nodemap->nmf_readonly_mount =
+ flags2 & NM_FL2_READONLY_MOUNT;
/* The fileset should be saved otherwise it will be empty
* every time in case of "NODEMAP_CLUSTER_IDX". */
(long long)(int)offsetof(struct nodemap_cluster_rec, ncr_name[16 + 1]));
LASSERTF((int)sizeof(((struct nodemap_cluster_rec *)0)->ncr_name[16 + 1]) == 1, "found %lld\n",
(long long)(int)sizeof(((struct nodemap_cluster_rec *)0)->ncr_name[16 + 1]));
- LASSERTF((int)offsetof(struct nodemap_cluster_rec, ncr_flags) == 17, "found %lld\n",
- (long long)(int)offsetof(struct nodemap_cluster_rec, ncr_flags));
- LASSERTF((int)sizeof(((struct nodemap_cluster_rec *)0)->ncr_flags) == 1, "found %lld\n",
- (long long)(int)sizeof(((struct nodemap_cluster_rec *)0)->ncr_flags));
- LASSERTF((int)offsetof(struct nodemap_cluster_rec, ncr_padding1) == 18, "found %lld\n",
+ /* ncr_flags' address and size cannot be taken because it is a bit-field (8 bits) */
+ /* ncr_flags2' address and size cannot be taken because it is a bit-field (8 bits) */
+ LASSERTF((int)offsetof(struct nodemap_cluster_rec, ncr_padding1) == 19, "found %lld\n",
(long long)(int)offsetof(struct nodemap_cluster_rec, ncr_padding1));
- LASSERTF((int)sizeof(((struct nodemap_cluster_rec *)0)->ncr_padding1) == 2, "found %lld\n",
+ LASSERTF((int)sizeof(((struct nodemap_cluster_rec *)0)->ncr_padding1) == 1, "found %lld\n",
(long long)(int)sizeof(((struct nodemap_cluster_rec *)0)->ncr_padding1));
LASSERTF((int)offsetof(struct nodemap_cluster_rec, ncr_squash_projid) == 20, "found %lld\n",
(long long)(int)offsetof(struct nodemap_cluster_rec, ncr_squash_projid));
(unsigned)LCFG_NODEMAP_FORBID_ENCRYPT);
LASSERTF(LCFG_NODEMAP_SQUASH_PROJID == 0x000ce05dUL, "found 0x%.8xUL\n",
(unsigned)LCFG_NODEMAP_SQUASH_PROJID);
+ LASSERTF(LCFG_NODEMAP_READONLY_MOUNT == 0x000ce05eUL, "found 0x%.8xUL\n",
+ (unsigned)LCFG_NODEMAP_READONLY_MOUNT);
#endif /* HAVE_SERVER_SUPPORT */
LASSERTF(PORTALS_CFG_TYPE == 1, "found %lld\n",
(long long)PORTALS_CFG_TYPE);
if (strcmp(tsi->tsi_exp->exp_obd->obd_type->typ_name,
LUSTRE_MDT_NAME) == 0) {
+ struct lu_nodemap *nm = NULL;
+
rc = req_check_sepol(tsi->tsi_pill);
if (rc)
GOTO(out, rc);
+ if (tsi->tsi_pill->rc_req->rq_export)
+ nm =
+ nodemap_get_from_exp(tsi->tsi_pill->rc_req->rq_export);
+
if (reply->ocd_connect_flags & OBD_CONNECT_FLAGS2 &&
- reply->ocd_connect_flags2 & OBD_CONNECT2_ENCRYPT &&
- tsi->tsi_pill->rc_req->rq_export) {
+ reply->ocd_connect_flags2 & OBD_CONNECT2_ENCRYPT) {
bool forbid_encrypt = true;
- struct lu_nodemap *nm =
- nodemap_get_from_exp(tsi->tsi_pill->rc_req->rq_export);
- if (!nm) {
+ if (!nm)
/* nodemap_get_from_exp returns NULL in case
* nodemap is not active, so we do not forbid
*/
forbid_encrypt = false;
- } else if (!IS_ERR(nm)) {
+ else if (!IS_ERR(nm))
forbid_encrypt = nm->nmf_forbid_encryption;
- nodemap_putref(nm);
- }
-
if (forbid_encrypt)
- GOTO(out, rc = -EACCES);
+ GOTO(put_nm, rc = -EACCES);
}
+
+ if (!(reply->ocd_connect_flags & OBD_CONNECT_RDONLY)) {
+ bool readonly = false;
+
+ if (!IS_ERR_OR_NULL(nm))
+ readonly = nm->nmf_readonly_mount;
+ if (unlikely(readonly))
+ GOTO(put_nm, rc = -EROFS);
+ }
+
+put_nm:
+ if (!IS_ERR_OR_NULL(nm))
+ nodemap_putref(nm);
+ if (rc)
+ GOTO(out, rc);
}
RETURN(0);
}
cleanup_nodemap_after_enc_tests() {
+ umount_client $MOUNT || true
+
do_facet mgs $LCTL nodemap_modify --name default \
--property forbid_encryption --value 0
- wait_nm_sync default forbid_encryption
+ do_facet mgs $LCTL nodemap_modify --name default \
+ --property readonly_mount --value 0 || true
+ do_facet mgs $LCTL nodemap_modify --name default \
+ --property trusted --value 0
+ do_facet mgs $LCTL nodemap_modify --name default \
+ --property admin --value 0
do_facet mgs $LCTL nodemap_activate 0
+
+ wait_nm_sync default forbid_encryption '' inactive
+ [ -z "$(do_facet mgs \
+ lctl get_param -n nodemap.default.readonly_mount)" ] ||
+ wait_nm_sync default readonly_mount '' inactive
+ wait_nm_sync default trusted_nodemap '' inactive
+ wait_nm_sync default admin_nodemap '' inactive
wait_nm_sync active
+
+ mount_client $MOUNT ${MOUNT_OPTS} || error "re-mount failed"
}
test_36() {
}
run_test 60 "Subdirmount of encrypted dir"
+test_61() {
+ local testfile=$DIR/$tdir/$tfile
+ local readonly
+
+ readonly=$(do_facet mgs \
+ lctl get_param -n nodemap.default.readonly_mount)
+ [ -n "$readonly" ] ||
+ skip "Server does not have readonly_mount nodemap flag"
+
+ stack_trap cleanup_nodemap_after_enc_tests EXIT
+ umount_client $MOUNT || error "umount $MOUNT failed (1)"
+
+ # Activate nodemap, and mount rw.
+ # Should succeed as rw mount is not forbidden on default nodemap
+ # by default.
+ do_facet mgs $LCTL nodemap_activate 1
+ wait_nm_sync active
+ do_facet mgs $LCTL nodemap_modify --name default \
+ --property admin --value 1
+ do_facet mgs $LCTL nodemap_modify --name default \
+ --property trusted --value 1
+ wait_nm_sync default admin_nodemap
+ wait_nm_sync default trusted_nodemap
+ readonly=$(do_facet mgs \
+ lctl get_param -n nodemap.default.readonly_mount)
+ [ $readonly -eq 0 ] || error "wrong default value for readonly_mount"
+
+ mount_client $MOUNT ${MOUNT_OPTS},rw ||
+ error "mount '-o rw' failed with default"
+ findmnt $MOUNT --output=options -n -f | grep -q "rw," ||
+ error "should be rw mount"
+ mkdir -p $DIR/$tdir || error "mkdir $DIR/$tdir failed"
+ echo a > $testfile || error "write $testfile failed"
+ umount_client $MOUNT || error "umount $MOUNT failed (2)"
+
+ # Now enforce read-only, and retry.
+ do_facet mgs $LCTL nodemap_modify --name default \
+ --property readonly_mount --value 1
+ wait_nm_sync default readonly_mount
+ mount_client $MOUNT ${MOUNT_OPTS},rw &&
+ error "mount '-o rw' should have failed"
+ mount_client $MOUNT ${MOUNT_OPTS},ro ||
+ error "mount '-o ro' failed"
+ cat $testfile || error "read $testfile failed"
+ echo b > $testfile && error "write $testfile should fail"
+ umount_client $MOUNT || error "umount $MOUNT failed (3)"
+}
+run_test 61 "Nodemap enforces read-only mount"
+
log "cleanup: ======================================================"
sec_unsetup() {
fprintf(stderr,
"usage: nodemap_modify --name <nodemap_name> --property <property_name> --value <value>\n");
fprintf(stderr,
- "valid properties: admin trusted map_mode squash_uid squash_gid squash_projid deny_unknown audit_mode forbid_encryption\n");
+ "valid properties: admin trusted map_mode squash_uid squash_gid squash_projid deny_unknown audit_mode forbid_encryption readonly_mount\n");
return -1;
}
cmd = LCFG_NODEMAP_AUDIT_MODE;
} else if (strcmp("forbid_encryption", param) == 0) {
cmd = LCFG_NODEMAP_FORBID_ENCRYPT;
+ } else if (strcmp("readonly_mount", param) == 0) {
+ cmd = LCFG_NODEMAP_READONLY_MOUNT;
} else {
fprintf(stderr,
"error: %s: nodemap_modify invalid subcommand: %s\n",
CHECK_CDEFINE(LUSTRE_NODEMAP_NAME_LENGTH);
CHECK_MEMBER(nodemap_cluster_rec, ncr_name[LUSTRE_NODEMAP_NAME_LENGTH + 1]);
CHECK_MEMBER(nodemap_cluster_rec, ncr_flags);
+ CHECK_MEMBER(nodemap_cluster_rec, ncr_flags2);
CHECK_MEMBER(nodemap_cluster_rec, ncr_padding1);
CHECK_MEMBER(nodemap_cluster_rec, ncr_squash_projid);
CHECK_MEMBER(nodemap_cluster_rec, ncr_squash_uid);
CHECK_VALUE_X(LCFG_NODEMAP_SET_SEPOL);
CHECK_VALUE_X(LCFG_NODEMAP_FORBID_ENCRYPT);
CHECK_VALUE_X(LCFG_NODEMAP_SQUASH_PROJID);
+ CHECK_VALUE_X(LCFG_NODEMAP_READONLY_MOUNT);
printf("#endif /* HAVE_SERVER_SUPPORT */\n");
#endif /* !HAVE_NATIVE_LINUX_CLIENT */
CHECK_VALUE(PORTALS_CFG_TYPE);
(long long)(int)offsetof(struct nodemap_cluster_rec, ncr_name[16 + 1]));
LASSERTF((int)sizeof(((struct nodemap_cluster_rec *)0)->ncr_name[16 + 1]) == 1, "found %lld\n",
(long long)(int)sizeof(((struct nodemap_cluster_rec *)0)->ncr_name[16 + 1]));
- LASSERTF((int)offsetof(struct nodemap_cluster_rec, ncr_flags) == 17, "found %lld\n",
- (long long)(int)offsetof(struct nodemap_cluster_rec, ncr_flags));
- LASSERTF((int)sizeof(((struct nodemap_cluster_rec *)0)->ncr_flags) == 1, "found %lld\n",
- (long long)(int)sizeof(((struct nodemap_cluster_rec *)0)->ncr_flags));
- LASSERTF((int)offsetof(struct nodemap_cluster_rec, ncr_padding1) == 18, "found %lld\n",
+ /* ncr_flags' address and size cannot be taken because it is a bit-field (8 bits) */
+ /* ncr_flags2' address and size cannot be taken because it is a bit-field (8 bits) */
+ LASSERTF((int)offsetof(struct nodemap_cluster_rec, ncr_padding1) == 19, "found %lld\n",
(long long)(int)offsetof(struct nodemap_cluster_rec, ncr_padding1));
- LASSERTF((int)sizeof(((struct nodemap_cluster_rec *)0)->ncr_padding1) == 2, "found %lld\n",
+ LASSERTF((int)sizeof(((struct nodemap_cluster_rec *)0)->ncr_padding1) == 1, "found %lld\n",
(long long)(int)sizeof(((struct nodemap_cluster_rec *)0)->ncr_padding1));
LASSERTF((int)offsetof(struct nodemap_cluster_rec, ncr_squash_projid) == 20, "found %lld\n",
(long long)(int)offsetof(struct nodemap_cluster_rec, ncr_squash_projid));
(unsigned)LCFG_NODEMAP_FORBID_ENCRYPT);
LASSERTF(LCFG_NODEMAP_SQUASH_PROJID == 0x000ce05dUL, "found 0x%.8xUL\n",
(unsigned)LCFG_NODEMAP_SQUASH_PROJID);
+ LASSERTF(LCFG_NODEMAP_READONLY_MOUNT == 0x000ce05eUL, "found 0x%.8xUL\n",
+ (unsigned)LCFG_NODEMAP_READONLY_MOUNT);
#endif /* HAVE_SERVER_SUPPORT */
LASSERTF(PORTALS_CFG_TYPE == 1, "found %lld\n",
(long long)PORTALS_CFG_TYPE);