]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
libcephfs: add a ceph_ll_getattrx and ceph_statx
authorJeff Layton <jlayton@redhat.com>
Mon, 29 Aug 2016 11:16:38 +0000 (07:16 -0400)
committerJeff Layton <jlayton@redhat.com>
Mon, 29 Aug 2016 11:16:38 +0000 (07:16 -0400)
New interfaces for fetching extended (and selective) stat information.
Additionally, applications can specify AT_NO_ATTR_SYNC in the flags to
indicate that they want to do a "lazy" statx that just hands out the
inode info from the cache, or AT_SYMLINK_NOFOLLOW to avoid following
symlinks when walking the path.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
ceph.spec.in
debian/libcephfs-dev.install
src/client/Client.cc
src/client/Client.h
src/include/cephfs/ceph_statx.h [new file with mode: 0644]
src/include/cephfs/libcephfs.h
src/libcephfs.cc
src/test/libcephfs/test.cc

index 6fd2cb3f977422970f5257d450b018818dd5332d..d52e2ab2b6503eae0372324c6992f9bf60419f58 100644 (file)
@@ -1371,6 +1371,7 @@ ln -sf %{_libdir}/librbd.so.1 /usr/lib64/qemu/librbd.so.1
 %defattr(-,root,root,-)
 %dir %{_includedir}/cephfs
 %{_includedir}/cephfs/libcephfs.h
+%{_includedir}/cephfs/ceph_statx.h
 %{_libdir}/libcephfs.so
 
 %files -n python-cephfs
index 625c874ec146bd8c646732415f5ca3236cebc750..d2d648719fdbe673df796cd5d8d5b7dfbdf59341 100644 (file)
@@ -1,2 +1,3 @@
 usr/include/cephfs/libcephfs.h
+usr/include/cephfs/ceph_statx.h
 usr/lib/libcephfs.so
index f8cdce0e870390eb860da9583ac0a5c61153315b..6234ea4b26b74332c40a0d6617e6672285d8790a 100644 (file)
@@ -94,6 +94,8 @@
 #include "include/assert.h"
 #include "include/stat.h"
 
+#include "include/cephfs/ceph_statx.h"
+
 #if HAVE_GETGROUPLIST
 #include <grp.h>
 #include <pwd.h>
@@ -6648,6 +6650,56 @@ int Client::stat(const char *relpath, struct stat *stbuf,
   return r;
 }
 
