From 23bae7dc21b5c955c096431d485e49a7298ec404 Mon Sep 17 00:00:00 2001 From: Jinshan Xiong Date: Tue, 18 Dec 2018 15:36:33 -0800 Subject: [PATCH] LU-11811 mdt: Add a proc entry to set MDT to readonly It is sometimes desirable to be able to mark the filesystem read-only directly on the server, rather than remounting the clients and setting the option there. This can be useful if there is a rogue client that is deleting files, or when decommissioning a system to prevent already-mounted clients from modifying it anymore. Add the mdt.*.readonly parameter to allow setting the MDT read-only immediately if set to 1. All future MDT access will immediately return -EROFS until the parameter is set to 0 again. Signed-off-by: Jinshan Xiong Change-Id: I6d8d529ed4ba797012ded6920a9d9e7db6659efc Reviewed-on: https://review.whamcloud.com/33892 Tested-by: Jenkins Reviewed-by: Andreas Dilger Tested-by: Maloo Reviewed-by: Patrick Farrell Reviewed-by: Oleg Drokin --- lustre/mdt/mdt_handler.c | 28 ++++++++++++++++++++-------- lustre/mdt/mdt_internal.h | 10 +++++----- lustre/mdt/mdt_lproc.c | 30 ++++++++++++++++++++++++++++++ lustre/tests/sanity.sh | 41 ++++++++++++++++++++++++++++++++++++----- 4 files changed, 91 insertions(+), 18 deletions(-) diff --git a/lustre/mdt/mdt_handler.c b/lustre/mdt/mdt_handler.c index 5168791..b1b9cc0 100644 --- a/lustre/mdt/mdt_handler.c +++ b/lustre/mdt/mdt_handler.c @@ -2087,6 +2087,12 @@ static int mdt_fix_attr_ucred(struct mdt_thread_info *info, __u32 op) return 0; } +static inline bool mdt_is_readonly_open(struct mdt_thread_info *info, __u32 op) +{ + return op == REINT_OPEN && + !(info->mti_spec.sp_cr_flags & (MDS_FMODE_WRITE | MDS_OPEN_CREAT)); +} + static int mdt_reint_internal(struct mdt_thread_info *info, struct mdt_lock_handle *lhc, __u32 op) @@ -2097,15 +2103,21 @@ static int mdt_reint_internal(struct mdt_thread_info *info, ENTRY; - rc = mdt_reint_unpack(info, op); - if (rc != 0) { - CERROR("Can't unpack reint, rc %d\n", rc); - RETURN(err_serious(rc)); - } + rc = mdt_reint_unpack(info, op); + if (rc != 0) { + CERROR("Can't unpack reint, rc %d\n", rc); + RETURN(err_serious(rc)); + } + + + /* check if the file system is set to readonly. O_RDONLY open + * is still allowed even the file system is set to readonly mode */ + if (mdt_rdonly(info->mti_exp) && !mdt_is_readonly_open(info, op)) + RETURN(err_serious(-EROFS)); - /* for replay (no_create) lmm is not needed, client has it already */ - if (req_capsule_has_field(pill, &RMF_MDT_MD, RCL_SERVER)) - req_capsule_set_size(pill, &RMF_MDT_MD, RCL_SERVER, + /* for replay (no_create) lmm is not needed, client has it already */ + if (req_capsule_has_field(pill, &RMF_MDT_MD, RCL_SERVER)) + req_capsule_set_size(pill, &RMF_MDT_MD, RCL_SERVER, DEF_REP_MD_SIZE); /* llog cookies are always 0, the field is kept for compatibility */ diff --git a/lustre/mdt/mdt_internal.h b/lustre/mdt/mdt_internal.h index 5bf811f..7784448 100644 --- a/lustre/mdt/mdt_internal.h +++ b/lustre/mdt/mdt_internal.h @@ -251,7 +251,8 @@ struct mdt_device { mdt_enable_remote_dir:1, mdt_enable_striped_dir:1, mdt_enable_dir_migration:1, - mdt_skip_lfsck:1; + mdt_skip_lfsck:1, + mdt_readonly:1; /* user with gid can create remote/striped * dir, and set default dir stripe */ @@ -1089,10 +1090,9 @@ static inline struct mdt_device *mdt_exp2dev(struct obd_export *exp) static inline bool mdt_rdonly(struct obd_export *exp) { - if (exp_connect_flags(exp) & OBD_CONNECT_RDONLY || - mdt_exp2dev(exp)->mdt_bottom->dd_rdonly) - return true; - return false; + return (exp_connect_flags(exp) & OBD_CONNECT_RDONLY || + mdt_exp2dev(exp)->mdt_bottom->dd_rdonly || + mdt_exp2dev(exp)->mdt_readonly); } typedef void (*mdt_reconstruct_t)(struct mdt_thread_info *mti, diff --git a/lustre/mdt/mdt_lproc.c b/lustre/mdt/mdt_lproc.c index b64b74a..3007c4d 100644 --- a/lustre/mdt/mdt_lproc.c +++ b/lustre/mdt/mdt_lproc.c @@ -975,6 +975,34 @@ mdt_migrate_hsm_allowed_seq_write(struct file *file, const char __user *buffer, } LPROC_SEQ_FOPS(mdt_migrate_hsm_allowed); +static int mdt_readonly_seq_show(struct seq_file *m, void *data) +{ + struct obd_device *obd = m->private; + struct mdt_device *mdt = mdt_dev(obd->obd_lu_dev); + + seq_printf(m, "%u\n", mdt->mdt_readonly); + return 0; +} + +static ssize_t +mdt_readonly_seq_write(struct file *file, const char __user *buffer, + size_t count, loff_t *off) +{ + struct seq_file *m = file->private_data; + struct obd_device *obd = m->private; + struct mdt_device *mdt = mdt_dev(obd->obd_lu_dev); + bool val; + int rc; + + rc = kstrtobool_from_user(buffer, count, &val); + if (rc) + return rc; + + mdt->mdt_readonly = val; + return count; +} +LPROC_SEQ_FOPS(mdt_readonly); + LPROC_SEQ_FOPS_RO_TYPE(mdt, recovery_status); LPROC_SEQ_FOPS_RO_TYPE(mdt, num_exports); LPROC_SEQ_FOPS_RO_TYPE(mdt, target_instance); @@ -1062,6 +1090,8 @@ static struct lprocfs_vars lprocfs_mdt_obd_vars[] = { .fops = &mdt_dom_read_open_fops }, { .name = "migrate_hsm_allowed", .fops = &mdt_migrate_hsm_allowed_fops }, + { .name = "readonly", + .fops = &mdt_readonly_fops }, { NULL } }; diff --git a/lustre/tests/sanity.sh b/lustre/tests/sanity.sh index d95a43d..d701c5e 100755 --- a/lustre/tests/sanity.sh +++ b/lustre/tests/sanity.sh @@ -19717,7 +19717,7 @@ saved_MGS_MOUNT_OPTS=$MGS_MOUNT_OPTS saved_MDS_MOUNT_OPTS=$MDS_MOUNT_OPTS saved_OST_MOUNT_OPTS=$OST_MOUNT_OPTS -cleanup_802() { +cleanup_802a() { trap 0 stopall @@ -19727,7 +19727,7 @@ cleanup_802() { setupall } -test_802() { +test_802a() { [[ $(lustre_version_code mds1) -lt $(version_code 2.9.55) ]] || [[ $OST1_VERSION -lt $(version_code 2.9.55) ]] && @@ -19740,7 +19740,7 @@ test_802() { cp $LUSTRE/tests/test-framework.sh $DIR/$tdir/ || error "(2) Fail to copy" - trap cleanup_802 EXIT + trap cleanup_802a EXIT # sync by force before remount as readonly sync; sync_all_data; sleep 3; sync_all_data @@ -19769,9 +19769,40 @@ test_802() { diff $LUSTRE/tests/test-framework.sh $DIR/$tdir/test-framework.sh || error "(7) Read should succeed under ro mode" - cleanup_802 + cleanup_802a } -run_test 802 "simulate readonly device" +run_test 802a "simulate readonly device" + +test_802b() { + [ $PARALLEL == "yes" ] && skip "skip parallel run" + remote_mds_nodsh && skip "remote MDS with nodsh" + + do_facet $SINGLEMDS $LCTL get_param mdt.*.readonly || + skip "readonly option not available" + + $LFS mkdir -i 0 -c 1 $DIR/$tdir || error "(1) fail to mkdir" + + cp $LUSTRE/tests/test-framework.sh $DIR/$tdir/ || + error "(2) Fail to copy" + + # write back all cached data before setting MDT to readonly + cancel_lru_locks + sync_all_data + + do_facet $SINGLEMDS $LCTL set_param mdt.*.readonly=1 + stack_trap "do_facet $SINGLEMDS $LCTL set_param mdt.*.readonly=0" EXIT + + echo "Modify should be refused" + touch $DIR/$tdir/guard && error "(6) Touch should fail under ro mode" + + echo "Read should be allowed" + diff $LUSTRE/tests/test-framework.sh $DIR/$tdir/test-framework.sh || + error "(7) Read should succeed under ro mode" + + # disable readonly + do_facet $SINGLEMDS $LCTL set_param mdt.*.readonly=0 +} +run_test 802b "be able to set MDTs to readonly" test_803() { [[ $MDSCOUNT -lt 2 ]] && skip_env "needs >= 2 MDTs" -- 1.8.3.1