]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
client: add the ability to set the btime
authorJeff Layton <jlayton@redhat.com>
Mon, 29 Aug 2016 11:16:41 +0000 (07:16 -0400)
committerJeff Layton <jlayton@redhat.com>
Mon, 29 Aug 2016 14:33:46 +0000 (10:33 -0400)
This adds a new set of libcephfs calls: ceph_ll_setattrx and
ceph_setattrx. This allows clients to set the btime in addition to other
values that are typically settable via ceph_setattr calls.

Currently, the setattrx mask uses the same CEPH_SETATTR values that the
ceph_setattr interface uses. I'm not sure this is what we will want
though. Would it be better to rephrase that via STATX_* constants?

Signed-off-by: Jeff Layton <jlayton@redhat.com>
src/client/Client.cc
src/client/Client.h
src/client/MetaRequest.h
src/include/cephfs/libcephfs.h
src/libcephfs.cc
src/test/libcephfs/test.cc

index ddf38f9beff1a89cb8d690ed8c5005041d138b8c..16b4ffaf33f1ae60c7ab52b04f57422283a7eaf3 100644 (file)
@@ -5064,7 +5064,7 @@ out:
   return r;
 }
 
-int Client::may_setattr(Inode *in, struct stat *st, int mask, int uid, int gid)
+int Client::may_setattr(Inode *in, struct ceph_statx *stx, int mask, int uid, int gid)
 {
   if (uid < 0)
     uid = get_uid();
@@ -5084,12 +5084,12 @@ int Client::may_setattr(Inode *in, struct stat *st, int mask, int uid, int gid)
 
   r = -EPERM;
   if (mask & CEPH_SETATTR_UID) {
-    if (uid != 0 && ((uid_t)uid != in->uid || st->st_uid != in->uid))
+    if (uid != 0 && ((uid_t)uid != in->uid || stx->stx_uid != in->uid))
       goto out;
   }
   if (mask & CEPH_SETATTR_GID) {
     if (uid != 0 && ((uid_t)uid != in->uid ||
-              (!groups.is_in(st->st_gid) && st->st_gid != in->gid)))
+           (!groups.is_in(stx->stx_gid) && stx->stx_gid != in->gid)))
       goto out;
   }
 
@@ -5097,14 +5097,14 @@ int Client::may_setattr(Inode *in, struct stat *st, int mask, int uid, int gid)
     if (uid != 0 && (uid_t)uid != in->uid)
       goto out;
 
-    gid_t i_gid = (mask & CEPH_SETATTR_GID) ? st->st_gid : in->gid;
+    gid_t i_gid = (mask & CEPH_SETATTR_GID) ? stx->stx_gid : in->gid;
     if (uid != 0 && !groups.is_in(i_gid))
-      st->st_mode &= ~S_ISGID;
+      stx->stx_mode &= ~S_ISGID;
   }
 
