Whamcloud - gitweb
LU-10030 hsm: make changelog flag argument an enum
[fs/lustre-release.git] / lustre / mdd / mdd_object.c
index 1b3cb2b..c04c685 100644 (file)
@@ -55,9 +55,12 @@ static const struct lu_object_operations mdd_lu_obj_ops;
 
 struct mdd_object_user {
        struct list_head        mou_list;       /**< linked off mod_users */
-       int                     mou_flags;      /**< open mode by client */
+       u64                     mou_open_flags; /**< open mode by client */
        __u64                   mou_uidgid;     /**< uid_gid on client */
        int                     mou_opencount;  /**< # opened */
+       ktime_t                 mou_deniednext; /**< time of next access denied
+                                                * notfication
+                                                */
 };
 
 static int mdd_xattr_get(const struct lu_env *env,
@@ -67,27 +70,29 @@ static int mdd_xattr_get(const struct lu_env *env,
 static int mdd_changelog_data_store_by_fid(const struct lu_env *env,
                                           struct mdd_device *mdd,
                                           enum changelog_rec_type type,
-                                          int flags, const struct lu_fid *fid,
+                                          enum changelog_rec_flags clf_flags,
+                                          const struct lu_fid *fid,
                                           const char *xattr_name,
                                           struct thandle *handle);
 
 static inline bool has_prefix(const char *str, const char *prefix);
 
 
-static fmode_t flags_helper(int flags)
+static u32 flags_helper(u64 open_flags)
 {
-       fmode_t mflags = 0;
+       u32 open_mode = 0;
 
-       if (flags & MDS_FMODE_EXEC) {
-               mflags = MDS_FMODE_EXEC;
+       if (open_flags & MDS_FMODE_EXEC) {
+               open_mode = MDS_FMODE_EXEC;
        } else {
-               if (flags & FMODE_READ)
-                       mflags = FMODE_READ;
-               if (flags & (FMODE_WRITE | MDS_OPEN_TRUNC | MDS_OPEN_APPEND))
-                       mflags |= FMODE_WRITE;
+               if (open_flags & MDS_FMODE_READ)
+                       open_mode = MDS_FMODE_READ;
+               if (open_flags &
+                   (MDS_FMODE_WRITE | MDS_OPEN_TRUNC | MDS_OPEN_APPEND))
+                       open_mode |= MDS_FMODE_WRITE;
        }
 
-       return mflags;
+       return open_mode;
 }
 
 /** Allocate/init a user and its sub-structures.
@@ -98,7 +103,7 @@ static fmode_t flags_helper(int flags)
  * \retval mou [OUT] success valid structure
  * \retval mou [OUT]
  */
-static struct mdd_object_user *mdd_obj_user_alloc(int flags,
+static struct mdd_object_user *mdd_obj_user_alloc(u64 open_flags,
                                                  uid_t uid, gid_t gid)
 {
        struct mdd_object_user *mou;
@@ -109,9 +114,10 @@ static struct mdd_object_user *mdd_obj_user_alloc(int flags,
        if (mou == NULL)
                RETURN(ERR_PTR(-ENOMEM));
 
-       mou->mou_flags = flags;
+       mou->mou_open_flags = open_flags;
        mou->mou_uidgid = ((__u64)uid << 32) | gid;
        mou->mou_opencount = 0;
+       mou->mou_deniednext = ktime_set(0, 0);
 
        RETURN(mou);
 }
@@ -137,7 +143,8 @@ static void mdd_obj_user_free(struct mdd_object_user *mou)
  */
 static
 struct mdd_object_user *mdd_obj_user_find(struct mdd_object *mdd_obj,
-                                         uid_t uid, gid_t gid, int flags)
+                                         uid_t uid, gid_t gid,
+                                         u64 open_flags)
 {
        struct mdd_object_user *mou;
        __u64 uidgid;
@@ -147,7 +154,8 @@ struct mdd_object_user *mdd_obj_user_find(struct mdd_object *mdd_obj,
        uidgid = ((__u64)uid << 32) | gid;
        list_for_each_entry(mou, &mdd_obj->mod_users, mou_list) {
                if (mou->mou_uidgid == uidgid &&
-                   flags_helper(mou->mou_flags) == flags_helper(flags))
+                   flags_helper(mou->mou_open_flags) ==
+                   flags_helper(open_flags))
                        RETURN(mou);
        }
        RETURN(NULL);
@@ -163,22 +171,30 @@ struct mdd_object_user *mdd_obj_user_find(struct mdd_object *mdd_obj,
  * \retval -ve failure
  */
 static int mdd_obj_user_add(struct mdd_object *mdd_obj,
-                           struct mdd_object_user *mou)
+                           struct mdd_object_user *mou,
+                           bool denied)
 {
+       struct mdd_device *mdd = mdd_obj2mdd_dev(mdd_obj);
        struct mdd_object_user *tmp;
        __u32 uid = mou->mou_uidgid >> 32;
        __u32 gid = mou->mou_uidgid & ((1UL << 32) - 1);
 
        ENTRY;
-
-       tmp = mdd_obj_user_find(mdd_obj, uid,
-                               gid, mou->mou_flags);
+       tmp = mdd_obj_user_find(mdd_obj, uid, gid, mou->mou_open_flags);
        if (tmp != NULL)
                RETURN(-EEXIST);
 
        list_add_tail(&mou->mou_list, &mdd_obj->mod_users);
 
-       mou->mou_opencount++;
+       if (denied)
+               /* next 'access denied' notification cannot happen before
+                * mou_deniednext
+                */
+               mou->mou_deniednext =
+                       ktime_add(ktime_get(),
+                                 ktime_set(mdd->mdd_cl.mc_deniednext, 0));
+       else
+               mou->mou_opencount++;
 
        RETURN(0);
 }
@@ -379,7 +395,7 @@ static int mdd_xattr_get(const struct lu_env *env,
                          const char *name)
 {
        struct mdd_object *mdd_obj = md2mdd_obj(obj);
-       struct md_device *md_dev = lu2md_dev(mdd2lu_dev(mdo2mdd(obj)));
+       struct mdd_device *mdd;
        int rc;
 
        ENTRY;
@@ -410,28 +426,25 @@ static int mdd_xattr_get(const struct lu_env *env,
        rc = mdo_xattr_get(env, mdd_obj, buf, name);
        mdd_read_unlock(env, mdd_obj);
 
+       mdd = mdo2mdd(obj);
+
        /* record only getting user xattrs and acls */
        if (rc >= 0 &&
+           mdd_changelog_enabled(env, mdd, CL_GETXATTR) &&
            (has_prefix(name, XATTR_USER_PREFIX) ||
             has_prefix(name, XATTR_NAME_POSIX_ACL_ACCESS) ||
             has_prefix(name, XATTR_NAME_POSIX_ACL_DEFAULT))) {
                struct thandle *handle;
-               struct mdd_device *mdd = lu2mdd_dev(&md_dev->md_lu_dev);
                int rc2;
 
-               /* Not recording */
-               if (!(mdd->mdd_cl.mc_flags & CLM_ON))
-                       RETURN(rc);
-               if (!(mdd->mdd_cl.mc_mask & (1 << CL_GETXATTR)))
-                       RETURN(rc);
-
                LASSERT(mdo2fid(mdd_obj) != NULL);
 
                handle = mdd_trans_create(env, mdd);
                if (IS_ERR(handle))
                        RETURN(PTR_ERR(handle));
 
-               rc2 = mdd_declare_changelog_store(env, mdd, NULL, NULL, handle);
+               rc2 = mdd_declare_changelog_store(env, mdd, CL_GETXATTR, NULL,
+                                                 NULL, handle);
                if (rc2)
                        GOTO(stop, rc2);
 
@@ -831,32 +844,33 @@ static int mdd_fix_attr(const struct lu_env *env, struct mdd_object *obj,
 static int mdd_changelog_data_store_by_fid(const struct lu_env *env,
                                           struct mdd_device *mdd,
                                           enum changelog_rec_type type,
-                                          int flags, const struct lu_fid *fid,
+                                          enum changelog_rec_flags clf_flags,
+                                          const struct lu_fid *fid,
                                           const char *xattr_name,
                                           struct thandle *handle)
 {
        const struct lu_ucred *uc = lu_ucred(env);
+       enum changelog_rec_extra_flags xflags = CLFE_INVALID;
        struct llog_changelog_rec *rec;
        struct lu_buf *buf;
        int reclen;
-       int xflags = CLFE_INVALID;
        int rc;
 
-       flags = (flags & CLF_FLAGMASK) | CLF_VERSION | CLF_EXTRA_FLAGS;
+       clf_flags = (clf_flags & CLF_FLAGMASK) | CLF_VERSION | CLF_EXTRA_FLAGS;
 
        if (uc) {
                if (uc->uc_jobid[0] != '\0')
-                       flags |= CLF_JOBID;
+                       clf_flags |= CLF_JOBID;
                xflags |= CLFE_UIDGID;
                xflags |= CLFE_NID;
        }
-       if (type == CL_OPEN)
+       if (type == CL_OPEN || type == CL_DN_OPEN)
                xflags |= CLFE_OPEN;
        if (type == CL_SETXATTR || type == CL_GETXATTR)
                xflags |= CLFE_XATTR;
 
        reclen = llog_data_len(LLOG_CHANGELOG_HDR_SZ +
-                              changelog_rec_offset(flags & CLF_SUPPORTED,
+                              changelog_rec_offset(clf_flags & CLF_SUPPORTED,
                                                    xflags & CLFE_SUPPORTED));
        buf = lu_buf_check_and_alloc(&mdd_env_info(env)->mti_big_buf, reclen);
        if (buf->lb_buf == NULL)
@@ -864,15 +878,15 @@ static int mdd_changelog_data_store_by_fid(const struct lu_env *env,
        rec = buf->lb_buf;
 
        rec->cr_hdr.lrh_len = reclen;
-       rec->cr.cr_flags = flags;
+       rec->cr.cr_flags = clf_flags;
        rec->cr.cr_type = (__u32)type;
        rec->cr.cr_tfid = *fid;
        rec->cr.cr_namelen = 0;
 
-       if (flags & CLF_JOBID)
+       if (clf_flags & CLF_JOBID)
                mdd_changelog_rec_ext_jobid(&rec->cr, uc->uc_jobid);
 
-       if (flags & CLF_EXTRA_FLAGS) {
+       if (clf_flags & CLF_EXTRA_FLAGS) {
                mdd_changelog_rec_ext_extra_flags(&rec->cr, xflags);
                if (xflags & CLFE_UIDGID)
                        mdd_changelog_rec_extra_uidgid(&rec->cr,
@@ -880,7 +894,7 @@ static int mdd_changelog_data_store_by_fid(const struct lu_env *env,
                if (xflags & CLFE_NID)
                        mdd_changelog_rec_extra_nid(&rec->cr, uc->uc_nid);
                if (xflags & CLFE_OPEN)
-                       mdd_changelog_rec_extra_omode(&rec->cr, flags);
+                       mdd_changelog_rec_extra_omode(&rec->cr, clf_flags);
                if (xflags & CLFE_XATTR) {
                        if (xattr_name == NULL)
                                RETURN(-EINVAL);
@@ -900,7 +914,8 @@ static int mdd_changelog_data_store_by_fid(const struct lu_env *env,
  * \param handle - transaction handle
  */
 int mdd_changelog_data_store(const struct lu_env *env, struct mdd_device *mdd,
-                            enum changelog_rec_type type, int flags,
+                            enum changelog_rec_type type,
+                            enum changelog_rec_flags clf_flags,
                             struct mdd_object *mdd_obj, struct thandle *handle)
 {
        int                              rc;
@@ -908,10 +923,7 @@ int mdd_changelog_data_store(const struct lu_env *env, struct mdd_device *mdd,
        LASSERT(mdd_obj != NULL);
        LASSERT(handle != NULL);
 
-       /* Not recording */
-       if (!(mdd->mdd_cl.mc_flags & CLM_ON))
-               RETURN(0);
-       if ((mdd->mdd_cl.mc_mask & (1 << type)) == 0)
+       if (!mdd_changelog_enabled(env, mdd, type))
                RETURN(0);
 
        if (mdd_is_volatile_obj(mdd_obj))
@@ -925,7 +937,7 @@ int mdd_changelog_data_store(const struct lu_env *env, struct mdd_device *mdd,
                RETURN(0);
        }
 
-       rc = mdd_changelog_data_store_by_fid(env, mdd, type, flags,
+       rc = mdd_changelog_data_store_by_fid(env, mdd, type, clf_flags,
                                             mdo2fid(mdd_obj), NULL, handle);
        if (rc == 0)
                mdd_obj->mod_cltime = ktime_get();
@@ -933,22 +945,20 @@ int mdd_changelog_data_store(const struct lu_env *env, struct mdd_device *mdd,
        RETURN(rc);
 }
 
-static int mdd_changelog_data_store_xattr(const struct lu_env *env,
-                                         struct mdd_device *mdd,
-                                         enum changelog_rec_type type,
-                                         int flags, struct mdd_object *mdd_obj,
-                                         const char *xattr_name,
-                                         struct thandle *handle)
+int mdd_changelog_data_store_xattr(const struct lu_env *env,
+                                  struct mdd_device *mdd,
+                                  enum changelog_rec_type type,
+                                  enum changelog_rec_flags clf_flags,
+                                  struct mdd_object *mdd_obj,
+                                  const char *xattr_name,
+                                  struct thandle *handle)
 {
-       int                              rc;
+       int rc;
 
        LASSERT(mdd_obj != NULL);
        LASSERT(handle != NULL);
 
-       /* Not recording */
-       if (!(mdd->mdd_cl.mc_flags & CLM_ON))
-               RETURN(0);
-       if ((mdd->mdd_cl.mc_mask & (1 << type)) == 0)
+       if (!mdd_changelog_enabled(env, mdd, type))
                RETURN(0);
 
        if (mdd_is_volatile_obj(mdd_obj))
@@ -963,7 +973,7 @@ static int mdd_changelog_data_store_xattr(const struct lu_env *env,
                RETURN(0);
        }
 
-       rc = mdd_changelog_data_store_by_fid(env, mdd, type, flags,
+       rc = mdd_changelog_data_store_by_fid(env, mdd, type, clf_flags,
                                             mdo2fid(mdd_obj), xattr_name,
                                             handle);
        if (rc == 0)
@@ -972,27 +982,29 @@ static int mdd_changelog_data_store_xattr(const struct lu_env *env,
        RETURN(rc);
 }
 
+/* only the bottom CLF_FLAGSHIFT bits of @flags are stored in the record,
+ * except for open flags have a dedicated record to store 32 bits of flags */
 static int mdd_changelog(const struct lu_env *env, enum changelog_rec_type type,
-                 int flags, struct md_device *m, const struct lu_fid *fid)
+                        enum changelog_rec_flags clf_flags,
+                        struct md_device *m, const struct lu_fid *fid)
 {
        struct thandle *handle;
        struct mdd_device *mdd = lu2mdd_dev(&m->md_lu_dev);
        int rc;
        ENTRY;
 
-       /* Not recording */
-       if (!(mdd->mdd_cl.mc_flags & CLM_ON))
-               RETURN(0);
-       if (!(mdd->mdd_cl.mc_mask & (1 << type)))
-               RETURN(0);
-
        LASSERT(fid != NULL);
 
+       /* We'll check this again below, but we check now before we
+        * start a transaction. */
+       if (!mdd_changelog_enabled(env, mdd, type))
+               RETURN(0);
+
        handle = mdd_trans_create(env, mdd);
        if (IS_ERR(handle))
                RETURN(PTR_ERR(handle));
 
-       rc = mdd_declare_changelog_store(env, mdd, NULL, NULL, handle);
+       rc = mdd_declare_changelog_store(env, mdd, type, NULL, NULL, handle);
        if (rc)
                GOTO(stop, rc);
 
@@ -1000,7 +1012,7 @@ static int mdd_changelog(const struct lu_env *env, enum changelog_rec_type type,
        if (rc)
                GOTO(stop, rc);
 
-       rc = mdd_changelog_data_store_by_fid(env, mdd, type, flags,
+       rc = mdd_changelog_data_store_by_fid(env, mdd, type, clf_flags,
                                             fid, NULL, handle);
 
 stop:
@@ -1041,8 +1053,8 @@ static int mdd_attr_set_changelog(const struct lu_env *env,
        /* The record type is the lowest non-masked set bit */
        type = __ffs(bits);
 
-       /* FYI we only store the first CLF_FLAGMASK bits of la_valid */
-       return mdd_changelog_data_store(env, mdd, type, (int)valid,
+       /* XXX: we only store the low CLF_FLAGMASK bits of la_valid */
+       return mdd_changelog_data_store(env, mdd, type, valid,
                                        md2mdd_obj(obj), handle);
 }
 
@@ -1080,7 +1092,8 @@ static int mdd_declare_attr_set(const struct lu_env *env,
         }
 #endif
 
-       rc = mdd_declare_changelog_store(env, mdd, NULL, NULL, handle);
+       rc = mdd_declare_changelog_store(env, mdd, CL_SETXATTR, NULL, NULL,
+                                        handle);
        return rc;
 }
 
@@ -1115,6 +1128,30 @@ static inline bool permission_needs_sync(const struct lu_attr *old,
        return false;
 }
 
+static inline __u64 mdd_lmm_dom_size(void *buf)
+{
+       struct lov_mds_md *lmm = buf;
+       struct lov_comp_md_v1 *comp_v1;
+       struct lov_mds_md *v1;
+       __u32 off;
+
+       if (lmm == NULL)
+               return 0;
+
+       if (le32_to_cpu(lmm->lmm_magic) != LOV_MAGIC_COMP_V1)
+               return 0;
+
+       comp_v1 = (struct lov_comp_md_v1 *)lmm;
+       off = le32_to_cpu(comp_v1->lcm_entries[0].lcme_offset);
+       v1 = (struct lov_mds_md *)((char *)comp_v1 + off);
+
+       /* DoM entry is the first entry always */
+       if (lov_pattern(le32_to_cpu(v1->lmm_pattern)) == LOV_PATTERN_MDT)
+               return le64_to_cpu(comp_v1->lcm_entries[0].lcme_extent.e_end);
+
+       return 0;
+}
+
 /* set attr and LOV EA at once, return updated attr */
 int mdd_attr_set(const struct lu_env *env, struct md_object *obj,
                 const struct md_attr *ma)
@@ -1192,6 +1229,25 @@ int mdd_attr_set(const struct lu_env *env, struct md_object *obj,
                       la->la_mtime, la->la_ctime);
 
        mdd_write_lock(env, mdd_obj, MOR_TGT_CHILD);
+
+       /* LU-10509: setattr of LA_SIZE should be skipped case of DOM,
+        * otherwise following truncate will do nothing and truncated
+        * data may be read again. This is a quick fix until LU-11033
+        * will be resolved.
+        */
+       if (la_copy->la_valid & LA_SIZE) {
+               struct lu_buf *lov_buf = mdd_buf_get(env, NULL, 0);
+
+               rc = mdd_stripe_get(env, mdd_obj, lov_buf, XATTR_NAME_LOV);
+               if (rc) {
+                       rc = 0;
+               } else {
+                       if (mdd_lmm_dom_size(lov_buf->lb_buf) > 0)
+                               la_copy->la_valid &= ~LA_SIZE;
+                       lu_buf_free(lov_buf);
+               }
+       }
+
        if (la_copy->la_valid) {
                rc = mdd_attr_set_internal(env, mdd_obj, la_copy, handle, 1);
 
@@ -1234,10 +1290,10 @@ static int mdd_xattr_sanity_check(const struct lu_env *env,
                    (uc->uc_fsuid != attr->la_uid) &&
                    !md_capable(uc, CFS_CAP_FOWNER))
                        RETURN(-EPERM);
-       } else {
-               if ((uc->uc_fsuid != attr->la_uid) &&
-                   !md_capable(uc, CFS_CAP_FOWNER))
-                       RETURN(-EPERM);
+       } else if (strcmp(name, XATTR_NAME_SOM) != 0 &&
+                  (uc->uc_fsuid != attr->la_uid) &&
+                  !md_capable(uc, CFS_CAP_FOWNER)) {
+               RETURN(-EPERM);
        }
 
        RETURN(0);
@@ -1276,6 +1332,10 @@ mdd_xattr_changelog_type(const struct lu_env *env, struct mdd_device *mdd,
        if (strcmp(XATTR_NAME_HSM, xattr_name) == 0)
                return CL_HSM;
 
+       /* Avoid logging SOM xattr for every file */
+       if (strcmp(XATTR_NAME_SOM, xattr_name) == 0)
+               return CL_NONE;
+
        if (has_prefix(xattr_name, XATTR_USER_PREFIX) ||
            has_prefix(xattr_name, XATTR_NAME_POSIX_ACL_ACCESS) ||
            has_prefix(xattr_name, XATTR_NAME_POSIX_ACL_DEFAULT) ||
@@ -1283,7 +1343,7 @@ mdd_xattr_changelog_type(const struct lu_env *env, struct mdd_device *mdd,
            has_prefix(xattr_name, XATTR_SECURITY_PREFIX))
                return CL_SETXATTR;
 
-       return -1;
+       return CL_NONE;
 }
 
 static int mdd_declare_xattr_set(const struct lu_env *env,
@@ -1293,16 +1353,18 @@ static int mdd_declare_xattr_set(const struct lu_env *env,
                                 const char *name,
                                 int fl, struct thandle *handle)
 {
-       int     rc;
+       enum changelog_rec_type type;
+       int rc;
 
        rc = mdo_declare_xattr_set(env, obj, buf, name, fl, handle);
        if (rc)
                return rc;
 
-       if (mdd_xattr_changelog_type(env, mdd, name) < 0)
+       type = mdd_xattr_changelog_type(env, mdd, name);
+       if (type < 0)
                return 0; /* no changelog to store */
 
-       return mdd_declare_changelog_store(env, mdd, NULL, NULL, handle);
+       return mdd_declare_changelog_store(env, mdd, type, NULL, NULL, handle);
 }
 
 /*
@@ -1317,16 +1379,17 @@ static int mdd_declare_xattr_set(const struct lu_env *env,
 static int mdd_hsm_update_locked(const struct lu_env *env,
                                 struct md_object *obj,
                                 const struct lu_buf *buf,
-                                struct thandle *handle, int *cl_flags)
+                                struct thandle *handle,
+                                enum changelog_rec_flags *clf_flags)
 {
        struct mdd_thread_info *info = mdd_env_info(env);
-       struct mdd_object      *mdd_obj = md2mdd_obj(obj);
-       struct lu_buf          *current_buf;
-       struct md_hsm          *current_mh;
-       struct md_hsm          *new_mh;
-       int                     rc;
-       ENTRY;
+       struct mdd_object *mdd_obj = md2mdd_obj(obj);
+       struct lu_buf *current_buf;
+       struct md_hsm *current_mh;
+       struct md_hsm *new_mh;
+       int rc;
 
+       ENTRY;
        OBD_ALLOC_PTR(current_mh);
        if (current_mh == NULL)
                RETURN(-ENOMEM);
@@ -1351,9 +1414,9 @@ static int mdd_hsm_update_locked(const struct lu_env *env,
 
        /* Flags differ, set flags for the changelog that will be added */
        if (current_mh->mh_flags != new_mh->mh_flags) {
-               hsm_set_cl_event(cl_flags, HE_STATE);
+               hsm_set_cl_event(clf_flags, HE_STATE);
                if (new_mh->mh_flags & HS_DIRTY)
-                       hsm_set_cl_flags(cl_flags, CLF_HSM_DIRTY);
+                       hsm_set_cl_flags(clf_flags, CLF_HSM_DIRTY);
        }
 
        OBD_FREE_PTR(new_mh);
@@ -1437,7 +1500,7 @@ static int mdd_xattr_merge(const struct lu_env *env, struct md_object *md_obj,
 
        /* get EA of victim file */
        memset(buf_vic, 0, sizeof(*buf_vic));
-       rc = mdd_get_lov_ea(env, vic, buf_vic);
+       rc = mdd_stripe_get(env, vic, buf_vic, XATTR_NAME_LOV);
        if (rc < 0) {
                if (rc == -ENODATA)
                        rc = 0;
@@ -1451,7 +1514,7 @@ static int mdd_xattr_merge(const struct lu_env *env, struct md_object *md_obj,
 
        /* save EA of target file for restore */
        memset(buf, 0, sizeof(*buf));
-       rc = mdd_get_lov_ea(env, obj, buf);
+       rc = mdd_stripe_get(env, obj, buf, XATTR_NAME_LOV);
        if (rc < 0)
                GOTO(out, rc);
 
@@ -1663,7 +1726,7 @@ static int mdd_xattr_split(const struct lu_env *env, struct md_object *md_obj,
 
        /* get EA of mirrored file */
        memset(buf_save, 0, sizeof(*buf));
-       rc = mdd_get_lov_ea(env, obj, buf_save);
+       rc = mdd_stripe_get(env, obj, buf_save, XATTR_NAME_LOV);
        if (rc < 0)
                GOTO(out, rc);
 
@@ -1761,13 +1824,13 @@ static int mdd_xattr_set(const struct lu_env *env, struct md_object *obj,
                         const struct lu_buf *buf, const char *name,
                         int fl)
 {
-       struct mdd_object       *mdd_obj = md2mdd_obj(obj);
-       struct lu_attr          *attr = MDD_ENV_VAR(env, cattr);
-       struct mdd_device       *mdd = mdo2mdd(obj);
-       struct thandle          *handle;
+       struct mdd_object *mdd_obj = md2mdd_obj(obj);
+       struct lu_attr *attr = MDD_ENV_VAR(env, cattr);
+       struct mdd_device *mdd = mdo2mdd(obj);
+       struct thandle *handle;
        enum changelog_rec_type  cl_type;
-       int                      cl_flags = 0;
-       int                      rc;
+       enum changelog_rec_flags clf_flags = 0;
+       int rc;
        ENTRY;
 
        rc = mdd_la_get(env, mdd_obj, attr);
@@ -1798,6 +1861,11 @@ static int mdd_xattr_set(const struct lu_env *env, struct md_object *obj,
                RETURN(rc);
        }
 
+       if (strcmp(name, XATTR_NAME_LMV) == 0) {
+               rc = mdd_dir_layout_shrink(env, obj, buf);
+               RETURN(rc);
+       }
+
        if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0 ||
            strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0) {
                struct posix_acl *acl;
@@ -1835,7 +1903,7 @@ static int mdd_xattr_set(const struct lu_env *env, struct md_object *obj,
        mdd_write_lock(env, mdd_obj, MOR_TGT_CHILD);
 
        if (strcmp(XATTR_NAME_HSM, name) == 0) {
-               rc = mdd_hsm_update_locked(env, obj, buf, handle, &cl_flags);
+               rc = mdd_hsm_update_locked(env, obj, buf, handle, &clf_flags);
                if (rc) {
                        mdd_write_unlock(env, mdd_obj);
                        GOTO(stop, rc);
@@ -1851,7 +1919,7 @@ static int mdd_xattr_set(const struct lu_env *env, struct md_object *obj,
        if (cl_type < 0)
                GOTO(stop, rc = 0);
 
-       rc = mdd_changelog_data_store_xattr(env, mdd, cl_type, cl_flags,
+       rc = mdd_changelog_data_store_xattr(env, mdd, cl_type, clf_flags,
                                            mdd_obj, name, handle);
 
        EXIT;
@@ -1865,16 +1933,18 @@ static int mdd_declare_xattr_del(const struct lu_env *env,
                                  const char *name,
                                  struct thandle *handle)
 {
+       enum changelog_rec_type type;
        int rc;
 
        rc = mdo_declare_xattr_del(env, obj, name, handle);
        if (rc)
                return rc;
 
-       if (mdd_xattr_changelog_type(env, mdd, name) < 0)
+       type = mdd_xattr_changelog_type(env, mdd, name);
+       if (type < 0)
                return 0; /* no changelog to store */
 
-       return mdd_declare_changelog_store(env, mdd, NULL, NULL, handle);
+       return mdd_declare_changelog_store(env, mdd, type, NULL, NULL, handle);
 }
 
 /**
@@ -1929,58 +1999,50 @@ stop:
 }
 
 /*
- * read lov EA of an object
- * return the lov EA in an allocated lu_buf
+ * read lov/lmv EA of an object
+ * return the lov/lmv EA in an allocated lu_buf
  */
-int mdd_get_lov_ea(const struct lu_env *env, struct mdd_object *obj,
-                  struct lu_buf *lmm_buf)
+int mdd_stripe_get(const struct lu_env *env, struct mdd_object *obj,
+                  struct lu_buf *lmm_buf, const char *name)
 {
-       struct lu_buf   *buf = &mdd_env_info(env)->mti_big_buf;
-       int              rc, bufsize;
+       struct lu_buf *buf = &mdd_env_info(env)->mti_big_buf;
+       int rc;
+
        ENTRY;
 
-repeat:
-       rc = mdo_xattr_get(env, obj, buf, XATTR_NAME_LOV);
+       if (buf->lb_buf == NULL) {
+               buf = lu_buf_check_and_alloc(buf, 4096);
+               if (buf->lb_buf == NULL)
+                       RETURN(-ENOMEM);
+       }
 
+repeat:
+       rc = mdo_xattr_get(env, obj, buf, name);
        if (rc == -ERANGE) {
                /* mti_big_buf is allocated but is too small
                 * we need to increase it */
                buf = lu_buf_check_and_alloc(&mdd_env_info(env)->mti_big_buf,
                                             buf->lb_len * 2);
                if (buf->lb_buf == NULL)
-                       GOTO(out, rc = -ENOMEM);
+                       RETURN(-ENOMEM);
                goto repeat;
-       }
-
-       if (rc < 0)
+       } else if (rc < 0) {
                RETURN(rc);
-
-       if (rc == 0)
+       } else if (rc == 0) {
                RETURN(-ENODATA);
-
-       bufsize = rc;
-       if (memcmp(buf, &LU_BUF_NULL, sizeof(*buf)) == 0) {
-               /* mti_big_buf was not allocated, so we have to
-                * allocate it based on the ea size */
-               buf = lu_buf_check_and_alloc(&mdd_env_info(env)->mti_big_buf,
-                                            bufsize);
-               if (buf->lb_buf == NULL)
-                       GOTO(out, rc = -ENOMEM);
-               goto repeat;
        }
 
-       lu_buf_alloc(lmm_buf, bufsize);
+       lu_buf_alloc(lmm_buf, rc);
        if (lmm_buf->lb_buf == NULL)
-               GOTO(out, rc = -ENOMEM);
+               RETURN(-ENOMEM);
 
-       memcpy(lmm_buf->lb_buf, buf->lb_buf, bufsize);
-       rc = 0;
-       EXIT;
+       /*
+        * we don't use lmm_buf directly, because we don't know xattr size, so
+        * by using mti_big_buf we can avoid calling mdo_xattr_get() twice.
+        */
+       memcpy(lmm_buf->lb_buf, buf->lb_buf, rc);
 
-out:
-       if (rc < 0)
-               lu_buf_free(lmm_buf);
-       return rc;
+       RETURN(0);
 }
 
 static int mdd_xattr_hsm_replace(const struct lu_env *env,
@@ -1988,8 +2050,8 @@ static int mdd_xattr_hsm_replace(const struct lu_env *env,
                                 struct thandle *handle)
 {
        struct hsm_attrs *attrs;
-       __u32 hsm_flags;
-       int flags = 0;
+       enum hsm_states hsm_flags;
+       enum changelog_rec_flags clf_flags = 0;
        int rc;
        ENTRY;
 
@@ -2004,9 +2066,9 @@ static int mdd_xattr_hsm_replace(const struct lu_env *env,
                RETURN(0);
 
        /* Add a changelog record for release. */
-       hsm_set_cl_event(&flags, HE_RELEASE);
+       hsm_set_cl_event(&clf_flags, HE_RELEASE);
        rc = mdd_changelog_data_store(env, mdo2mdd(&o->mod_obj), CL_HSM,
-                                     flags, o, handle);
+                                     clf_flags, o, handle);
        RETURN(rc);
 }
 
@@ -2141,29 +2203,65 @@ static inline int mdd_set_lmm_gen(struct lov_mds_md *lmm, __u32 *gen)
        return mdd_lmm_gen(lmm, gen, false);
 }
 
+static int mdd_dom_data_truncate(const struct lu_env *env,
+                                struct mdd_device *mdd, struct mdd_object *mo)
+{
+       struct thandle *th;
+       struct dt_object *dom;
+       int rc;
+
+       dom = dt_object_locate(mdd_object_child(mo), mdd->mdd_bottom);
+       if (!dom)
+               GOTO(out, rc = -ENODATA);
+
+       th = dt_trans_create(env, mdd->mdd_bottom);
+       if (IS_ERR(th))
+               GOTO(out, rc = PTR_ERR(th));
+
+       rc = dt_declare_punch(env, dom, 0, OBD_OBJECT_EOF, th);
+       if (rc)
+               GOTO(stop, rc);
+
+       rc = dt_trans_start_local(env, mdd->mdd_bottom, th);
+       if (rc != 0)
+               GOTO(stop, rc);
+
+       rc = dt_punch(env, dom, 0, OBD_OBJECT_EOF, th);
+stop:
+       dt_trans_stop(env, mdd->mdd_bottom, th);
+out:
+       /* Ignore failure but report the error */
+       if (rc)
+               CERROR("%s: "DFID" can't truncate DOM inode data, rc = %d\n",
+                      mdd_obj_dev_name(mo), PFID(mdo2fid(mo)), rc);
+       return rc;
+}
+
 /**
  * swap layouts between 2 lustre objects
  */
 static int mdd_swap_layouts(const struct lu_env *env, struct md_object *obj1,
                            struct md_object *obj2, __u64 flags)
 {
-       struct mdd_thread_info  *info = mdd_env_info(env);
-       struct mdd_object       *fst_o = md2mdd_obj(obj1);
-       struct mdd_object       *snd_o = md2mdd_obj(obj2);
-       struct lu_attr          *fst_la = MDD_ENV_VAR(env, cattr);
-       struct lu_attr          *snd_la = MDD_ENV_VAR(env, tattr);
-       struct mdd_device       *mdd = mdo2mdd(obj1);
-       struct lov_mds_md       *fst_lmm, *snd_lmm;
-       struct lu_buf           *fst_buf = &info->mti_buf[0];
-       struct lu_buf           *snd_buf = &info->mti_buf[1];
-       struct lu_buf           *fst_hsm_buf = &info->mti_buf[2];
-       struct lu_buf           *snd_hsm_buf = &info->mti_buf[3];
-       struct ost_id           *saved_oi = NULL;
-       struct thandle          *handle;
-       __u32                    fst_gen, snd_gen, saved_gen;
-       int                      fst_fl;
-       int                      rc;
-       int                      rc2;
+       struct mdd_thread_info *info = mdd_env_info(env);
+       struct mdd_object *fst_o = md2mdd_obj(obj1);
+       struct mdd_object *snd_o = md2mdd_obj(obj2);
+       struct lu_attr *fst_la = MDD_ENV_VAR(env, cattr);
+       struct lu_attr *snd_la = MDD_ENV_VAR(env, tattr);
+       struct mdd_device *mdd = mdo2mdd(obj1);
+       struct lov_mds_md *fst_lmm, *snd_lmm;
+       struct lu_buf *fst_buf = &info->mti_buf[0];
+       struct lu_buf *snd_buf = &info->mti_buf[1];
+       struct lu_buf *fst_hsm_buf = &info->mti_buf[2];
+       struct lu_buf *snd_hsm_buf = &info->mti_buf[3];
+       struct ost_id *saved_oi = NULL;
+       struct thandle *handle;
+       struct mdd_object *dom_o = NULL;
+       __u64 domsize_dom, domsize_vlt;
+       __u32 fst_gen, snd_gen, saved_gen;
+       int fst_fl;
+       int rc, rc2;
+
        ENTRY;
 
        CLASSERT(ARRAY_SIZE(info->mti_buf) >= 4);
@@ -2199,14 +2297,59 @@ static int mdd_swap_layouts(const struct lu_env *env, struct md_object *obj1,
        mdd_write_lock(env, fst_o, MOR_TGT_CHILD);
        mdd_write_lock(env, snd_o, MOR_TGT_CHILD);
 
-       rc = mdd_get_lov_ea(env, fst_o, fst_buf);
+       rc = mdd_stripe_get(env, fst_o, fst_buf, XATTR_NAME_LOV);
        if (rc < 0 && rc != -ENODATA)
                GOTO(stop, rc);
 
-       rc = mdd_get_lov_ea(env, snd_o, snd_buf);
+       rc = mdd_stripe_get(env, snd_o, snd_buf, XATTR_NAME_LOV);
        if (rc < 0 && rc != -ENODATA)
                GOTO(stop, rc);
 
+       /* check if file has DoM. DoM file can be migrated only to another
+        * DoM layout with the same DoM component size or to an non-DOM
+        * layout. After migration to OSTs layout, local MDT inode data
+        * should be truncated.
+        * Objects are sorted by FIDs, considering that original file's FID
+        * is always smaller the snd_o is always original file we are migrating
+        * from.
+        */
+       domsize_dom = mdd_lmm_dom_size(snd_buf->lb_buf);
+       domsize_vlt = mdd_lmm_dom_size(fst_buf->lb_buf);
+
+       /* Only migration is supported for DoM files, not 'swap_layouts' so
+        * target file must be volatile and orphan.
+        */
+       if (fst_o->mod_flags & (ORPHAN_OBJ | VOLATILE_OBJ)) {
+               dom_o = domsize_dom ? snd_o : NULL;
+       } else if (snd_o->mod_flags & (ORPHAN_OBJ | VOLATILE_OBJ)) {
+               swap(domsize_dom, domsize_vlt);
+               dom_o = domsize_dom ? fst_o : NULL;
+       } else if (domsize_dom > 0 || domsize_vlt > 0) {
+               /* 'lfs swap_layouts' case, neither file should have DoM */
+               rc = -EOPNOTSUPP;
+               CDEBUG(D_LAYOUT, "cannot swap layouts with DOM component, "
+                      "use migration instead: rc = %d\n", rc);
+               GOTO(stop, rc);
+       }
+
+       if (domsize_vlt > 0 && domsize_dom == 0) {
+               rc = -EOPNOTSUPP;
+               CDEBUG(D_LAYOUT, "cannot swap layout for "DFID": OST to DOM "
+                      "migration is not supported: rc = %d\n",
+                      PFID(mdo2fid(snd_o)), rc);
+               GOTO(stop, rc);
+       } else if (domsize_vlt > 0 && domsize_dom != domsize_vlt) {
+               rc = -EOPNOTSUPP;
+               CDEBUG(D_LAYOUT, "cannot swap layout for "DFID": new layout "
+                      "must have the same DoM component size: rc = %d\n",
+                      PFID(mdo2fid(fst_o)), rc);
+               GOTO(stop, rc);
+       } else if (domsize_vlt > 0) {
+               /* Migration with the same DOM component size, no need to
+                * truncate local data, it is still being used */
+               dom_o = NULL;
+       }
+
        /* swapping 2 non existant layouts is a success */
        if (fst_buf->lb_buf == NULL && snd_buf->lb_buf == NULL)
                GOTO(stop, rc = 0);
@@ -2409,6 +2552,10 @@ out_restore:
 stop:
        rc = mdd_trans_stop(env, mdd, rc, handle);
 
+       /* Truncate local DOM data if all went well */
+       if (!rc && dom_o)
+               mdd_dom_data_truncate(env, mdd, dom_o);
+
        mdd_write_unlock(env, snd_o);
        mdd_write_unlock(env, fst_o);
 
@@ -2436,7 +2583,8 @@ static int mdd_declare_layout_change(const struct lu_env *env,
        if (rc)
                return rc;
 
-       return mdd_declare_changelog_store(env, mdd, NULL, NULL, handle);
+       return mdd_declare_changelog_store(env, mdd, CL_LAYOUT, NULL, NULL,
+                                          handle);
 }
 
 /* For PFL, this is used to instantiate necessary component objects. */
@@ -2489,32 +2637,49 @@ mdd_layout_update_rdonly(const struct lu_env *env, struct mdd_object *obj,
                         struct md_layout_change *mlc, struct thandle *handle)
 {
        struct mdd_device *mdd = mdd_obj2mdd_dev(obj);
+       struct lu_buf *som_buf = &mdd_env_info(env)->mti_buf[1];
+       struct lustre_som_attrs *som = &mlc->mlc_som;
+       int fl = 0;
        int rc;
        ENTRY;
 
        /* Verify acceptable operations */
        switch (mlc->mlc_opc) {
        case MD_LAYOUT_WRITE:
-               break;
        case MD_LAYOUT_RESYNC:
                /* these are legal operations - this represents the case that
-                * a few mirrors were missed in the last resync.
-                * XXX: it will be supported later */
+                * a few mirrors were missed in the last resync. */
+               break;
        case MD_LAYOUT_RESYNC_DONE:
        default:
                RETURN(0);
        }
 
+       som_buf->lb_buf = som;
+       som_buf->lb_len = sizeof(*som);
+       rc = mdo_xattr_get(env, obj, som_buf, XATTR_NAME_SOM);
+       if (rc < 0 && rc != -ENODATA)
+               RETURN(rc);
+
+       if (rc > 0) {
+               lustre_som_swab(som);
+               if (som->lsa_valid & SOM_FL_STRICT)
+                       fl = LU_XATTR_REPLACE;
+       }
+
        rc = mdd_declare_layout_change(env, mdd, obj, mlc, handle);
        if (rc)
                GOTO(out, rc);
 
-       rc = mdd_declare_xattr_del(env, mdd, obj, XATTR_NAME_SOM, handle);
-       if (rc)
-               GOTO(out, rc);
+       if (fl) {
+               rc = mdd_declare_xattr_set(env, mdd, obj, som_buf,
+                                          XATTR_NAME_SOM, fl, handle);
+               if (rc)
+                       GOTO(out, rc);
+       }
 
        /* record a changelog for data mover to consume */
-       rc = mdd_declare_changelog_store(env, mdd, NULL, NULL, handle);
+       rc = mdd_declare_changelog_store(env, mdd, CL_FLRW, NULL, NULL, handle);
        if (rc)
                GOTO(out, rc);
 
@@ -2527,10 +2692,12 @@ mdd_layout_update_rdonly(const struct lu_env *env, struct mdd_object *obj,
 
        mdd_write_lock(env, obj, MOR_TGT_CHILD);
        rc = mdo_layout_change(env, obj, mlc, handle);
-       if (!rc) {
-               rc = mdo_xattr_del(env, obj, XATTR_NAME_SOM, handle);
-               if (rc == -ENODATA)
-                       rc = 0;
+       if (!rc && fl) {
+               /* SOM state transition from STRICT to STALE */
+               som->lsa_valid = SOM_FL_STALE;
+               lustre_som_swab(som);
+               rc = mdo_xattr_set(env, obj, som_buf, XATTR_NAME_SOM,
+                                  fl, handle);
        }
        mdd_write_unlock(env, obj);
        if (rc)
@@ -2639,12 +2806,13 @@ mdd_object_update_sync_pending(const struct lu_env *env, struct mdd_object *obj,
                RETURN(-EBUSY);
        }
 
-       if (mlc->mlc_som.lsa_valid & LSOM_FL_VALID) {
+       if (mlc->mlc_som.lsa_valid & SOM_FL_STRICT) {
                rc = mdo_xattr_get(env, obj, &LU_BUF_NULL, XATTR_NAME_SOM);
-               if (rc && rc != -ENODATA)
+               if (rc < 0 && rc != -ENODATA)
                        RETURN(rc);
 
                fl = rc == -ENODATA ? LU_XATTR_CREATE : LU_XATTR_REPLACE;
+               lustre_som_swab(&mlc->mlc_som);
                som_buf->lb_buf = &mlc->mlc_som;
                som_buf->lb_len = sizeof(mlc->mlc_som);
        }
@@ -2654,7 +2822,8 @@ mdd_object_update_sync_pending(const struct lu_env *env, struct mdd_object *obj,
                GOTO(out, rc);
 
        /* record a changelog for the completion of resync */
-       rc = mdd_declare_changelog_store(env, mdd, NULL, NULL, handle);
+       rc = mdd_declare_changelog_store(env, mdd, CL_RESYNC, NULL, NULL,
+                                        handle);
        if (rc)
                GOTO(out, rc);
 
@@ -2725,7 +2894,7 @@ mdd_layout_change(const struct lu_env *env, struct md_object *o,
        if (IS_ERR(handle))
                RETURN(PTR_ERR(handle));
 
-       rc = mdd_get_lov_ea(env, obj, buf);
+       rc = mdd_stripe_get(env, obj, buf, XATTR_NAME_LOV);
        if (rc < 0) {
                if (rc == -ENODATA)
                        rc = -EINVAL;
@@ -2792,54 +2961,47 @@ void mdd_object_make_hint(const struct lu_env *env, struct mdd_object *parent,
        nc->do_ops->do_ah_init(env, hint, np, nc, attr->la_mode & S_IFMT);
 }
 
-/*
- * do NOT or the MAY_*'s, you'll get the weakest
- */
-int accmode(const struct lu_env *env, const struct lu_attr *la, int flags)
+static int accmode(const struct lu_env *env, const struct lu_attr *la,
+                  u64 open_flags)
 {
-       int res = 0;
-
        /* Sadly, NFSD reopens a file repeatedly during operation, so the
         * "acc_mode = 0" allowance for newly-created files isn't honoured.
         * NFSD uses the MDS_OPEN_OWNEROVERRIDE flag to say that a file
         * owner can write to a file even if it is marked readonly to hide
         * its brokenness. (bug 5781) */
-       if (flags & MDS_OPEN_OWNEROVERRIDE) {
+       if (open_flags & MDS_OPEN_OWNEROVERRIDE) {
                struct lu_ucred *uc = lu_ucred_check(env);
 
                if ((uc == NULL) || (la->la_uid == uc->uc_fsuid))
                        return 0;
        }
 
-       if (flags & FMODE_READ)
-               res |= MAY_READ;
-       if (flags & (FMODE_WRITE | MDS_OPEN_TRUNC | MDS_OPEN_APPEND))
-               res |= MAY_WRITE;
-       if (flags & MDS_FMODE_EXEC)
-               res = MAY_EXEC;
-       return res;
+       return mds_accmode(open_flags);
 }
 
 static int mdd_open_sanity_check(const struct lu_env *env,
-                               struct mdd_object *obj,
-                               const struct lu_attr *attr, int flag)
+                                struct mdd_object *obj,
+                                const struct lu_attr *attr, u64 open_flags)
 {
        int mode, rc;
        ENTRY;
 
-       /* EEXIST check */
-       if (mdd_is_dead_obj(obj))
+       /* EEXIST check, also opening of *open* orphans is allowed so we can
+        * open-by-handle unlinked files
+        */
+       if (mdd_is_dead_obj(obj) &&
+           likely(!(mdd_is_orphan_obj(obj) && obj->mod_count > 0)))
                RETURN(-ENOENT);
 
        if (S_ISLNK(attr->la_mode))
                RETURN(-ELOOP);
 
-       mode = accmode(env, attr, flag);
+       mode = accmode(env, attr, open_flags);
 
        if (S_ISDIR(attr->la_mode) && (mode & MAY_WRITE))
                RETURN(-EISDIR);
 
-       if (!(flag & MDS_OPEN_CREATED)) {
+       if (!(open_flags & MDS_OPEN_CREATED)) {
                rc = mdd_permission_internal(env, obj, attr, mode);
                if (rc)
                        RETURN(rc);
@@ -2847,13 +3009,14 @@ static int mdd_open_sanity_check(const struct lu_env *env,
 
        if (S_ISFIFO(attr->la_mode) || S_ISSOCK(attr->la_mode) ||
            S_ISBLK(attr->la_mode) || S_ISCHR(attr->la_mode))
-               flag &= ~MDS_OPEN_TRUNC;
+               open_flags &= ~MDS_OPEN_TRUNC;
 
        /* For writing append-only file must open it with append mode. */
        if (attr->la_flags & LUSTRE_APPEND_FL) {
-               if ((flag & FMODE_WRITE) && !(flag & MDS_OPEN_APPEND))
+               if ((open_flags & MDS_FMODE_WRITE) &&
+                   !(open_flags & MDS_OPEN_APPEND))
                        RETURN(-EPERM);
-               if (flag & MDS_OPEN_TRUNC)
+               if (open_flags & MDS_OPEN_TRUNC)
                        RETURN(-EPERM);
        }
 
@@ -2861,7 +3024,7 @@ static int mdd_open_sanity_check(const struct lu_env *env,
 }
 
 static int mdd_open(const struct lu_env *env, struct md_object *obj,
-                   int flags)
+                   u64 open_flags)
 {
        struct mdd_object *mdd_obj = md2mdd_obj(obj);
        struct md_device *md_dev = lu2md_dev(mdd2lu_dev(mdo2mdd(obj)));
@@ -2869,6 +3032,7 @@ static int mdd_open(const struct lu_env *env, struct md_object *obj,
        struct mdd_object_user *mou = NULL;
        const struct lu_ucred *uc = lu_ucred(env);
        struct mdd_device *mdd = mdo2mdd(obj);
+       enum changelog_rec_type type = CL_OPEN;
        int rc = 0;
 
        mdd_write_lock(env, mdd_obj, MOR_TGT_CHILD);
@@ -2877,56 +3041,78 @@ static int mdd_open(const struct lu_env *env, struct md_object *obj,
        if (rc != 0)
                GOTO(out, rc);
 
-       rc = mdd_open_sanity_check(env, mdd_obj, attr, flags);
-       if (rc != 0)
+       rc = mdd_open_sanity_check(env, mdd_obj, attr, open_flags);
+       if ((rc == -EACCES) && (mdd->mdd_cl.mc_mask & (1 << CL_DN_OPEN)))
+               type = CL_DN_OPEN;
+       else if (rc != 0)
                GOTO(out, rc);
+       else
+               mdd_obj->mod_count++;
 
-       mdd_obj->mod_count++;
-
-       /* Not recording */
-       if (!(mdd->mdd_cl.mc_flags & CLM_ON))
-               GOTO(out, rc);
-       if (!(mdd->mdd_cl.mc_mask & (1 << CL_OPEN)))
+       if (!mdd_changelog_enabled(env, mdd, type))
                GOTO(out, rc);
 
 find:
        /* look for existing opener in list under mdd_write_lock */
-       mou = mdd_obj_user_find(mdd_obj, uc->uc_uid, uc->uc_gid, flags);
+       mou = mdd_obj_user_find(mdd_obj, uc->uc_uid, uc->uc_gid, open_flags);
 
        if (!mou) {
+               int rc2;
+
                /* add user to list */
-               mou = mdd_obj_user_alloc(flags, uc->uc_uid, uc->uc_gid);
-               if (IS_ERR(mou))
-                       GOTO(out, rc = PTR_ERR(mou));
-               rc = mdd_obj_user_add(mdd_obj, mou);
-               if (rc != 0) {
+               mou = mdd_obj_user_alloc(open_flags, uc->uc_uid, uc->uc_gid);
+               if (IS_ERR(mou)) {
+                       if (rc == 0)
+                               rc = PTR_ERR(mou);
+                       GOTO(out, rc);
+               }
+               rc2 = mdd_obj_user_add(mdd_obj, mou, type == CL_DN_OPEN);
+               if (rc2 != 0) {
                        mdd_obj_user_free(mou);
-                       if (rc == -EEXIST)
-                               GOTO(find, rc);
+                       if (rc2 == -EEXIST)
+                               GOTO(find, rc2);
                }
        } else {
-               mou->mou_opencount++;
-               /* same user opening file again with same flags: don't record */
-               GOTO(out, rc);
+               if (type == CL_DN_OPEN) {
+                       if (ktime_before(ktime_get(), mou->mou_deniednext))
+                               /* same user denied again same access within
+                                * time interval: do not record
+                                */
+                               GOTO(out, rc);
+
+                       /* this user already denied, but some time ago:
+                        * update denied time
+                        */
+                       mou->mou_deniednext =
+                               ktime_add(ktime_get(),
+                                         ktime_set(mdd->mdd_cl.mc_deniednext,
+                                                   0));
+               } else {
+                       mou->mou_opencount++;
+                       /* same user opening file again with same flags:
+                        * don't record
+                        */
+                       GOTO(out, rc);
+               }
        }
 
-       mdd_changelog(env, CL_OPEN, flags, md_dev, mdo2fid(mdd_obj));
+       /* FYI, only the bottom 32 bits of open_flags are recorded */
+       mdd_changelog(env, type, open_flags, md_dev, mdo2fid(mdd_obj));
+
        EXIT;
 out:
        mdd_write_unlock(env, mdd_obj);
        return rc;
 }
 
-static int mdd_declare_close(const struct lu_env *env,
-                             struct mdd_object *obj,
-                             struct md_attr *ma,
-                             struct thandle *handle)
+static int mdd_declare_close(const struct lu_env *env, struct mdd_object *obj,
+                            struct md_attr *ma, struct thandle *handle)
 {
-        int rc;
+       int rc;
 
-        rc = orph_declare_index_delete(env, obj, handle);
-        if (rc)
-                return rc;
+       rc = mdd_orphan_declare_delete(env, obj, handle);
+       if (rc)
+               return rc;
 
        return mdo_declare_destroy(env, obj, handle);
 }
@@ -2935,7 +3121,7 @@ static int mdd_declare_close(const struct lu_env *env,
  * No permission check is needed.
  */
 static int mdd_close(const struct lu_env *env, struct md_object *obj,
-                    struct md_attr *ma, int mode)
+                    struct md_attr *ma, u64 open_flags)
 {
        struct mdd_object *mdd_obj = md2mdd_obj(obj);
        struct mdd_device *mdd = mdo2mdd(obj);
@@ -2992,7 +3178,8 @@ again:
                if (rc)
                        GOTO(stop, rc);
 
-               rc = mdd_declare_changelog_store(env, mdd, NULL, NULL, handle);
+               rc = mdd_declare_changelog_store(env, mdd, CL_CLOSE, NULL, NULL,
+                                                handle);
                if (rc)
                        GOTO(stop, rc);
 
@@ -3025,8 +3212,7 @@ cont:
 
        /* under mdd write lock */
        /* If recording, see if we need to remove UID from list */
-       if ((mdd->mdd_cl.mc_flags & CLM_ON) &&
-           (mdd->mdd_cl.mc_mask & (1 << CL_OPEN))) {
+       if (mdd_changelog_enabled(env, mdd, CL_OPEN)) {
                struct mdd_object_user *mou;
 
                /* look for UID in list */
@@ -3034,7 +3220,8 @@ cont:
                 * the user had the file open. So the corresponding close
                 * will not be logged.
                 */
-               mou = mdd_obj_user_find(mdd_obj, uc->uc_uid, uc->uc_gid, mode);
+               mou = mdd_obj_user_find(mdd_obj, uc->uc_uid, uc->uc_gid,
+                                       open_flags);
                if (mou) {
                        mou->mou_opencount--;
                        if (mou->mou_opencount == 0) {
@@ -3054,7 +3241,7 @@ cont:
        if ((mdd_obj->mod_flags & ORPHAN_OBJ) != 0) {
                /* remove link to object from orphan index */
                LASSERT(handle != NULL);
-               rc = __mdd_orphan_del(env, mdd_obj, handle);
+               rc = mdd_orphan_delete(env, mdd_obj, handle);
                if (rc != 0) {
                        CERROR("%s: unable to delete "DFID" from orphan list: "
                               "rc = %d\n", lu_dev_name(mdd2lu_dev(mdd)),
@@ -3082,15 +3269,19 @@ cont:
 out:
        mdd_write_unlock(env, mdd_obj);
 
+       if (rc != 0 || blocked ||
+           !mdd_changelog_enabled(env, mdd, CL_CLOSE))
+               GOTO(stop, rc);
+
        /* Record CL_CLOSE in changelog only if file was opened in write mode,
         * or if CL_OPEN was recorded and it's last close by user.
         * Changelogs mask may change between open and close operations, but
         * this is not a big deal if we have a CL_CLOSE entry with no matching
         * CL_OPEN. Plus Changelogs mask may not change often.
         */
-       if (!rc && !blocked &&
-           ((!(mdd->mdd_cl.mc_mask & (1 << CL_OPEN)) &&
-             (mode & (FMODE_WRITE | MDS_OPEN_APPEND | MDS_OPEN_TRUNC))) ||
+       if (((!(mdd->mdd_cl.mc_mask & (1 << CL_OPEN)) &&
+             (open_flags & (MDS_FMODE_WRITE | MDS_OPEN_APPEND |
+                            MDS_OPEN_TRUNC))) ||
             ((mdd->mdd_cl.mc_mask & (1 << CL_OPEN)) && last_close_by_uid)) &&
            !(ma->ma_valid & MA_FLAGS && ma->ma_attr_flags & MDS_RECOV_OPEN)) {
                if (handle == NULL) {
@@ -3098,19 +3289,20 @@ out:
                        if (IS_ERR(handle))
                                GOTO(stop, rc = PTR_ERR(handle));
 
-                       rc = mdd_declare_changelog_store(env, mdd, NULL, NULL,
-                                                        handle);
+                       rc = mdd_declare_changelog_store(env, mdd, CL_CLOSE,
+                                                        NULL, NULL, handle);
                        if (rc)
                                GOTO(stop, rc);
 
-                        rc = mdd_trans_start(env, mdo2mdd(obj), handle);
-                        if (rc)
-                                GOTO(stop, rc);
-                }
+                       rc = mdd_trans_start(env, mdo2mdd(obj), handle);
+                       if (rc)
+                               GOTO(stop, rc);
+               }
 
-                mdd_changelog_data_store(env, mdd, CL_CLOSE, mode,
-                                         mdd_obj, handle);
-        }
+               /* FYI, only the bottom 32 bits of open_flags are recorded */
+               mdd_changelog_data_store(env, mdd, CL_CLOSE, open_flags,
+                                        mdd_obj, handle);
+       }
 
 stop:
        if (handle != NULL && !IS_ERR(handle))