+unsigned Client::statx_to_mask(unsigned int flags, unsigned int want)
+{
+  unsigned mask = 0;
+
+  /* if NO_ATTR_SYNC is set, then we don't need any -- just use what's in cache */
+  if (flags & AT_NO_ATTR_SYNC)
+    goto out;
+
+  /* Always set PIN to distinguish from AT_NO_ATTR_SYNC case */
+  mask |= CEPH_CAP_PIN;
+  if (want & (CEPH_STATX_MODE|CEPH_STATX_UID|CEPH_STATX_GID|CEPH_STATX_RDEV|CEPH_STATX_BTIME))
+    mask |= CEPH_CAP_AUTH_SHARED;
+  if (want & CEPH_STATX_NLINK)
+    mask |= CEPH_CAP_LINK_SHARED;
+  if (want & (CEPH_STATX_ATIME|CEPH_STATX_MTIME|CEPH_STATX_CTIME|CEPH_STATX_SIZE|CEPH_STATX_BLOCKS))
+    mask |= CEPH_CAP_FILE_SHARED;
+
+out:
+  return mask;
+}
+
+int Client::statx(const char *relpath, struct ceph_statx *stx,
+                 unsigned int want, unsigned int flags)
+{
+  ldout(cct, 3) << "statx enter (relpath " << relpath << " want " << want << ")" << dendl;
+  Mutex::Locker lock(client_lock);
+  tout(cct) << "statx" << std::endl;
+  tout(cct) << relpath << std::endl;
+  filepath path(relpath);
+  InodeRef in;
+
+  unsigned mask = statx_to_mask(flags, want);
+
+  int r = path_walk(path, &in, flags & AT_SYMLINK_NOFOLLOW, mask);
+  if (r < 0)
+    return r;
+
+  if (mask && !in->caps_issued_mask(mask)) {
+    r = _getattr(in, mask);
+    if (r < 0) {
+      ldout(cct, 3) << "statx exit on error!" << dendl;
+      return r;
+    }
+  }
+
+  fill_statx(in, mask, stx);
+  ldout(cct, 3) << "statx exit (relpath " << relpath << " mask " << stx->stx_mask << ")" << dendl;
+  return r;
+}
+
 int Client::lstat(const char *relpath, struct stat *stbuf,
                          frag_info_t *dirstat, int mask)
 {
@@ -6718,6 +6770,74 @@ int Client::fill_stat(Inode *in, struct stat *st, frag_info_t *dirstat, nest_inf
   return in->caps_issued();
 }
 
+void Client::fill_statx(Inode *in, unsigned int mask, struct ceph_statx *stx)
+{
+  ldout(cct, 10) << "fill_statx on " << in->ino << " snap/dev" << in->snapid
+          << " mode 0" << oct << in->mode << dec
+          << " mtime " << in->mtime << " ctime " << in->ctime << dendl;
+  memset(stx, 0, sizeof(struct ceph_statx));
+
+  /*
+   * If mask is 0, then the caller set AT_NO_ATTR_SYNC. Reset the mask
+   * so that all bits are set.
+   */
+  if (!mask)
+    mask = ~0;
+
+  /* These are always considered to be available */
+  stx->stx_dev_major = in->snapid >> 32;
+  stx->stx_dev_minor = (uint32_t)in->snapid;
+  stx->stx_blksize = MAX(in->layout.stripe_unit, 4096);
+
+  if (use_faked_inos())
+   stx->stx_ino = in->faked_ino;
+  else
+    stx->stx_ino = in->ino;
+  stx->stx_rdev_minor = MINOR(in->rdev);
+  stx->stx_rdev_major = MAJOR(in->rdev);
+  stx->stx_mask |= (CEPH_STATX_INO|CEPH_STATX_RDEV);
+
+  if (mask & CEPH_CAP_AUTH_SHARED) {
+    stx->stx_uid = in->uid;
+    stx->stx_gid = in->gid;
+    stx->stx_mode = in->mode;
+    stx->stx_btime = in->btime.sec();
+    stx->stx_btime_ns = in->btime.nsec();
+    stx->stx_mask |= (CEPH_STATX_MODE|CEPH_STATX_UID|CEPH_STATX_GID|CEPH_STATX_BTIME);
+  }
+
+  if (mask & CEPH_CAP_LINK_SHARED) {
+    stx->stx_nlink = in->nlink;
+    stx->stx_mask |= CEPH_STATX_NLINK;
+  }
+
+  if (mask & CEPH_CAP_FILE_SHARED) {
+    if (in->ctime > in->mtime) {
+      stx->stx_ctime = in->ctime.sec();
+      stx->stx_ctime_ns = in->ctime.nsec();
+    } else {
+      stx->stx_ctime = in->mtime.sec();
+      stx->stx_ctime_ns = in->mtime.nsec();
+    }
+    stx->stx_atime = in->atime.sec();
+    stx->stx_atime_ns = in->atime.nsec();
+    stx->stx_mtime = in->mtime.sec();
+    stx->stx_mtime_ns = in->mtime.nsec();
+
+    if (in->is_dir()) {
+      if (cct->_conf->client_dirsize_rbytes)
+       stx->stx_size = in->rstat.rbytes;
+      else
+       stx->stx_size = in->dirstat.size();
+      stx->stx_blocks = 1;
+    } else {
+      stx->stx_size = in->size;
+      stx->stx_blocks = (in->size + 511) >> 9;
+    }
+    stx->stx_mask |= (CEPH_STATX_ATIME|CEPH_STATX_MTIME|CEPH_STATX_CTIME|CEPH_STATX_SIZE|CEPH_STATX_BLOCKS);
+  }
+}
+
 void Client::touch_dn(Dentry *dn)
 {
   lru.lru_touch(dn);
@@ -9705,6 +9825,23 @@ int Client::ll_getattr(Inode *in, struct stat *attr, int uid, int gid)
   return res;
 }
 
+int Client::ll_getattrx(Inode *in, struct ceph_statx *stx, unsigned int want,
+                       unsigned int flags, int uid, int gid)
+{
+  Mutex::Locker lock(client_lock);
+
+  int res = 0;
+  unsigned mask = statx_to_mask(flags, want);
+
+  if (mask && !in->caps_issued_mask(mask))
+    res = _ll_getattr(in, uid, gid);
+
+  if (res == 0)
+    fill_statx(in, mask, stx);
+  ldout(cct, 3) << "ll_getattrx " << _get_vino(in) << " = " << res << dendl;
+  return res;
+}
+
 int Client::ll_setattr(Inode *in, struct stat *attr, int mask, int uid,
                       int gid)
 {
index 5b0b7ee0a4c3a7cb9fbe805155b00d9c82dfcf82..520f000a3e09561dd9e51d4357cf483e006ba053 100644 (file)
@@ -532,6 +532,12 @@ protected:
   int fill_stat(InodeRef& in, struct stat *st, frag_info_t *dirstat=0, nest_info_t *rstat=0) {
     return fill_stat(in.get(), st, dirstat, rstat);
   }
+
+  void fill_statx(Inode *in, unsigned int mask, struct ceph_statx *stx);
+  void fill_statx(InodeRef& in, unsigned int mask, struct ceph_statx *stx) {
+    return fill_statx(in.get(), mask, stx);
+  }
+
   void touch_dn(Dentry *dn);
 
   // trim cache.
@@ -987,7 +993,9 @@ public:
   int symlink(const char *existing, const char *newname);
 
   // inode stuff
+  unsigned statx_to_mask(unsigned int flags, unsigned int want);
   int stat(const char *path, struct stat *stbuf, frag_info_t *dirstat=0, int mask=CEPH_STAT_CAP_INODE_ALL);
+  int statx(const char *path, struct ceph_statx *stx, unsigned int want, unsigned int flags);
   int lstat(const char *path, struct stat *stbuf, frag_info_t *dirstat=0, int mask=CEPH_STAT_CAP_INODE_ALL);
   int lstatlite(const char *path, struct statlite *buf);
 
@@ -1087,6 +1095,8 @@ public:
   bool ll_forget(Inode *in, int count);
   bool ll_put(Inode *in);
   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_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,
diff --git a/src/include/cephfs/ceph_statx.h b/src/include/cephfs/ceph_statx.h
new file mode 100644 (file)
index 0000000..48ce0f9
--- /dev/null
@@ -0,0 +1,85 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * scalable distributed file system
+ *
+ * Copyright (C) Jeff Layton <jlayton@redhat.com>
+ *
+ * Heavily borrowed from David Howells' draft statx patchset.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation.  See file COPYING.
+ *
+ */
+
+#ifndef CEPH_CEPH_STATX_H
+#define CEPH_CEPH_STATX_H
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Since the xstat patches are still a work in progress, we borrow its data
+ * structures and #defines to implement ceph_getattrx. Once the xstat stuff
+ * has been merged we should drop this and switch over to using that instead.
+ */
+struct ceph_statx {
+       uint32_t        stx_mask;
+       uint32_t        stx_information;
+       uint32_t        stx_blksize;
+       uint32_t        stx_nlink;
+       uint32_t        stx_gen;
+       uint32_t        stx_uid;
+       uint32_t        stx_gid;
+       uint16_t        stx_mode;
+       uint16_t        __spare0[1];
+       uint64_t        stx_ino;
+       uint64_t        stx_size;
+       uint64_t        stx_blocks;
+       uint64_t        stx_version;
+       int64_t         stx_atime;
+       int64_t         stx_btime;
+       int64_t         stx_ctime;
+       int64_t         stx_mtime;
+       int32_t         stx_atime_ns;
+       int32_t         stx_btime_ns;
+       int32_t         stx_ctime_ns;
+       int32_t         stx_mtime_ns;
+       uint32_t        stx_rdev_major;
+       uint32_t        stx_rdev_minor;
+       uint32_t        stx_dev_major;
+       uint32_t        stx_dev_minor;
+       uint64_t        __spare1[16];
+};
+
+#define CEPH_STATX_MODE                0x00000001U     /* Want/got stx_mode */
+#define CEPH_STATX_NLINK       0x00000002U     /* Want/got stx_nlink */
+#define CEPH_STATX_UID         0x00000004U     /* Want/got stx_uid */
+#define CEPH_STATX_GID         0x00000008U     /* Want/got stx_gid */
+#define CEPH_STATX_RDEV                0x00000010U     /* Want/got stx_rdev */
+#define CEPH_STATX_ATIME       0x00000020U     /* Want/got stx_atime */
+#define CEPH_STATX_MTIME       0x00000040U     /* Want/got stx_mtime */
+#define CEPH_STATX_CTIME       0x00000080U     /* Want/got stx_ctime */
+#define CEPH_STATX_INO         0x00000100U     /* Want/got stx_ino */
+#define CEPH_STATX_SIZE                0x00000200U     /* Want/got stx_size */
+#define CEPH_STATX_BLOCKS      0x00000400U     /* Want/got stx_blocks */
+#define CEPH_STATX_BASIC_STATS 0x000007ffU     /* The stuff in the normal stat struct */
+#define CEPH_STATX_BTIME       0x00000800U     /* Want/got stx_btime */
+#define CEPH_STATX_GEN         0x00002000U     /* Want/got stx_gen */
+#define CEPH_STATX_ALL_STATS   0x00003fffU     /* All supported stats */
+
+/* statx request flags. Callers can set these in the "flags" field */
+#ifndef AT_NO_ATTR_SYNC
+#define AT_NO_ATTR_SYNC                0x4000 /* Don't sync attributes with the server */
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CEPH_STATX_H */
+
index 491fd658ba63d1c3cbdcb8c30aebdc8f15f19736..c550bcd7b02d2e9dff068f6e8f75e653bfa7f516 100644 (file)
@@ -27,6 +27,8 @@
 #include <stdbool.h>
 #include <fcntl.h>
 
+#include "ceph_statx.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -618,6 +620,19 @@ int ceph_rename(struct ceph_mount_info *cmount, const char *from, const char *to
  */
 int ceph_stat(struct ceph_mount_info *cmount, const char *path, struct stat *stbuf);
 
+/**
+ * Get a file's extended statistics and attributes.
+ *
+ * @param cmount the ceph mount handle to use for performing the stat.
+ * @param path the file or directory to get the statistics of.
+ * @param stx the ceph_statx struct that will be filled in with the file's statistics.
+ * @param want bitfield of CEPH_STATX_* flags showing designed attributes
+ * @param flags bitfield that can be used to set AT_* modifier flags (only AT_NO_ATTR_SYNC and AT_SYMLINK_NOFOLLOW)
+ * @returns 0 on success or negative error code on failure.
+ */
+int ceph_statx(struct ceph_mount_info *cmount, const char *path, struct ceph_statx *stx,
+              unsigned int want, unsigned int flags);
+
 /**
  * Get a file's statistics and attributes, without following symlinks.
  *
@@ -1393,6 +1408,9 @@ int ceph_ll_walk(struct ceph_mount_info *cmount, const char *name,
                 struct stat *attr);
 int ceph_ll_getattr(struct ceph_mount_info *cmount, struct Inode *in,
                    struct stat *attr, int uid, int gid);
+int ceph_ll_getattrx(struct ceph_mount_info *cmount, struct Inode *in,
+                   struct ceph_statx *stx, unsigned int want, unsigned int flags,
+                   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_open(struct ceph_mount_info *cmount, struct Inode *in, int flags,
index 75653610fdea4e27a47a96008c555675b73be31b..c01255e59063b41d9db3e30d8981f9efc7b89fa3 100644 (file)
@@ -610,6 +610,14 @@ extern "C" int ceph_stat(struct ceph_mount_info *cmount, const char *path,
   return cmount->get_client()->stat(path, stbuf);
 }
 
+extern "C" int ceph_statx(struct ceph_mount_info *cmount, const char *path,
+                         struct ceph_statx *stx, unsigned int want, unsigned int flags)
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->statx(path, stx, want, flags);
+}
+
 extern "C" int ceph_lstat(struct ceph_mount_info *cmount, const char *path,
                          struct stat *stbuf)
 {
@@ -1406,6 +1414,14 @@ extern "C" int ceph_ll_getattr(class ceph_mount_info *cmount,
   return (cmount->get_client()->ll_getattr(in, attr, uid, gid));
 }
 
+extern "C" int ceph_ll_getattrx(class ceph_mount_info *cmount,
+                               Inode *in, struct ceph_statx *stx,
+                               unsigned int want, unsigned int flags,
+                               int uid, int gid)
+{
+  return (cmount->get_client()->ll_getattrx(in, stx, want, flags, uid, gid));
+}
+
 extern "C" int ceph_ll_setattr(class ceph_mount_info *cmount,
                               Inode *in, struct stat *st,
                               int mask, int uid, int gid)
index 62dfd785104c081bed890b096b99c0b4ae4f1766..eeb54c6c5d8bf40c7b7ab60215e476b6a855937a 100644 (file)
@@ -1467,3 +1467,38 @@ TEST(LibCephFS, SlashDotDot) {
 
   ceph_shutdown(cmount);
 }
+
+TEST(LibCephFS, Btime) {
+  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, "/getattrx%x", getpid());
+
+  ceph_unlink(cmount, filename);
+  int fd = ceph_open(cmount, filename, O_RDWR|O_CREAT|O_EXCL, 0666);
+  ASSERT_LT(0, fd);
+
+  struct ceph_statx    stx;
+  ASSERT_EQ(ceph_statx(cmount, filename, &stx, CEPH_STATX_CTIME|CEPH_STATX_BTIME, 0), 0);
+  ASSERT_TRUE(stx.stx_mask & (CEPH_STATX_CTIME|CEPH_STATX_BTIME));
+  ASSERT_EQ(stx.stx_btime, stx.stx_ctime);
+  ASSERT_EQ(stx.stx_btime_ns, stx.stx_ctime_ns);
+
+  int64_t old_btime = stx.stx_btime;
+  int32_t old_btime_ns = stx.stx_btime_ns;
+
+  /* Now sleep, do a chmod and verify that the ctime changed, but btime didn't */
+  sleep(1);
+  ASSERT_EQ(ceph_chmod(cmount, filename, 0644), 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, old_btime);
+  ASSERT_EQ(stx.stx_btime_ns, old_btime_ns);
+  ASSERT_FALSE(old_btime == stx.stx_ctime && old_btime_ns == stx.stx_ctime_ns);
+
+  ceph_shutdown(cmount);
+}