gfapi: new api glfs_statx as linux's statx

Change-Id: I44dd6ceef0954ae7fc13f920e84d81bbd3f6a774
Updates: #389
Signed-off-by: Kinglong Mee <mijinlong@open-fs.com>
Signed-off-by: ShyamsundarR <srangana@redhat.com>
This commit is contained in:
ShyamsundarR 2018-11-21 09:43:23 -05:00 committed by Amar Tumballi
parent f99f51ca87
commit c31f1c232a
9 changed files with 500 additions and 7 deletions

View File

@ -170,6 +170,8 @@ _pub_glfs_h_lease _glfs_h_lease$GFAPI_4.0.0
_pub_glfs_upcall_lease_get_object _glfs_upcall_lease_get_object$GFAPI_4.1.6
_pub_glfs_upcall_lease_get_lease_type _glfs_upcall_lease_get_lease_type$GFAPI_4.1.6
_priv_glfs_statx _glfs_statx$GFAPI_future
_pub_glfs_read_async _glfs_read_async$GFAPI_future
_pub_glfs_write_async _glfs_write_async$GFAPI_future
_pub_glfs_readv_async _glfs_readv_async$GFAPI_future

View File

@ -235,6 +235,11 @@ GFAPI_4.1.6 {
glfs_upcall_lease_get_lease_type;
} GFAPI_4.0.0;
GFAPI_PRIVATE_future {
global:
glfs_statx;
} GFAPI_4.1.6;
GFAPI_future {
global:
glfs_read_async;
@ -256,5 +261,5 @@ GFAPI_future {
glfs_discard_async;
glfs_zerofill_async;
glfs_copy_file_range;
} GFAPI_4.1.6;
} GFAPI_PRIVATE_future;

View File

