/*
Copyright (c) 2012 Red Hat, Inc. <http://www.redhat.com>
This file is part of GlusterFS.
This file is licensed to you under your choice of the GNU Lesser
General Public License, version 3 or any later version (LGPLv3 or
later), or the GNU General Public License, version 2 (GPLv2), in all
cases as published by the Free Software Foundation.
*/
#include "glfs-internal.h"
#include "glfs-mem-types.h"
#include "syncop.h"
#include "glfs.h"
#define DEFAULT_REVAL_COUNT 1
#define ESTALE_RETRY(ret,errno,reval,loc,label) do { \
if (ret == -1 && errno == ESTALE) { \
if (reval < DEFAULT_REVAL_COUNT) { \
reval++; \
loc_wipe (loc); \
goto label; \
} \
} \
} while (0)
static int
glfs_loc_link (loc_t *loc, struct iatt *iatt)
{
int ret = -1;
inode_t *linked_inode = NULL;
if (!loc->inode) {
errno = EINVAL;
return -1;
}
linked_inode = inode_link (loc->inode, loc->parent, loc->name, iatt);
if (linked_inode) {
inode_lookup (linked_inode);
inode_unref (linked_inode);
ret = 0;
} else {
ret = -1;
errno = ENOMEM;
}
return ret;
}
static void
glfs_iatt_to_stat (struct glfs *fs, struct iatt *iatt, struct stat *stat)
{
iatt_to_stat (iatt, stat);
stat->st_dev = fs->dev_id;
}
static int
glfs_loc_unlink (loc_t *loc)
{
inode_unlink (loc->inode, loc->parent, loc->name);
return 0;
}
struct glfs_fd *
glfs_open (struct glfs *fs, const char *path, int flags)
{
int ret = -1;
struct glfs_fd *glfd = NULL;
xlator_t *subvol = NULL;
loc_t loc = {0, };
struct iatt iatt = {0, };
int reval = 0;
__glfs_entry_fs (fs);
subvol = glfs_active_subvol (fs);
if (!subvol) {
ret = -1;
errno = EIO;
goto out;
}
glfd = glfs_fd_new (fs);
if (!glfd)
goto out;
retry:
ret = glfs_resolve (fs, subvol, path, &loc, &iatt, reval);
ESTALE_RETRY (ret, errno, reval, &loc, retry);
if (ret)
goto out;
if (IA_ISDIR (iatt.ia_type)) {
ret = -1;
errno = EISDIR;
goto out;
}
if (!IA_ISREG (iatt.ia_type)) {
ret = -1;
errno = EINVAL;
goto out;
}
if (glfd->fd) {
/* Retry. Safe to touch glfd->fd as we
still have not glfs_fd_bind() yet.
*/
fd_unref (glfd->fd);
glfd->fd = NULL;
}
glfd->fd = fd_create (loc.inode, getpid());
if (!glfd->fd) {
ret = -1;
errno = ENOMEM;
goto out;
}
ret = syncop_open (subvol, &loc, flags, glfd->fd);
ESTALE_RETRY (ret, errno, reval, &loc, retry);
out:
loc_wipe (&loc);
if (ret && glfd) {
glfs_fd_destroy (glfd);
glfd = NULL;
} else if (glfd) {
glfd->fd->flags = flags;
fd_bind (glfd->fd);
glfs_fd_bind (glfd);
}
glfs_subvol_done (fs, subvol);
return glfd;
}
int
glfs_close (struct glfs_fd *glfd)
{
xlator_t *subvol = NULL;
int ret = -1;
fd_t *fd = NULL;
struct glfs *fs = NULL;
__glfs_entry_fd (glfd);
subvol = glfs_active_subvol (glfd->fs);
if (!subvol) {
ret = -1;
errno = EIO;
goto out;
}
fd = glfs_resolve_fd (glfd->fs, subvol, glfd);
if (!fd) {
ret = -1;
errno = EBADFD;
goto out;
}
ret = syncop_flush (subvol, fd);
out:
fs = glfd->fs;
glfs_fd_destroy (glfd);
if (fd)
fd_unref (fd);
glfs_subvol_done (fs, subvol);
return ret;
}
int
glfs_lstat (struct glfs *fs, const char *path, struct stat *stat)
{
int ret = -1;
xlator_t *subvol = NULL;
loc_t loc = {0, };
struct iatt iatt = {0, };
int reval = 0;
__glfs_entry_fs (fs);
subvol = glfs_active_subvol (fs);
if (!subvol) {
ret = -1;
errno = EIO;
goto out;
}
retry:
ret = glfs_lresolve (fs, subvol, path, &loc, &iatt, reval);
ESTALE_RETRY (ret, errno, reval, &loc, retry);
if (ret == 0 && stat)
glfs_iatt_to_stat (fs, &iatt, stat);
out:
loc_wipe (&loc);
glfs_subvol_done (fs, subvol);
return ret;
}
int
glfs_stat (struct glfs *fs, const char *path, struct stat *stat)
{
int ret = -1;
xlator_t *subvol = NULL;
loc_t loc = {0, };
struct iatt iatt = {0, };
int reval = 0;
__glfs_entry_fs (fs);
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 && stat)
glfs_iatt_to_stat (fs, &iatt, stat);
out:
loc_wipe (&loc);
glfs_subvol_done (fs, subvol);
return ret;
}
int
glfs_fstat (struct glfs_fd *glfd, struct stat *stat)
{
int ret = -1;
xlator_t *subvol = NULL;
struct iatt iatt = {0, };
fd_t *fd = NULL;
__glfs_entry_fd (glfd);
subvol = glfs_active_subvol (glfd->fs);
if (!subvol) {
ret = -1;
errno = EIO;
goto out;
}
fd = glfs_resolve_fd (glfd->fs, subvol, glfd);
if (!fd) {
ret = -1;
errno = EBADFD;
goto out;
}
ret = syncop_fstat (subvol, fd, &iatt);
if (ret == 0 && stat)
glfs_iatt_to_stat (glfd->fs, &iatt, stat);
out:
if (fd)
fd_unref (fd);
glfs_subvol_done (glfd->fs, subvol);
return ret;
}
struct glfs_fd *
glfs_creat (struct glfs *fs, const char *path, int flags, mode_t mode)
{
int ret = -1;
struct glfs_fd *glfd = NULL;
xlator_t *subvol = NULL;
loc_t loc = {0, };
struct iatt iatt = {0, };
uuid_t gfid;
dict_t *xattr_req = NULL;
int reval = 0;
__glfs_entry_fs (fs);
subvol = glfs_active_subvol (fs);
if (!subvol) {
ret = -1;
errno = EIO;
goto out;
}
xattr_req = dict_new ();
if (!xattr_req) {
ret = -1;
errno = ENOMEM;
goto out;
}
uuid_generate (gfid);
ret = dict_set_static_bin (xattr_req, "gfid-req", gfid, 16);
if (ret) {
ret = -1;
errno = ENOMEM;
goto out;
}
glfd = glfs_fd_new (fs);
if (!glfd)
goto out;
/* This must be glfs_resolve() and NOT glfs_lresolve().
That is because open("name", O_CREAT) where "name"
is a danging symlink must create the dangling
destinataion.
*/
retry:
ret = glfs_resolve (fs, subvol, path, &loc, &iatt, reval);
ESTALE_RETRY (ret, errno, reval, &loc, retry);
if (ret == -1 && errno != ENOENT)
/* Any other type of error is fatal */
goto out;
if (ret == -1 && errno == ENOENT && !loc.parent)
/* The parent directory or an ancestor even
higher does not exist
*/
goto out;
if (loc.inode) {
if (flags & O_EXCL) {
ret = -1;
errno = EEXIST;
goto out;
}
if (IA_ISDIR (iatt.ia_type)) {
ret = -1;
errno = EISDIR;
goto out;
}
if (!IA_ISREG (iatt.ia_type)) {
ret = -1;
errno = EINVAL;
goto out;
}
}
if (ret == -1 && errno == ENOENT) {
loc.inode = inode_new (loc.parent->table);
if (!loc.inode) {
ret = -1;
errno = ENOMEM;
goto out;
}
}
if (glfd->fd) {
/* Retry. Safe to touch glfd->fd as we
still have not glfs_fd_bind() yet.
*/
fd_unref (glfd->fd);
glfd->fd = NULL;
}
glfd->fd = fd_create (loc.inode, getpid());
if (!glfd->fd) {
ret = -1;
errno = ENOMEM;
goto out;
}
if (ret == 0) {
ret = syncop_open (subvol, &loc, flags, glfd->fd);
} else {
ret = syncop_create (subvol, &loc, flags, mode, glfd->fd,
xattr_req, &iatt);
}
ESTALE_RETRY (ret, errno, reval, &loc, retry);
if (ret == 0)
ret = glfs_loc_link (&loc, &iatt);
out:
loc_wipe (&loc);
if (xattr_req)
dict_unref (xattr_req);
if (ret && glfd) {
glfs_fd_destroy (glfd);
glfd = NULL;
} else if (glfd) {
glfd->fd->flags = flags;
fd_bind (glfd->fd);
glfs_fd_bind (glfd);
}
glfs_subvol_done (fs, subvol);
return glfd;
}
off_t
glfs_lseek (struct glfs_fd *glfd, off_t offset, int whence)
{
struct stat sb = {0, };
int ret = -1;
__glfs_entry_fd (glfd);
switch (whence) {
case SEEK_SET:
glfd->offset = offset;
break;
case SEEK_CUR:
glfd->offset += offset;
break;
case SEEK_END:
ret = glfs_fstat (glfd, &sb);
if (ret) {
/* seek cannot fail :O */
break;
}
glfd->offset = sb.st_size + offset;
break;
}
return glfd->offset;
}
//////////////
ssize_t
glfs_preadv (struct glfs_fd *glfd, const struct iovec *iovec, int iovcnt,
off_t offset, int flags)
{
xlator_t *subvol = NULL;
ssize_t ret = -1;
ssize_t size = -1;
struct iovec *iov = NULL;
int cnt = 0;
struct iobref *iobref = NULL;
fd_t *fd = NULL;
__glfs_entry_fd (glfd);
subvol = glfs_active_subvol (glfd->fs);
if (!subvol) {
ret = -1;
errno = EIO;
goto out;
}
fd = glfs_resolve_fd (glfd->fs, subvol, glfd);
if (!fd) {
ret = -1;
errno = EBADFD;
goto out;
}
size = iov_length (iovec, iovcnt);
ret = syncop_readv (subvol, fd, size, offset, 0, &iov, &cnt, &iobref);
if (ret <= 0)
goto out;
size = iov_copy (iovec, iovcnt, iov, cnt); /* FIXME!!! */
glfd->offset = (offset + size);
if (iov)
GF_FREE (iov);
if (iobref)
iobref_unref (iobref);
ret = size;
out:
if (fd)
fd_unref (fd);
glfs_subvol_done (glfd->fs, subvol);
return ret;
}
ssize_t
glfs_read (struct glfs_fd *glfd, void *buf, size_t count, int flags)
{
struct iovec iov = {0, };
ssize_t ret = 0;
iov.iov_base = buf;
iov.iov_len = count;
ret = glfs_preadv (glfd, &iov, 1, glfd->offset, flags);
return ret;
}
ssize_t
glfs_pread (struct glfs_fd *glfd, void *buf, size_t count, off_t offset,
int flags)
{
struct iovec iov = {0, };
ssize_t ret = 0;
iov.iov_base = buf;
iov.iov_len = count;
ret = glfs_preadv (glfd, &iov, 1, offset, flags);
return ret;
}
ssize_t
glfs_readv (struct glfs_fd *glfd, const struct iovec *iov, int count,
int flags)
{
ssize_t ret = 0;
ret = glfs_preadv (glfd, iov, count, glfd->offset, flags);
return ret;
}
struct glfs_io {
struct glfs_fd *glfd;
int op;
off_t offset;
struct iovec *iov;
int count;
int flags;
glfs_io_cbk fn;
void *data;
};
static int
glfs_io_async_cbk (int ret, call_frame_t *frame, void *data)
{
struct glfs_io *gio = data;
gio->fn (gio->glfd, ret, gio->data);
GF_FREE (gio->iov);
GF_FREE (gio);
return 0;
}
static int
glfs_io_async_task (void *data)
{
struct glfs_io *gio = data;
ssize_t ret = 0;
switch (gio->op) {
case GF_FOP_WRITE:
ret = glfs_pwritev (gio->glfd, gio->iov, gio->count,
gio->offset, gio->flags);
break;
case GF_FOP_FTRUNCATE:
ret = glfs_ftruncate (gio->glfd, gio->offset);
break;
case GF_FOP_FSYNC:
if (gio->flags)
ret = glfs_fdatasync (gio->glfd);
else
ret = glfs_fsync (gio->glfd);
break;
case GF_FOP_DISCARD:
ret = glfs_discard (gio->glfd, gio->offset, gio->count);
break;
}
return (int) ret;
}
int
glfs_preadv_async_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
int op_ret, int op_errno, struct iovec *iovec,
int count, struct iatt *stbuf, struct iobref *iobref,
dict_t *xdata)
{
struct glfs_io *gio = NULL;
xlator_t *subvol = NULL;
struct glfs *fs = NULL;
struct glfs_fd *glfd = NULL;
gio = frame->local;
frame->local = NULL;
subvol = cookie;
glfd = gio->glfd;
fs = glfd->fs;
if (op_ret <= 0)
goto out;
op_ret = iov_copy (gio->iov, gio->count, iovec, count);
glfd->offset = gio->offset + op_ret;
out:
errno = op_errno;
gio->fn (gio->glfd, op_ret, gio->data);
GF_FREE (gio->iov);
GF_FREE (gio);
STACK_DESTROY (frame->root);
glfs_subvol_done (fs, subvol);
return 0;
}
int
glfs_preadv_async (struct glfs_fd *glfd, const struct iovec *iovec, int count,
off_t offset, int flags, glfs_io_cbk fn, void *data)
{
struct glfs_io *gio = NULL;
int ret = 0;
call_frame_t *frame = NULL;
xlator_t *subvol = NULL;
glfs_t *fs = NULL;
fd_t *fd = NULL;
__glfs_entry_fd (glfd);
subvol = glfs_active_subvol (glfd->fs);
if (!subvol) {
ret = -1;
errno = EIO;
goto out;
}
fd = glfs_resolve_fd (glfd->fs, subvol, glfd);
if (!fd) {
ret = -1;
errno = EBADFD;
goto out;
}
fs = glfd->fs;
frame = syncop_create_frame (THIS);
if (!frame) {
ret = -1;
errno = ENOMEM;
goto out;
}
gio = GF_CALLOC (1, sizeof (*gio), glfs_mt_glfs_io_t);
if (!gio) {
ret = -1;
errno = ENOMEM;
goto out;
}
gio->iov = iov_dup (iovec, count);
if (!gio->iov) {
ret = -1;
errno = ENOMEM;
goto out;
}
gio->op = GF_FOP_READ;
gio->glfd = glfd;
gio->count = count;
gio->offset = offset;
gio->flags = flags;
gio->fn = fn;
gio->data = data;
frame->local = gio;
STACK_WIND_COOKIE (frame, glfs_preadv_async_cbk, subvol, subvol,
subvol->fops->readv, fd, iov_length (iovec, count),
offset, flags, NULL);
out:
if (ret) {
GF_FREE (gio->iov);
GF_FREE (gio);
STACK_DESTROY (frame->root);
glfs_subvol_done (fs, subvol);
}
if (fd)
fd_unref (fd);
return ret;
}
int
glfs_read_async (struct glfs_fd *glfd, void *buf, size_t count, int flags,
glfs_io_cbk fn, void *data)
{
struct iovec iov = {0, };
ssize_t ret = 0;
iov.iov_base = buf;
iov.iov_len = count;
ret = glfs_preadv_async (glfd, &iov, 1, glfd->offset, flags, fn, data);
return ret;
}
int
glfs_pread_async (struct glfs_fd *glfd, void *buf, size_t count, off_t offset,
int flags, glfs_io_cbk fn, void *data)
{
struct iovec iov = {0, };
ssize_t ret = 0;
iov.iov_base = buf;
iov.iov_len = count;
ret = glfs_preadv_async (glfd, &iov, 1, offset, flags, fn, data);
return ret;
}
int
glfs_readv_async (struct glfs_fd *glfd, const struct iovec *iov, int count,
int flags, glfs_io_cbk fn, void *data)
{
ssize_t ret = 0;
ret = glfs_preadv_async (glfd, iov, count, glfd->offset, flags,
fn, data);
return ret;
}
///// writev /////
ssize_t
glfs_pwritev (struct glfs_fd *glfd, const struct iovec *iovec, int iovcnt,
off_t offset, int flags)
{
xlator_t *subvol = NULL;
int ret = -1;
size_t size = -1;
struct iobref *iobref = NULL;
struct iobuf *iobuf = NULL;
struct iovec iov = {0, };
fd_t *fd = NULL;
__glfs_entry_fd (glfd);
subvol = glfs_active_subvol (glfd->fs);
if (!subvol) {
ret = -1;
errno = EIO;
goto out;
}
fd = glfs_resolve_fd (glfd->fs, subvol, glfd);
if (!fd) {
ret = -1;
errno = EBADFD;
goto out;
}
size = iov_length (iovec, iovcnt);
iobuf = iobuf_get2 (subvol->ctx->iobuf_pool, size);
if (!iobuf) {
ret = -1;
errno = ENOMEM;
goto out;
}
iobref = iobref_new ();
if (!iobref) {
iobuf_unref (iobuf);
errno = ENOMEM;
ret = -1;
goto out;
}
ret = iobref_add (iobref, iobuf);
if (ret) {
iobuf_unref (iobuf);
iobref_unref (iobref);
errno = ENOMEM;
ret = -1;
goto out;
}
iov_unload (iobuf_ptr (iobuf), iovec, iovcnt); /* FIXME!!! */
iov.iov_base = iobuf_ptr (iobuf);
iov.iov_len = size;
ret = syncop_writev (subvol, fd, &iov, 1, offset, iobref, flags);
iobuf_unref (iobuf);
iobref_unref (iobref);
if (ret <= 0)
goto out;
glfd->offset = (offset + size);
out:
if (fd)
fd_unref (fd);
glfs_subvol_done (glfd->fs, subvol);
return ret;
}
ssize_t
glfs_write (struct glfs_fd *glfd, const void *buf, size_t count, int flags)
{
struct iovec iov = {0, };
ssize_t ret = 0;
iov.iov_base = (void *) buf;
iov.iov_len = count;
ret = glfs_pwritev (glfd, &iov, 1, glfd->offset, flags);
return ret;
}
ssize_t
glfs_writev (struct glfs_fd *glfd, const struct iovec *iov, int count,
int flags)
{
ssize_t ret = 0;
ret = glfs_pwritev (glfd, iov, count, glfd->offset, flags);
return ret;
}
ssize_t
glfs_pwrite (struct glfs_fd *glfd, const void *buf, size_t count, off_t offset,
int flags)
{
struct iovec iov = {0, };
ssize_t ret = 0;
iov.iov_base = (void *) buf;
iov.iov_len = count;
ret = glfs_pwritev (glfd, &iov, 1, offset, flags);
return ret;
}
int
glfs_pwritev_async (struct glfs_fd *glfd, const struct iovec *iovec, int count,
off_t offset, int flags, glfs_io_cbk fn, void *data)
{
struct glfs_io *gio = NULL;
int ret = 0;
gio = GF_CALLOC (1, sizeof (*gio), glfs_mt_glfs_io_t);
if (!gio) {
errno = ENOMEM;
return -1;
}
gio->iov = iov_dup (iovec, count);
if (!gio->iov) {
GF_FREE (gio);
errno = ENOMEM;
return -1;
}
gio->op = GF_FOP_WRITE;
gio->glfd = glfd;
gio->count = count;
gio->offset = offset;
gio->flags = flags;
gio->fn = fn;
gio->data = data;
ret = synctask_new (glfs_from_glfd (glfd)->ctx->env,
glfs_io_async_task, glfs_io_async_cbk,
NULL, gio);
if (ret) {
GF_FREE (gio->iov);
GF_FREE (gio);
}
return ret;
}
int
glfs_write_async (struct glfs_fd *glfd, const void *buf, size_t count, int flags,
glfs_io_cbk fn, void *data)
{
struct iovec iov = {0, };
ssize_t ret = 0;
iov.iov_base = (void *) buf;
iov.iov_len = count;
ret = glfs_pwritev_async (glfd, &iov, 1, glfd->offset, flags, fn, data);
return ret;
}
int
glfs_pwrite_async (struct glfs_fd *glfd, const void *buf, int count,
off_t offset, int flags, glfs_io_cbk fn, void *data)
{
struct iovec iov = {0, };
ssize_t ret = 0;
iov.iov_base = (void *) buf;
iov.iov_len = count;
ret = glfs_pwritev_async (glfd, &iov, 1, offset, flags, fn, data);
return ret;
}
int
glfs_writev_async (struct glfs_fd *glfd, const struct iovec *iov, int count,
int flags, glfs_io_cbk fn, void *data)
{
ssize_t ret = 0;
ret = glfs_pwritev_async (glfd, iov, count, glfd->offset, flags,
fn, data);
return ret;
}
int
glfs_fsync (struct glfs_fd *glfd)
{
int ret = -1;
xlator_t *subvol = NULL;
fd_t *fd = NULL;
__glfs_entry_fd (glfd);
subvol = glfs_active_subvol (glfd->fs);
if (!subvol) {
ret = -1;
errno = EIO;
goto out;
}
fd = glfs_resolve_fd (glfd->fs, subvol, glfd);
if (!fd) {
ret = -1;
errno = EBADFD;
goto out;
}
ret = syncop_fsync (subvol, fd, 0);
out:
if (fd)
fd_unref (fd);
glfs_subvol_done (glfd->fs, subvol);
return ret;
}
static int
glfs_fsync_async_common (struct glfs_fd *glfd, glfs_io_cbk fn, void *data,
int dataonly)
{
struct glfs_io *gio = NULL;
int ret = 0;
gio = GF_CALLOC (1, sizeof (*gio), glfs_mt_glfs_io_t);
if (!gio) {
errno = ENOMEM;
return -1;
}
gio->op = GF_FOP_FSYNC;
gio->glfd = glfd;
gio->flags = dataonly;
gio->fn = fn;
gio->data = data;
ret = synctask_new (glfs_from_glfd (glfd)->ctx->env,
glfs_io_async_task, glfs_io_async_cbk,
NULL, gio);
if (ret) {
GF_FREE (gio->iov);
GF_FREE (gio);
}
return ret;
}
int
glfs_fsync_async (struct glfs_fd *glfd, glfs_io_cbk fn, void *data)
{
return glfs_fsync_async_common (glfd, fn, data, 0);
}
int
glfs_fdatasync (struct glfs_fd *glfd)
{
int ret = -1;
xlator_t *subvol = NULL;
fd_t *fd = NULL;
__glfs_entry_fd (glfd);
subvol = glfs_active_subvol (glfd->fs);
if (!subvol) {
ret = -1;
errno = EIO;
goto out;
}
fd = glfs_resolve_fd (glfd->fs, subvol, glfd);
if (!fd) {
ret = -1;
errno = EBADFD;
goto out;
}
ret = syncop_fsync (subvol, fd, 1);
out:
if (fd)
fd_unref (fd);
glfs_subvol_done (glfd->fs, subvol);
return ret;
}
int
glfs_fdatasync_async (struct glfs_fd *glfd, glfs_io_cbk fn, void *data)
{
return glfs_fsync_async_common (glfd, fn, data, 1);
}
int
glfs_ftruncate (struct glfs_fd *glfd, off_t offset)
{
|