+/*
+ * VBR: rename versions in reply: 0 - src parent; 1 - tgt parent;
+ * 2 - src child; 3 - tgt child.
+ * Update on disk version of src child.
+ */
+/**
+ * For DNE phase I, only these renames are allowed
+ * mv src_p/src_c tgt_p/tgt_c
+ * 1. src_p/src_c/tgt_p/tgt_c are in the same MDT.
+ * 2. src_p and tgt_p are same directory, and tgt_c does not
+ * exists. In this case, all of modification will happen
+ * in the MDT where ithesource parent is, only one remote
+ * update is needed, i.e. set c_time/m_time on the child.
+ * And tgt_c will be still in the same MDT as the original
+ * src_c.
+ */
+static int mdt_reint_rename_internal(struct mdt_thread_info *info,
+ struct mdt_lock_handle *lhc)
+{
+ struct mdt_reint_record *rr = &info->mti_rr;
+ struct md_attr *ma = &info->mti_attr;
+ struct ptlrpc_request *req = mdt_info_req(info);
+ struct mdt_object *msrcdir = NULL;
+ struct mdt_object *mtgtdir = NULL;
+ struct mdt_object *mold;
+ struct mdt_object *mnew = NULL;
+ struct mdt_lock_handle *lh_srcdirp;
+ struct mdt_lock_handle *lh_tgtdirp;
+ struct mdt_lock_handle *lh_oldp = NULL;
+ struct mdt_lock_handle *lh_newp = NULL;
+ struct lu_fid *old_fid = &info->mti_tmp_fid1;
+ struct lu_fid *new_fid = &info->mti_tmp_fid2;
+ int rc;
+ ENTRY;
+
+ DEBUG_REQ(D_INODE, req, "rename "DFID"/"DNAME" to "DFID"/"DNAME,
+ PFID(rr->rr_fid1), PNAME(&rr->rr_name),
+ PFID(rr->rr_fid2), PNAME(&rr->rr_tgt_name));
+
+ lh_srcdirp = &info->mti_lh[MDT_LH_PARENT];
+ mdt_lock_pdo_init(lh_srcdirp, LCK_PW, &rr->rr_name);
+ lh_tgtdirp = &info->mti_lh[MDT_LH_CHILD];
+ mdt_lock_pdo_init(lh_tgtdirp, LCK_PW, &rr->rr_tgt_name);
+
+ /* step 1&2: lock the source and target dirs. */
+ rc = mdt_rename_parents_lock(info, &msrcdir, &mtgtdir);
+ if (rc)
+ RETURN(rc);
+
+ OBD_FAIL_TIMEOUT(OBD_FAIL_MDS_RENAME2, 5);
+
+ /* step 3: find & lock the old object. */
+ fid_zero(old_fid);
+ rc = mdt_lookup_version_check(info, msrcdir, &rr->rr_name, old_fid, 2);
+ if (rc != 0)
+ GOTO(out_unlock_parents, rc);
+
+ if (lu_fid_eq(old_fid, rr->rr_fid1) || lu_fid_eq(old_fid, rr->rr_fid2))
+ GOTO(out_unlock_parents, rc = -EINVAL);
+
+ if (!fid_is_md_operative(old_fid))
+ GOTO(out_unlock_parents, rc = -EPERM);
+
+ mold = mdt_object_find(info->mti_env, info->mti_mdt, old_fid);
+ if (IS_ERR(mold))
+ GOTO(out_unlock_parents, rc = PTR_ERR(mold));
+
+ /* Check if @mtgtdir is subdir of @mold, before locking child
+ * to avoid reverse locking. */
+ rc = mdt_is_subdir(info, mtgtdir, old_fid);
+ if (rc)
+ GOTO(out_put_old, rc);
+
+ tgt_vbr_obj_set(info->mti_env, mdt_obj2dt(mold));
+ /* save version after locking */
+ mdt_version_get_save(info, mold, 2);
+ mdt_set_capainfo(info, 2, old_fid, BYPASS_CAPA);
+
+ /* step 4: find & lock the new object. */
+ /* new target object may not exist now */
+ /* lookup with version checking */
+ fid_zero(new_fid);
+ rc = mdt_lookup_version_check(info, mtgtdir, &rr->rr_tgt_name, new_fid,
+ 3);
+ if (rc == 0) {
+ /* the new_fid should have been filled at this moment */
+ if (lu_fid_eq(old_fid, new_fid))
+ GOTO(out_put_old, rc);
+
+ if (lu_fid_eq(new_fid, rr->rr_fid1) ||
+ lu_fid_eq(new_fid, rr->rr_fid2))
+ GOTO(out_put_old, rc = -EINVAL);
+
+ if (!fid_is_md_operative(new_fid))
+ GOTO(out_put_old, rc = -EPERM);
+
+ if (mdt_object_remote(mold)) {
+ CDEBUG(D_INFO, "Src child "DFID" is on another MDT\n",
+ PFID(old_fid));
+ GOTO(out_put_old, rc = -EXDEV);
+ }
+
+ mnew = mdt_object_find(info->mti_env, info->mti_mdt, new_fid);
+ if (IS_ERR(mnew))
+ GOTO(out_put_old, rc = PTR_ERR(mnew));
+
+ if (mdt_object_remote(mnew)) {
+ CDEBUG(D_INFO, "src child "DFID" is on another MDT\n",
+ PFID(new_fid));
+ GOTO(out_put_new, rc = -EXDEV);
+ }
+
+ /* Before locking the target dir, check we do not replace
+ * a dir with a non-dir, otherwise it may deadlock with
+ * link op which tries to create a link in this dir
+ * back to this non-dir. */
+ if (S_ISDIR(lu_object_attr(&mnew->mot_obj)) &&
+ !S_ISDIR(lu_object_attr(&mold->mot_obj)))
+ GOTO(out_put_new, rc = -EISDIR);
+
+ lh_oldp = &info->mti_lh[MDT_LH_OLD];
+ mdt_lock_reg_init(lh_oldp, LCK_EX);
+ rc = mdt_object_lock(info, mold, lh_oldp, MDS_INODELOCK_LOOKUP |
+ MDS_INODELOCK_XATTR, MDT_CROSS_LOCK);
+ if (rc != 0)
+ GOTO(out_put_new, rc);
+
+ /* Check if @msrcdir is subdir of @mnew, before locking child
+ * to avoid reverse locking. */
+ rc = mdt_is_subdir(info, msrcdir, new_fid);
+ if (rc)
+ GOTO(out_unlock_old, rc);
+
+ /* We used to acquire MDS_INODELOCK_FULL here but we
+ * can't do this now because a running HSM restore on
+ * the rename onto victim will hold the layout
+ * lock. See LU-4002. */
+
+ lh_newp = &info->mti_lh[MDT_LH_NEW];
+ mdt_lock_reg_init(lh_newp, LCK_EX);
+ rc = mdt_object_lock(info, mnew, lh_newp,
+ MDS_INODELOCK_LOOKUP |
+ MDS_INODELOCK_UPDATE,
+ MDT_LOCAL_LOCK);
+ if (rc != 0)
+ GOTO(out_unlock_old, rc);
+
+ /* get and save version after locking */
+ mdt_version_get_save(info, mnew, 3);
+ mdt_set_capainfo(info, 3, new_fid, BYPASS_CAPA);
+ } else if (rc != -EREMOTE && rc != -ENOENT) {
+ GOTO(out_put_old, rc);
+ } else {
+ /* If mnew does not exist and mold are remote directory,
+ * it only allows rename if they are under same directory */
+ if (mtgtdir != msrcdir && mdt_object_remote(mold)) {
+ CDEBUG(D_INFO, "Src child "DFID" is on another MDT\n",
+ PFID(old_fid));
+ GOTO(out_put_old, rc = -EXDEV);
+ }
+
+ lh_oldp = &info->mti_lh[MDT_LH_OLD];
+ mdt_lock_reg_init(lh_oldp, LCK_EX);
+ rc = mdt_object_lock(info, mold, lh_oldp, MDS_INODELOCK_LOOKUP |
+ MDS_INODELOCK_XATTR, MDT_CROSS_LOCK);
+ if (rc != 0)
+ GOTO(out_put_old, rc);
+
+ mdt_enoent_version_save(info, 3);
+ }
+
+ /* step 5: rename it */
+ mdt_reint_init_ma(info, ma);
+
+ mdt_fail_write(info->mti_env, info->mti_mdt->mdt_bottom,
+ OBD_FAIL_MDS_REINT_RENAME_WRITE);
+
+ if (mnew != NULL)
+ mutex_lock(&mnew->mot_lov_mutex);
+
+ rc = mdo_rename(info->mti_env, mdt_object_child(msrcdir),
+ mdt_object_child(mtgtdir), old_fid, &rr->rr_name,
+ mnew != NULL ? mdt_object_child(mnew) : NULL,
+ &rr->rr_tgt_name, ma);
+
+ if (mnew != NULL)
+ mutex_unlock(&mnew->mot_lov_mutex);
+
+ /* handle last link of tgt object */
+ if (rc == 0) {
+ mdt_counter_incr(req, LPROC_MDT_RENAME);
+ if (mnew)
+ mdt_handle_last_unlink(info, mnew, ma);
+
+ mdt_rename_counter_tally(info, info->mti_mdt, req,
+ msrcdir, mtgtdir);
+ }
+
+ EXIT;
+ if (mnew != NULL)
+ mdt_object_unlock(info, mnew, lh_newp, rc);
+out_unlock_old:
+ mdt_object_unlock(info, mold, lh_oldp, rc);
+out_put_new:
+ if (mnew != NULL)
+ mdt_object_put(info->mti_env, mnew);
+out_put_old:
+ mdt_object_put(info->mti_env, mold);
+out_unlock_parents:
+ mdt_object_unlock_put(info, mtgtdir, lh_tgtdirp, rc);
+ mdt_object_unlock_put(info, msrcdir, lh_srcdirp, rc);
+ return rc;
+}