+/**
+ * migrate dirent from \a spobj to \a tpobj.
+ **/
+static int mdd_migrate_update(const struct lu_env *env,
+ struct mdd_object *spobj,
+ struct mdd_object *tpobj,
+ struct mdd_object *obj,
+ const struct lu_name *sname,
+ const struct lu_name *tname,
+ struct lu_attr *attr,
+ struct lu_attr *spattr,
+ struct lu_attr *tpattr,
+ struct linkea_data *ldata,
+ struct md_attr *ma,
+ struct thandle *handle)
+{
+ struct mdd_thread_info *info = mdd_env_info(env);
+ struct lu_attr *la = &info->mti_la_for_fix;
+ int rc;
+
+ ENTRY;
+
+ CDEBUG(D_INFO, "update "DFID" from "DFID"/%s to "DFID"/%s\n",
+ PFID(mdd_object_fid(obj)), PFID(mdd_object_fid(spobj)),
+ sname->ln_name, PFID(mdd_object_fid(tpobj)), tname->ln_name);
+
+ rc = __mdd_index_delete(env, spobj, sname->ln_name,
+ S_ISDIR(attr->la_mode), handle);
+ if (rc)
+ RETURN(rc);
+
+ rc = __mdd_index_insert(env, tpobj, mdd_object_fid(obj),
+ attr->la_mode & S_IFMT,
+ tname->ln_name, handle);
+ if (rc)
+ RETURN(rc);
+
+ rc = mdd_links_write(env, obj, ldata, handle);
+ if (rc)
+ RETURN(rc);
+
+ la->la_ctime = la->la_mtime = ma->ma_attr.la_ctime;
+ la->la_valid = LA_CTIME | LA_MTIME;
+ mdd_write_lock(env, spobj, DT_SRC_PARENT);
+ rc = mdd_update_time(env, spobj, spattr, la, handle);
+ mdd_write_unlock(env, spobj);
+ if (rc)
+ RETURN(rc);
+
+ if (tpobj != spobj) {
+ la->la_valid = LA_CTIME | LA_MTIME;
+ mdd_write_lock(env, tpobj, DT_TGT_PARENT);
+ rc = mdd_update_time(env, tpobj, tpattr, la, handle);
+ mdd_write_unlock(env, tpobj);
+ if (rc)
+ RETURN(rc);
+ }
+
+ RETURN(rc);
+}
+
+/**
+ * Migrate file/dir to target MDT.
+ *
+ * Create target according to \a spec, and then migrate xattrs, if it's
+ * directory, migrate source stripes to target.
+ *
+ * \param[in] env execution environment
+ * \param[in] spobj source parent object
+ * \param[in] tpobj target parent object
+ * \param[in] sobj source object
+ * \param[in] tobj target object
+ * \param[in] lname file name
+ * \param[in] spattr source parent attributes
+ * \param[in] tpattr target parent attributes
+ * \param[in] attr source attributes
+ * \param[in] sbuf source LMV buf
+ * \param[in] spec migrate create spec
+ * \param[in] hint target creation hint
+ * \param[in] handle tranasction handle
+ *
+ * \retval 0 on success
+ * \retval -errno on failure
+ **/
+static int mdd_migrate_create(const struct lu_env *env,
+ struct mdd_object *spobj,
+ struct mdd_object *tpobj,
+ struct mdd_object *sobj,
+ struct mdd_object *tobj,
+ const struct lu_name *sname,
+ const struct lu_name *tname,
+ struct lu_attr *spattr,
+ struct lu_attr *tpattr,
+ struct lu_attr *attr,
+ const struct lu_buf *sbuf,
+ struct linkea_data *ldata,
+ struct md_attr *ma,
+ struct md_op_spec *spec,
+ struct dt_allocation_hint *hint,
+ struct thandle *handle)
+{
+ int rc;
+
+ ENTRY;
+
+ /*
+ * migrate sobj stripes to tobj if it's directory:
+ * 1. detach stripes from sobj.
+ * 2. attach stripes to tobj, see mdd_declare_migrate_mdt().
+ * 3. create stripes for tobj, see lod_xattr_set_lmv().
+ */
+ if (S_ISDIR(attr->la_mode)) {
+ struct mdd_thread_info *info = mdd_env_info(env);
+ struct md_layout_change *mlc = &info->mti_mlc;
+
+ mlc->mlc_opc = MD_LAYOUT_DETACH;
+
+ mdd_write_lock(env, sobj, DT_SRC_PARENT);
+ rc = mdo_layout_change(env, sobj, mlc, handle);
+ mdd_write_unlock(env, sobj);
+ if (rc)
+ RETURN(rc);
+ }
+
+ /* don't set nlink from sobj */
+ attr->la_valid &= ~LA_NLINK;
+
+ rc = mdd_create_object(env, tpobj, tobj, attr, spec, NULL, NULL, NULL,
+ hint, handle, false);
+ if (rc)
+ RETURN(rc);
+
+ mdd_write_lock(env, tobj, DT_TGT_CHILD);
+ rc = mdd_iterate_xattrs(env, sobj, tobj, true, handle, mdo_xattr_set);
+ mdd_write_unlock(env, tobj);
+ if (rc)
+ RETURN(rc);
+
+ /* for regular file, update OST objects XATTR_NAME_FID */
+ if (S_ISREG(attr->la_mode)) {
+ struct lu_buf fid_buf;
+
+ /* target may be remote, update PFID via sobj. */
+ fid_buf.lb_buf = (void *)mdd_object_fid(tobj);
+ fid_buf.lb_len = sizeof(struct lu_fid);
+ rc = mdo_xattr_set(env, sobj, &fid_buf, XATTR_NAME_FID, 0,
+ handle);
+ if (rc)
+ RETURN(rc);
+
+ /* delete LOV to avoid deleting OST objs when destroying sobj */
+ mdd_write_lock(env, sobj, DT_SRC_CHILD);
+ rc = mdo_xattr_del(env, sobj, XATTR_NAME_LOV, handle);
+ mdd_write_unlock(env, sobj);
+ /* O_DELAY_CREATE file may not have LOV, ignore -ENODATA */
+ if (rc && rc != -ENODATA)
+ RETURN(rc);
+ rc = 0;
+ }
+
+ /* update links FID */
+ if (!S_ISDIR(attr->la_mode)) {
+ rc = mdd_iterate_linkea(env, sobj, tobj, tname,
+ mdd_object_fid(tpobj), ldata,
+ NULL, handle, mdd_update_link);
+ if (rc)
+ RETURN(rc);
+ }
+
+ /* don't destroy sobj if it's plain directory */
+ if (!S_ISDIR(attr->la_mode) || sbuf->lb_buf) {
+ mdd_write_lock(env, sobj, DT_SRC_CHILD);
+ rc = mdo_ref_del(env, sobj, handle);
+ if (!rc) {
+ if (S_ISDIR(attr->la_mode))
+ rc = mdo_ref_del(env, sobj, handle);
+ if (!rc)
+ rc = mdo_destroy(env, sobj, handle);