-  if (mask & (CEPH_SETATTR_CTIME | CEPH_SETATTR_MTIME | CEPH_SETATTR_ATIME)) {
+  if (mask & (CEPH_SETATTR_CTIME | CEPH_SETATTR_BTIME | CEPH_SETATTR_MTIME | CEPH_SETATTR_ATIME)) {
     if (uid != 0 && (uid_t)uid != in->uid) {
-      int check_mask = CEPH_SETATTR_CTIME;
+      int check_mask = CEPH_SETATTR_CTIME | CEPH_SETATTR_BTIME;
       if (!(mask & CEPH_SETATTR_MTIME_NOW))
        check_mask |= CEPH_SETATTR_MTIME;
       if (!(mask & CEPH_SETATTR_ATIME_NOW))
@@ -6421,7 +6421,7 @@ int Client::_getattr(Inode *in, int mask, int uid, int gid, bool force)
   return res;
 }
 
-int Client::_do_setattr(Inode *in, struct stat *attr, int mask, int uid, int gid,
+int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask, int uid, int gid,
                        InodeRef *inp)
 {
   int issued = in->caps_issued();
@@ -6433,8 +6433,8 @@ int Client::_do_setattr(Inode *in, struct stat *attr, int mask, int uid, int gid
     return -EROFS;
   }
   if ((mask & CEPH_SETATTR_SIZE) &&
-      (unsigned long)attr->st_size > in->size &&
-      is_quota_bytes_exceeded(in, (unsigned long)attr->st_size - in->size)) {
+      (unsigned long)stx->stx_size > in->size &&
+      is_quota_bytes_exceeded(in, (unsigned long)stx->stx_size - in->size)) {
     return -EDQUOT;
   }
 
@@ -6486,36 +6486,45 @@ int Client::_do_setattr(Inode *in, struct stat *attr, int mask, int uid, int gid
       in->ctime = ceph_clock_now(cct);
       in->cap_dirtier_uid = uid;
       in->cap_dirtier_gid = gid;
-      in->mode = (in->mode & ~07777) | (attr->st_mode & 07777);
+      in->mode = (in->mode & ~07777) | (stx->stx_mode & 07777);
       mark_caps_dirty(in, CEPH_CAP_AUTH_EXCL);
       mask &= ~CEPH_SETATTR_MODE;
-      ldout(cct,10) << "changing mode to " << attr->st_mode << dendl;
+      ldout(cct,10) << "changing mode to " << stx->stx_mode << dendl;
     }
     if (mask & CEPH_SETATTR_UID) {
       in->ctime = ceph_clock_now(cct);
       in->cap_dirtier_uid = uid;
       in->cap_dirtier_gid = gid;
-      in->uid = attr->st_uid;
+      in->uid = stx->stx_uid;
       mark_caps_dirty(in, CEPH_CAP_AUTH_EXCL);
       mask &= ~CEPH_SETATTR_UID;
-      ldout(cct,10) << "changing uid to " << attr->st_uid << dendl;
+      ldout(cct,10) << "changing uid to " << stx->stx_uid << dendl;
     }
     if (mask & CEPH_SETATTR_GID) {
       in->ctime = ceph_clock_now(cct);
       in->cap_dirtier_uid = uid;
       in->cap_dirtier_gid = gid;
-      in->gid = attr->st_gid;
+      in->gid = stx->stx_gid;
       mark_caps_dirty(in, CEPH_CAP_AUTH_EXCL);
       mask &= ~CEPH_SETATTR_GID;
-      ldout(cct,10) << "changing gid to " << attr->st_gid << dendl;
+      ldout(cct,10) << "changing gid to " << stx->stx_gid << dendl;
+    }
+    if (mask & CEPH_SETATTR_BTIME) {
+      in->ctime = ceph_clock_now(cct);
+      in->cap_dirtier_uid = uid;
+      in->cap_dirtier_gid = gid;
+      in->btime = utime_t(stx->stx_btime, stx->stx_btime_ns);
+      mark_caps_dirty(in, CEPH_CAP_AUTH_EXCL);
+      mask &= ~CEPH_SETATTR_BTIME;
+      ldout(cct,10) << "changing btime to " << in->btime << dendl;
     }
   }
   if (in->caps_issued_mask(CEPH_CAP_FILE_EXCL)) {
     if (mask & (CEPH_SETATTR_MTIME|CEPH_SETATTR_ATIME)) {
       if (mask & CEPH_SETATTR_MTIME)
-        in->mtime = utime_t(stat_get_mtime_sec(attr), stat_get_mtime_nsec(attr));
+        in->mtime = utime_t(stx->stx_mtime, stx->stx_mtime_ns);
       if (mask & CEPH_SETATTR_ATIME)
-        in->atime = utime_t(stat_get_atime_sec(attr), stat_get_atime_nsec(attr));
+        in->atime = utime_t(stx->stx_atime, stx->stx_atime_ns);
       in->ctime = ceph_clock_now(cct);
       in->cap_dirtier_uid = uid;
       in->cap_dirtier_gid = gid;
@@ -6539,35 +6548,38 @@ force_request:
   req->set_inode(in);
 
   if (mask & CEPH_SETATTR_MODE) {
-    req->head.args.setattr.mode = attr->st_mode;
+    req->head.args.setattr.mode = stx->stx_mode;
     req->inode_drop |= CEPH_CAP_AUTH_SHARED;
-    ldout(cct,10) << "changing mode to " << attr->st_mode << dendl;
+    ldout(cct,10) << "changing mode to " << stx->stx_mode << dendl;
   }
   if (mask & CEPH_SETATTR_UID) {
-    req->head.args.setattr.uid = attr->st_uid;
+    req->head.args.setattr.uid = stx->stx_uid;
     req->inode_drop |= CEPH_CAP_AUTH_SHARED;
-    ldout(cct,10) << "changing uid to " << attr->st_uid << dendl;
+    ldout(cct,10) << "changing uid to " << stx->stx_uid << dendl;
   }
   if (mask & CEPH_SETATTR_GID) {
-    req->head.args.setattr.gid = attr->st_gid;
+    req->head.args.setattr.gid = stx->stx_gid;
     req->inode_drop |= CEPH_CAP_AUTH_SHARED;
-    ldout(cct,10) << "changing gid to " << attr->st_gid << dendl;
+    ldout(cct,10) << "changing gid to " << stx->stx_gid << dendl;
   }
   if (mask & CEPH_SETATTR_MTIME) {
-    utime_t mtime = utime_t(stat_get_mtime_sec(attr), stat_get_mtime_nsec(attr));
-    req->head.args.setattr.mtime = mtime;
+    req->head.args.setattr.mtime = utime_t(stx->stx_mtime, stx->stx_mtime_ns);
     req->inode_drop |= CEPH_CAP_AUTH_SHARED | CEPH_CAP_FILE_RD |
       CEPH_CAP_FILE_WR;
   }
   if (mask & CEPH_SETATTR_ATIME) {
-    utime_t atime = utime_t(stat_get_atime_sec(attr), stat_get_atime_nsec(attr));
-    req->head.args.setattr.atime = atime;
+    req->head.args.setattr.atime = utime_t(stx->stx_atime, stx->stx_atime_ns);
+    req->inode_drop |= CEPH_CAP_FILE_CACHE | CEPH_CAP_FILE_RD |
+      CEPH_CAP_FILE_WR;
+  }
+  if (mask & CEPH_SETATTR_BTIME) {
+    req->head.args.setattr.btime = utime_t(stx->stx_btime, stx->stx_btime_ns);
     req->inode_drop |= CEPH_CAP_FILE_CACHE | CEPH_CAP_FILE_RD |
       CEPH_CAP_FILE_WR;
   }
   if (mask & CEPH_SETATTR_SIZE) {
-    if ((unsigned long)attr->st_size < mdsmap->get_max_filesize())
-      req->head.args.setattr.size = attr->st_size;
+    if ((unsigned long)stx->stx_size < mdsmap->get_max_filesize())
+      req->head.args.setattr.size = stx->stx_size;
     else { //too big!
       put_request(req);
       return -EFBIG;
@@ -6584,29 +6596,51 @@ force_request:
   return res;
 }
 
-int Client::_setattr(Inode *in, struct stat *attr, int mask, int uid, int gid,
+/* Note that we only care about attrs that setattr cares about */
+void Client::stat_to_statx(struct stat *st, struct ceph_statx *stx)
+{
+  stx->stx_size = st->st_size;
+  stx->stx_mode = st->st_mode;
+  stx->stx_uid = st->st_uid;
+  stx->stx_gid = st->st_gid;
+  stx->stx_mtime = stat_get_mtime_sec(st);
+  stx->stx_mtime_ns = stat_get_mtime_nsec(st);
+  stx->stx_atime = stat_get_atime_sec(st);
+  stx->stx_atime_ns = stat_get_atime_nsec(st);
+}
+
+int Client::__setattrx(Inode *in, struct ceph_statx *stx, int mask, int uid, int gid,
                     InodeRef *inp)
 {
-  int ret = _do_setattr(in, attr, mask, uid, gid, inp);
+  int ret = _do_setattr(in, stx, mask, uid, gid, inp);
   if (ret < 0)
    return ret;
   if (mask & CEPH_SETATTR_MODE)
-    ret = _posix_acl_chmod(in, attr->st_mode, uid, gid);
+    ret = _posix_acl_chmod(in, stx->stx_mode, uid, gid);
   return ret;
 }
 
-int Client::_setattr(InodeRef &in, struct stat *attr, int mask)
+int Client::_setattrx(InodeRef &in, struct ceph_statx *stx, int mask)
 {
   mask &= (CEPH_SETATTR_MODE | CEPH_SETATTR_UID |
           CEPH_SETATTR_GID | CEPH_SETATTR_MTIME |
           CEPH_SETATTR_ATIME | CEPH_SETATTR_SIZE |
-          CEPH_SETATTR_CTIME);
+          CEPH_SETATTR_CTIME | CEPH_SETATTR_BTIME);
   if (cct->_conf->client_permissions) {
-    int r = may_setattr(in.get(), attr, mask);
+    int r = may_setattr(in.get(), stx, mask);
     if (r < 0)
       return r;
   }
-  return _setattr(in.get(), attr, mask);
+  return __setattrx(in.get(), stx, mask);
+}
+
+int Client::_setattr(InodeRef &in, struct stat *attr, int mask)
+{
+  struct ceph_statx stx;
+
+  stat_to_statx(attr, &stx);
+  mask &= ~CEPH_SETATTR_BTIME;
+  return _setattrx(in, &stx, mask);
 }
 
 int Client::setattr(const char *relpath, struct stat *attr, int mask)
@@ -6624,6 +6658,21 @@ int Client::setattr(const char *relpath, struct stat *attr, int mask)
   return _setattr(in, attr, mask);
 }
 
+int Client::setattrx(const char *relpath, struct ceph_statx *stx, int mask, int flags)
+{
+  Mutex::Locker lock(client_lock);
+  tout(cct) << "setattrx" << std::endl;
+  tout(cct) << relpath << std::endl;
+  tout(cct) << mask  << std::endl;
+
+  filepath path(relpath);
+  InodeRef in;
+  int r = path_walk(path, &in, flags & AT_SYMLINK_NOFOLLOW);
+  if (r < 0)
+    return r;
+  return _setattrx(in, stx, mask);
+}
+
 int Client::fsetattr(int fd, struct stat *attr, int mask)
 {
   Mutex::Locker lock(client_lock);
@@ -8844,9 +8893,9 @@ int Client::_flush(Fh *f)
 
 int Client::truncate(const char *relpath, loff_t length) 
 {
-  struct stat attr;
-  attr.st_size = length;
-  return setattr(relpath, &attr, CEPH_SETATTR_SIZE);
+  struct ceph_statx stx;
+  stx.stx_size = length;
+  return setattrx(relpath, &stx, CEPH_SETATTR_SIZE);
 }
 
 int Client::ftruncate(int fd, loff_t length) 
@@ -9859,41 +9908,66 @@ int Client::ll_getattrx(Inode *in, struct ceph_statx *stx, unsigned int want,
   return res;
 }
 
-int Client::ll_setattr(Inode *in, struct stat *attr, int mask, int uid,
-                      int gid)
+int Client::_ll_setattrx(Inode *in, struct ceph_statx *stx, int mask, int uid,
+                      int gid, InodeRef *inp)
 {
   Mutex::Locker lock(client_lock);
 
   vinodeno_t vino = _get_vino(in);
 
-  ldout(cct, 3) << "ll_setattr " << vino << " mask " << hex << mask << dec
+  ldout(cct, 3) << "ll_setattrx " << vino << " mask " << hex << mask << dec
                << dendl;
-  tout(cct) << "ll_setattr" << std::endl;
+  tout(cct) << "ll_setattrx" << std::endl;
   tout(cct) << vino.ino.val << std::endl;
-  tout(cct) << attr->st_mode << std::endl;
-  tout(cct) << attr->st_uid << std::endl;
-  tout(cct) << attr->st_gid << std::endl;
-  tout(cct) << attr->st_size << std::endl;
-  tout(cct) << attr->st_mtime << std::endl;
-  tout(cct) << attr->st_atime << std::endl;
+  tout(cct) << stx->stx_mode << std::endl;
+  tout(cct) << stx->stx_uid << std::endl;
+  tout(cct) << stx->stx_gid << std::endl;
+  tout(cct) << stx->stx_size << std::endl;
+  tout(cct) << stx->stx_mtime << std::endl;
+  tout(cct) << stx->stx_atime << std::endl;
+  tout(cct) << stx->stx_btime << std::endl;
   tout(cct) << mask << std::endl;
 
   if (!cct->_conf->fuse_default_permissions) {
-    int res = may_setattr(in, attr, mask, uid, gid);
+    int res = may_setattr(in, stx, mask, uid, gid);
     if (res < 0)
       return res;
   }
 
   mask &= ~(CEPH_SETATTR_MTIME_NOW | CEPH_SETATTR_ATIME_NOW);
 
+  return __setattrx(in, stx, mask, uid, gid, inp);
+}
+
+int Client::ll_setattrx(Inode *in, struct ceph_statx *stx, int mask, int uid,
+                      int gid)
+{
+  InodeRef target(in);
+  int res = _ll_setattrx(in, stx, mask, uid, gid, &target);
+  if (res == 0) {
+    assert(in == target.get());
+    fill_statx(in, in->caps_issued(), stx);
+  }
+
+  ldout(cct, 3) << "ll_setattrx " << _get_vino(in) << " = " << res << dendl;
+  return res;
+}
+
+int Client::ll_setattr(Inode *in, struct stat *attr, int mask, int uid,
+                      int gid)
+{
+  struct ceph_statx stx;
+
+  stat_to_statx(attr, &stx);
+
   InodeRef target(in);
-  int res = _setattr(in, attr, mask, uid, gid, &target);
+  int res = _ll_setattrx(in, &stx, mask, uid, gid, &target);
   if (res == 0) {
     assert(in == target.get());
     fill_stat(in, attr);
   }
 
-  ldout(cct, 3) << "ll_setattr " << vino << " = " << res << dendl;
+  ldout(cct, 3) << "ll_setattr " << _get_vino(in) << " = " << res << dendl;
   return res;
 }
 
@@ -10217,9 +10291,9 @@ int Client::_setxattr(Inode *in, const char *name, const void *value,
          size = 0;
        }
        if (new_mode != in->mode) {
-         struct stat attr;
-         attr.st_mode = new_mode;
-         ret = _do_setattr(in, &attr, CEPH_SETATTR_MODE, uid, gid, NULL);
+         struct ceph_statx stx;
+         stx.stx_mode = new_mode;
+         ret = _do_setattr(in, &stx, CEPH_SETATTR_MODE, uid, gid, NULL);
          if (ret < 0)
            return ret;
        }
index 4413d4b459106e9a00639656a7a3e308ca7c1dea..e5c655c4fbdea8b193a7be0de3deec16ec703ba5 100644 (file)
@@ -773,9 +773,13 @@ private:
   int _rmdir(Inode *dir, const char *name, int uid=-1, int gid=-1);
   int _symlink(Inode *dir, const char *name, const char *target, int uid=-1, int gid=-1, InodeRef *inp = 0);
   int _mknod(Inode *dir, const char *name, mode_t mode, dev_t rdev, int uid=-1, int gid=-1, InodeRef *inp = 0);
-  int _do_setattr(Inode *in, struct stat *attr, int mask, int uid, int gid, InodeRef *inp);
-  int _setattr(Inode *in, struct stat *attr, int mask, int uid=-1, int gid=-1, InodeRef *inp = 0);
+  int _do_setattr(Inode *in, struct ceph_statx *stx, int mask, int uid, int gid, InodeRef *inp);
+  void stat_to_statx(struct stat *st, struct ceph_statx *stx);
+  int __setattrx(Inode *in, struct ceph_statx *stx, int mask, int uid=-1, int gid=-1, InodeRef *inp = 0);
+  int _setattrx(InodeRef &in, struct ceph_statx *stx, int mask);
   int _setattr(InodeRef &in, struct stat *attr, int mask);
+  int _ll_setattrx(Inode *in, struct ceph_statx *stx, int mask, int uid = -1,
+                int gid = -1, InodeRef *inp = 0);
   int _getattr(Inode *in, int mask, int uid=-1, int gid=-1, bool force=false);
   int _getattr(InodeRef &in, int mask, int uid=-1, int gid=-1, bool force=false) {
     return _getattr(in.get(), mask, uid, gid, force);
@@ -845,7 +849,7 @@ private:
 
   int inode_permission(Inode *in, uid_t uid, UserGroups& groups, unsigned want);
   int xattr_permission(Inode *in, const char *name, unsigned want, int uid=-1, int gid=-1);
-  int may_setattr(Inode *in, struct stat *st, int mask, int uid=-1, int gid=-1);
+  int may_setattr(Inode *in, struct ceph_statx *stx, int mask, int uid=-1, int gid=-1);
   int may_open(Inode *in, int flags, int uid=-1, int gid=-1);
   int may_lookup(Inode *dir, int uid=-1, int gid=-1);
   int may_create(Inode *dir, int uid=-1, int gid=-1);
@@ -999,6 +1003,7 @@ public:
   int lstatlite(const char *path, struct statlite *buf);
 
   int setattr(const char *relpath, struct stat *attr, int mask);
+  int setattrx(const char *relpath, struct ceph_statx *stx, int mask, int flags=0);
   int fsetattr(int fd, struct stat *attr, int mask);
   int chmod(const char *path, mode_t mode);
   int fchmod(int fd, mode_t mode);
@@ -1096,6 +1101,8 @@ public:
   int ll_getattr(Inode *in, struct stat *st, int uid = -1, int gid = -1);
   int ll_getattrx(Inode *in, struct ceph_statx *stx, unsigned int want,
                  unsigned int flags, int uid = -1, int gid = -1);
+  int ll_setattrx(Inode *in, struct ceph_statx *stx, int mask, int uid = -1,
+                int gid = -1);
   int ll_setattr(Inode *in, struct stat *st, int mask, int uid = -1,
                 int gid = -1);
   int ll_getxattr(Inode *in, const char *name, void *value, size_t size,
index 8eb02048da0b2626d0b3a89d4548694568fb2d3e..341533ebc1e1273c9a40611b7cb519245158c8c3 100644 (file)
@@ -27,7 +27,7 @@ private:
 public:
   uint64_t tid;
   utime_t  op_stamp;
-  ceph_mds_request_head_legacy head;
+  ceph_mds_request_head head;
   filepath path, path2;
   bufferlist data;
   int inode_drop; //the inode caps this operation will drop
index c550bcd7b02d2e9dff068f6e8f75e653bfa7f516..688702130b621895270fb22a6d420447d5c39b67 100644 (file)
@@ -106,13 +106,14 @@ struct CephContext;
 
 /* setattr mask bits */
 #ifndef CEPH_SETATTR_MODE
-# define CEPH_SETATTR_MODE   1
-# define CEPH_SETATTR_UID    2
-# define CEPH_SETATTR_GID    4
-# define CEPH_SETATTR_MTIME  8
-# define CEPH_SETATTR_ATIME 16
-# define CEPH_SETATTR_SIZE  32
-# define CEPH_SETATTR_CTIME 64
+# define CEPH_SETATTR_MODE     1
+# define CEPH_SETATTR_UID      2
+# define CEPH_SETATTR_GID      4
+# define CEPH_SETATTR_MTIME    8
+# define CEPH_SETATTR_ATIME    16
+# define CEPH_SETATTR_SIZE     32
+# define CEPH_SETATTR_CTIME    64
+# define CEPH_SETATTR_BTIME    512
 #endif
 
 /* define error codes for the mount function*/
@@ -649,11 +650,23 @@ int ceph_lstat(struct ceph_mount_info *cmount, const char *path, struct stat *st
  * @param cmount the ceph mount handle to use for performing the setattr.
  * @param relpath the path to the file/directory to set the attributes of.
  * @param attr the stat struct that must include attribute values to set on the file.
- * @param mask a mask of all the stat values that have been set on the stat struct.
+ * @param mask a mask of all the CEPH_SETATTR_* values that have been set in the stat struct.
  * @returns 0 on success or negative error code on failure.
  */
 int ceph_setattr(struct ceph_mount_info *cmount, const char *relpath, struct stat *attr, int mask);
 
+/**
+ * Set a file's attributes (extended version).
+ *
+ * @param cmount the ceph mount handle to use for performing the setattr.
+ * @param relpath the path to the file/directory to set the attributes of.
+ * @param stx the statx struct that must include attribute values to set on the file.
+ * @param mask a mask of all the CEPH_SETATTR_* values that have been set in the statx struct.
+ * @param flags mask of AT_* flags (only AT_ATTR_NOFOLLOW is respected for now)
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_setattrx(struct ceph_mount_info *cmount, const char *relpath, struct ceph_statx *stx, int mask, int flags);
+
 /**
  * Change the mode bits (permissions) of a file/directory.
  *
@@ -1413,6 +1426,8 @@ int ceph_ll_getattrx(struct ceph_mount_info *cmount, struct Inode *in,
                    int uid, int gid);
 int ceph_ll_setattr(struct ceph_mount_info *cmount, struct Inode *in,
                    struct stat *st, int mask, int uid, int gid);
+int ceph_ll_setattrx(struct ceph_mount_info *cmount, struct Inode *in,
+                   struct ceph_statx *stx, int mask, int uid, int gid);
 int ceph_ll_open(struct ceph_mount_info *cmount, struct Inode *in, int flags,
                 struct Fh **fh, int uid, int gid);
 off_t ceph_ll_lseek(struct ceph_mount_info *cmount, struct Fh* filehandle,
index c01255e59063b41d9db3e30d8981f9efc7b89fa3..05ddf8e82b47f59d1b2c2f90bfbef4c330160adf 100644 (file)
@@ -634,6 +634,14 @@ extern "C" int ceph_setattr(struct ceph_mount_info *cmount, const char *relpath,
   return cmount->get_client()->setattr(relpath, attr, mask);
 }
 
+extern "C" int ceph_setattrx(struct ceph_mount_info *cmount, const char *relpath,
+                           struct ceph_statx *stx, int mask, int flags)
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->setattrx(relpath, stx, mask, flags);
+}
+
 // *xattr() calls supporting samba/vfs
 extern "C" int ceph_getxattr(struct ceph_mount_info *cmount, const char *path, const char *name, void *value, size_t size)
 {
@@ -1429,6 +1437,13 @@ extern "C" int ceph_ll_setattr(class ceph_mount_info *cmount,
   return (cmount->get_client()->ll_setattr(in, st, mask, uid, gid));
 }
 
+extern "C" int ceph_ll_setattrx(class ceph_mount_info *cmount,
+                              Inode *in, struct ceph_statx *stx,
+                              int mask, int uid, int gid)
+{
+  return (cmount->get_client()->ll_setattrx(in, stx, mask, uid, gid));
+}
+
 extern "C" int ceph_ll_open(class ceph_mount_info *cmount, Inode *in,
                            int flags, Fh **fh, int uid, int gid)
 {
index ac34f3f0d19b49abcc2cc3bcb7f97361f09e9aa6..a4062a95e9b8d4aa1a2ec5b196225558ab1f426d 100644 (file)
@@ -1504,6 +1504,37 @@ TEST(LibCephFS, Btime) {
   ceph_shutdown(cmount);
 }
 
+TEST(LibCephFS, SetBtime) {
+  struct ceph_mount_info *cmount;
+  ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+  ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+  ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
+  ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+  char filename[32];
+  sprintf(filename, "/setbtime%x", getpid());
+
+  ceph_unlink(cmount, filename);
+  int fd = ceph_open(cmount, filename, O_RDWR|O_CREAT|O_EXCL, 0666);
+  ASSERT_LT(0, fd);
+  ceph_close(cmount, fd);
+
+  struct ceph_statx stx;
+
+  stx.stx_btime = 1;
+  stx.stx_btime_ns = 2;
+
+  ASSERT_EQ(ceph_setattrx(cmount, filename, &stx, CEPH_SETATTR_BTIME, 0), 0);
+
+  ASSERT_EQ(ceph_statx(cmount, filename, &stx, CEPH_STATX_BTIME, 0), 0);
+  ASSERT_TRUE(stx.stx_mask & CEPH_STATX_BTIME);
+
+  ASSERT_EQ(stx.stx_btime, 1);
+  ASSERT_EQ(stx.stx_btime_ns, 2);
+
+  ceph_shutdown(cmount);
+}
+
 TEST(LibCephFS, LazyStatx) {
   struct ceph_mount_info *cmount1, *cmount2;
   ASSERT_EQ(ceph_create(&cmount1, NULL), 0);