From 45c1f367a1379cb7adf9e2ed79c1221913eaf2e2 Mon Sep 17 00:00:00 2001
From: fanyong <fanyong>
Date: Tue, 1 Jul 2008 02:06:00 +0000
Subject: [PATCH] Branch HEAD b=15797 i=huanghua i=vitaly

Misc fix for updating file {a/m/c}time as following policy:
1) Lustre file "{a/m/c}time" should base on client-side clock, including all
the file operations.
2) All the client-side clocks should be synchronized. It is no matter whether
the server-side clocks are synchronized with client-side ones or not.
3) File "ctime" is increased only.
4) File "{a,m}time" are increased only, except "utime()" operation can change
them backwards.
---
 lustre/cmm/cmm_object.c            |  15 +--
 lustre/cmm/cmm_split.c             |   2 +-
 lustre/include/lu_object.h         |   1 +
 lustre/include/lustre/lustre_idl.h |   5 +-
 lustre/include/md_object.h         |  14 +--
 lustre/liblustre/super.c           |   2 +-
 lustre/llite/llite_lib.c           |   2 +-
 lustre/mdc/mdc_lib.c               |   2 +
 lustre/mdc/mdc_request.c           |   5 +-
 lustre/mdd/mdd_dir.c               |  29 ++---
 lustre/mdd/mdd_internal.h          |  22 ++--
 lustre/mdd/mdd_object.c            | 166 +++++++++++++++++++++--------
 lustre/mdt/mdt_lib.c               |  11 +-
 lustre/mdt/mdt_xattr.c             |  19 ++--
 lustre/osd/osd_handler.c           |  34 ++++--
 15 files changed, 228 insertions(+), 101 deletions(-)

diff --git a/lustre/cmm/cmm_object.c b/lustre/cmm/cmm_object.c
index e6212dc6d6..3cb6962f02 100644
--- a/lustre/cmm/cmm_object.c
+++ b/lustre/cmm/cmm_object.c
@@ -258,21 +258,21 @@ static int cml_xattr_list(const struct lu_env *env, struct md_object *mo,
 }
 
 static int cml_xattr_set(const struct lu_env *env, struct md_object *mo,
-                         const struct lu_buf *buf,
-                         const char *name, int fl)
+                         const struct lu_buf *buf, const char *name,
+                         int fl, const struct lu_attr *la)
 {
         int rc;
         ENTRY;
-        rc = mo_xattr_set(env, md_object_next(mo), buf, name, fl);
+        rc = mo_xattr_set(env, md_object_next(mo), buf, name, fl, la);
         RETURN(rc);
 }
 
 static int cml_xattr_del(const struct lu_env *env, struct md_object *mo,
-                         const char *name)
+                         const char *name, const struct lu_attr *la)
 {
         int rc;
         ENTRY;
-        rc = mo_xattr_del(env, md_object_next(mo), name);
+        rc = mo_xattr_del(env, md_object_next(mo), name, la);
         RETURN(rc);
 }
 
@@ -877,13 +877,14 @@ static int cmr_xattr_list(const struct lu_env *env, struct md_object *mo,
 }
 
 static int cmr_xattr_set(const struct lu_env *env, struct md_object *mo,
-                         const struct lu_buf *buf, const char *name, int fl)
+                         const struct lu_buf *buf, const char *name,
+                         int fl, const struct lu_attr *la)
 {
         return -EFAULT;
 }
 
 static int cmr_xattr_del(const struct lu_env *env, struct md_object *mo,
-                         const char *name)
+                         const char *name, const struct lu_attr *la)
 {
         return -EFAULT;
 }
