+ if (declare)
+ rc = mdo_declare_attr_set(env, obj, attr, handle);
+ else
+ rc = mdo_attr_set(env, obj, attr, handle,
+ mdd_object_capa(env, obj));
+
+ return rc;
+}
+
+static int mdd_declare_finish_unlink(const struct lu_env *env,
+ struct mdd_object *obj,
+ struct thandle *handle)
+{
+ int rc;
+
+ rc = mdd_mark_dead_object(env, obj, handle, true);
+ if (rc != 0)
+ return rc;
+
+ rc = orph_declare_index_insert(env, obj, mdd_object_type(obj), handle);
+ if (rc != 0)
+ return rc;
+
+ rc = mdo_declare_destroy(env, obj, handle);
+ if (rc != 0)
+ return rc;
+
+ return mdd_declare_links_del(env, obj, handle);
+}
+
+/* caller should take a lock before calling */
+int mdd_finish_unlink(const struct lu_env *env,
+ struct mdd_object *obj, struct md_attr *ma,
+ const struct mdd_object *pobj,
+ const struct lu_name *lname,
+ struct thandle *th)
+{
+ int rc = 0;
+ int is_dir = S_ISDIR(ma->ma_attr.la_mode);
+ ENTRY;
+
+ LASSERT(mdd_write_locked(env, obj) != 0);
+
+ if (ma->ma_attr.la_nlink == 0 || is_dir) {
+ rc = mdd_mark_dead_object(env, obj, th, false);
+ if (rc != 0)
+ RETURN(rc);
+
+ /* add new orphan and the object
+ * will be deleted during mdd_close() */
+ if (obj->mod_count) {
+ rc = __mdd_orphan_add(env, obj, th);
+ if (rc == 0)
+ CDEBUG(D_HA, "Object "DFID" is inserted into "
+ "orphan list, open count = %d\n",
+ PFID(mdd_object_fid(obj)),
+ obj->mod_count);
+ else
+ CERROR("Object "DFID" fail to be an orphan, "
+ "open count = %d, maybe cause failed "
+ "open replay\n",
+ PFID(mdd_object_fid(obj)),
+ obj->mod_count);
+ } else {
+ rc = mdo_destroy(env, obj, th);
+ }
+ } else if (!is_dir) {
+ /* old files may not have link ea; ignore errors */
+ mdd_links_del(env, obj, mdo2fid(pobj), lname, th);
+ }
+
+ RETURN(rc);
+}
+
+/*
+ * pobj maybe NULL
+ * has mdd_write_lock on cobj already, but not on pobj yet
+ */
+int mdd_unlink_sanity_check(const struct lu_env *env, struct mdd_object *pobj,
+ const struct lu_attr *pattr,
+ struct mdd_object *cobj,
+ const struct lu_attr *cattr)
+{
+ int rc;
+ ENTRY;
+
+ rc = mdd_may_delete(env, pobj, pattr, cobj, cattr, NULL, 1, 1);
+
+ RETURN(rc);
+}
+
+static int mdd_declare_unlink(const struct lu_env *env, struct mdd_device *mdd,
+ struct mdd_object *p, struct mdd_object *c,
+ const struct lu_name *name, struct md_attr *ma,
+ struct thandle *handle, int no_name)
+{
+ struct lu_attr *la = &mdd_env_info(env)->mti_la_for_fix;
+ int rc;
+
+ if (likely(no_name == 0)) {
+ rc = mdo_declare_index_delete(env, p, name->ln_name, handle);
+ if (rc)
+ return rc;
+ }
+
+ rc = mdo_declare_ref_del(env, p, handle);
+ if (rc)
+ return rc;
+
+ LASSERT(ma->ma_attr.la_valid & LA_CTIME);
+ la->la_ctime = la->la_mtime = ma->ma_attr.la_ctime;
+ la->la_valid = LA_CTIME | LA_MTIME;
+ rc = mdo_declare_attr_set(env, p, la, handle);
+ if (rc)
+ return rc;
+
+ if (c != NULL) {
+ rc = mdo_declare_ref_del(env, c, handle);
+ if (rc)
+ return rc;
+
+ rc = mdo_declare_ref_del(env, c, handle);
+ if (rc)
+ return rc;
+
+ la->la_valid = LA_CTIME;
+ rc = mdo_declare_attr_set(env, c, la, handle);
+ if (rc)
+ return rc;
+
+ rc = mdd_declare_finish_unlink(env, c, handle);
+ if (rc)
+ return rc;
+
+ /* FIXME: need changelog for remove entry */
+ rc = mdd_declare_changelog_store(env, mdd, name, handle);
+ }
+
+ return rc;
+}
+
+/*
+ * test if a file has an HSM archive
+ * if HSM attributes are not found in ma update them from
+ * HSM xattr
+ */
+static bool mdd_hsm_archive_exists(const struct lu_env *env,
+ struct mdd_object *obj,
+ struct md_attr *ma)
+{
+ ENTRY;
+
+ if (!(ma->ma_valid & MA_HSM)) {
+ /* no HSM MD provided, read xattr */
+ struct lu_buf *hsm_buf;
+ const size_t buflen = sizeof(struct hsm_attrs);
+ int rc;
+
+ hsm_buf = mdd_buf_get(env, NULL, 0);
+ lu_buf_alloc(hsm_buf, buflen);
+ rc = mdo_xattr_get(env, obj, hsm_buf, XATTR_NAME_HSM,
+ mdd_object_capa(env, obj));
+ rc = lustre_buf2hsm(hsm_buf->lb_buf, rc, &ma->ma_hsm);
+ lu_buf_free(hsm_buf);
+ if (rc < 0)
+ RETURN(false);
+
+ ma->ma_valid = MA_HSM;
+ }
+ if (ma->ma_hsm.mh_flags & HS_EXISTS)
+ RETURN(true);
+ RETURN(false);
+}
+
+/**
+ * Delete name entry and the object.
+ * Note: no_name == 1 means it only destory the object, i.e. name_entry
+ * does not exist for this object, and it could only happen during resending
+ * of remote unlink. see the comments in mdt_reint_unlink. Unfortunately, lname
+ * is also needed in this case(needed by changelog), so we have to add another
+ * parameter(no_name)here. XXX: this is only needed in DNE phase I, on Phase II,
+ * the ENOENT failure should be able to be fixed by redo mechanism.
+ */
+static int mdd_unlink(const struct lu_env *env, struct md_object *pobj,
+ struct md_object *cobj, const struct lu_name *lname,
+ struct md_attr *ma, int no_name)
+{
+ const char *name = lname->ln_name;
+ struct lu_attr *pattr = MDD_ENV_VAR(env, pattr);
+ struct lu_attr *cattr = MDD_ENV_VAR(env, cattr);
+ struct lu_attr *la = &mdd_env_info(env)->mti_la_for_fix;
+ struct mdd_object *mdd_pobj = md2mdd_obj(pobj);
+ struct mdd_object *mdd_cobj = NULL;
+ struct mdd_device *mdd = mdo2mdd(pobj);
+ struct thandle *handle;
+ int rc, is_dir = 0;
+ ENTRY;
+
+ /* cobj == NULL means only delete name entry */
+ if (likely(cobj != NULL)) {
+ mdd_cobj = md2mdd_obj(cobj);
+ if (mdd_object_exists(mdd_cobj) == 0)
+ RETURN(-ENOENT);
+ }
+
+ rc = mdd_la_get(env, mdd_pobj, pattr, BYPASS_CAPA);
+ if (rc)
+ RETURN(rc);
+
+ if (likely(mdd_cobj != NULL)) {
+ /* fetch cattr */
+ rc = mdd_la_get(env, mdd_cobj, cattr, BYPASS_CAPA);
+ if (rc)
+ RETURN(rc);
+
+ is_dir = S_ISDIR(cattr->la_mode);
+ }
+
+ rc = mdd_unlink_sanity_check(env, mdd_pobj, pattr, mdd_cobj, cattr);
+ if (rc)
+ RETURN(rc);
+
+ handle = mdd_trans_create(env, mdd);
+ if (IS_ERR(handle))
+ RETURN(PTR_ERR(handle));
+
+ rc = mdd_declare_unlink(env, mdd, mdd_pobj, mdd_cobj,
+ lname, ma, handle, no_name);
+ if (rc)
+ GOTO(stop, rc);
+
+ rc = mdd_trans_start(env, mdd, handle);
+ if (rc)
+ GOTO(stop, rc);
+
+ if (likely(mdd_cobj != NULL))
+ mdd_write_lock(env, mdd_cobj, MOR_TGT_CHILD);
+
+ if (likely(no_name == 0)) {
+ rc = __mdd_index_delete(env, mdd_pobj, name, is_dir, handle,
+ mdd_object_capa(env, mdd_pobj));
+ if (rc)
+ GOTO(cleanup, rc);
+ }
+
+ if (likely(mdd_cobj != NULL)) {
+ rc = mdo_ref_del(env, mdd_cobj, handle);
+ if (rc != 0) {
+ __mdd_index_insert_only(env, mdd_pobj,
+ mdo2fid(mdd_cobj),
+ mdd_object_type(mdd_cobj),
+ name, handle,
+ mdd_object_capa(env, mdd_pobj));
+ GOTO(cleanup, rc);
+ }
+
+ if (is_dir)
+ /* unlink dot */
+ mdo_ref_del(env, mdd_cobj, handle);
+
+ /* fetch updated nlink */
+ rc = mdd_la_get(env, mdd_cobj, cattr, BYPASS_CAPA);
+ if (rc)
+ GOTO(cleanup, rc);
+ }
+
+ LASSERT(ma->ma_attr.la_valid & LA_CTIME);
+ la->la_ctime = la->la_mtime = ma->ma_attr.la_ctime;
+
+ la->la_valid = LA_CTIME | LA_MTIME;
+ rc = mdd_update_time(env, mdd_pobj, pattr, la, handle);
+ if (rc)
+ GOTO(cleanup, rc);
+
+ /* Enough for only unlink the entry */
+ if (unlikely(mdd_cobj == NULL))
+ GOTO(stop, rc);
+
+ if (cattr->la_nlink > 0 || mdd_cobj->mod_count > 0) {
+ /* update ctime of an unlinked file only if it is still
+ * opened or a link still exists */
+ la->la_valid = LA_CTIME;
+ rc = mdd_update_time(env, mdd_cobj, cattr, la, handle);
+ if (rc)
+ GOTO(cleanup, rc);
+ }
+
+ /* XXX: this transfer to ma will be removed with LOD/OSP */
+ ma->ma_attr = *cattr;