From: Jeff Layton Date: Mon, 29 Aug 2016 11:16:38 +0000 (-0400) Subject: libcephfs: add a ceph_ll_getattrx and ceph_statx X-Git-Tag: v11.0.1~326^2~27 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=f7c885e1f9cefc359f8b6fe7e51e212387432529;p=ceph.git libcephfs: add a ceph_ll_getattrx and ceph_statx 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 --- diff --git a/ceph.spec.in b/ceph.spec.in index 6fd2cb3f977..d52e2ab2b65 100644 --- a/ceph.spec.in +++ b/ceph.spec.in @@ -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 diff --git a/debian/libcephfs-dev.install b/debian/libcephfs-dev.install index 625c874ec14..d2d648719fd 100644 --- a/debian/libcephfs-dev.install +++ b/debian/libcephfs-dev.install @@ -1,2 +1,3 @@ usr/include/cephfs/libcephfs.h +usr/include/cephfs/ceph_statx.h usr/lib/libcephfs.so diff --git a/src/client/Client.cc b/src/client/Client.cc index f8cdce0e870..6234ea4b26b 100644 --- a/src/client/Client.cc +++ b/src/client/Client.cc @@ -94,6 +94,8 @@ #include "include/assert.h" #include "include/stat.h" +#include "include/cephfs/ceph_statx.h" + #if HAVE_GETGROUPLIST #include #include @@ -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) { diff --git a/src/client/Client.h b/src/client/Client.h index 5b0b7ee0a4c..520f000a3e0 100644 --- a/src/client/Client.h +++ b/src/client/Client.h @@ -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 index 00000000000..48ce0f9154a --- /dev/null +++ b/src/include/cephfs/ceph_statx.h @@ -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 + * + * 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 + +#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 */ + diff --git a/src/include/cephfs/libcephfs.h b/src/include/cephfs/libcephfs.h index 491fd658ba6..c550bcd7b02 100644 --- a/src/include/cephfs/libcephfs.h +++ b/src/include/cephfs/libcephfs.h @@ -27,6 +27,8 @@ #include #include +#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, diff --git a/src/libcephfs.cc b/src/libcephfs.cc index 75653610fde..c01255e5906 100644 --- a/src/libcephfs.cc +++ b/src/libcephfs.cc @@ -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) diff --git a/src/test/libcephfs/test.cc b/src/test/libcephfs/test.cc index 62dfd785104..eeb54c6c5d8 100644 --- a/src/test/libcephfs/test.cc +++ b/src/test/libcephfs/test.cc @@ -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); +}