diff --git a/lustre/cmm/cmm_split.c b/lustre/cmm/cmm_split.c
index 42ca592a8d..6893ab3dbd 100644
--- a/lustre/cmm/cmm_split.c
+++ b/lustre/cmm/cmm_split.c
@@ -707,7 +707,7 @@ int cmm_split_dir(const struct lu_env *env, struct md_object *mo)
         LASSERT(ma->ma_valid & MA_LMV);
         buf = cmm_buf_get(env, ma->ma_lmv, ma->ma_lmv_size);
         rc = mo_xattr_set(env, md_object_next(mo), buf,
-                          MDS_LMV_MD_NAME, 0);
+                          MDS_LMV_MD_NAME, 0, NULL);
         if (rc) {
                 CERROR("Can't set MEA to master dir, " "rc %d\n", rc);
                 GOTO(cleanup, rc);
diff --git a/lustre/include/lu_object.h b/lustre/include/lu_object.h
index cf83ef1913..378a8fc96c 100644
--- a/lustre/include/lu_object.h
+++ b/lustre/include/lu_object.h
@@ -356,6 +356,7 @@ enum la_valid {
         LA_NLINK  = 1 << 10,
         LA_RDEV   = 1 << 11,
         LA_BLKSIZE = 1 << 12,
+        LA_TRUNC = 1 << 13,
 };
 
 struct lu_attr {
diff --git a/lustre/include/lustre/lustre_idl.h b/lustre/include/lustre/lustre_idl.h
index 0273ab1880..9d4913f768 100644
--- a/lustre/include/lustre/lustre_idl.h
+++ b/lustre/include/lustre/lustre_idl.h
@@ -1295,6 +1295,9 @@ extern void lustre_swab_mdt_rec_setattr (struct mdt_rec_setattr *sa);
 #define MDS_ATTR_CTIME_SET  0x2000ULL /* = 8192 */
 #define MDS_ATTR_FROM_OPEN  0x4000ULL /* = 16384, called from open path, ie O_TRUNC */
 #define MDS_ATTR_BLOCKS     0x8000ULL /* = 32768 */
+#define MDS_ATTR_TRUNC     0x10000ULL /* = 65536 */
+
+#define ATTR_TRUNC MDS_ATTR_TRUNC
 
 #ifndef FMODE_READ
 #define FMODE_READ               00000001
@@ -1544,7 +1547,7 @@ struct mdt_rec_setxattr {
         __u32           sx_padding_2;
         __u32           sx_padding_3;
         __u64           sx_valid;
-        __u64           sx_padding_4;
+        __u64           sx_time;
         __u64           sx_padding_5;
         __u64           sx_padding_6;
         __u64           sx_padding_7;
diff --git a/lustre/include/md_object.h b/lustre/include/md_object.h
index 432cf0d858..bc677599e6 100644
--- a/lustre/include/md_object.h
+++ b/lustre/include/md_object.h
@@ -186,10 +186,10 @@ struct md_object_operations {
 
         int (*moo_xattr_set)(const struct lu_env *env, struct md_object *obj,
                              const struct lu_buf *buf, const char *name,
-                             int fl);
+                             int fl, const struct lu_attr *la);
 
         int (*moo_xattr_del)(const struct lu_env *env, struct md_object *obj,
-                             const char *name);
+                             const char *name, const struct lu_attr *la);
 
         int (*moo_readpage)(const struct lu_env *env, struct md_object *obj,
                             const struct lu_rdpg *rdpg);
@@ -452,20 +452,22 @@ static inline int mo_xattr_get(const struct lu_env *env,
 
 static inline int mo_xattr_del(const struct lu_env *env,
                                struct md_object *m,
-                               const char *name)
+                               const char *name,
+                               const struct lu_attr *la)
 {
         LASSERT(m->mo_ops->moo_xattr_del);
-        return m->mo_ops->moo_xattr_del(env, m, name);
+        return m->mo_ops->moo_xattr_del(env, m, name, la);
 }
 
 static inline int mo_xattr_set(const struct lu_env *env,
                                struct md_object *m,
                                const struct lu_buf *buf,
                                const char *name,
-                               int flags)
+                               int flags,
+                               const struct lu_attr *la)
 {
         LASSERT(m->mo_ops->moo_xattr_set);
-        return m->mo_ops->moo_xattr_set(env, m, buf, name, flags);
+        return m->mo_ops->moo_xattr_set(env, m, buf, name, flags, la);
 }
 
 static inline int mo_xattr_list(const struct lu_env *env,
diff --git a/lustre/liblustre/super.c b/lustre/liblustre/super.c
index 42d6b4e156..48d5efa8a4 100644
--- a/lustre/liblustre/super.c
+++ b/lustre/liblustre/super.c
@@ -730,7 +730,7 @@ int llu_setattr_raw(struct inode *inode, struct iattr *attr)
                         RETURN(-EFBIG);
                 }
 
-                attr->ia_valid |= ATTR_MTIME | ATTR_CTIME;
+                attr->ia_valid |= ATTR_MTIME | ATTR_CTIME | ATTR_TRUNC;
         }
 
         /* We mark all of the fields "set" so MDS/OST does not re-set them */
diff --git a/lustre/llite/llite_lib.c b/lustre/llite/llite_lib.c
index 76104a93cc..0d2e1cab9d 100644
--- a/lustre/llite/llite_lib.c
+++ b/lustre/llite/llite_lib.c
@@ -1404,7 +1404,7 @@ int ll_setattr_raw(struct inode *inode, struct iattr *attr)
                         RETURN(-EFBIG);
                 }
 
-                attr->ia_valid |= ATTR_MTIME | ATTR_CTIME;
+                attr->ia_valid |= ATTR_MTIME | ATTR_CTIME | ATTR_TRUNC;
         }
 
         /* POSIX: check before ATTR_*TIME_SET set (from inode_change_ok) */