@ -24,6 +24,7 @@
#include <glusterfs/compat-errno.h>
#include <limits.h>
#include "glusterfs3.h"
#include <glusterfs/iatt.h>
#ifdef NAME_MAX
#define GF_NAME_MAX NAME_MAX
@ -206,6 +207,90 @@ glfs_iatt_to_stat(struct glfs *fs, struct iatt *iatt, struct stat *stat)
stat->st_dev = fs->dev_id;
}
void
glfs_iatt_to_statx(struct glfs *fs, struct iatt *iatt, struct glfs_stat *statx)
{
statx->glfs_st_mask = 0;
statx->glfs_st_mode = 0;
if (IATT_TYPE_VALID(iatt->ia_flags)) {
statx->glfs_st_mode |= st_mode_type_from_ia(iatt->ia_type);
statx->glfs_st_mask |= GLFS_STAT_TYPE;
}
if (IATT_MODE_VALID(iatt->ia_flags)) {
statx->glfs_st_mode |= st_mode_prot_from_ia(iatt->ia_prot);
statx->glfs_st_mask |= GLFS_STAT_MODE;
}
if (IATT_NLINK_VALID(iatt->ia_flags)) {
statx->glfs_st_nlink = iatt->ia_nlink;
statx->glfs_st_mask |= GLFS_STAT_NLINK;
}
if (IATT_UID_VALID(iatt->ia_flags)) {
statx->glfs_st_uid = iatt->ia_uid;
statx->glfs_st_mask |= GLFS_STAT_UID;
}
if (IATT_GID_VALID(iatt->ia_flags)) {
statx->glfs_st_gid = iatt->ia_gid;
statx->glfs_st_mask |= GLFS_STAT_GID;
}
if (IATT_ATIME_VALID(iatt->ia_flags)) {
statx->glfs_st_atime.tv_sec = iatt->ia_atime;
statx->glfs_st_atime.tv_nsec = iatt->ia_atime_nsec;
statx->glfs_st_mask |= GLFS_STAT_ATIME;
}
if (IATT_MTIME_VALID(iatt->ia_flags)) {
statx->glfs_st_mtime.tv_sec = iatt->ia_mtime;
statx->glfs_st_mtime.tv_nsec = iatt->ia_mtime_nsec;
statx->glfs_st_mask |= GLFS_STAT_MTIME;
}
if (IATT_CTIME_VALID(iatt->ia_flags)) {
statx->glfs_st_ctime.tv_sec = iatt->ia_ctime;
statx->glfs_st_ctime.tv_nsec = iatt->ia_ctime_nsec;
statx->glfs_st_mask |= GLFS_STAT_CTIME;
}
if (IATT_BTIME_VALID(iatt->ia_flags)) {
statx->glfs_st_btime.tv_sec = iatt->ia_btime;
statx->glfs_st_btime.tv_nsec = iatt->ia_btime_nsec;
statx->glfs_st_mask |= GLFS_STAT_BTIME;
}
if (IATT_INO_VALID(iatt->ia_flags)) {
statx->glfs_st_ino = iatt->ia_ino;
statx->glfs_st_mask |= GLFS_STAT_INO;
}
if (IATT_SIZE_VALID(iatt->ia_flags)) {
statx->glfs_st_size = iatt->ia_size;
statx->glfs_st_mask |= GLFS_STAT_SIZE;
}
if (IATT_BLOCKS_VALID(iatt->ia_flags)) {
statx->glfs_st_blocks = iatt->ia_blocks;
statx->glfs_st_mask |= GLFS_STAT_BLOCKS;
}
/* unconditionally present, encode as is */
statx->glfs_st_blksize = iatt->ia_blksize;
statx->glfs_st_rdev_major = ia_major(iatt->ia_rdev);
statx->glfs_st_rdev_minor = ia_minor(iatt->ia_rdev);
statx->glfs_st_dev_major = ia_major(fs->dev_id);
statx->glfs_st_dev_minor = ia_minor(fs->dev_id);
/* At present we do not read any localFS attributes and pass them along,
* so setting this to 0. As we start supporting file attributes we can
* populate the same here as well */
statx->glfs_st_attributes = 0;
statx->glfs_st_attributes_mask = 0;
}
int
glfs_loc_unlink(loc_t *loc)
{
@ -455,6 +540,61 @@ invalid_fs:
GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_stat, 3.4.0);
int
priv_glfs_statx(struct glfs *fs, const char *path, unsigned int mask,
struct glfs_stat *statxbuf)
{
int ret = -1;
xlator_t *subvol = NULL;
loc_t loc = {
0,
};
struct iatt iatt = {
0,
};
int reval = 0;
DECLARE_OLD_THIS;
__GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs);
if (path == NULL) {
ret = -1;
errno = EINVAL;
goto out;
}
if (mask & ~GLFS_STAT_ALL) {
ret = -1;
errno = EINVAL;
goto out;
}
subvol = glfs_active_subvol(fs);
if (!subvol) {
ret = -1;
errno = EIO;
goto out;
}
retry:
ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval);
ESTALE_RETRY(ret, errno, reval, &loc, retry);
if (ret == 0 && statxbuf)
glfs_iatt_to_statx(fs, &iatt, statxbuf);
out:
loc_wipe(&loc);
glfs_subvol_done(fs, subvol);
__GLFS_EXIT_FS;
invalid_fs:
return ret;
}
GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_statx, future);
int
pub_glfs_fstat(struct glfs_fd *glfd, struct stat *stat)
{

View File

@ -12,7 +12,6 @@
#define _GLFS_HANDLES_H
#include "glfs.h"
#include <inttypes.h>
/* GLFS OBJECT BASED OPERATIONS
*

View File

@ -667,4 +667,32 @@ get_fop_attr_thrd_key(dict_t **fop_attr);
void
unset_fop_attr(dict_t **fop_attr);
/*
SYNOPSIS
glfs_statx: Fetch extended file attributes for the given path.
DESCRIPTION
This function fetches extended file attributes for the given path.
PARAMETERS
@fs: The 'virtual mount' object referencing a volume, under which file exists.
@path: Path of the file within the virtual mount.
@mask: Requested extended file attributes mask, (See mask defines above)
RETURN VALUES
-1 : Failure. @errno will be set with the type of failure.
0 : Filled in statxbuf with appropriate masks for valid items in the
structure.
ERRNO VALUES
EINVAL: fs is invalid
EINVAL: mask has unsupported bits set
Other errors as returned by stat(2)
*/
int
glfs_statx(struct glfs *fs, const char *path, unsigned int mask,
struct glfs_stat *statxbuf) GFAPI_PRIVATE(glfs_statx, future);
#endif /* !_GLFS_INTERNAL_H */

View File

@ -40,7 +40,8 @@
#include <sys/cdefs.h>
#include <dirent.h>
#include <sys/statvfs.h>
#include <inttypes.h>
#include <stdint.h>
#include <sys/time.h>
/*
* For off64_t to be defined, we need both
@ -403,6 +404,101 @@ glfs_get_volumeid(glfs_t *fs, char *volid, size_t size) __THROW
struct glfs_fd;
typedef struct glfs_fd glfs_fd_t;
/*
* Mask for request/result items in the struct glfs_stat.
*
* Query request/result mask for glfs_stat() (family of functions) and
* struct glfs_stat::glfs_st_mask.
*
* These bits should be set in the mask argument of glfs_stat() (family of
* functions) to request particular items when calling glfs_stat().
*
* NOTE: Lower order 32 bits are used to reflect statx(2) bits. For Gluster
* specific attrs/extensions, use higher order 32 bits.
*
*/
#define GLFS_STAT_TYPE 0x0000000000000001U /* Want/got stx_mode & S_IFMT */
#define GLFS_STAT_MODE 0x0000000000000002U /* Want/got stx_mode & ~S_IFMT */
#define GLFS_STAT_NLINK 0x0000000000000004U /* Want/got stx_nlink */
#define GLFS_STAT_UID 0x0000000000000008U /* Want/got stx_uid */
#define GLFS_STAT_GID 0x0000000000000010U /* Want/got stx_gid */
#define GLFS_STAT_ATIME 0x0000000000000020U /* Want/got stx_atime */
#define GLFS_STAT_MTIME 0x0000000000000040U /* Want/got stx_mtime */
#define GLFS_STAT_CTIME 0x0000000000000080U /* Want/got stx_ctime */
#define GLFS_STAT_INO 0x0000000000000100U /* Want/got stx_ino */
#define GLFS_STAT_SIZE 0x0000000000000200U /* Want/got stx_size */
#define GLFS_STAT_BLOCKS 0x0000000000000400U /* Want/got stx_blocks */
#define GLFS_STAT_BASIC_STATS \
0x00000000000007ffU /* Items in the normal stat struct */
#define GLFS_STAT_BTIME 0x0000000000000800U /* Want/got stx_btime */
#define GLFS_STAT_ALL 0x0000000000000fffU /* All currently supported flags */
#define GLFS_STAT_RESERVED \
0x8000000000000000U /* Reserved to denote future expansion */
/*
* Attributes to be found in glfs_st_attributes and masked in
* glfs_st_attributes_mask.
*
* These give information about the features or the state of a file that might
* be of use to programs.
*
* NOTE: Lower order 32 bits are used to reflect statx(2) attribute bits. For
* Gluster specific attrs, use higher order 32 bits.
*
* NOTE: We do not support any file attributes or state as yet!
*/
#define GLFS_STAT_ATTR_RESERVED \
0x8000000000000000U /* Reserved to denote future expansion */
/* Extended file attribute structure.
*
* The caller passes a mask of what they're specifically interested in as a
* parameter to glfs_stat(). What glfs_stat() actually got will be indicated
* in glfs_st_mask upon return.
*
* For each bit in the mask argument:
*
* - if the datum is not supported:
*
* - the bit will be cleared, and
*
* - the datum value is undefined
*
* - otherwise, if explicitly requested:
*
* - the field will be filled in and the bit will be set;
*
* - otherwise, if not requested, but available in, it will be filled in
* anyway, and the bit will be set upon return;
*
* - otherwise the field and the bit will be cleared before returning.
*
*/
struct glfs_stat {
uint64_t glfs_st_mask; /* What results were written [uncond] */
uint64_t glfs_st_attributes; /* Flags conveying information about the file
[uncond] */
uint64_t glfs_st_attributes_mask; /* Mask to show what's supported in
st_attributes [ucond] */
struct timespec glfs_st_atime; /* Last access time */
struct timespec glfs_st_btime; /* File creation time */
struct timespec glfs_st_ctime; /* Last attribute change time */
struct timespec glfs_st_mtime; /* Last data modification time */
ino_t glfs_st_ino; /* Inode number */
off_t glfs_st_size; /* File size */
blkcnt_t glfs_st_blocks; /* Number of 512-byte blocks allocated */
uint32_t glfs_st_rdev_major; /* Device ID of special file [if bdev/cdev] */
uint32_t glfs_st_rdev_minor;
uint32_t glfs_st_dev_major; /* ID of device containing file [uncond] */
uint32_t glfs_st_dev_minor;
blksize_t glfs_st_blksize; /* Preferred general I/O size [uncond] */
nlink_t glfs_st_nlink; /* Number of hard links */
uid_t glfs_st_uid; /* User ID of owner */
gid_t glfs_st_gid; /* Group ID of owner */
mode_t glfs_st_mode; /* File mode */
};
#define GLFS_LEASE_ID_SIZE 16 /* 128bits */
typedef char glfs_leaseid_t[GLFS_LEASE_ID_SIZE];

View File

@ -264,12 +264,10 @@ st_mode_prot_from_ia(ia_prot_t prot)
return prot_bit;
}
static inline mode_t
st_mode_from_ia(ia_prot_t prot, ia_type_t type)
static inline uint32_t
st_mode_type_from_ia(ia_type_t type)
{
mode_t st_mode = 0;
uint32_t type_bit = 0;
uint32_t prot_bit = 0;
switch (type) {
case IA_IFREG:
@ -297,6 +295,17 @@ st_mode_from_ia(ia_prot_t prot, ia_type_t type)
break;
}
return type_bit;
}
static inline mode_t
st_mode_from_ia(ia_prot_t prot, ia_type_t type)
{
mode_t st_mode = 0;
uint32_t type_bit = 0;
uint32_t prot_bit = 0;
type_bit = st_mode_type_from_ia(type);
prot_bit = st_mode_prot_from_ia(prot);
st_mode = (type_bit | prot_bit);

View File

@ -0,0 +1,184 @@
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <glusterfs/api/glfs.h>
#define VALIDATE_AND_GOTO_LABEL_ON_ERROR(func, ret, label) \
do { \
if (ret < 0) { \
fprintf(stderr, "%s : returned error %d (%s)\n", func, ret, \
strerror(errno)); \
goto label; \
} \
} while (0)
#define GOTO_LABEL_ON_FALSE(compstr, ret, label) \
do { \
if (ret == false) { \
fprintf(stderr, "%s : comparison failed!\n", compstr); \
goto label; \
} \
} while (0)
#define WRITE_SIZE 513
#define TRUNC_SIZE 4096
/* Using private function and hence providing a forward declation in sync with
code in glfs-internal.h */
int
glfs_statx(struct glfs *fs, const char *path, unsigned int mask,
struct glfs_stat *statxbuf);
int
main(int argc, char *argv[])
{
int ret = -1;
int flags = O_RDWR | O_SYNC;
glfs_t *fs = NULL;
glfs_fd_t *fd1 = NULL;
char *volname = NULL;
char *logfile = NULL;
const char *filename = "file_tmp";
const char buff[WRITE_SIZE];
struct stat sb;
unsigned int mask;
struct glfs_stat statx;
bool bret;
if (argc != 3) {
fprintf(stderr, "Invalid argument\n");
fprintf(stderr, "Usage: %s <volname> <logfile>\n", argv[0]);
return 1;
}
volname = argv[1];
logfile = argv[2];
fs = glfs_new(volname);
if (!fs)
VALIDATE_AND_GOTO_LABEL_ON_ERROR("glfs_new", ret, out);
ret = glfs_set_volfile_server(fs, "tcp", "localhost", 24007);
VALIDATE_AND_GOTO_LABEL_ON_ERROR("glfs_set_volfile_server", ret, out);
ret = glfs_set_logging(fs, logfile, 7);
VALIDATE_AND_GOTO_LABEL_ON_ERROR("glfs_set_logging", ret, out);
ret = glfs_init(fs);
VALIDATE_AND_GOTO_LABEL_ON_ERROR("glfs_init", ret, out);
fd1 = glfs_creat(fs, filename, flags, 0644);
if (fd1 == NULL) {
ret = -1;
VALIDATE_AND_GOTO_LABEL_ON_ERROR("glfs_creat", ret, out);
}
ret = glfs_truncate(fs, filename, TRUNC_SIZE);
VALIDATE_AND_GOTO_LABEL_ON_ERROR("glfs_truncate", ret, out);
ret = glfs_write(fd1, buff, WRITE_SIZE, flags);
VALIDATE_AND_GOTO_LABEL_ON_ERROR("glfs_write", ret, out);
ret = glfs_fstat(fd1, &sb);
VALIDATE_AND_GOTO_LABEL_ON_ERROR("glfs_fstat", ret, out);
if (sb.st_size != TRUNC_SIZE) {
fprintf(stderr, "wrong size %jd should be %jd\n", (intmax_t)sb.st_size,
(intmax_t)2048);
ret = -1;
goto out;
}
glfs_close(fd1);
fd1 = NULL;
/* TEST 1: Invalid mask to statx */
mask = 0xfafadbdb;
ret = glfs_statx(fs, filename, mask, NULL);
if (ret == 0 || ((ret == -1) && (errno != EINVAL))) {
fprintf(stderr,
"Invalid args passed, but error returned is"
" incorrect (ret - %d, errno - %d)\n",
ret, errno);
ret = -1;
goto out;
}
ret = 0;
/* TEST 2: Call statx and validate fields against prior fstat data */
/* NOTE: This fails, as iatt->ia_flags are not carried through the stack,
* for example if mdc_to_iatt is invoked to serve cached stat, we will loose
* the flags. */
mask = GLFS_STAT_ALL;
ret = glfs_statx(fs, filename, mask, &statx);
VALIDATE_AND_GOTO_LABEL_ON_ERROR("glfs_statx", ret, out);
if ((statx.glfs_st_mask & GLFS_STAT_BASIC_STATS) != GLFS_STAT_BASIC_STATS) {
fprintf(stderr, "Invalid glfs_st_mask, expecting 0x%x got 0x%x\n",
GLFS_STAT_ALL, statx.glfs_st_mask);
ret = -1;
goto out;
}
bret = (sb.st_ino == statx.glfs_st_ino);
GOTO_LABEL_ON_FALSE("(sb.st_ino == statx.glfs_st_ino)", bret, out);
bret = (sb.st_mode == statx.glfs_st_mode);
GOTO_LABEL_ON_FALSE("(sb.st_mode == statx.glfs_st_mode)", bret, out);
bret = (sb.st_nlink == statx.glfs_st_nlink);
GOTO_LABEL_ON_FALSE("(sb.st_nlink == statx.glfs_st_nlink)", bret, out);
bret = (sb.st_uid == statx.glfs_st_uid);
GOTO_LABEL_ON_FALSE("(sb.st_uid == statx.glfs_st_uid)", bret, out);
bret = (sb.st_gid == statx.glfs_st_gid);
GOTO_LABEL_ON_FALSE("(sb.st_gid == statx.glfs_st_gid)", bret, out);
bret = (sb.st_size == statx.glfs_st_size);
GOTO_LABEL_ON_FALSE("(sb.st_size == statx.glfs_st_size)", bret, out);
bret = (sb.st_blksize == statx.glfs_st_blksize);
GOTO_LABEL_ON_FALSE("(sb.st_blksize == statx.glfs_st_blksize)", bret, out);
bret = (sb.st_blocks == statx.glfs_st_blocks);
GOTO_LABEL_ON_FALSE("(sb.st_blocks == statx.glfs_st_blocks)", bret, out);
bret = (!memcmp(&sb.st_atim, &statx.glfs_st_atime,
sizeof(struct timespec)));
GOTO_LABEL_ON_FALSE("(sb.st_atim == statx.glfs_st_atime)", bret, out);
bret = (!memcmp(&sb.st_mtim, &statx.glfs_st_mtime,
sizeof(struct timespec)));
GOTO_LABEL_ON_FALSE("(sb.st_mtim == statx.glfs_st_mtime)", bret, out);
bret = (!memcmp(&sb.st_ctim, &statx.glfs_st_ctime,
sizeof(struct timespec)));
GOTO_LABEL_ON_FALSE("(sb.st_ctim == statx.glfs_st_ctime)", bret, out);
/* TEST 3: Check if partial masks are accepted */
mask = GLFS_STAT_TYPE | GLFS_STAT_UID | GLFS_STAT_GID;
ret = glfs_statx(fs, filename, mask, &statx);
VALIDATE_AND_GOTO_LABEL_ON_ERROR("glfs_statx", ret, out);
/* We currently still return all stats, as is acceptable based on the API
* definition in the header (and in statx as well) */
if ((statx.glfs_st_mask & GLFS_STAT_BASIC_STATS) != GLFS_STAT_BASIC_STATS) {
fprintf(stderr, "Invalid glfs_st_mask, expecting 0x%x got 0x%x\n",
GLFS_STAT_ALL, statx.glfs_st_mask);
ret = -1;
goto out;
}
out:
if (fd1 != NULL)
glfs_close(fd1);
if (fs) {
(void)glfs_fini(fs);
}
return ret;
}

View File

@ -0,0 +1,30 @@
#!/bin/bash
. $(dirname $0)/../../include.rc
. $(dirname $0)/../../volume.rc
cleanup;
TEST glusterd
TEST $CLI volume create $V0 ${H0}:$B0/brick1;
EXPECT 'Created' volinfo_field $V0 'Status';
TEST $CLI volume start $V0;
EXPECT 'Started' volinfo_field $V0 'Status';
# NOTE: Test is passing due to very specific volume configuration
# Disable md-cache, as it does not save and return ia_flags from iatt
# This is possibly going to be true of other xlators as well (ec/afr), need to
# ensure these are fixed, or hack statx to return all basic attrs anyway.
TEST $CLI volume set $V0 performance.md-cache-timeout 0
logdir=`gluster --print-logdir`
build_tester $(dirname $0)/gfapi-statx-basic.c -lgfapi
TEST ./$(dirname $0)/gfapi-statx-basic $V0 $logdir/gfapi-statx-basic.log
cleanup_tester $(dirname $0)/gfapi-statx-basic
cleanup;