From: Fan Yong Date: Mon, 4 Aug 2014 12:33:58 +0000 (+0800) Subject: LU-5511 lfsck: repair unmatched parent-child pairs X-Git-Tag: 2.6.54~38 X-Git-Url: https://git.whamcloud.com/?p=fs%2Flustre-release.git;a=commitdiff_plain;h=d35997e6e100acca3effb75ca402c9df7f6252ef LU-5511 lfsck: repair unmatched parent-child pairs If the parent directry_A reference the child directory_B via the name entry_C, but the child directory_B back references another parent directory_D via its ".." name entry, then the namespace LFSCK should can find out such inconsistency in the second-stage scanning and repair it by trusting the name entry_B basically. Usually, for local filesystem or Lustre backend system, such as ldiskfs, the local filesystem consistency verification tools, such as e2fsck can guarantee such consistency. So the namespace LFSCK will mainly focus on the corss-MDTs parent-child pairs consistency verification. Lustre does not support hardlink on directory object. So if the directory contains multiple linkEA entries or bad linkEA entry, then the namespace LFSCK should can find out and repair related inconsistency. Signed-off-by: Fan Yong Change-Id: If8cb02f76329fe04fe3a6c280e6926d014654322 Reviewed-on: http://review.whamcloud.com/11486 Tested-by: Jenkins Tested-by: Maloo Reviewed-by: Andreas Dilger Reviewed-by: Alex Zhuravlev Reviewed-by: Oleg Drokin --- diff --git a/lustre/include/obd_support.h b/lustre/include/obd_support.h index 8dfcce8..8eddbca 100644 --- a/lustre/include/obd_support.h +++ b/lustre/include/obd_support.h @@ -517,6 +517,8 @@ int obd_alloc_fail(const void *ptr, const char *name, const char *type, #define OBD_FAIL_LFSCK_DELAY5 0x161b #define OBD_FAIL_LFSCK_BAD_NETWORK 0x161c #define OBD_FAIL_LFSCK_NO_LINKEA 0x161d +#define OBD_FAIL_LFSCK_BAD_PARENT 0x161e +#define OBD_FAIL_LFSCK_BAD_PARENT2 0x161f #define OBD_FAIL_LFSCK_NOTIFY_NET 0x16f0 #define OBD_FAIL_LFSCK_QUERY_NET 0x16f1 diff --git a/lustre/lfsck/lfsck_engine.c b/lustre/lfsck/lfsck_engine.c index 8831e3e..34277e2 100644 --- a/lustre/lfsck/lfsck_engine.c +++ b/lustre/lfsck/lfsck_engine.c @@ -239,7 +239,7 @@ static int lfsck_needs_scan_dir(const struct lu_env *env, * @obj, because the lfsck_master_oit_engine() has * filtered out agent object. So current FID is for * the ancestor of the original input parameter @obj. - * So he ancestor is a remote directory. The input + * So the ancestor is a remote directory. The input * parameter @obj is local directory, and should be * scanned under such case. */ LASSERT(depth > 0); @@ -662,7 +662,8 @@ static int lfsck_master_dir_engine(const struct lu_env *env, goto checkpoint; } - if (ent->lde_attrs & LUDA_IGNORE) + if (ent->lde_attrs & LUDA_IGNORE && + strcmp(ent->lde_name, dotdot) != 0) goto checkpoint; /* The type in the @ent structure may has been overwritten, diff --git a/lustre/lfsck/lfsck_internal.h b/lustre/lfsck/lfsck_internal.h index ec4e0eb..e24acd3 100644 --- a/lustre/lfsck/lfsck_internal.h +++ b/lustre/lfsck/lfsck_internal.h @@ -114,6 +114,12 @@ enum lfsck_namespace_trace_flags { LNTF_ALL = 0xff }; +enum lfsck_namespace_inconsistency_type { + LNIT_NONE = 0, + LNIT_BAD_LINKEA = 1, + LNIT_UNMATCHED_PAIRS = 2, +}; + struct lfsck_namespace { /* Magic number to detect that this struct contains valid data. */ __u32 ln_magic; @@ -193,8 +199,14 @@ struct lfsck_namespace { /* How many multiple-linked objects have been repaired. */ __u64 ln_mul_linked_repaired; + /* How many undefined inconsistency found in phase2. */ + __u64 ln_unknown_inconsistency; + + /* How many unmatched pairs have been repaired. */ + __u64 ln_unmatched_pairs_repaired; + /* For further using. 256-bytes aligned now. */ - __u64 ln_reserved[31]; + __u64 ln_reserved[29]; }; enum lfsck_layout_inconsistency_type { @@ -620,6 +632,7 @@ struct lfsck_thread_info { struct lu_fid lti_fid; struct lu_fid lti_fid2; struct lu_fid lti_fid3; + struct lu_fid lti_fid4; struct lu_attr lti_la; struct lu_attr lti_la2; struct lu_attr lti_la3; @@ -727,6 +740,10 @@ int lfsck_namespace_trace_update(const struct lu_env *env, const __u8 flags, bool add); int __lfsck_links_read(const struct lu_env *env, struct dt_object *obj, struct linkea_data *ldata); +int lfsck_namespace_rebuild_linkea(const struct lu_env *env, + struct lfsck_component *com, + struct dt_object *obj, + struct linkea_data *ldata); int lfsck_verify_linkea(const struct lu_env *env, struct dt_device *dev, struct dt_object *obj, const struct lu_name *cname, const struct lu_fid *pfid); @@ -746,6 +763,8 @@ int lfsck_namespace_setup(const struct lu_env *env, /* lfsck_layout.c */ int lfsck_layout_setup(const struct lu_env *env, struct lfsck_instance *lfsck); +extern const char dot[]; +extern const char dotdot[]; extern const char *lfsck_flags_names[]; extern const char *lfsck_param_names[]; extern struct lu_context_key lfsck_thread_key; @@ -897,6 +916,11 @@ static inline void lfsck_object_put(const struct lu_env *env, lu_object_put(env, &obj->do_lu); } +static inline u32 lfsck_dev_idx(struct dt_device *dev) +{ + return dev->dd_lu_dev.ld_site->ld_seq_site->ss_node_id; +} + static inline struct dt_object * lfsck_object_find_by_dev_nowait(const struct lu_env *env, struct dt_device *dev, const struct lu_fid *fid) @@ -932,6 +956,32 @@ static inline struct dt_object *lfsck_object_find(const struct lu_env *env, return lfsck_object_find_by_dev(env, lfsck->li_next, fid); } +static inline struct dt_object * +lfsck_object_find_bottom(const struct lu_env *env, struct lfsck_instance *lfsck, + const struct lu_fid *fid) +{ + struct dt_device *dev; + int idx; + + idx = lfsck_find_mdt_idx_by_fid(env, lfsck, fid); + if (idx < 0) + return ERR_PTR(idx); + + if (idx == lfsck_dev_idx(lfsck->li_bottom)) { + dev = lfsck->li_bottom; + } else { + struct lfsck_tgt_desc *ltd; + + ltd = LTD_TGT(&lfsck->li_mdt_descs, idx); + if (unlikely(ltd == NULL)) + return ERR_PTR(-ENODEV); + + dev = ltd->ltd_tgt; + } + + return lfsck_object_find_by_dev(env, dev, fid); +} + static inline struct lfsck_tgt_desc *lfsck_tgt_get(struct lfsck_tgt_descs *ltds, __u32 index) { @@ -993,11 +1043,6 @@ static inline void lfsck_instance_put(const struct lu_env *env, lfsck_instance_cleanup(env, lfsck); } -static inline u32 lfsck_dev_idx(struct dt_device *dev) -{ - return dev->dd_lu_dev.ld_site->ld_seq_site->ss_node_id; -} - static inline bool lfsck_phase2_next_ready(struct lfsck_assistant_data *lad) { return list_empty(&lad->lad_mdt_phase1_list) && diff --git a/lustre/lfsck/lfsck_layout.c b/lustre/lfsck/lfsck_layout.c index bbb2ce9..8e7fd40 100644 --- a/lustre/lfsck/lfsck_layout.c +++ b/lustre/lfsck/lfsck_layout.c @@ -4371,9 +4371,7 @@ again: lmm->lmm_oi = *oi; if (bk->lb_param & LPF_DRYRUN) { - down_write(&com->lc_sem); lo->ll_objs_repaired[LLIT_OTHERS - 1]++; - up_write(&com->lc_sem); GOTO(out, stripe = true); } @@ -4412,9 +4410,7 @@ again: if (rc != 0) GOTO(out, rc); - down_write(&com->lc_sem); lo->ll_objs_repaired[LLIT_OTHERS - 1]++; - up_write(&com->lc_sem); GOTO(out, stripe = true); diff --git a/lustre/lfsck/lfsck_lib.c b/lustre/lfsck/lfsck_lib.c index cf6ce46..3e6a1d5 100644 --- a/lustre/lfsck/lfsck_lib.c +++ b/lustre/lfsck/lfsck_lib.c @@ -439,8 +439,8 @@ int lfsck_find_mdt_idx_by_fid(const struct lu_env *env, return rc; } -static const char dot[] = "."; -static const char dotdot[] = ".."; +const char dot[] = "."; +const char dotdot[] = ".."; static const char dotlustre[] = ".lustre"; static const char lostfound[] = "lost+found"; diff --git a/lustre/lfsck/lfsck_namespace.c b/lustre/lfsck/lfsck_namespace.c index 625a26c..9b759b6 100644 --- a/lustre/lfsck/lfsck_namespace.c +++ b/lustre/lfsck/lfsck_namespace.c @@ -136,6 +136,10 @@ static void lfsck_namespace_le_to_cpu(struct lfsck_namespace *dst, dst->ln_linkea_repaired = le64_to_cpu(src->ln_linkea_repaired); dst->ln_mul_linked_checked = le64_to_cpu(src->ln_mul_linked_checked); dst->ln_mul_linked_repaired = le64_to_cpu(src->ln_mul_linked_repaired); + dst->ln_unknown_inconsistency = + le64_to_cpu(src->ln_unknown_inconsistency); + dst->ln_unmatched_pairs_repaired = + le64_to_cpu(src->ln_unmatched_pairs_repaired); } static void lfsck_namespace_cpu_to_le(struct lfsck_namespace *dst, @@ -173,6 +177,10 @@ static void lfsck_namespace_cpu_to_le(struct lfsck_namespace *dst, dst->ln_linkea_repaired = cpu_to_le64(src->ln_linkea_repaired); dst->ln_mul_linked_checked = cpu_to_le64(src->ln_mul_linked_checked); dst->ln_mul_linked_repaired = cpu_to_le64(src->ln_mul_linked_repaired); + dst->ln_unknown_inconsistency = + cpu_to_le64(src->ln_unknown_inconsistency); + dst->ln_unmatched_pairs_repaired = + cpu_to_le64(src->ln_unmatched_pairs_repaired); } static void lfsck_namespace_record_failure(const struct lu_env *env, @@ -840,6 +848,781 @@ static int lfsck_namespace_shrink_linkea_cond(const struct lu_env *env, } /** + * Overwrite the linkEA for the object with the given ldata. + * + * The caller should take the ldlm lock before the calling. + * + * \param[in] env pointer to the thread context + * \param[in] com pointer to the lfsck component + * \param[in] obj pointer to the dt_object to be handled + * \param[in] ldata pointer to the new linkEA data + * + * \retval positive number for repaired cases + * \retval 0 if nothing to be repaired + * \retval negative error number on failure + */ +int lfsck_namespace_rebuild_linkea(const struct lu_env *env, + struct lfsck_component *com, + struct dt_object *obj, + struct linkea_data *ldata) +{ + struct lfsck_instance *lfsck = com->lc_lfsck; + struct dt_device *dev = lfsck->li_bottom; + struct thandle *th = NULL; + struct lu_buf linkea_buf; + int rc = 0; + ENTRY; + + LASSERT(!dt_object_remote(obj)); + + th = dt_trans_create(env, dev); + if (IS_ERR(th)) + GOTO(log, rc = PTR_ERR(th)); + + lfsck_buf_init(&linkea_buf, ldata->ld_buf->lb_buf, + ldata->ld_leh->leh_len); + rc = dt_declare_xattr_set(env, obj, &linkea_buf, + XATTR_NAME_LINK, 0, th); + if (rc != 0) + GOTO(stop, rc); + + rc = dt_trans_start_local(env, dev, th); + if (rc != 0) + GOTO(stop, rc); + + dt_write_lock(env, obj, 0); + if (unlikely(lfsck_is_dead_obj(obj))) + GOTO(unlock, rc = 0); + + if (lfsck->li_bookmark_ram.lb_param & LPF_DRYRUN) + GOTO(unlock, rc = 1); + + rc = dt_xattr_set(env, obj, &linkea_buf, + XATTR_NAME_LINK, 0, th, BYPASS_CAPA); + + GOTO(unlock, rc = (rc == 0 ? 1 : rc)); + +unlock: + dt_write_unlock(env, obj); + +stop: + dt_trans_stop(env, dev, th); + +log: + CDEBUG(D_LFSCK, "%s: namespace LFSCK rebuild linkEA for the " + "object "DFID": rc = %d\n", + lfsck_lfsck2name(lfsck), PFID(lfsck_dto2fid(obj)), rc); + + if (rc != 0) { + struct lfsck_namespace *ns = com->lc_file_ram; + + ns->ln_flags |= LF_INCONSISTENT; + } + + return rc; +} + +/** + * Update the ".." name entry for the given object. + * + * The object's ".." is corrupted, this function will update the ".." name + * entry with the given pfid, and the linkEA with the given ldata. + * + * The caller should take the ldlm lock before the calling. + * + * \param[in] env pointer to the thread context + * \param[in] com pointer to the lfsck component + * \param[in] obj pointer to the dt_object to be handled + * \param[in] pfid the new fid for the object's ".." name entry + * \param[in] cname the name for the @obj in the parent directory + * + * \retval positive number for repaired cases + * \retval 0 if nothing to be repaired + * \retval negative error number on failure + */ +static int lfsck_namespace_repair_unmatched_pairs(const struct lu_env *env, + struct lfsck_component *com, + struct dt_object *obj, + const struct lu_fid *pfid, + struct lu_name *cname) +{ + struct lfsck_thread_info *info = lfsck_env_info(env); + struct dt_insert_rec *rec = &info->lti_dt_rec; + struct lfsck_instance *lfsck = com->lc_lfsck; + struct dt_device *dev = lfsck->li_bottom; + struct thandle *th = NULL; + struct linkea_data ldata = { 0 }; + struct lu_buf linkea_buf; + int rc = 0; + ENTRY; + + LASSERT(!dt_object_remote(obj)); + LASSERT(S_ISDIR(lfsck_object_type(obj))); + + rc = linkea_data_new(&ldata, &info->lti_big_buf); + if (rc != 0) + GOTO(log, rc); + + rc = linkea_add_buf(&ldata, cname, pfid); + if (rc != 0) + GOTO(log, rc); + + lfsck_buf_init(&linkea_buf, ldata.ld_buf->lb_buf, + ldata.ld_leh->leh_len); + + th = dt_trans_create(env, dev); + if (IS_ERR(th)) + GOTO(log, rc = PTR_ERR(th)); + + rc = dt_declare_delete(env, obj, (const struct dt_key *)dotdot, th); + if (rc != 0) + GOTO(stop, rc); + + rec->rec_type = S_IFDIR; + rec->rec_fid = pfid; + rc = dt_declare_insert(env, obj, (const struct dt_rec *)rec, + (const struct dt_key *)dotdot, th); + if (rc != 0) + GOTO(stop, rc); + + rc = dt_declare_xattr_set(env, obj, &linkea_buf, + XATTR_NAME_LINK, 0, th); + if (rc != 0) + GOTO(stop, rc); + + rc = dt_trans_start_local(env, dev, th); + if (rc != 0) + GOTO(stop, rc); + + dt_write_lock(env, obj, 0); + if (unlikely(lfsck_is_dead_obj(obj))) + GOTO(unlock, rc = 0); + + if (lfsck->li_bookmark_ram.lb_param & LPF_DRYRUN) + GOTO(unlock, rc = 1); + + /* The old ".." name entry maybe not exist. */ + dt_delete(env, obj, (const struct dt_key *)dotdot, th, + BYPASS_CAPA); + + rc = dt_insert(env, obj, (const struct dt_rec *)rec, + (const struct dt_key *)dotdot, th, BYPASS_CAPA, 1); + if (rc != 0) + GOTO(unlock, rc); + + rc = dt_xattr_set(env, obj, &linkea_buf, + XATTR_NAME_LINK, 0, th, BYPASS_CAPA); + + GOTO(unlock, rc = (rc == 0 ? 1 : rc)); + +unlock: + dt_write_unlock(env, obj); + +stop: + dt_trans_stop(env, dev, th); + +log: + CDEBUG(D_LFSCK, "%s: namespace LFSCK rebuild dotdot name entry for " + "the object "DFID", new parent "DFID": rc = %d\n", + lfsck_lfsck2name(lfsck), PFID(lfsck_dto2fid(obj)), + PFID(pfid), rc); + + if (rc != 0) { + struct lfsck_namespace *ns = com->lc_file_ram; + + ns->ln_flags |= LF_INCONSISTENT; + } + + return rc; +} + +/** + * Handle orphan @obj during Double Scan Directory. + * + * Remove the @obj's current (invalid) linkEA entries, and insert + * it in the directory .lustre/lost+found/MDTxxxx/ with the name: + * ${FID}-${PFID}-D-${conflict_version} + * + * The caller should take the ldlm lock before the calling. + * + * \param[in] env pointer to the thread context + * \param[in] com pointer to the lfsck component + * \param[in] obj pointer to the orphan object to be handled + * \param[in] pfid the new fid for the object's ".." name entry + * \param[in,out] lh ldlm lock handler for the given @obj + * + * \retval positive number for repaired cases + * \retval 0 if nothing to be repaired + * \retval negative error number on failure + */ +static int lfsck_namespace_dsd_orphan(const struct lu_env *env, + struct lfsck_component *com, + struct dt_object *obj, + const struct lu_fid *pfid, + struct lustre_handle *lh) +{ + struct lfsck_thread_info *info = lfsck_env_info(env); + int rc; + ENTRY; + + /* Remove the unrecognized linkEA. */ + rc = lfsck_namespace_links_remove(env, com, obj); + lfsck_ibits_unlock(lh, LCK_EX); + if (rc < 0 && rc != -ENODATA) + RETURN(rc); + + /* The unique linkEA is invalid, even if the ".." name entry may be + * valid, we still cannot know via which name entry this directory + * will be referenced. Then handle it as pure orphan. */ + snprintf(info->lti_tmpbuf, sizeof(info->lti_tmpbuf), + "-"DFID, PFID(pfid)); + rc = lfsck_namespace_insert_orphan(env, com, obj, + info->lti_tmpbuf, "D", NULL); + + RETURN(rc); +} + +/** + * Double Scan Directory object for single linkEA entry case. + * + * The given @child has unique linkEA entry. If the linkEA entry is valid, + * then check whether the name is in the namespace or not, if not, add the + * missing name entry back to namespace. If the linkEA entry is invalid, + * then remove it and insert the @child in the .lustre/lost+found/MDTxxxx/ + * as an orphan. + * + * \param[in] env pointer to the thread context + * \param[in] com pointer to the lfsck component + * \param[in] child pointer to the directory to be double scanned + * \param[in] pfid the FID corresponding to the ".." entry + * \param[in] ldata pointer to the linkEA data for the given @child + * \param[in,out] lh ldlm lock handler for the given @child + * \param[out] type to tell the caller what the inconsistency is + * \param[in] retry if found inconsistency, but the caller does not hold + * ldlm lock on the @child, then set @retry as true + * + * \retval positive number for repaired cases + * \retval 0 if nothing to be repaired + * \retval negative error number on failure + */ +static int +lfsck_namespace_dsd_single(const struct lu_env *env, + struct lfsck_component *com, + struct dt_object *child, + const struct lu_fid *pfid, + struct linkea_data *ldata, + struct lustre_handle *lh, + enum lfsck_namespace_inconsistency_type *type, + bool *retry) +{ + struct lfsck_thread_info *info = lfsck_env_info(env); + struct lu_name *cname = &info->lti_name; + const struct lu_fid *cfid = lfsck_dto2fid(child); + struct lu_fid *tfid = &info->lti_fid3; + struct lfsck_instance *lfsck = com->lc_lfsck; + struct dt_object *parent = NULL; + int rc = 0; + ENTRY; + + lfsck_namespace_unpack_linkea_entry(ldata, cname, tfid, info->lti_key); + /* The unique linkEA entry with bad parent will be handled as orphan. */ + if (!fid_is_sane(tfid)) { + if (!lustre_handle_is_used(lh) && retry != NULL) + *retry = true; + else + rc = lfsck_namespace_dsd_orphan(env, com, child, + pfid, lh); + + GOTO(out, rc); + } + + parent = lfsck_object_find_bottom(env, lfsck, tfid); + if (IS_ERR(parent)) + GOTO(out, rc = PTR_ERR(parent)); + + /* We trust the unique linkEA entry in spite of whether it matches the + * ".." name entry or not. Because even if the linkEA entry is wrong + * and the ".." name entry is right, we still cannot know via which + * name entry the child will be referenced, since all known entries + * have been verified during the first-stage scanning. */ + if (!dt_object_exists(parent)) { + if (!lustre_handle_is_used(lh) && retry != NULL) { + *retry = true; + + GOTO(out, rc = 0); + } + + lfsck_ibits_unlock(lh, LCK_EX); + /* Create the lost parent as an orphan. */ + rc = lfsck_namespace_create_orphan(env, com, parent); + if (rc >= 0) + /* Add the missing name entry to the parent. */ + rc = lfsck_namespace_insert_normal(env, com, parent, + child, cname->ln_name); + + GOTO(out, rc); + } + + /* The unique linkEA entry with bad parent will be handled as orphan. */ + if (unlikely(!dt_try_as_dir(env, parent))) { + if (!lustre_handle_is_used(lh) && retry != NULL) + *retry = true; + else + rc = lfsck_namespace_dsd_orphan(env, com, child, + pfid, lh); + + GOTO(out, rc); + } + + rc = dt_lookup(env, parent, (struct dt_rec *)tfid, + (const struct dt_key *)cname->ln_name, BYPASS_CAPA); + if (rc == -ENOENT) { + if (!lustre_handle_is_used(lh) && retry != NULL) { + *retry = true; + + GOTO(out, rc = 0); + } + + lfsck_ibits_unlock(lh, LCK_EX); + /* Add the missing name entry back to the namespace. */ + rc = lfsck_namespace_insert_normal(env, com, parent, child, + cname->ln_name); + + GOTO(out, rc); + } + + if (rc != 0) + GOTO(out, rc); + + /* XXX: The name entry references another MDT-object that may be + * created by the LFSCK for repairing dangling name entry. + * There will be another patch for further processing. */ + if (!lu_fid_eq(tfid, cfid)) { + if (!lustre_handle_is_used(lh) && retry != NULL) + *retry = true; + else + rc = lfsck_namespace_dsd_orphan(env, com, child, + pfid, lh); + + GOTO(out, rc); + } + + /* The ".." name entry is wrong, update it. */ + if (!lu_fid_eq(pfid, lfsck_dto2fid(parent))) { + if (!lustre_handle_is_used(lh) && retry != NULL) { + *retry = true; + + GOTO(out, rc = 0); + } + + *type = LNIT_UNMATCHED_PAIRS; + rc = lfsck_namespace_repair_unmatched_pairs(env, com, child, + lfsck_dto2fid(parent), cname); + } + + GOTO(out, rc); + +out: + if (parent != NULL && !IS_ERR(parent)) + lfsck_object_put(env, parent); + + return rc; +} + +/** + * Double Scan Directory object for single linkEA entry case. + * + * The given @child has multiple linkEA entries. There is at most one linkEA + * entry will be valid, all the others will be removed. Firstly, the function + * will try to find out the linkEA entry for which the name entry exists under + * the given parent (@pfid). If there is no linkEA entry that matches the given + * ".." name entry, then tries to find out the first linkEA entry that both the + * parent and the name entry exist to rebuild a new ".." name entry. + * + * \param[in] env pointer to the thread context + * \param[in] com pointer to the lfsck component + * \param[in] child pointer to the directory to be double scanned + * \param[in] pfid the FID corresponding to the ".." entry + * \param[in] ldata pointer to the linkEA data for the given @child + * \param[in,out] lh ldlm lock handler for the given @child + * \param[out] type to tell the caller what the inconsistency is + * \param[in] lpf true if the ".." entry is under lost+found/MDTxxxx/ + * + * \retval positive number for repaired cases + * \retval 0 if nothing to be repaired + * \retval negative error number on failure + */ +static int +lfsck_namespace_dsd_multiple(const struct lu_env *env, + struct lfsck_component *com, + struct dt_object *child, + const struct lu_fid *pfid, + struct linkea_data *ldata, + struct lustre_handle *lh, + enum lfsck_namespace_inconsistency_type *type, + bool lpf) +{ + struct lfsck_thread_info *info = lfsck_env_info(env); + struct lu_name *cname = &info->lti_name; + const struct lu_fid *cfid = lfsck_dto2fid(child); + struct lu_fid *tfid = &info->lti_fid3; + struct lu_fid *pfid2 = &info->lti_fid4; + struct lfsck_instance *lfsck = com->lc_lfsck; + struct dt_object *parent = NULL; + struct linkea_data ldata_new = { 0 }; + int rc = 0; + bool once = true; + ENTRY; + +again: + while (ldata->ld_lee != NULL) { + lfsck_namespace_unpack_linkea_entry(ldata, cname, tfid, + info->lti_key); + /* Drop repeated linkEA entries. */ + lfsck_namespace_filter_linkea_entry(ldata, cname, tfid, true); + /* Drop invalid linkEA entry. */ + if (!fid_is_sane(tfid)) { + linkea_del_buf(ldata, cname); + continue; + } + + /* If current dotdot is the .lustre/lost+found/MDTxxxx/, + * then it is possible that: the directry object has ever + * been lost, but its name entry was there. In the former + * LFSCK run, during the first-stage scanning, the LFSCK + * found the dangling name entry, but it did not recreate + * the lost object, and when moved to the second-stage + * scanning, some children objects of the lost directory + * object were found, then the LFSCK recreated such lost + * directory object as an orphan. + * + * When the LFSCK runs again, if the dangling name is still + * there, the LFSCK should move the orphan directory object + * back to the normal namespace. */ + if (!lpf && !lu_fid_eq(pfid, tfid) && once) { + linkea_next_entry(ldata); + continue; + } + + parent = lfsck_object_find_bottom(env, lfsck, tfid); + if (IS_ERR(parent)) + RETURN(PTR_ERR(parent)); + + if (!dt_object_exists(parent)) { + lfsck_object_put(env, parent); + if (ldata->ld_leh->leh_reccount > 1) { + /* If it is NOT the last linkEA entry, then + * there is still other chance to make the + * child to be visible via other parent, then + * remove this linkEA entry. */ + linkea_del_buf(ldata, cname); + continue; + } + + break; + } + + /* The linkEA entry with bad parent will be removed. */ + if (unlikely(!dt_try_as_dir(env, parent))) { + lfsck_object_put(env, parent); + linkea_del_buf(ldata, cname); + continue; + } + + rc = dt_lookup(env, parent, (struct dt_rec *)tfid, + (const struct dt_key *)cname->ln_name, + BYPASS_CAPA); + *pfid2 = *lfsck_dto2fid(parent); + lfsck_object_put(env, parent); + if (rc == -ENOENT) { + linkea_next_entry(ldata); + continue; + } + + if (rc != 0) + RETURN(rc); + + if (lu_fid_eq(tfid, cfid)) { + if (!lu_fid_eq(pfid, pfid2)) { + *type = LNIT_UNMATCHED_PAIRS; + rc = lfsck_namespace_repair_unmatched_pairs(env, + com, child, pfid2, cname); + + RETURN(rc); + } + + /* It is the most common case that we find the + * name entry corresponding to the linkEA entry + * that matches the ".." name entry. */ + rc = linkea_data_new(&ldata_new, &info->lti_big_buf); + if (rc != 0) + RETURN(rc); + + rc = linkea_add_buf(&ldata_new, cname, pfid2); + if (rc != 0) + RETURN(rc); + + rc = lfsck_namespace_rebuild_linkea(env, com, child, + &ldata_new); + + /* XXX: there will be other patch. */ + + RETURN(rc); + } + + /* XXX: The name entry references another MDT-object that + * may be created by the LFSCK for repairing dangling + * name entry. There will be another patch for further + * processing. */ + linkea_del_buf(ldata, cname); + } + + if (ldata->ld_leh->leh_reccount == 1) { + rc = lfsck_namespace_dsd_single(env, com, child, pfid, ldata, + lh, type, NULL); + + RETURN(rc); + } + + /* All linkEA entries are invalid and removed, then handle the @child + * as an orphan.*/ + if (ldata->ld_leh->leh_reccount == 0) { + rc = lfsck_namespace_dsd_orphan(env, com, child, pfid, lh); + + RETURN(rc); + } + + linkea_first_entry(ldata); + /* If the dangling name entry for the orphan directory object has + * been remvoed, then just check whether the directory object is + * still under the .lustre/lost+found/MDTxxxx/ or not. */ + if (lpf) { + lpf = false; + goto again; + } + + /* There is no linkEA entry that matches the ".." name entry. Find + * the first linkEA entry that both parent and name entry exist to + * rebuild a new ".." name entry. */ + if (once) { + once = false; + goto again; + } + + RETURN(rc); +} + +/** + * Double scan the directory object for namespace LFSCK. + * + * This function will verify the pairs in the namespace tree: + * the parent references the child via some name entry that should be in the + * child's linkEA entry, the child should back references the parent via its + * ".." name entry. + * + * The LFSCK will scan every linkEA entry in turn until find out the first + * matched pairs. If found, then all other linkEA entries will be dropped. + * If all the linkEA entries cannot match the ".." name entry, then there + * are serveral possible cases: + * + * 1) If there is only one linkEA entry, then trust it as long as the PFID + * in the linkEA entry is valid. + * + * 2) If there are multiple linkEA entries, then try to find the linkEA + * that matches the ".." name entry. If found, then all other entries + * are invalid; otherwise, it is quite possible that the ".." name entry + * is corrupted. Under such case, the LFSCK will rebuild the ".." name + * entry according to the first valid linkEA entry (both the parent and + * the name entry should exist). + * + * 3) If the directory object has no (valid) linkEA entry, then the + * directory object will be handled as pure orphan and inserted + * in the .lustre/lost+found/MDTxxxx/ with the name: + * ${self_FID}-${PFID}-D-${conflict_version} + * + * \param[in] env pointer to the thread context + * \param[in] com pointer to the lfsck component + * \param[in] child pointer to the directory object to be handled + * \param[in] flags to indicate the specical checking on the @child + * + * \retval positive number for repaired cases + * \retval 0 if nothing to be repaired + * \retval negative error number on failure + */ +static int lfsck_namespace_double_scan_dir(const struct lu_env *env, + struct lfsck_component *com, + struct dt_object *child, __u8 flags) +{ + struct lfsck_thread_info *info = lfsck_env_info(env); + const struct lu_fid *cfid = lfsck_dto2fid(child); + struct lu_fid *pfid = &info->lti_fid2; + struct lfsck_namespace *ns = com->lc_file_ram; + struct lfsck_instance *lfsck = com->lc_lfsck; + struct lustre_handle lh = { 0 }; + struct linkea_data ldata = { 0 }; + bool unknown = false; + bool lpf = false; + bool retry = false; + enum lfsck_namespace_inconsistency_type type = LNIT_BAD_LINKEA; + int rc = 0; + ENTRY; + + LASSERT(!dt_object_remote(child)); + + if (!(lfsck->li_bookmark_ram.lb_param & LPF_ALL_TGT)) { + CDEBUG(D_LFSCK, "%s: some MDT(s) maybe NOT take part in the" + "the namespace LFSCK, then the LFSCK cannot guarantee" + "all the name entries have been verified in first-stage" + "scanning. So have to skip orphan related handling for" + "the directory object "DFID" with remote name entry\n", + lfsck_lfsck2name(lfsck), PFID(cfid)); + + RETURN(0); + } + + if (unlikely(!dt_try_as_dir(env, child))) + GOTO(out, rc = -ENOTDIR); + + /* We only take ldlm lock on the @child when required. When the + * logic comes here for the first time, it is always false. */ + if (0) { + +lock: + rc = lfsck_ibits_lock(env, lfsck, child, &lh, + MDS_INODELOCK_UPDATE | + MDS_INODELOCK_XATTR, LCK_EX); + if (rc != 0) + GOTO(out, rc); + } + + dt_read_lock(env, child, 0); + if (unlikely(lfsck_is_dead_obj(child))) { + dt_read_unlock(env, child); + + GOTO(out, rc = 0); + } + + rc = dt_lookup(env, child, (struct dt_rec *)pfid, + (const struct dt_key *)dotdot, BYPASS_CAPA); + if (rc != 0) { + if (rc != -ENOENT && rc != -ENODATA && rc != -EINVAL) { + dt_read_unlock(env, child); + + GOTO(out, rc); + } + + if (!lustre_handle_is_used(&lh)) { + dt_read_unlock(env, child); + goto lock; + } + + fid_zero(pfid); + } else if (lfsck->li_lpf_obj != NULL && + lu_fid_eq(pfid, lfsck_dto2fid(lfsck->li_lpf_obj))) { + lpf = true; + } + + rc = lfsck_links_read(env, child, &ldata); + dt_read_unlock(env, child); + if (rc != 0) { + if (rc != -ENODATA && rc != -EINVAL) + GOTO(out, rc); + + if (!lustre_handle_is_used(&lh)) + goto lock; + + if (rc == -EINVAL && !fid_is_zero(pfid)) { + /* Remove the corrupted linkEA. */ + rc = lfsck_namespace_links_remove(env, com, child); + if (rc == 0) + /* Here, because of the crashed linkEA, we + * cannot know whether there is some parent + * that references the child directory via + * some name entry or not. So keep it there, + * when the LFSCK run next time, if there is + * some parent that references this object, + * then the LFSCK can rebuild the linkEA; + * otherwise, this object will be handled + * as orphan as above. */ + unknown = true; + } else { + /* 1. If we have neither ".." nor linkEA, + * then it is an orphan. + * + * 2. If we only have the ".." name entry, + * but no parent references this child + * directory, then handle it as orphan. */ + lfsck_ibits_unlock(&lh, LCK_EX); + snprintf(info->lti_tmpbuf, sizeof(info->lti_tmpbuf), + "-"DFID, PFID(pfid)); + rc = lfsck_namespace_insert_orphan(env, com, child, + info->lti_tmpbuf, "D", NULL); + } + + GOTO(out, rc); + } + + linkea_first_entry(&ldata); + /* This is the most common case: the object has unique linkEA entry. */ + if (ldata.ld_leh->leh_reccount == 1) { + rc = lfsck_namespace_dsd_single(env, com, child, pfid, &ldata, + &lh, &type, &retry); + if (retry) { + LASSERT(!lustre_handle_is_used(&lh)); + + retry = false; + goto lock; + } + + GOTO(out, rc); + } + + if (!lustre_handle_is_used(&lh)) + goto lock; + + if (unlikely(ldata.ld_leh->leh_reccount == 0)) { + rc = lfsck_namespace_dsd_orphan(env, com, child, pfid, &lh); + + GOTO(out, rc); + } + + /* When we come here, the cases usually like that: + * 1) The directory object has a corrupted linkEA entry. During the + * first-stage scanning, the LFSCK cannot know such corruption, + * then it appends the right linkEA entry according to the found + * name entry after the bad one. + * + * 2) The directory object has a right linkEA entry. During the + * first-stage scanning, the LFSCK finds some bad name entry, + * but the LFSCK cannot aware that at that time, then it adds + * the bad linkEA entry for further processing. */ + rc = lfsck_namespace_dsd_multiple(env, com, child, pfid, &ldata, + &lh, &type, lpf); + + GOTO(out, rc); + +out: + lfsck_ibits_unlock(&lh, LCK_EX); + if (rc > 0) { + switch (type) { + case LNIT_BAD_LINKEA: + ns->ln_linkea_repaired++; + break; + case LNIT_UNMATCHED_PAIRS: + ns->ln_unmatched_pairs_repaired++; + break; + default: + break; + } + } + + if (unknown) + ns->ln_unknown_inconsistency++; + + return rc; +} + +/** * Double scan the MDT-object for namespace LFSCK. * * If the MDT-object contains invalid or repeated linkEA entries, then drop @@ -883,6 +1666,13 @@ static int lfsck_namespace_double_scan_one(const struct lu_env *env, RETURN(0); } + if (S_ISDIR(lfsck_object_type(child))) { + dt_read_unlock(env, child); + rc = lfsck_namespace_double_scan_dir(env, com, child, flags); + + RETURN(rc); + } + rc = lfsck_links_read(env, child, &ldata); dt_read_unlock(env, child); if (rc != 0) @@ -922,7 +1712,7 @@ static int lfsck_namespace_double_scan_one(const struct lu_env *env, continue; } - parent = lfsck_object_find(env, lfsck, pfid); + parent = lfsck_object_find_bottom(env, lfsck, pfid); if (IS_ERR(parent)) GOTO(out, rc = PTR_ERR(parent)); @@ -947,7 +1737,7 @@ static int lfsck_namespace_double_scan_one(const struct lu_env *env, if (rc > 0) repaired = true; - /* Add the missed name entry to the parent. */ + /* Add the missing name entry to the parent. */ rc = lfsck_namespace_insert_normal(env, com, parent, child, cname->ln_name); linkea_next_entry(&ldata); @@ -1031,7 +1821,7 @@ static int lfsck_namespace_double_scan_one(const struct lu_env *env, continue; } - /* Add the missed name entry back to the namespace. */ + /* Add the missing name entry back to the namespace. */ rc = lfsck_namespace_insert_normal(env, com, parent, child, cname->ln_name); lfsck_object_put(env, parent); @@ -1078,11 +1868,8 @@ out: } if (repaired) { - if (la->la_nlink > 1) { - down_write(&com->lc_sem); + if (la->la_nlink > 1) ns->ln_mul_linked_repaired++; - up_write(&com->lc_sem); - } if (rc == 0) rc = 1; @@ -1111,6 +1898,8 @@ static void lfsck_namespace_dump_statistics(struct seq_file *m, "lost_found: "LPU64"\n" "multiple_linked_checked: "LPU64"\n" "multiple_linked_repaired: "LPU64"\n" + "unknown_inconsistency: "LPU64"\n" + "unmatched_pairs_repaired: "LPU64"\n" "success_count: %u\n" "run_time_phase1: %u seconds\n" "run_time_phase2: %u seconds\n", @@ -1127,6 +1916,8 @@ static void lfsck_namespace_dump_statistics(struct seq_file *m, ns->ln_objs_lost_found, ns->ln_mul_linked_checked, ns->ln_mul_linked_repaired, + ns->ln_unknown_inconsistency, + ns->ln_unmatched_pairs_repaired, ns->ln_success_count, time_phase1, time_phase2); @@ -1302,6 +2093,8 @@ static int lfsck_namespace_prep(const struct lu_env *env, ns->ln_linkea_repaired = 0; ns->ln_mul_linked_checked = 0; ns->ln_mul_linked_repaired = 0; + ns->ln_unknown_inconsistency = 0; + ns->ln_unmatched_pairs_repaired = 0; fid_zero(&ns->ln_fid_latest_scanned_phase2); if (list_empty(&com->lc_link_dir)) list_add_tail(&com->lc_link_dir, @@ -1897,10 +2690,18 @@ static int lfsck_namespace_assistant_handler_p1(const struct lu_env *env, repaired = true; } + if (unlikely(fid_is_zero(&lnr->lnr_fid))) { + if (strcmp(lnr->lnr_name, dotdot) != 0) + LBUG(); + else + rc = lfsck_namespace_trace_update(env, com, pfid, + LNTF_CHECK_PARENT, true); + + GOTO(out, rc); + } + if (lnr->lnr_name[0] == '.' && - (lnr->lnr_namelen == 1 || - (lnr->lnr_namelen == 2 && lnr->lnr_name[1] == '.') || - fid_seq_is_dot(fid_seq(&lnr->lnr_fid)))) + (lnr->lnr_namelen == 1 || fid_seq_is_dot(fid_seq(&lnr->lnr_fid)))) GOTO(out, rc = 0); idx = lfsck_find_mdt_idx_by_fid(env, lfsck, &lnr->lnr_fid); @@ -1908,10 +2709,23 @@ static int lfsck_namespace_assistant_handler_p1(const struct lu_env *env, GOTO(out, rc = idx); if (idx == lfsck_dev_idx(lfsck->li_bottom)) { + if (unlikely(strcmp(lnr->lnr_name, dotdot) == 0)) + GOTO(out, rc = 0); + dev = lfsck->li_next; } else { struct lfsck_tgt_desc *ltd; + /* Usually, some local filesystem consistency verification + * tools can guarantee the local namespace tree consistenct. + * So the LFSCK will only verify the remote directory. */ + if (unlikely(strcmp(lnr->lnr_name, dotdot) == 0)) { + rc = lfsck_namespace_trace_update(env, com, pfid, + LNTF_CHECK_PARENT, true); + + GOTO(out, rc); + } + ltd = LTD_TGT(&lfsck->li_mdt_descs, idx); if (unlikely(ltd == NULL)) { CDEBUG(D_LFSCK, "%s: cannot talk with MDT %x which " @@ -1977,16 +2791,17 @@ again: goto record; ns->ln_flags |= LF_INCONSISTENT; - /* For dir, if there are more than one linkea entries, or the - * linkea entry does not match the name entry, then remove all - * and add the correct one. */ - if (S_ISDIR(lfsck_object_type(obj))) { - remove = true; - newdata = true; - } else { - remove = false; - newdata = false; - } + /* For sub-dir object, we cannot make sure whether the sub-dir + * back references the parent via ".." name entry correctly or + * not in the LFSCK first-stage scanning. It may be that the + * (remote) sub-dir ".." name entry has no parent FID after + * file-level backup/restore and its linkEA may be wrong. + * So under such case, we should replace the linkEA according + * to current name entry. But this needs to be done during the + * LFSCK second-stage scanning. The LFSCK will record the name + * entry for further possible using. */ + remove = false; + newdata = false; goto nodata; } else if (unlikely(rc == -EINVAL)) { count = 1; @@ -2004,9 +2819,7 @@ again: nodata: if (bk->lb_param & LPF_DRYRUN) { - down_write(&com->lc_sem); ns->ln_linkea_repaired++; - up_write(&com->lc_sem); repaired = true; log = true; goto record; @@ -2040,11 +2853,12 @@ nodata: GOTO(stop, rc); count = ldata.ld_leh->leh_reccount; - down_write(&com->lc_sem); - ns->ln_linkea_repaired++; - up_write(&com->lc_sem); - repaired = true; - log = true; + if (!S_ISDIR(lfsck_object_type(obj)) || + !dt_object_remote(obj)) { + ns->ln_linkea_repaired++; + repaired = true; + log = true; + } } else if (rc == -ENOENT) { log = false; repaired = false; @@ -2061,8 +2875,8 @@ record: if (rc != 0) GOTO(stop, rc); - if ((count == 1) && - (la->la_nlink == 1 || S_ISDIR(lfsck_object_type(obj)))) + if ((count == 1 && la->la_nlink == 1) || + S_ISDIR(lfsck_object_type(obj))) /* Usually, it is for single linked object or dir, do nothing.*/ GOTO(stop, rc); @@ -2079,9 +2893,7 @@ record: lfsck_ibits_unlock(&lh, LCK_EX); } - down_write(&com->lc_sem); ns->ln_mul_linked_checked++; - up_write(&com->lc_sem); rc = lfsck_namespace_trace_update(env, com, &lnr->lnr_fid, LNTF_CHECK_LINKEA, true); diff --git a/lustre/mdd/mdd_dir.c b/lustre/mdd/mdd_dir.c index 1577c54..acb4775 100644 --- a/lustre/mdd/mdd_dir.c +++ b/lustre/mdd/mdd_dir.c @@ -2346,9 +2346,17 @@ static int mdd_create(const struct lu_env *env, struct md_object *pobj, mdd_object_make_hint(env, mdd_pobj, son, attr, spec, hint); memset(ldata, 0, sizeof(*ldata)); - rc = mdd_linkea_prepare(env, son, NULL, NULL, - mdd_object_fid(mdd_pobj), - lname, 1, 0, ldata); + if (OBD_FAIL_CHECK(OBD_FAIL_LFSCK_BAD_PARENT2)) { + struct lu_fid tfid = *mdd_object_fid(mdd_pobj); + + tfid.f_oid--; + rc = mdd_linkea_prepare(env, son, NULL, NULL, + &tfid, lname, 1, 0, ldata); + } else { + rc = mdd_linkea_prepare(env, son, NULL, NULL, + mdd_object_fid(mdd_pobj), + lname, 1, 0, ldata); + } rc = mdd_declare_create(env, mdd, mdd_pobj, son, lname, attr, handle, spec, ldata, &def_acl_buf, &acl_buf, diff --git a/lustre/osd-ldiskfs/osd_handler.c b/lustre/osd-ldiskfs/osd_handler.c index b542546..7ad3f8c 100644 --- a/lustre/osd-ldiskfs/osd_handler.c +++ b/lustre/osd-ldiskfs/osd_handler.c @@ -3994,9 +3994,20 @@ static int osd_add_dot_dotdot(struct osd_thread_info *info, dot_dot_fid, NULL, th); } - result = osd_add_dot_dotdot_internal(info, dir->oo_inode, - parent_dir, dot_fid, - dot_dot_fid, oth); + if (OBD_FAIL_CHECK(OBD_FAIL_LFSCK_BAD_PARENT) || + OBD_FAIL_CHECK(OBD_FAIL_LFSCK_BAD_PARENT2)) { + struct lu_fid tfid = *dot_dot_fid; + + tfid.f_oid--; + result = osd_add_dot_dotdot_internal(info, + dir->oo_inode, parent_dir, dot_fid, + &tfid, oth); + } else { + result = osd_add_dot_dotdot_internal(info, + dir->oo_inode, parent_dir, dot_fid, + dot_dot_fid, oth); + } + if (result == 0) dir->oo_compat_dotdot_created = 1; } diff --git a/lustre/tests/sanity-lfsck.sh b/lustre/tests/sanity-lfsck.sh index 4bb9e36..0456e17 100644 --- a/lustre/tests/sanity-lfsck.sh +++ b/lustre/tests/sanity-lfsck.sh @@ -44,7 +44,7 @@ setupall ALWAYS_EXCEPT="$ALWAYS_EXCEPT 11 12 13 14 15 16 17 18 19 20 21" [[ $(lustre_version_code $SINGLEMDS) -lt $(version_code 2.6.50) ]] && - ALWAYS_EXCEPT="$ALWAYS_EXCEPT 2d 3" + ALWAYS_EXCEPT="$ALWAYS_EXCEPT 2d 2e 3 22" build_test_filter @@ -386,6 +386,40 @@ test_2d() } run_test 2d "LFSCK can recover the missed linkEA entry" +test_2e() +{ + [ $MDSCOUNT -lt 2 ] && + skip "We need at least 2 MDSes for this test" && exit 0 + + check_mount_and_prep + + $LFS mkdir -i 1 $DIR/$tdir/d0 || error "(1) Fail to mkdir d0 on MDT1" + + #define OBD_FAIL_LFSCK_LINKEA_CRASH 0x1603 + do_facet $SINGLEMDS $LCTL set_param fail_loc=0x1603 + $LFS mkdir -i 0 $DIR/$tdir/d0/d1 || error "(2) Fail to mkdir d1 on MDT0" + do_facet $SINGLEMDS $LCTL set_param fail_loc=0 + + $START_NAMESPACE -r -A || error "(3) Fail to start LFSCK for namespace!" + wait_update_facet $SINGLEMDS "$LCTL get_param -n \ + mdd.${MDT_DEV}.lfsck_namespace | + awk '/^status/ { print \\\$2 }'" "completed" 32 || { + $SHOW_NAMESPACE + error "(4) unexpected status" + } + + local repaired=$($SHOW_NAMESPACE | + awk '/^linkea_repaired/ { print $2 }') + [ $repaired -eq 1 ] || + error "(5) Fail to repair crashed linkEA: $repaired" + + local fid=$($LFS path2fid $DIR/$tdir/d0/d1) + local name=$($LFS fid2path $DIR $fid) + [ "$name" == "$DIR/$tdir/d0/d1" ] || + error "(6) Fail to repair linkEA: $fid $name" +} +run_test 2e "namespace LFSCK can verify remote object linkEA" + test_3() { lfsck_prep 4 4 @@ -2702,6 +2736,109 @@ test_21() { } run_test 21 "run all LFSCK components by default" +test_22a() { + [ $MDSCOUNT -lt 2 ] && + skip "We need at least 2 MDSes for this test" && exit 0 + + echo "#####" + echo "The parent_A references the child directory via some name entry," + echo "but the child directory back references another parent_B via its" + echo "".." name entry. The parent_A does not exist. Then the namesapce" + echo "LFSCK will repair the child directory's ".." name entry." + echo "#####" + + check_mount_and_prep + + $LFS mkdir -i 1 $DIR/$tdir/guard || error "(1) Fail to mkdir on MDT1" + $LFS mkdir -i 1 $DIR/$tdir/foo || error "(2) Fail to mkdir on MDT1" + + echo "Inject failure stub on MDT0 to simulate bad dotdot name entry" + echo "The dummy's dotdot name entry references the guard." + #define OBD_FAIL_LFSCK_BAD_PARENT 0x161e + do_facet $SINGLEMDS $LCTL set_param fail_loc=0x161e + $LFS mkdir -i 0 $DIR/$tdir/foo/dummy || + error "(3) Fail to mkdir on MDT0" + do_facet $SINGLEMDS $LCTL set_param fail_loc=0 + + rmdir $DIR/$tdir/guard || error "(4) Fail to rmdir $DIR/$tdir/guard" + + echo "Trigger namespace LFSCK to repair unmatched pairs" + $START_NAMESPACE -A -r || + error "(5) Fail to start LFSCK for namespace" + + wait_update_facet $SINGLEMDS "$LCTL get_param -n \ + mdd.${MDT_DEV}.lfsck_namespace | + awk '/^status/ { print \\\$2 }'" "completed" 32 || { + $SHOW_NAMESPACE + error "(6) unexpected status" + } + + local repaired=$($SHOW_NAMESPACE | + awk '/^unmatched_pairs_repaired/ { print $2 }') + [ $repaired -eq 1 ] || + error "(7) Fail to repair unmatched pairs: $repaired" + + echo "'ls' should success after namespace LFSCK repairing" + ls -ail $DIR/$tdir/foo/dummy > /dev/null || + error "(8) ls should success." +} +run_test 22a "LFSCK can repair unmatched pairs (1)" + +test_22b() { + [ $MDSCOUNT -lt 2 ] && + skip "We need at least 2 MDSes for this test" && exit 0 + + echo "#####" + echo "The parent_A references the child directory via the name entry_B," + echo "but the child directory back references another parent_C via its" + echo "".." name entry. The parent_C exists, but there is no the name" + echo "entry_B under the parent_B. Then the namesapce LFSCK will repair" + echo "the child directory's ".." name entry and its linkEA." + echo "#####" + + check_mount_and_prep + + $LFS mkdir -i 1 $DIR/$tdir/guard || error "(1) Fail to mkdir on MDT1" + $LFS mkdir -i 1 $DIR/$tdir/foo || error "(2) Fail to mkdir on MDT1" + + echo "Inject failure stub on MDT0 to simulate bad dotdot name entry" + echo "and bad linkEA. The dummy's dotdot name entry references the" + echo "guard. The dummy's linkEA references n non-exist name entry." + #define OBD_FAIL_LFSCK_BAD_PARENT2 0x161f + do_facet $SINGLEMDS $LCTL set_param fail_loc=0x161f + $LFS mkdir -i 0 $DIR/$tdir/foo/dummy || + error "(3) Fail to mkdir on MDT0" + do_facet $SINGLEMDS $LCTL set_param fail_loc=0 + + local dummyfid=$($LFS path2fid $DIR/$tdir/foo/dummy) + echo "fid2path should NOT work on the dummy's FID $dummyfid" + local dummyname=$($LFS fid2path $DIR $dummyfid) + [ "$dummyname" != "$DIR/$tdir/foo/dummy" ] || + error "(4) fid2path works unexpectedly." + + echo "Trigger namespace LFSCK to repair unmatched pairs" + $START_NAMESPACE -A -r || + error "(5) Fail to start LFSCK for namespace" + + wait_update_facet $SINGLEMDS "$LCTL get_param -n \ + mdd.${MDT_DEV}.lfsck_namespace | + awk '/^status/ { print \\\$2 }'" "completed" 32 || { + $SHOW_NAMESPACE + error "(6) unexpected status" + } + + local repaired=$($SHOW_NAMESPACE | + awk '/^unmatched_pairs_repaired/ { print $2 }') + [ $repaired -eq 1 ] || + error "(7) Fail to repair unmatched pairs: $repaired" + + echo "fid2path should work on the dummy's FID $dummyfid after LFSCK" + local dummyname=$($LFS fid2path $DIR $dummyfid) + [ "$dummyname" == "$DIR/$tdir/foo/dummy" ] || + error "(8) fid2path does not work" +} +run_test 22b "LFSCK can repair unmatched pairs (2)" + $LCTL set_param debug=-lfsck > /dev/null || true # restore MDS/OST size