diff --git a/lustre/mdc/mdc_lib.c b/lustre/mdc/mdc_lib.c
index a6bc50f3bc..4d7cdf261d 100644
--- a/lustre/mdc/mdc_lib.c
+++ b/lustre/mdc/mdc_lib.c
@@ -274,6 +274,8 @@ static inline __u64 attr_pack(unsigned int ia_valid) {
                 sa_valid |= MDS_ATTR_FROM_OPEN;
         if (ia_valid & ATTR_BLOCKS)
                 sa_valid |= MDS_ATTR_BLOCKS;
+        if (ia_valid & ATTR_TRUNC)
+                sa_valid |= MDS_ATTR_TRUNC;
         if (ia_valid & MDS_OPEN_OWNEROVERRIDE)
                 /* NFSD hack (see bug 5781) */
                 sa_valid |= MDS_OPEN_OWNEROVERRIDE;
diff --git a/lustre/mdc/mdc_request.c b/lustre/mdc/mdc_request.c
index 7148cf9401..cb4b12da78 100644
--- a/lustre/mdc/mdc_request.c
+++ b/lustre/mdc/mdc_request.c
@@ -364,9 +364,10 @@ static int mdc_xattr_common(struct obd_export *exp,const struct req_format *fmt,
                 rec->sx_fsgid  = current->fsgid;
                 rec->sx_cap    = current->cap_effective;
                 rec->sx_suppgid1 = suppgid;
-                rec->sx_suppgid1 = -1;
+                rec->sx_suppgid2 = -1;
                 rec->sx_fid    = *fid;
-                rec->sx_valid  = valid;
+                rec->sx_valid  = valid | OBD_MD_FLCTIME;
+                rec->sx_time   = cfs_time_current_sec();
                 rec->sx_size   = output_size;
                 rec->sx_flags  = flags;
 
diff --git a/lustre/mdd/mdd_dir.c b/lustre/mdd/mdd_dir.c
index c90f83ba01..ba4b1e3c7a 100644
--- a/lustre/mdd/mdd_dir.c
+++ b/lustre/mdd/mdd_dir.c
@@ -558,12 +558,12 @@ static int mdd_link(const struct lu_env *env, struct md_object *tgt_obj,
         la->la_ctime = la->la_mtime = ma->ma_attr.la_ctime;
 
         la->la_valid = LA_CTIME | LA_MTIME;
-        rc = mdd_attr_set_internal_locked(env, mdd_tobj, la, handle, 0);
+        rc = mdd_attr_check_set_internal_locked(env, mdd_tobj, la, handle, 0);
         if (rc)
                 GOTO(out_unlock, rc);
 
         la->la_valid = LA_CTIME;
-        rc = mdd_attr_set_internal(env, mdd_sobj, la, handle, 0);
+        rc = mdd_attr_check_set_internal(env, mdd_sobj, la, handle, 0);
         EXIT;
 out_unlock:
         mdd_write_unlock(env, mdd_sobj);
@@ -666,12 +666,12 @@ static int mdd_unlink(const struct lu_env *env, struct md_object *pobj,
         la->la_ctime = la->la_mtime = ma->ma_attr.la_ctime;
 
         la->la_valid = LA_CTIME | LA_MTIME;
-        rc = mdd_attr_set_internal_locked(env, mdd_pobj, la, handle, 0);
+        rc = mdd_attr_check_set_internal_locked(env, mdd_pobj, la, handle, 0);
         if (rc)
                 GOTO(cleanup, rc);
 
         la->la_valid = LA_CTIME;
-        rc = mdd_attr_set_internal(env, mdd_cobj, la, handle, 0);
+        rc = mdd_attr_check_set_internal(env, mdd_cobj, la, handle, 0);
         if (rc)
                 GOTO(cleanup, rc);
 
@@ -753,7 +753,8 @@ static int mdd_name_insert(const struct lu_env *env,
         if (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_attr_set_internal_locked(env, mdd_obj, la, handle, 0);
+                rc = mdd_attr_check_set_internal_locked(env, mdd_obj, la,
+                                                        handle, 0);
         }
         EXIT;
 out_unlock:
@@ -825,7 +826,8 @@ static int mdd_name_remove(const struct lu_env *env,
         if (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_attr_set_internal_locked(env, mdd_obj, la, handle, 0);
+                rc = mdd_attr_check_set_internal_locked(env, mdd_obj, la,
+                                                        handle, 0);
         }
         EXIT;
 out_unlock:
@@ -910,7 +912,7 @@ static int mdd_rename_tgt(const struct lu_env *env,
         la->la_ctime = la->la_mtime = ma->ma_attr.la_ctime;
 
         la->la_valid = LA_CTIME | LA_MTIME;
-        rc = mdd_attr_set_internal_locked(env, mdd_tpobj, la, handle, 0);
+        rc = mdd_attr_check_set_internal_locked(env, mdd_tpobj, la, handle, 0);
         if (rc)
                 GOTO(cleanup, rc);
 
@@ -927,7 +929,7 @@ static int mdd_rename_tgt(const struct lu_env *env,
                         mdo_ref_del(env, mdd_tobj, handle);
 
                 la->la_valid = LA_CTIME;
-                rc = mdd_attr_set_internal(env, mdd_tobj, la, handle, 0);
+                rc = mdd_attr_check_set_internal(env, mdd_tobj, la, handle, 0);
                 if (rc)
                         GOTO(cleanup, rc);
 
@@ -1361,7 +1363,7 @@ static int mdd_create(const struct lu_env *env,
 
         *la = ma->ma_attr;
         la->la_valid = LA_CTIME | LA_MTIME;
-        rc = mdd_attr_set_internal_locked(env, mdd_pobj, la, handle, 0);
+        rc = mdd_attr_check_set_internal_locked(env, mdd_pobj, la, handle, 0);
         if (rc)
                 GOTO(cleanup, rc);
 
@@ -1568,7 +1570,8 @@ static int mdd_rename(const struct lu_env *env,
         /* XXX: mdd_sobj must be local one if it is NOT NULL. */
         if (mdd_sobj) {
                 la->la_valid = LA_CTIME;
-                rc = mdd_attr_set_internal_locked(env, mdd_sobj, la, handle, 0);
+                rc = mdd_attr_check_set_internal_locked(env, mdd_sobj, la,
+                                                        handle, 0);
                 if (rc)
                         GOTO(cleanup, rc);
         }
@@ -1587,7 +1590,7 @@ static int mdd_rename(const struct lu_env *env,
                         mdo_ref_del(env, mdd_tobj, handle);
 
                 la->la_valid = LA_CTIME;
-                rc = mdd_attr_set_internal(env, mdd_tobj, la, handle, 0);
+                rc = mdd_attr_check_set_internal(env, mdd_tobj, la, handle, 0);
                 if (rc)
                         GOTO(cleanup, rc);
 
@@ -1598,13 +1601,13 @@ static int mdd_rename(const struct lu_env *env,
         }
 
         la->la_valid = LA_CTIME | LA_MTIME;
-        rc = mdd_attr_set_internal_locked(env, mdd_spobj, la, handle, 0);
+        rc = mdd_attr_check_set_internal_locked(env, mdd_spobj, la, handle, 0);
         if (rc)
                 GOTO(cleanup, rc);
 
         if (mdd_spobj != mdd_tpobj) {
                 la->la_valid = LA_CTIME | LA_MTIME;
-                rc = mdd_attr_set_internal_locked(env, mdd_tpobj, la,
+                rc = mdd_attr_check_set_internal_locked(env, mdd_tpobj, la,
                                                   handle, 0);
         }
 
diff --git a/lustre/mdd/mdd_internal.h b/lustre/mdd/mdd_internal.h
index db71b9a66f..e77e84f472 100644
--- a/lustre/mdd/mdd_internal.h
+++ b/lustre/mdd/mdd_internal.h
@@ -153,9 +153,16 @@ int mdd_get_md_locked(const struct lu_env *env, struct mdd_object *obj,
                       void *md, int *md_size, const char *name);
 int mdd_la_get(const struct lu_env *env, struct mdd_object *obj,
                struct lu_attr *la, struct lustre_capa *capa);
-int mdd_attr_set_internal(const struct lu_env *env, struct mdd_object *o,
-                          const struct lu_attr *attr, struct thandle *handle,
-                          const int needacl);
+int mdd_attr_set_internal(const struct lu_env *env,
+                          struct mdd_object *obj,
+                          struct lu_attr *attr,
+                          struct thandle *handle,
+                          int needacl);
+int mdd_attr_check_set_internal(const struct lu_env *env,
+                                struct mdd_object *obj,
+                                struct lu_attr *attr,
+                                struct thandle *handle,
+                                int needacl);
 int mdd_object_kill(const struct lu_env *env, struct mdd_object *obj,
                     struct md_attr *ma);
 int mdd_iattr_get(const struct lu_env *env, struct mdd_object *mdd_obj,
@@ -166,10 +173,11 @@ int mdd_attr_get_internal_locked(const struct lu_env *env,
 int mdd_object_create_internal(const struct lu_env *env, struct mdd_object *p,
                                struct mdd_object *c, struct md_attr *ma,
                                struct thandle *handle);
-int mdd_attr_set_internal_locked(const struct lu_env *env,
-                                 struct mdd_object *o,
-                                 const struct lu_attr *attr,
-                                 struct thandle *handle, const int needacl);
+int mdd_attr_check_set_internal_locked(const struct lu_env *env,
+                                       struct mdd_object *obj,
+                                       struct lu_attr *attr,
+                                       struct thandle *handle,
+                                       int needacl);
 int mdd_lmm_get_locked(const struct lu_env *env, struct mdd_object *mdd_obj,
                        struct md_attr *ma);
 /* mdd_lock.c */
diff --git a/lustre/mdd/mdd_object.c b/lustre/mdd/mdd_object.c
index a177f05d25..83e4a3822e 100644
--- a/lustre/mdd/mdd_object.c
+++ b/lustre/mdd/mdd_object.c
@@ -510,10 +510,33 @@ int mdd_object_create_internal(const struct lu_env *env, struct mdd_object *p,
         RETURN(rc);
 }
 
+static inline int mdd_attr_check(const struct lu_env *env,
+                                 struct mdd_object *obj,
+                                 struct lu_attr *attr)
+{
+        struct lu_attr *tmp_la = &mdd_env_info(env)->mti_la;
+        int rc;
+        ENTRY;
+
+        if (attr->la_valid & LA_CTIME) {
+                rc = mdd_la_get(env, obj, tmp_la, BYPASS_CAPA);
+                if (rc)
+                        RETURN(rc);
 
-int mdd_attr_set_internal(const struct lu_env *env, struct mdd_object *obj,
-                          const struct lu_attr *attr, struct thandle *handle,
-                          const int needacl)
+                if (attr->la_ctime < tmp_la->la_ctime)
+                        attr->la_valid &= ~(LA_MTIME | LA_CTIME);
+                else if (attr->la_valid == LA_CTIME &&
+                         attr->la_ctime == tmp_la->la_ctime)
+                        attr->la_valid &= ~LA_CTIME;
+        }
+        RETURN(0);
+}
+
+int mdd_attr_set_internal(const struct lu_env *env,
+                          struct mdd_object *obj,
+                          struct lu_attr *attr,
+                          struct thandle *handle,
+                          int needacl)
 {
         int rc;
         ENTRY;
@@ -526,10 +549,29 @@ int mdd_attr_set_internal(const struct lu_env *env, struct mdd_object *obj,
         RETURN(rc);
 }
 
-int mdd_attr_set_internal_locked(const struct lu_env *env,
-                                 struct mdd_object *o,
-                                 const struct lu_attr *attr,
-                                 struct thandle *handle, int needacl)
+int mdd_attr_check_set_internal(const struct lu_env *env,
+                                struct mdd_object *obj,
+                                struct lu_attr *attr,
+                                struct thandle *handle,
+                                int needacl)
+{
+        int rc;
+        ENTRY;
+
+        rc = mdd_attr_check(env, obj, attr);
+        if (rc)
+                RETURN(rc);
+
+        if (attr->la_valid)
+                rc = mdd_attr_set_internal(env, obj, attr, handle, needacl);
+        RETURN(rc);
+}
+
+static int mdd_attr_set_internal_locked(const struct lu_env *env,
+                                        struct mdd_object *obj,
+                                        struct lu_attr *attr,
+                                        struct thandle *handle,
+                                        int needacl)
 {
         int rc;
         ENTRY;
@@ -537,12 +579,31 @@ int mdd_attr_set_internal_locked(const struct lu_env *env,
         needacl = needacl && (attr->la_valid & LA_MODE);
 
         if (needacl)
-                mdd_write_lock(env, o);
+                mdd_write_lock(env, obj);
 
-        rc = mdd_attr_set_internal(env, o, attr, handle, needacl);
+        rc = mdd_attr_set_internal(env, obj, attr, handle, needacl);
 
         if (needacl)
-                mdd_write_unlock(env, o);
+                mdd_write_unlock(env, obj);
+        RETURN(rc);
+}
+
+int mdd_attr_check_set_internal_locked(const struct lu_env *env,
+                                       struct mdd_object *obj,
+                                       struct lu_attr *attr,
+                                       struct thandle *handle,
+                                       int needacl)
+{
+        int rc;
+        ENTRY;
+
+        rc = mdd_attr_check(env, obj, attr);
+        if (rc)
+                RETURN(rc);
+
+        if (attr->la_valid)
+                rc = mdd_attr_set_internal_locked(env, obj, attr, handle,
+                                                  needacl);
         RETURN(rc);
 }
 
@@ -588,16 +649,18 @@ static int mdd_fix_attr(const struct lu_env *env, struct mdd_object *obj,
         if (la->la_valid & (LA_NLINK | LA_RDEV | LA_BLKSIZE))
                 RETURN(-EPERM);
 
+        rc = mdd_la_get(env, obj, tmp_la, BYPASS_CAPA);
+        if (rc)
+                RETURN(rc);
+
         /* This is only for set ctime when rename's source is on remote MDS. */
         if (unlikely(la->la_valid == LA_CTIME)) {
                 rc = mdd_may_delete(env, NULL, obj, (struct md_attr *)ma, 1, 0);
+                if (rc == 0 && la->la_ctime <= tmp_la->la_ctime)
+                        la->la_valid &= ~LA_CTIME;
                 RETURN(rc);
         }
 
-        rc = mdd_la_get(env, obj, tmp_la, BYPASS_CAPA);
-        if (rc)
-                RETURN(rc);
-
         if (la->la_valid == LA_ATIME) {
                 /* This is atime only set for read atime update on close. */
                 if (la->la_atime <= tmp_la->la_atime +
@@ -726,16 +789,10 @@ static int mdd_fix_attr(const struct lu_env *env, struct mdd_object *obj,
                 }
         }
 
-        /* For truncate (or setsize), we should have MAY_WRITE perm */
-        if (la->la_valid & (LA_SIZE | LA_BLOCKS)) {
-                if (!((la->la_valid & MDS_OPEN_OWNEROVERRIDE) &&
-                    (uc->mu_fsuid == tmp_la->la_uid)) &&
-                    !(ma->ma_attr_flags & MDS_PERM_BYPASS)) {
-                        rc = mdd_permission_internal_locked(env, obj, tmp_la,
-                                                            MAY_WRITE);
-                        if (rc)
-                                RETURN(rc);
-                }
+        if (la->la_valid & (LA_SIZE | LA_BLOCKS) &&
+            !(la->la_valid & LA_TRUNC)) {
+                /* For "Size-on-MDS" case, the MAY_WRITE perm
+                 * has been checked when file open. */
 
                 /* For the "Size-on-MDS" setattr update, merge coming
                  * attributes with the set in the inode. BUG 10641 */
@@ -748,11 +805,25 @@ static int mdd_fix_attr(const struct lu_env *env, struct mdd_object *obj,
                 if ((la->la_valid & LA_CTIME) &&
                     (la->la_ctime <= tmp_la->la_ctime))
                         la->la_valid &= ~(LA_MTIME | LA_CTIME);
-        } else if (la->la_valid & LA_CTIME) {
-                /* The pure setattr, it has the priority over what is already
-                 * set, do not drop it if ctime is equal. */
-                if (la->la_ctime < tmp_la->la_ctime)
-                        la->la_valid &= ~(LA_ATIME | LA_MTIME | LA_CTIME);
+        } else {
+                if (la->la_valid & LA_TRUNC) {
+                        /* For truncate, we should have MAY_WRITE perm. */
+                        if (!((la->la_valid & MDS_OPEN_OWNEROVERRIDE) &&
+                              (uc->mu_fsuid == tmp_la->la_uid)) &&
+                            !(ma->ma_attr_flags & MDS_PERM_BYPASS)) {
+                                rc = mdd_permission_internal_locked(env, obj,
+                                                            tmp_la, MAY_WRITE);
+                                if (rc)
+                                        RETURN(rc);
+                        }
+                }
+                if (la->la_valid & LA_CTIME) {
+                        /* The pure setattr, it has the priority over what is
+                         * already set, do not drop it if ctime is equal. */
+                        if (la->la_ctime < tmp_la->la_ctime)
+                                la->la_valid &= ~(LA_ATIME | LA_MTIME |
+                                                  LA_CTIME);
+                }
         }
 
         RETURN(0);
@@ -881,7 +952,8 @@ static int mdd_xattr_sanity_check(const struct lu_env *env,
 }
 
 static int mdd_xattr_set(const struct lu_env *env, struct md_object *obj,
-                         const struct lu_buf *buf, const char *name, int fl)
+                         const struct lu_buf *buf, const char *name,
+                         int fl, const struct lu_attr *la)
 {
         struct lu_attr *la_copy = &mdd_env_info(env)->mti_la_for_fix;
         struct mdd_object *mdd_obj = md2mdd_obj(obj);
@@ -899,13 +971,13 @@ static int mdd_xattr_set(const struct lu_env *env, struct md_object *obj,
         if (IS_ERR(handle))
                 RETURN(PTR_ERR(handle));
 
-        rc = mdd_xattr_set_txn(env, md2mdd_obj(obj), buf, name,
-                               fl, handle);
-        if (rc == 0) {
-                la_copy->la_ctime = cfs_time_current_sec();
+        rc = mdd_xattr_set_txn(env, mdd_obj, buf, name, fl, handle);
+        if (rc == 0 && likely(la != NULL)) {
+                LASSERT(la->la_valid & LA_CTIME);
+                la_copy->la_ctime = la->la_ctime;
                 la_copy->la_valid = LA_CTIME;
-                rc = mdd_attr_set_internal_locked(env, mdd_obj, la_copy,
-                                                  handle, 0);
+                rc = mdd_attr_check_set_internal_locked(env, mdd_obj, la_copy,
+                                                        handle, 0);
         }
         mdd_trans_stop(env, mdd, rc, handle);
 
@@ -913,7 +985,7 @@ static int mdd_xattr_set(const struct lu_env *env, struct md_object *obj,
 }
 
 int mdd_xattr_del(const struct lu_env *env, struct md_object *obj,
-                  const char *name)
+                  const char *name, const struct lu_attr *la)
 {
         struct lu_attr *la_copy = &mdd_env_info(env)->mti_la_for_fix;
         struct mdd_object *mdd_obj = md2mdd_obj(obj);
@@ -932,14 +1004,15 @@ int mdd_xattr_del(const struct lu_env *env, struct md_object *obj,
                 RETURN(PTR_ERR(handle));
 
         mdd_write_lock(env, mdd_obj);
-        rc = mdo_xattr_del(env, md2mdd_obj(obj), name, handle,
+        rc = mdo_xattr_del(env, mdd_obj, name, handle,
                            mdd_object_capa(env, mdd_obj));
         mdd_write_unlock(env, mdd_obj);
-        if (rc == 0) {
-                la_copy->la_ctime = cfs_time_current_sec();
+        if (rc == 0 && likely(la != NULL)) {
+                LASSERT(la->la_valid & LA_CTIME);
+                la_copy->la_ctime = la->la_ctime;
                 la_copy->la_valid = LA_CTIME;
-                rc = mdd_attr_set_internal_locked(env, mdd_obj, la_copy,
-                                                  handle, 0);
+                rc = mdd_attr_check_set_internal_locked(env, mdd_obj, la_copy,
+                                                        handle, 0);
         }
 
         mdd_trans_stop(env, mdd, rc, handle);
@@ -992,7 +1065,7 @@ static int mdd_ref_del(const struct lu_env *env, struct md_object *obj,
         la_copy->la_ctime = ma->ma_attr.la_ctime;
 
         la_copy->la_valid = LA_CTIME;
-        rc = mdd_attr_set_internal(env, mdd_obj, la_copy, handle, 0);
+        rc = mdd_attr_check_set_internal(env, mdd_obj, la_copy, handle, 0);
         if (rc)
                 GOTO(cleanup, rc);
 
@@ -1071,7 +1144,8 @@ static int mdd_object_create(const struct lu_env *env,
                 if (rc)
                         GOTO(unlock, rc);
 
-                rc = mdd_attr_set_internal(env, mdd_obj, &ma->ma_attr, handle, 0);
+                rc = mdd_attr_set_internal(env, mdd_obj, &ma->ma_attr,
+                                           handle, 0);
         } else {
 #ifdef CONFIG_FS_POSIX_ACL
                 if (spec->sp_cr_flags & MDS_CREATE_RMT_ACL) {
@@ -1130,8 +1204,8 @@ static int mdd_ref_add(const struct lu_env *env, struct md_object *obj,
                 la_copy->la_ctime = ma->ma_attr.la_ctime;
 
                 la_copy->la_valid = LA_CTIME;
-                rc = mdd_attr_set_internal_locked(env, mdd_obj, la_copy,
-                                                  handle, 0);
+                rc = mdd_attr_check_set_internal_locked(env, mdd_obj, la_copy,
+                                                        handle, 0);
         }
         mdd_trans_stop(env, mdd, 0, handle);
 
diff --git a/lustre/mdt/mdt_lib.c b/lustre/mdt/mdt_lib.c
index 0d36467884..85ce484ec0 100644
--- a/lustre/mdt/mdt_lib.c
+++ b/lustre/mdt/mdt_lib.c
@@ -630,6 +630,8 @@ static inline unsigned int attr_unpack(__u64 sa_valid) {
                 ia_valid |= ATTR_FROM_OPEN;
         if (sa_valid & MDS_ATTR_BLOCKS)
                 ia_valid |= ATTR_BLOCKS;
+        if (sa_valid & MDS_ATTR_TRUNC)
+                ia_valid |= ATTR_TRUNC;
         if (sa_valid & MDS_OPEN_OWNEROVERRIDE)
                 ia_valid |= MDS_OPEN_OWNEROVERRIDE;
         return ia_valid;
@@ -667,6 +669,9 @@ static __u64 mdt_attr_valid_xlate(__u64 in, struct mdt_reint_record *rr,
         if (in & ATTR_ATTR_FLAG)
                 out |= LA_FLAGS;
 
+        if (in & ATTR_TRUNC)
+                out |= LA_TRUNC;
+
         if (in & MDS_OPEN_OWNEROVERRIDE)
                 out |= MDS_OPEN_OWNEROVERRIDE;
 
@@ -674,7 +679,7 @@ static __u64 mdt_attr_valid_xlate(__u64 in, struct mdt_reint_record *rr,
         in &= ~(ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_SIZE|ATTR_BLOCKS|
                 ATTR_ATIME|ATTR_MTIME|ATTR_CTIME|ATTR_FROM_OPEN|
                 ATTR_ATIME_SET|ATTR_CTIME_SET|ATTR_MTIME_SET|
-                ATTR_ATTR_FLAG|ATTR_RAW|MDS_OPEN_OWNEROVERRIDE);
+                ATTR_ATTR_FLAG|ATTR_RAW|ATTR_TRUNC|MDS_OPEN_OWNEROVERRIDE);
         if (in != 0)
                 CERROR("Unknown attr bits: %#llx\n", in);
         return out;
@@ -1138,11 +1143,13 @@ static int mdt_setxattr_unpack(struct mdt_thread_info *info)
         uc->mu_fsuid  = rec->sx_fsuid;
         uc->mu_fsgid  = rec->sx_fsgid;
         uc->mu_cap    = rec->sx_cap;
-        uc->mu_suppgids[0] = uc->mu_suppgids[1] = -1;
+        uc->mu_suppgids[0] = rec->sx_suppgid1;
+        uc->mu_suppgids[1] = -1;
 
         rr->rr_opcode = rec->sx_opcode;
         rr->rr_fid1   = &rec->sx_fid;
         attr->la_valid = rec->sx_valid;
+        attr->la_ctime = rec->sx_time;
         attr->la_size = rec->sx_size;
         attr->la_flags = rec->sx_flags;
 
diff --git a/lustre/mdt/mdt_xattr.c b/lustre/mdt/mdt_xattr.c
index 83ecc742f9..c1eae8d2d2 100644
--- a/lustre/mdt/mdt_xattr.c
+++ b/lustre/mdt/mdt_xattr.c
@@ -59,7 +59,7 @@ static int mdt_getxattr_pack_reply(struct mdt_thread_info * info)
                 RETURN(-ENOMEM);
 
         /* Determine how many bytes we need */
-        if ((valid & OBD_MD_FLXATTR) == OBD_MD_FLXATTR) {
+        if (valid & OBD_MD_FLXATTR) {
                 xattr_name = req_capsule_client_get(pill, &RMF_NAME);
                 if (!xattr_name)
                         RETURN(-EFAULT);
@@ -71,7 +71,7 @@ static int mdt_getxattr_pack_reply(struct mdt_thread_info * info)
                 size = mo_xattr_get(info->mti_env,
                                     mdt_object_child(info->mti_object),
                                     &LU_BUF_NULL, xattr_name);
-        } else if ((valid & OBD_MD_FLXATTRLS) == OBD_MD_FLXATTRLS) {
+        } else if (valid & OBD_MD_FLXATTRLS) {
                 size = mo_xattr_list(info->mti_env,
                                      mdt_object_child(info->mti_object),
                                      &LU_BUF_NULL);
@@ -330,8 +330,15 @@ int mdt_reint_setxattr(struct mdt_thread_info *info,
         if (IS_ERR(obj))
                 GOTO(out, rc =  PTR_ERR(obj));
 
+        if (unlikely(!(valid & OBD_MD_FLCTIME))) {
+                CWARN("client miss to set OBD_MD_FLCTIME when "
+                      "setxattr: [object "DFID"] [valid %llu]\n",
+                      PFID(rr->rr_fid1), valid);
+                attr->la_ctime = cfs_time_current_sec();
+        }
+        attr->la_valid = LA_CTIME;
         child = mdt_object_child(obj);
-        if ((valid & OBD_MD_FLXATTR) == OBD_MD_FLXATTR) {
+        if (valid & OBD_MD_FLXATTR) {
                 char * xattr;
 
                 if (!req_capsule_field_present(pill, &RMF_EADATA, RCL_CLIENT)) {
@@ -368,10 +375,10 @@ int mdt_reint_setxattr(struct mdt_thread_info *info,
 
                         buf->lb_buf = xattr;
                         buf->lb_len = xattr_len;
-                        rc = mo_xattr_set(env, child, buf, xattr_name, flags);
+                        rc = mo_xattr_set(env, child, buf, xattr_name, flags, attr);
                 }
-        } else if ((valid & OBD_MD_FLXATTRRM) == OBD_MD_FLXATTRRM) {
-                rc = mo_xattr_del(env, child, xattr_name);
+        } else if (valid & OBD_MD_FLXATTRRM) {
+                rc = mo_xattr_del(env, child, xattr_name, attr);
         } else {
                 CDEBUG(D_INFO, "valid bits: "LPX64"\n", valid);
                 rc = -EINVAL;
diff --git a/lustre/osd/osd_handler.c b/lustre/osd/osd_handler.c
index 25df3a4667..fe067c365d 100644
--- a/lustre/osd/osd_handler.c
+++ b/lustre/osd/osd_handler.c
@@ -1366,12 +1366,12 @@ static int osd_xattr_set(const struct lu_env *env, struct dt_object *dt,
                          const struct lu_buf *buf, const char *name, int fl,
                          struct thandle *handle, struct lustre_capa *capa)
 {
-        int fs_flags;
-
         struct osd_object      *obj    = osd_dt_obj(dt);
         struct inode           *inode  = obj->oo_inode;
         struct osd_thread_info *info   = osd_oti_get(env);
         struct dentry          *dentry = &info->oti_dentry;
+        struct timespec        *t      = &info->oti_time;
+        int                     fs_flags = 0, rc;
 
         LASSERT(dt_object_exists(dt));
         LASSERT(inode->i_op != NULL && inode->i_op->setxattr != NULL);
@@ -1381,17 +1381,24 @@ static int osd_xattr_set(const struct lu_env *env, struct dt_object *dt,
         if (osd_object_auth(env, dt, capa, CAPA_OPC_META_WRITE))
                 return -EACCES;
 
-        dentry->d_inode = inode;
-
-        fs_flags = 0;
         if (fl & LU_XATTR_REPLACE)
                 fs_flags |= XATTR_REPLACE;
 
         if (fl & LU_XATTR_CREATE)
                 fs_flags |= XATTR_CREATE;
 
-        return inode->i_op->setxattr(dentry, name,
-                                     buf->lb_buf, buf->lb_len, fs_flags);
+        dentry->d_inode = inode;
+        *t = inode->i_ctime;
+        rc = inode->i_op->setxattr(dentry, name,
+                                   buf->lb_buf, buf->lb_len, fs_flags);
+        if (likely(rc == 0)) {
+                /* ctime should not be updated with server-side time. */
+                spin_lock(&obj->oo_guard);
+                inode->i_ctime = *t;
+                spin_unlock(&obj->oo_guard);
+                mark_inode_dirty(inode);
+        }
+        return rc;
 }
 
 /*
@@ -1431,6 +1438,8 @@ static int osd_xattr_del(const struct lu_env *env,
         struct inode           *inode  = obj->oo_inode;
         struct osd_thread_info *info   = osd_oti_get(env);
         struct dentry          *dentry = &info->oti_dentry;
+        struct timespec        *t      = &info->oti_time;
+        int                     rc;
 
         LASSERT(dt_object_exists(dt));
         LASSERT(inode->i_op != NULL && inode->i_op->removexattr != NULL);
@@ -1441,7 +1450,16 @@ static int osd_xattr_del(const struct lu_env *env,
                 return -EACCES;
 
         dentry->d_inode = inode;
-        return inode->i_op->removexattr(dentry, name);
+        *t = inode->i_ctime;
+        rc = inode->i_op->removexattr(dentry, name);
+        if (likely(rc == 0)) {
+                /* ctime should not be updated with server-side time. */
+                spin_lock(&obj->oo_guard);
+                inode->i_ctime = *t;
+                spin_unlock(&obj->oo_guard);
+                mark_inode_dirty(inode);
+        }
+        return rc;
 }
 
 static struct obd_capa *osd_capa_get(const struct lu_env *env,
-- 
GitLab