Whamcloud - gitweb
LU-5515 lfsck: repair bad file type in name entry 48/12248/3
authorFan Yong <fan.yong@intel.com>
Sun, 17 Aug 2014 07:58:04 +0000 (15:58 +0800)
committerAndreas Dilger <andreas.dilger@intel.com>
Thu, 16 Oct 2014 17:07:11 +0000 (17:07 +0000)
If the file type stored in the name entry does not match the file
type claimed by the object, then trust the object and update the
file type in the name entry.

At the same time, if the object does not recognize the name entry
(via the linkEA), then it is quite possible that the name entry is
corrupted. Then remove the bad name entry from namespace.

Signed-off-by: Fan Yong <fan.yong@intel.com>
Change-Id: I616b482005f6379dd10bcd17f656b0ea3dd98111
Reviewed-on: http://review.whamcloud.com/12248
Tested-by: Jenkins
Tested-by: Maloo <hpdd-maloo@intel.com>
Reviewed-by: Andreas Dilger <andreas.dilger@intel.com>
lustre/include/obd_support.h
lustre/lfsck/lfsck_internal.h
lustre/lfsck/lfsck_namespace.c
lustre/osd-ldiskfs/osd_handler.c
lustre/tests/sanity-lfsck.sh

index b4098ea..3d8f047 100644 (file)
@@ -522,6 +522,7 @@ int obd_alloc_fail(const void *ptr, const char *name, const char *type,
 #define OBD_FAIL_LFSCK_DANGLING2       0x1620
 #define OBD_FAIL_LFSCK_DANGLING3       0x1621
 #define OBD_FAIL_LFSCK_MUL_REF         0x1622
+#define OBD_FAIL_LFSCK_BAD_TYPE                0x1623
 
 #define OBD_FAIL_LFSCK_NOTIFY_NET      0x16f0
 #define OBD_FAIL_LFSCK_QUERY_NET       0x16f1
index 1c3639b..12b648d 100644 (file)
@@ -120,6 +120,8 @@ enum lfsck_namespace_inconsistency_type {
        LNIT_UNMATCHED_PAIRS    = 2,
        LNIT_DANGLING           = 3,
        LNIT_MUL_REF            = 4,
+       LNIT_BAD_TYPE           = 5,
+       LNIT_BAD_DIRENT         = 6,
 };
 
 struct lfsck_namespace {
@@ -214,8 +216,11 @@ struct lfsck_namespace {
         * found/repaired. */
        __u64   ln_mul_ref_repaired;
 
+       /* How many name entries with bad file type have been repaired. */
+       __u64   ln_bad_type_repaired;
+
        /* For further using. 256-bytes aligned now. */
-       __u64   ln_reserved[27];
+       __u64   ln_reserved[26];
 };
 
 enum lfsck_layout_inconsistency_type {
@@ -771,6 +776,12 @@ int lfsck_namespace_repair_dangling(const struct lu_env *env,
                                    struct lfsck_component *com,
                                    struct dt_object *child,
                                    struct lfsck_namespace_req *lnr);
+int lfsck_namespace_repair_dirent(const struct lu_env *env,
+                                 struct lfsck_component *com,
+                                 struct dt_object *parent,
+                                 struct dt_object *child,
+                                 const char *name, const char *name2,
+                                 __u16 type, bool update, bool dec);
 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);
index 8a688c7..4b1a48e 100644 (file)
@@ -129,6 +129,7 @@ static void lfsck_namespace_le_to_cpu(struct lfsck_namespace *dst,
                                le64_to_cpu(src->ln_unmatched_pairs_repaired);
        dst->ln_dangling_repaired = le64_to_cpu(src->ln_dangling_repaired);
        dst->ln_mul_ref_repaired = le64_to_cpu(src->ln_mul_ref_repaired);
+       dst->ln_bad_type_repaired = le64_to_cpu(src->ln_bad_type_repaired);
 }
 
 static void lfsck_namespace_cpu_to_le(struct lfsck_namespace *dst,
@@ -172,6 +173,7 @@ static void lfsck_namespace_cpu_to_le(struct lfsck_namespace *dst,
                                cpu_to_le64(src->ln_unmatched_pairs_repaired);
        dst->ln_dangling_repaired = cpu_to_le64(src->ln_dangling_repaired);
        dst->ln_mul_ref_repaired = cpu_to_le64(src->ln_mul_ref_repaired);
+       dst->ln_bad_type_repaired = cpu_to_le64(src->ln_bad_type_repaired);
 }
 
 static void lfsck_namespace_record_failure(const struct lu_env *env,
@@ -1322,6 +1324,155 @@ log:
 }
 
 /**
+ * Repair invalid name entry.
+ *
+ * If the name entry contains invalid information, such as bad file type
+ * or (and) corrupted object FID, then either remove the name entry or
+ * udpate the name entry with the given (right) information.
+ *
+ * \param[in] env      pointer to the thread context
+ * \param[in] com      pointer to the lfsck component
+ * \param[in] parent   pointer to the parent directory
+ * \param[in] child    pointer to the object referenced by the name entry
+ * \param[in] name     the old name of the child under the parent directory
+ * \param[in] name2    the new name of the child under the parent directory
+ * \param[in] type     the type claimed by the name entry
+ * \param[in] update   update the name entry if true; otherwise, remove it
+ * \param[in] dec      decrease the parent nlink count if true
+ *
+ * \retval             positive number for repaired successfully
+ * \retval             0 if nothing to be repaired
+ * \retval             negative error number on failure
+ */
+int lfsck_namespace_repair_dirent(const struct lu_env *env,
+                                 struct lfsck_component *com,
+                                 struct dt_object *parent,
+                                 struct dt_object *child,
+                                 const char *name, const char *name2,
+                                 __u16 type, bool update, bool dec)
+{
+       struct lfsck_thread_info        *info   = lfsck_env_info(env);
+       struct dt_insert_rec            *rec    = &info->lti_dt_rec;
+       const struct lu_fid             *cfid   = lfsck_dto2fid(child);
+       struct lu_fid                   *tfid   = &info->lti_fid5;
+       struct lfsck_instance           *lfsck  = com->lc_lfsck;
+       struct dt_device                *dev    = lfsck->li_next;
+       struct thandle                  *th     = NULL;
+       struct lustre_handle             lh     = { 0 };
+       int                              rc     = 0;
+       ENTRY;
+
+       if (unlikely(!dt_try_as_dir(env, parent)))
+               GOTO(log, rc = -ENOTDIR);
+
+       rc = lfsck_ibits_lock(env, lfsck, parent, &lh,
+                             MDS_INODELOCK_UPDATE, LCK_EX);
+       if (rc != 0)
+               GOTO(log, rc);
+
+       th = dt_trans_create(env, dev);
+       if (IS_ERR(th))
+               GOTO(unlock1, rc = PTR_ERR(th));
+
+       rc = dt_declare_delete(env, parent, (const struct dt_key *)name, th);
+       if (rc != 0)
+               GOTO(stop, rc);
+
+       if (update) {
+               rec->rec_type = lfsck_object_type(child) & S_IFMT;
+               rec->rec_fid = cfid;
+               rc = dt_declare_insert(env, parent,
+                                      (const struct dt_rec *)rec,
+                                      (const struct dt_key *)name2, th);
+               if (rc != 0)
+                       GOTO(stop, rc);
+       }
+
+       if (dec) {
+               rc = dt_declare_ref_del(env, parent, th);
+               if (rc != 0)
+                       GOTO(stop, rc);
+       }
+
+       rc = dt_trans_start(env, dev, th);
+       if (rc != 0)
+               GOTO(stop, rc);
+
+       dt_write_lock(env, parent, 0);
+       rc = dt_lookup(env, parent, (struct dt_rec *)tfid,
+                      (const struct dt_key *)name, BYPASS_CAPA);
+       /* Someone has removed the bad name entry by race. */
+       if (rc == -ENOENT)
+               GOTO(unlock2, rc = 0);
+
+       if (rc != 0)
+               GOTO(unlock2, rc);
+
+       /* Someone has removed the bad name entry and reuse it for other
+        * object by race. */
+       if (!lu_fid_eq(tfid, cfid))
+               GOTO(unlock2, rc = 0);
+
+       if (lfsck->li_bookmark_ram.lb_param & LPF_DRYRUN)
+               GOTO(unlock2, rc = 1);
+
+       rc = dt_delete(env, parent, (const struct dt_key *)name, th,
+                      BYPASS_CAPA);
+       if (rc != 0)
+               GOTO(unlock2, rc);
+
+       if (update) {
+               rc = dt_insert(env, parent,
+                              (const struct dt_rec *)rec,
+                              (const struct dt_key *)name2, th,
+                              BYPASS_CAPA, 1);
+               if (rc != 0)
+                       GOTO(unlock2, rc);
+       }
+
+       if (dec) {
+               rc = dt_ref_del(env, parent, th);
+               if (rc != 0)
+                       GOTO(unlock2, rc);
+       }
+
+       GOTO(unlock2, rc = (rc == 0 ? 1 : rc));
+
+unlock2:
+       dt_write_unlock(env, parent);
+
+stop:
+       dt_trans_stop(env, dev, th);
+
+       /* We are not sure whether the child will become orphan or not.
+        * Record it in the LFSCK tracing file for further checking in
+        * the second-stage scanning. */
+       if (!update && !dec && rc == 0)
+               lfsck_namespace_trace_update(env, com, cfid,
+                                            LNTF_CHECK_LINKEA, true);
+
+unlock1:
+       lfsck_ibits_unlock(&lh, LCK_EX);
+
+log:
+       CDEBUG(D_LFSCK, "%s: namespace LFSCK assistant found bad name "
+              "entry for: parent "DFID", child "DFID", name %s, type "
+              "in name entry %o, type claimed by child %o. repair it "
+              "by %s with new name2 %s: rc = %d\n", lfsck_lfsck2name(lfsck),
+              PFID(lfsck_dto2fid(parent)), PFID(lfsck_dto2fid(child)),
+              name, type, update ? lfsck_object_type(child) : 0,
+              update ? "updating" : "removing", name2, 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
@@ -1677,9 +1828,12 @@ lfsck_namespace_dsd_multiple(const struct lu_env *env,
        const struct lu_fid      *cfid          = lfsck_dto2fid(child);
        struct lu_fid            *tfid          = &info->lti_fid3;
        struct lu_fid            *pfid2         = &info->lti_fid4;
+       struct lfsck_namespace   *ns            = com->lc_file_ram;
        struct lfsck_instance    *lfsck         = com->lc_lfsck;
+       struct lfsck_bookmark    *bk            = &lfsck->li_bookmark_ram;
        struct dt_object         *parent        = NULL;
        struct linkea_data        ldata_new     = { 0 };
+       int                       count         = 0;
        int                       rc            = 0;
        bool                      once          = true;
        ENTRY;
@@ -1779,8 +1933,53 @@ rebuild:
 
                        rc = lfsck_namespace_rebuild_linkea(env, com, child,
                                                            &ldata_new);
+                       if (rc < 0)
+                               RETURN(rc);
+
+                       linkea_del_buf(ldata, cname);
+                       linkea_first_entry(ldata);
+                       /* There may be some invalid dangling name entries under
+                        * other parent directories, remove all of them. */
+                       while (ldata->ld_lee != NULL) {
+                               lfsck_namespace_unpack_linkea_entry(ldata,
+                                               cname, tfid, info->lti_key);
+                               if (!fid_is_sane(tfid))
+                                       goto next;
+
+                               parent = lfsck_object_find_bottom(env, lfsck,
+                                                                 tfid);
+                               if (IS_ERR(parent)) {
+                                       rc = PTR_ERR(parent);
+                                       if (rc != -ENOENT &&
+                                           bk->lb_param & LPF_FAILOUT)
+                                               RETURN(rc);
+
+                                       goto next;
+                               }
+
+                               if (!dt_object_exists(parent)) {
+                                       lfsck_object_put(env, parent);
+                                       goto next;
+                               }
+
+                               rc = lfsck_namespace_repair_dirent(env, com,
+                                       parent, child, cname->ln_name,
+                                       cname->ln_name, S_IFDIR, false, true);
+                               lfsck_object_put(env, parent);
+                               if (rc < 0) {
+                                       if (bk->lb_param & LPF_FAILOUT)
+                                               RETURN(rc);
+
+                                       goto next;
+                               }
+
+                               count += rc;
+
+next:
+                               linkea_del_buf(ldata, cname);
+                       }
 
-                       /* XXX: there will be other patch. */
+                       ns->ln_dirent_repaired += count;
 
                        RETURN(rc);
                }
@@ -2345,6 +2544,7 @@ static void lfsck_namespace_dump_statistics(struct seq_file *m,
                      "unmatched_pairs_repaired: "LPU64"\n"
                      "dangling_repaired: "LPU64"\n"
                      "multiple_referenced_repaired: "LPU64"\n"
+                     "bad_file_type_repaired: "LPU64"\n"
                      "success_count: %u\n"
                      "run_time_phase1: %u seconds\n"
                      "run_time_phase2: %u seconds\n",
@@ -2365,6 +2565,7 @@ static void lfsck_namespace_dump_statistics(struct seq_file *m,
                      ns->ln_unmatched_pairs_repaired,
                      ns->ln_dangling_repaired,
                      ns->ln_mul_ref_repaired,
+                     ns->ln_bad_type_repaired,
                      ns->ln_success_count,
                      time_phase1,
                      time_phase2);
@@ -2544,6 +2745,7 @@ static int lfsck_namespace_prep(const struct lu_env *env,
                        ns->ln_unmatched_pairs_repaired = 0;
                        ns->ln_dangling_repaired = 0;
                        ns->ln_mul_ref_repaired = 0;
+                       ns->ln_bad_type_repaired = 0;
                        fid_zero(&ns->ln_fid_latest_scanned_phase2);
                        if (list_empty(&com->lc_link_dir))
                                list_add_tail(&com->lc_link_dir,
@@ -3454,16 +3656,34 @@ again:
                }
 
                /* It may happen when the remote object has been removed,
-                * but the local MDT does not aware of that. */
+                * but the local MDT is not aware of that. */
                goto dangling;
        } else if (rc == 0) {
                count = ldata.ld_leh->leh_reccount;
                rc = linkea_links_find(&ldata, cname, pfid);
                if ((rc == 0) &&
-                   (count == 1 || !S_ISDIR(lfsck_object_type(obj))))
+                   (count == 1 || !S_ISDIR(lfsck_object_type(obj)))) {
+                       if ((lfsck_object_type(obj) & S_IFMT) !=
+                           lnr->lnr_type) {
+                               ns->ln_flags |= LF_INCONSISTENT;
+                               type = LNIT_BAD_TYPE;
+                       }
+
                        goto record;
+               }
 
                ns->ln_flags |= LF_INCONSISTENT;
+
+               /* If the file type stored in the name entry does not match
+                * the file type claimed by the object, and the object does
+                * not recognize the name entry, then it is quite possible
+                * that the name entry is corrupted. */
+               if ((lfsck_object_type(obj) & S_IFMT) != lnr->lnr_type) {
+                       type = LNIT_BAD_DIRENT;
+
+                       GOTO(stop, rc = 0);
+               }
+
                /* 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
@@ -3477,6 +3697,9 @@ again:
                newdata = false;
                goto nodata;
        } else if (unlikely(rc == -EINVAL)) {
+               if ((lfsck_object_type(obj) & S_IFMT) != lnr->lnr_type)
+                       type = LNIT_BAD_TYPE;
+
                count = 1;
                ns->ln_flags |= LF_INCONSISTENT;
                /* The magic crashed, we are not sure whether there are more
@@ -3485,6 +3708,9 @@ again:
                newdata = true;
                goto nodata;
        } else if (rc == -ENODATA) {
+               if ((lfsck_object_type(obj) & S_IFMT) != lnr->lnr_type)
+                       type = LNIT_BAD_TYPE;
+
                count = 1;
                ns->ln_flags |= LF_UPGRADE;
                remove = false;
@@ -3574,6 +3800,34 @@ stop:
 
 out:
        lfsck_ibits_unlock(&lh, LCK_EX);
+
+       if (rc >= 0) {
+               switch (type) {
+               case LNIT_BAD_TYPE:
+                       log = false;
+                       rc = lfsck_namespace_repair_dirent(env, com, dir,
+                                       obj, lnr->lnr_name, lnr->lnr_name,
+                                       lnr->lnr_type, true, false);
+                       if (rc > 0)
+                               repaired = true;
+                       break;
+               case LNIT_BAD_DIRENT:
+                       log = false;
+                       /* XXX: This is a bad dirent, we do not know whether
+                        *      the original name entry reference a regular
+                        *      file or a directory, then keep the parent's
+                        *      nlink count unchanged here. */
+                       rc = lfsck_namespace_repair_dirent(env, com, dir,
+                                       obj, lnr->lnr_name, lnr->lnr_name,
+                                       lnr->lnr_type, false, false);
+                       if (rc > 0)
+                               repaired = true;
+                       break;
+               default:
+                       break;
+               }
+       }
+
        down_write(&com->lc_sem);
        if (rc < 0) {
                CDEBUG(D_LFSCK, "%s: namespace LFSCK assistant fail to handle "
@@ -3601,6 +3855,12 @@ out:
                        case LNIT_DANGLING:
                                ns->ln_dangling_repaired++;
                                break;
+                       case LNIT_BAD_TYPE:
+                               ns->ln_bad_type_repaired++;
+                               break;
+                       case LNIT_BAD_DIRENT:
+                               ns->ln_dirent_repaired++;
+                               break;
                        default:
                                break;
                        }
@@ -3611,6 +3871,7 @@ out:
                                               &ns->ln_pos_first_inconsistent,
                                               false);
                }
+
                rc = 0;
        }
        up_write(&com->lc_sem);
index 5167d9a..65ffaca 100644 (file)
@@ -3946,6 +3946,29 @@ static int __osd_ea_add_rec(struct osd_thread_info *info,
        child->d_fsdata = (void *)ldp;
        ll_vfs_dq_init(pobj->oo_inode);
        rc = osd_ldiskfs_add_entry(oth->ot_handle, child, cinode, hlock);
+       if (rc == 0 && OBD_FAIL_CHECK(OBD_FAIL_LFSCK_BAD_TYPE)) {
+               struct ldiskfs_dir_entry_2      *de;
+               struct buffer_head              *bh;
+               int                              rc1;
+
+               bh = osd_ldiskfs_find_entry(pobj->oo_inode, &child->d_name, &de,
+                                           NULL, hlock);
+               if (bh != NULL) {
+                       rc1 = ldiskfs_journal_get_write_access(oth->ot_handle,
+                                                              bh);
+                       if (rc1 == 0) {
+                               if (S_ISDIR(cinode->i_mode))
+                                       de->file_type = LDISKFS_DIRENT_LUFID |
+                                                       LDISKFS_FT_REG_FILE;
+                               else
+                                       de->file_type = LDISKFS_DIRENT_LUFID |
+                                                       LDISKFS_FT_DIR;
+                               ldiskfs_journal_dirty_metadata(oth->ot_handle,
+                                                              bh);
+                               brelse(bh);
+                       }
+               }
+       }
 
        RETURN(rc);
 }
index 8fe3c86..3d6fbbc 100644 (file)
@@ -46,7 +46,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 2e 3 22 23 24"
+       ALWAYS_EXCEPT="$ALWAYS_EXCEPT 2d 2e 3 22 23 24 25"
 
 build_test_filter
 
@@ -3104,6 +3104,47 @@ test_24() {
 }
 run_test 24 "LFSCK can repair multiple-referenced name entry"
 
+test_25() {
+       [ $(facet_fstype $SINGLEMDS) != ldiskfs ] &&
+               skip "Only support to inject failure on ldiskfs" && return
+
+       echo "#####"
+       echo "The file type in the name entry does not match the file type"
+       echo "claimed by the referenced object. Then the LFSCK will update"
+       echo "the file type in the name entry."
+       echo "#####"
+
+       check_mount_and_prep
+
+       $LFS mkdir -i 0 $DIR/$tdir/d0 || error "(1) Fail to mkdir d0"
+
+       echo "Inject failure stub on MDT0 to simulate the case that"
+       echo "the file type stored in the name entry is wrong."
+
+       #define OBD_FAIL_LFSCK_BAD_TYPE         0x1623
+       do_facet $SINGLEMDS $LCTL set_param fail_loc=0x1623
+       touch $DIR/$tdir/d0/foo || error "(2) Fail to touch $DIR/$tdir/d0/foo"
+       do_facet $SINGLEMDS $LCTL set_param fail_loc=0
+
+       echo "Trigger namespace LFSCK to repair bad file type in the name entry"
+       $START_NAMESPACE -r || 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 '/^bad_file_type_repaired/ { print $2 }')
+       [ $repaired -eq 1 ] ||
+       error "(5) Fail to repair bad file type in name entry: $repaired"
+
+       ls -ail $DIR/$tdir/d0 || error "(6) Fail to 'ls' the $DIR/$tdir/d0"
+}
+run_test 25 "LFSCK can repair bad file type in the name entry"
+
 $LCTL set_param debug=-lfsck > /dev/null || true
 
 # restore MDS/OST size