/*
Copyright (c) 2013 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 "gfid-access.h"
#include <glusterfs/inode.h>
#include <glusterfs/byte-order.h>
#include <glusterfs/statedump.h>
int
ga_valid_inode_loc_copy(loc_t *dst, loc_t *src, xlator_t *this)
{
int ret = 0;
uint64_t value = 0;
/* if its an entry operation, on the virtual */
/* directory inode as parent, we need to handle */
/* it properly */
ret = loc_copy(dst, src);
if (ret < 0)
goto out;
/*
* Change ALL virtual inodes with real-inodes in loc
*/
if (dst->parent) {
ret = inode_ctx_get(dst->parent, this, &value);
if (ret < 0) {
ret = 0; // real-inode
goto out;
}
inode_unref(dst->parent);
dst->parent = inode_ref((inode_t *)(uintptr_t)value);
gf_uuid_copy(dst->pargfid, dst->parent->gfid);
}
if (dst->inode) {
ret = inode_ctx_get(dst->inode, this, &value);
if (ret < 0) {
ret = 0; // real-inode
goto out;
}
inode_unref(dst->inode);
dst->inode = inode_ref((inode_t *)(uintptr_t)value);
gf_uuid_copy(dst->gfid, dst->inode->gfid);
}
out:
return ret;
}
void
ga_newfile_args_free(ga_newfile_args_t *args)
{
if (!args)
goto out;
GF_FREE(args->bname);
if (S_ISLNK(args->st_mode) && args->args.symlink.linkpath) {
GF_FREE(args->args.symlink.linkpath);
args->args.symlink.linkpath = NULL;
}
mem_put(args);
out:
return;
}
void
ga_heal_args_free(ga_heal_args_t *args)
{
if (!args)
goto out;
GF_FREE(args->bname);
mem_put(args);
out:
return;
}
ga_newfile_args_t *
ga_newfile_parse_args(xlator_t *this, data_t *data)
{
ga_newfile_args_t *args = NULL;
ga_private_t *priv = NULL;
int len = 0;
int blob_len = 0;
int min_len = 0;
void *blob = NULL;
priv = this->private;
blob = data->data;
blob_len = data->len;
min_len = sizeof(args->uid) + sizeof(args->gid) + sizeof(args->gfid) +
sizeof(args->st_mode) + 2 + 2;
if (blob_len < min_len) {
gf_log(this->name, GF_LOG_ERROR,
"Invalid length: Total length is less "
"than minimum length.");
goto err;
}
args = mem_get0(priv->newfile_args_pool);
if (args == NULL)
goto err;
args->uid = ntoh32(*(uint32_t *)blob);
blob += sizeof(uint32_t);
blob_len -= sizeof(uint32_t);
args->gid = ntoh32(*(uint32_t *)blob);
blob += sizeof(uint32_t);
blob_len -= sizeof(uint32_t);
memcpy(args->gfid, blob, sizeof(args->gfid));
blob += sizeof(args->gfid);
blob_len -= sizeof(args->gfid);
args->st_mode = ntoh32(*(uint32_t *)blob);
blob += sizeof(uint32_t);
blob_len -= sizeof(uint32_t);
len = strnlen(blob, blob_len);
if (len == blob_len) {
gf_log(this->name, GF_LOG_ERROR, "gfid: %s. No null byte present.",
args->gfid);
goto err;
}
args->bname = GF_MALLOC(len + 1, gf_common_mt_char);
if (args->bname == NULL)
goto err;
memcpy(args->bname, blob, (len + 1));
blob += (len + 1);
blob_len -= (len + 1);
if (S_ISDIR(args->st_mode)) {
if (blob_len < sizeof(uint32_t)) {
gf_log(this->name, GF_LOG_ERROR, "gfid: %s. Invalid length",
args->gfid);
goto err;
}
args->args.mkdir.mode = ntoh32(*(uint32_t *)blob);
blob += sizeof(uint32_t);
blob_len -= sizeof(uint32_t);
if (blob_len < sizeof(uint32_t)) {
gf_log(this->name, GF_LOG_ERROR, "gfid: %s. Invalid length",
args->gfid);
goto err;
}
args->args.mkdir.umask = ntoh32(*(uint32_t *)blob);
blob_len -= sizeof(uint32_t);
if (blob_len < 0) {
gf_log(this->name, GF_LOG_ERROR, "gfid: %s. Invalid length",
args->gfid);
goto err;
}
} else if (S_ISLNK(args->st_mode)) {
len = strnlen(blob, blob_len);
if (len == blob_len) {
gf_log(this->name, GF_LOG_ERROR, "gfid: %s. Invalid length",
args->gfid);
goto err;
}
args->args.symlink.linkpath = GF_MALLOC(len + 1, gf_common_mt_char);
if (args->args.symlink.linkpath == NULL)
goto err;
memcpy(args->args.symlink.linkpath, blob, (len + 1));
blob_len -= (len + 1);
} else {
if (blob_len < sizeof(uint32_t)) {
gf_log(this->name, GF_LOG_ERROR, "gfid: %s. Invalid length",
args->gfid);
goto err;
}
args->args.mknod.mode = ntoh32(*(uint32_t *)blob);
blob += sizeof(uint32_t);
blob_len -= sizeof(uint32_t);
if (blob_len < sizeof(uint32_t)) {
gf_log(this->name, GF_LOG_ERROR, "gfid: %s. Invalid length",
args->gfid);
goto err;
}
args->args.mknod.rdev = ntoh32(*(uint32_t *)blob);
blob += sizeof(uint32_t);
blob_len -= sizeof(uint32_t);
if (blob_len < sizeof(uint32_t)) {
gf_log(this->name, GF_LOG_ERROR, "gfid: %s. Invalid length",
args->gfid);
goto err;
}
args->args.mknod.umask = ntoh32(*(uint32_t *)blob);
blob_len -= sizeof(uint32_t);
}
if (blob_len) {
gf_log(this->name, GF_LOG_ERROR, "gfid: %s. Invalid length",
args->gfid);
goto err;
}
return args;
err:
if (args)
ga_newfile_args_free(args);
return NULL;
}
ga_heal_args_t *
ga_heal_parse_args(xlator_t *this, data_t *data)
{
ga_heal_args_t *args = NULL;
ga_private_t *priv = NULL;
void *blob = NULL;
int len = 0;
int blob_len = 0;
blob = data->data;
blob_len = data->len;
priv = this->private;
/* bname should at least contain a character */
if (blob_len < (sizeof(args->gfid) + 2))
goto err;
args = mem_get0(priv->heal_args_pool);
if (!args)
goto err;
memcpy(args->gfid, blob, sizeof(args->gfid));
blob += sizeof(args->gfid);
blob_len -= sizeof(args->gfid);
len = strnlen(blob, blob_len);
if (len == blob_len)
goto err;
args->bname = GF_MALLOC(len + 1, gf_common_mt_char);
if (!args->bname)
goto err;
memcpy(args->bname, blob, len);
args->bname[len] = '\0';
blob_len -= (len + 1);
if (blob_len)
goto err;
return args;
err:
if (args)
ga_heal_args_free(args);
return NULL;
}
static int32_t
ga_fill_tmp_loc(loc_t *loc, xlator_t *this, uuid_t gfid, char *bname,
dict_t *xdata, loc_t *new_loc)
{
int ret = -1;
uint64_t value = 0;
inode_t *parent = NULL;
unsigned char *gfid_ptr = NULL;
parent = loc->inode;
ret = inode_ctx_get(loc->inode, this, &value);
if (!ret) {
parent = (void *)(uintptr_t)value;
if (gf_uuid_is_null(parent->gfid))
parent = loc->inode;
}
/* parent itself should be looked up */
gf_uuid_copy(new_loc->pargfid, parent->gfid);
new_loc->parent = inode_ref(parent);
new_loc->inode = inode_grep(parent->table, parent, bname);
if (!new_loc->inode) {
new_loc->inode = inode_new(parent->table);
gf_uuid_copy(new_loc->inode->gfid, gfid);
}
loc_path(new_loc, bname);
if (new_loc->path) {
new_loc->name = strrchr(new_loc->path, '/');
if (new_loc->name)
new_loc->name++;
}
gfid_ptr = GF_MALLOC(sizeof(uuid_t), gf_common_mt_uuid_t);
if (!gfid_ptr) {
ret = -1;
goto out;
}
gf_uuid_copy(gfid_ptr, gfid);
ret = dict_set_gfuuid(xdata, "gfid-req", gfid_ptr, false);
if (ret < 0)
goto out;
ret = 0;
out:
if (ret && gfid_ptr)
GF_FREE(gfid_ptr);
return ret;
}
static gf_boolean_t
__is_gfid_access_dir(uuid_t gfid)
{
uuid_t aux_gfid;
memset(aux_gfid, 0, 16);
aux_gfid[15] = GF_AUX_GFID;
if (gf_uuid_compare(gfid, aux_gfid) == 0)
return _gf_true;
return _gf_false;
}
int32_t
ga_forget(xlator_t *this, inode_t *inode)
{
int ret = -1;
uint64_t value = 0;
inode_t *tmp_inode = NULL;
ret = inode_ctx_del(inode, this, &value);
if (ret)
goto out;
tmp_inode = (void *)(uintptr_t)value;
inode_unref(tmp_inode);
out:
return 0;
}
static int
ga_heal_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret,
int32_t op_errno, inode_t *inode, struct iatt *stat, dict_t *dict,
struct iatt *postparent)
{
call_frame_t *orig_frame = NULL;
orig_frame = frame->local;
frame->local = NULL;
/* don't worry about inode linking and other stuff. They'll happen on
* the next lookup.
*/
STACK_DESTROY(frame->root);
STACK_UNWIND_STRICT(setxattr, orig_frame, op_ret, op_errno, dict);
return 0;
}
static int
ga_newentry_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
int32_t op_ret, int32_t op_errno, inode_t *inode,
struct iatt *buf, struct iatt *preparent,
struct iatt *postparent, dict_t *xdata)
{
ga_local_t *local = NULL;
local = frame->local;
/* don't worry about inode linking and other stuff. They'll happen on
* the next lookup.
*/
frame->local = NULL;
STACK_DESTROY(frame->root);
STACK_UNWIND_STRICT(setxattr, local->orig_frame, op_ret, op_errno, xdata);
if (local->xdata)
dict_unref(local->xdata);
loc_wipe(&local->loc);
mem_put(local);
return 0;
}
static int
ga_newentry_lookup_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
int32_t op_ret, int32_t op_errno, inode_t *inode,
struct iatt *stat, dict_t *xdata,
struct iatt *postparent)
{
ga_local_t *local = NULL;
local = frame->local;
if ((op_ret < 0) && ((op_errno != ENOENT) && (op_errno != ESTALE)))
goto err;
STACK_WIND(frame, ga_newentry_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->mknod, &local->loc, local->mode,
local->rdev, local->umask, local->xdata);
return 0;
err:
frame->local = NULL;
STACK_DESTROY(frame->root);
STACK_UNWIND_STRICT(setxattr, local->orig_frame, op_ret, op_errno, xdata);
if (local->xdata)
dict_unref(local->xdata);
loc_wipe(&local->loc);
mem_put(local);
return 0;
}
int32_t
ga_new_entry(call_frame_t *frame, xlator_t *this, loc_t *loc, data_t *data,
dict_t *xdata)
{
int ret = -1;
ga_newfile_args_t *args = NULL;
loc_t tmp_loc = {
0,
};
call_frame_t *new_frame = NULL;
ga_local_t *local = NULL;
uuid_t gfid = {
0,
};
if (!xdata) {
xdata = dict_new();
} else {
xdata = dict_ref(xdata);
}
if (!xdata) {
ret = -1;
goto out;
}
args = ga_newfile_parse_args(this, data);
if (!args)
goto out;
ret = gf_uuid_parse(args->gfid, gfid);
if (ret)
goto out;
ret = ga_fill_tmp_loc(loc, this, gfid, args->bname, xdata, &tmp_loc);
if (ret)
goto out;
new_frame = copy_frame(frame);
if (!new_frame)
goto out;
local = mem_get0(this->local_pool);
local->orig_frame = frame;
loc_copy(&local->loc, &tmp_loc);
new_frame->local = local;
new_frame->root->uid = args->uid;
new_frame->root->gid = args->gid;
if (S_ISDIR(args->st_mode)) {
STACK_WIND(new_frame, ga_newentry_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->mkdir, &tmp_loc,
args->args.mkdir.mode, args->args.mkdir.umask, xdata);
} else if (S_ISLNK(args->st_mode)) {
STACK_WIND(new_frame, ga_newentry_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->symlink,
args->args.symlink.linkpath, &tmp_loc, 0, xdata);
} else {
/* use 07777 (4 7s) for considering the Sticky bits etc) */
((ga_local_t *)new_frame->local)->mode = (S_IFMT & args->st_mode) |
(07777 &
args->args.mknod.mode);
((ga_local_t *)new_frame->local)->umask = args->args.mknod.umask;
((ga_local_t *)new_frame->local)->rdev = args->args.mknod.rdev;
((ga_local_t *)new_frame->local)->xdata = dict_ref(xdata);
/* send a named lookup, so that dht can cleanup up stale linkto
* files etc.
*/
STACK_WIND(new_frame, ga_newentry_lookup_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->lookup, &tmp_loc, NULL);
}
ret = 0;
out:
ga_newfile_args_free(args);
if (xdata)
dict_unref(xdata);
loc_wipe(&tmp_loc);
return ret;
}
int32_t
ga_heal_entry(call_frame_t *frame, xlator_t *this, loc_t *loc, data_t *data,
dict_t *xdata)
{
int ret = -1;
ga_heal_args_t *args = NULL;
loc_t tmp_loc = {
0,
};
call_frame_t *new_frame = NULL;
uuid_t gfid = {
0,
};
args = ga_heal_parse_args(this, data);
if (!args)
goto out;
ret = gf_uuid_parse(args->gfid, gfid);
if (ret)
goto out;
if (!xdata)
xdata = dict_new();
else
xdata = dict_ref(xdata);
if (!xdata) {
ret = -1;
goto out;
}
ret = ga_fill_tmp_loc(loc, this, gfid, args->bname, xdata, &tmp_loc);
if (ret)
goto out;
new_frame = copy_frame(frame);
if (!new_frame)
goto out;
new_frame->local = (void *)frame;
STACK_WIND(new_frame, ga_heal_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->lookup, &tmp_loc, xdata);
ret = 0;
out:
if (args)
ga_heal_args_free(args);
loc_wipe(&tmp_loc);
if (xdata)
dict_unref(xdata);
return ret;
}
int32_t
ga_setxattr_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
int32_t op_ret, int32_t op_errno, dict_t *xdata)
{
STACK_UNWIND_STRICT(setxattr, frame, op_ret, op_errno, xdata);
return 0;
}
int32_t
ga_setxattr(call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *dict,
int32_t flags, dict_t *xdata)
{
data_t *data = NULL;
int op_errno = ENOMEM;
int ret = 0;
loc_t ga_loc = {
0,
};
GFID_ACCESS_INODE_OP_CHECK(loc, op_errno, err);
data = dict_get(dict, GF_FUSE_AUX_GFID_NEWFILE);
if (data) {
ret = ga_new_entry(frame, this, loc, data, xdata);
if (ret)
goto err;
return 0;
}
data = dict_get(dict, GF_FUSE_AUX_GFID_HEAL);
if (data) {
ret = ga_heal_entry(frame, this, loc, data, xdata);
if (ret)
goto err;
return 0;
}
// If the inode is a virtual inode change the inode otherwise perform
// the operation on same inode
ret = ga_valid_inode_loc_copy(&ga_loc, loc, this);
if (ret < 0)
goto err;
STACK_WIND(frame, ga_setxattr_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->setxattr, &ga_loc, dict, flags, xdata);
loc_wipe(&ga_loc);
return 0;
err:
STACK_UNWIND_STRICT(setxattr, frame, -1, op_errno, xdata);
return 0;
}
int32_t
ga_virtual_lookup_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
int32_t op_ret, int32_t op_errno, inode_t *inode,
struct iatt *buf, dict_t *xdata, struct iatt *postparent)
{
int ret = 0;
inode_t *cbk_inode = NULL;
inode_t *true_inode = NULL;
uuid_t random_gfid = {
0,
};
inode_t *linked_inode = NULL;
if (frame->local)
cbk_inode = frame->local;
else
cbk_inode = inode_ref(inode);
frame->local = NULL;
if (op_ret)
goto unwind;
if (!IA_ISDIR(buf->ia_type))
goto unwind;
/* need to send back a different inode for linking in itable */
if (cbk_inode == inode) {
/* check if the inode is in the 'itable' or
if its just previously discover()'d inode */
true_inode = inode_find(inode->table, buf->ia_gfid);
if (!true_inode) {
/* This unref is for 'inode_ref()' done in beginning.
This is needed as cbk_inode is allocated new inode
whose unref is taken at the end*/
inode_unref(cbk_inode);
cbk_inode = inode_new(inode->table);
if (!cbk_inode) {
op_ret = -1;
op_errno = ENOMEM;
goto unwind;
}
/* the inode is not present in itable, ie, the actual
path is not yet looked up. Use the current inode
itself for now */
linked_inode = inode_link(inode, NULL, NULL, buf);
inode = linked_inode;
} else {
/* 'inode_ref()' has been done in inode_find() */
inode = true_inode;
}
ret = inode_ctx_put(cbk_inode, this, (uint64_t)(uintptr_t)inode);
if (ret) {
gf_log(this->name, GF_LOG_WARNING,
"failed to set the inode ctx with"
"the actual inode");
if (inode)
inode_unref(inode);
}
inode = NULL;
}
if (!gf_uuid_is_null(cbk_inode->gfid)) {
/* if the previous linked inode is used, use the
same gfid */
gf_uuid_copy(random_gfid, cbk_inode->gfid);
} else {
/* replace the buf->ia_gfid to a random gfid
for directory, for files, what we received is fine */
gf_uuid_generate(random_gfid);
}
gf_uuid_copy(buf->ia_gfid, random_gfid);
buf->ia_ino = gfid_to_ino(buf->ia_gfid);
unwind:
/* Lookup on non-existing gfid returns ESTALE.
Convert into ENOENT for virtual lookup*/
if (op_errno == ESTALE)
op_errno = ENOENT;
STACK_UNWIND_STRICT(lookup, frame, op_ret, op_errno, cbk_inode, buf, xdata,
postparent);
/* Also handles inode_unref of frame->local if done in ga_lookup */
if (cbk_inode)
inode_unref(cbk_inode);
return 0;
}
int32_t
ga_lookup_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret,
int32_t op_errno, inode_t *inode, struct iatt *buf, dict_t *xdata,
struct iatt *postparent)
{
ga_private_t *priv = NULL;
/* if the entry in question is not 'root',
then follow the normal path */
if (op_ret || !__is_root_gfid(buf->ia_gfid))
goto unwind;
priv = this->private;
/* do we need to copy root stbuf every time? */
/* mostly yes, as we want to have the 'stat' info show latest
in every _cbk() */
/* keep the reference for root stat buf */
priv->root_stbuf = *buf;
priv->gfiddir_stbuf = priv->root_stbuf;
priv->gfiddir_stbuf.ia_gfid[15] = GF_AUX_GFID;
priv->gfiddir_stbuf.ia_ino = GF_AUX_GFID;
unwind:
STACK_UNWIND_STRICT(lookup, frame, op_ret, op_errno, inode, buf, xdata,
postparent);
return 0;
}
int32_t
ga_lookup(call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata)
{
ga_private_t *priv = NULL;
int ret = -1;
uuid_t tmp_gfid = {
0,
};
loc_t tmp_loc = {
0,
};
uint64_t value = 0;
inode_t *inode = NULL;
inode_t *true_inode = NULL;
int32_t op_errno = ENOENT;
priv = this->private;
/* Handle nameless lookup on ".gfid" */
if (!loc->parent && __is_gfid_access_dir(loc->gfid)) {
STACK_UNWIND_STRICT(lookup, frame, 0, 0, loc->inode,
&priv->gfiddir_stbuf, xdata, &priv->root_stbuf);
return 0;
}
/* if its discover(), no need for any action here */
if (!loc->name)
goto wind;
/* if its revalidate, and inode is not of type directory,
proceed with 'wind' */
if (loc->inode && loc->inode->ia_type && !IA_ISDIR(loc->inode->ia_type)) {
/* a revalidate on ".gfid/<dentry>" is possible, check for it */
if (((loc->parent && __is_gfid_access_dir(loc->parent->gfid)) ||
__is_gfid_access_dir(loc->pargfid))) {
/* here, just send 'loc->gfid' and 'loc->inode' */
tmp_loc.inode = inode_ref(loc->inode);
gf_uuid_copy(tmp_loc.gfid, loc->inode->gfid);
STACK_WIND(frame, default_lookup_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->lookup, &tmp_loc, xdata);
inode_unref(tmp_loc.inode);
return 0;
}
/* not something to bother, continue the flow */
goto wind;
}
/* need to check if the lookup is on virtual dir */
if ((loc->name && !strcmp(GF_GFID_DIR, loc->name)) &&
((loc->parent && __is_root_gfid(loc->parent->gfid)) ||
__is_root_gfid(loc->pargfid))) {
/* this means, the query is on '/.gfid', return the fake stat,
and say success */
STACK_UNWIND_STRICT(lookup, frame, 0, 0, loc->inode,
&priv->gfiddir_stbuf, xdata, &priv->root_stbuf);
return 0;
}
/* now, check if the lookup() is on an existing entry,
but on gfid-path */
if (!((loc->parent && __is_gfid_access_dir(loc->parent->gfid)) ||
__is_gfid_access_dir(loc->pargfid))) {
if (!loc->parent)
goto wind;
ret = inode_ctx_get(loc->parent, this, &value);
if (ret)
goto wind;
inode = (inode_t *)(uintptr_t)value;
ret = loc_copy_overload_parent(&tmp_loc, loc, inode);
if (ret)
goto err;
STACK_WIND(frame, ga_lookup_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->lookup, &tmp_loc, xdata);
loc_wipe(&tmp_loc);
return 0;
}
/* make sure the 'basename' is actually a 'canonical-gfid',
otherwise, return error */
ret = gf_uuid_parse(loc->name, tmp_gfid);
if (ret)
goto err;
/* if its fresh lookup, go ahead and send it down, if not,
for directory, we need indirection to actual dir inode */
if (!(loc->inode && loc->inode->ia_type))
goto discover;
/* revalidate on directory */
ret = inode_ctx_get(loc->inode, this, &value);
if (ret)
goto err;
inode = (void *)(uintptr_t)value;
/* valid inode, already looked up, work on that */
if (inode->ia_type)
goto discover;
/* check if the inode is in the 'itable' or
if its just previously discover()'d inode */
true_inode = inode_find(loc->inode->table, tmp_gfid);
if (true_inode) {
/* time do another lookup and update the context
with proper inode */
op_errno = ESTALE;
/* 'inode_ref()' done in inode_find */
inode_unref(true_inode);
goto err;
}
discover:
/* for the virtual entries, we don't need to send 'gfid-req' key, as
for these entries, we don't want to 'set' a new gfid */
if (xdata)
dict_del(xdata, "gfid-req");
gf_uuid_copy(tmp_loc.gfid, tmp_gfid);
/* if revalidate, then we need to have the proper reference */
if (inode) {
tmp_loc.inode = inode_ref(inode);
frame->local = inode_ref(loc->inode);
} else {
tmp_loc.inode = inode_ref(loc->inode);
}
STACK_WIND(frame, ga_virtual_lookup_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->lookup, &tmp_loc, xdata);
inode_unref(tmp_loc.inode);
return 0;
wind:
/* used for all the normal lookup path */
STACK_WIND(frame, ga_lookup_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->lookup, loc, xdata);
return 0;
err:
STACK_UNWIND_STRICT(lookup, frame, -1, op_errno, loc->inode,
&priv->gfiddir_stbuf, xdata, &priv->root_stbuf);
return 0;
}
int
ga_mkdir(call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode,
mode_t umask, dict_t *xdata)
{
int op_errno = ENOMEM;
GFID_ACCESS_ENTRY_OP_CHECK(loc, op_errno, err);
STACK_WIND(frame, default_mkdir_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->mkdir, loc, mode, umask, xdata);
return 0;
err:
STACK_UNWIND_STRICT(mkdir, frame, -1, op_errno, loc->inode, NULL, NULL,
NULL, xdata);
return 0;
}
int
ga_create(call_frame_t *frame, xlator_t *this, loc_t *loc, int flags,
mode_t mode, mode_t umask, fd_t *fd, dict_t *xdata)
{
int op_errno = ENOMEM;
GFID_ACCESS_ENTRY_OP_CHECK(loc, op_errno, err);
STACK_WIND(frame, default_create_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->create, loc, flags, mode, umask, fd,
xdata);
return 0;
err:
STACK_UNWIND_STRICT(create, frame, -1, op_errno, NULL, NULL, NULL, NULL,
NULL, xdata);
return 0;
}
int
ga_symlink(call_frame_t *frame, xlator_t *this, const char *linkname,
loc_t *loc, mode_t umask, dict_t *xdata)
{
int op_errno = ENOMEM;
GFID_ACCESS_ENTRY_OP_CHECK(loc, op_errno, err);
STACK_WIND(frame, default_symlink_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->symlink, linkname, loc, umask, xdata);
return 0;
err:
STACK_UNWIND_STRICT(symlink, frame, -1, op_errno, NULL, NULL, NULL, NULL,
xdata);
return 0;
}
int
ga_mknod(call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode,
dev_t rdev, mode_t umask, dict_t *xdata)
{
int op_errno = ENOMEM;
GFID_ACCESS_ENTRY_OP_CHECK(loc, op_errno, err);
STACK_WIND(frame, default_mknod_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->mknod, loc, mode, rdev, umask, xdata);
return 0;
err:
STACK_UNWIND_STRICT(mknod, frame, -1, op_errno, NULL, NULL, NULL, NULL,
xdata);
return 0;
}
int
ga_rmdir(call_frame_t *frame, xlator_t *this, loc_t *loc, int flag,
dict_t *xdata)
{
int op_errno = ENOMEM;
int ret = -1;
loc_t ga_loc = {
0,
};
GFID_ACCESS_ENTRY_OP_CHECK(loc, op_errno, err);
ret = ga_valid_inode_loc_copy(&ga_loc, loc, this);
if (ret < 0)
goto err;
STACK_WIND(frame, default_rmdir_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->rmdir, &ga_loc, flag, xdata);
loc_wipe(&ga_loc);
return 0;
err:
STACK_UNWIND_STRICT(rmdir, frame, -1, op_errno, NULL, NULL, xdata);
return 0;
}
int
ga_unlink(call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t xflag,
dict_t *xdata)
{
int op_errno = ENOMEM;
int ret = -1;
loc_t ga_loc = {
0,
};
GFID_ACCESS_ENTRY_OP_CHECK(loc, op_errno, err);
ret = ga_valid_inode_loc_copy(&ga_loc, loc, this);
if (ret < 0)
goto err;
STACK_WIND(frame, default_unlink_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->unlink, &ga_loc, xflag, xdata);
loc_wipe(&ga_loc);
return 0;
err:
STACK_UNWIND_STRICT(unlink, frame, -1, op_errno, NULL, NULL, xdata);
return 0;
}
int
ga_rename(call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc,
dict_t *xdata)
{
int op_errno = ENOMEM;
int ret = 0;
loc_t ga_oldloc = {
0,
};
loc_t ga_newloc = {
0,
};
GFID_ACCESS_ENTRY_OP_CHECK(oldloc, op_errno, err);
GFID_ACCESS_ENTRY_OP_CHECK(newloc, op_errno, err);
ret = ga_valid_inode_loc_copy(&ga_oldloc, oldloc, this);
if (ret < 0)
goto err;
ret = ga_valid_inode_loc_copy(&ga_newloc, newloc, this);
if (ret < 0) {
loc_wipe(&ga_oldloc);
goto err;
}
STACK_WIND(frame, default_rename_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->rename, &ga_oldloc, &ga_newloc, xdata);
loc_wipe(&ga_newloc);
loc_wipe(&ga_oldloc);
return 0;
err:
STACK_UNWIND_STRICT(rename, frame, -1, op_errno, NULL, NULL, NULL, NULL,
NULL, xdata);
return 0;
}
int
ga_link(call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc,
dict_t *xdata)
{
int op_errno = ENOMEM;
int ret = 0;
loc_t ga_oldloc = {
0,
};
loc_t ga_newloc = {
0,
};
GFID_ACCESS_ENTRY_OP_CHECK(oldloc, op_errno, err);
GFID_ACCESS_ENTRY_OP_CHECK(newloc, op_errno, err);
ret = ga_valid_inode_loc_copy(&ga_oldloc, oldloc, this);
if (ret < 0)
goto err;
ret = ga_valid_inode_loc_copy(&ga_newloc, newloc, this);
if (ret < 0) {
loc_wipe(&ga_oldloc);
goto err;
}
STACK_WIND(frame, default_link_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->link, &ga_oldloc, &ga_newloc, xdata);
loc_wipe(&ga_newloc);
loc_wipe(&ga_oldloc);
return 0;
err:
STACK_UNWIND_STRICT(link, frame, -1, op_errno, NULL, NULL, NULL, NULL,
xdata);
return 0;
}
int32_t
ga_opendir(call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd,
dict_t *xdata)
{
int op_errno = ENOMEM;
GFID_ACCESS_INODE_OP_CHECK(loc, op_errno, err);
/* also check if the loc->inode itself is virtual
inode, if yes, return with failure, mainly because we
can't handle all the readdirp and other things on it. */
if (inode_ctx_get(loc->inode, this, NULL) == 0) {
op_errno = ENOTSUP;
goto err;
}
STACK_WIND(frame, default_opendir_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->opendir, loc, fd, xdata);
return 0;
err:
STACK_UNWIND_STRICT(opendir, frame, -1, op_errno, NULL, xdata);
return 0;
}
int32_t
ga_getxattr(call_frame_t *frame, xlator_t *this, loc_t *loc, const char *name,
dict_t *xdata)
{
int op_errno = ENOMEM;
int ret = -1;
loc_t ga_loc = {
0,
};
GFID_ACCESS_INODE_OP_CHECK(loc, op_errno, err);
ret = ga_valid_inode_loc_copy(&ga_loc, loc, this);
if (ret < 0)
goto err;
STACK_WIND(frame, default_getxattr_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->getxattr, &ga_loc, name, xdata);
loc_wipe(&ga_loc);
return 0;
err:
STACK_UNWIND_STRICT(getxattr, frame, -1, op_errno, NULL, xdata);
return 0;
}
int32_t
ga_stat(call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata)
{
int op_errno = ENOMEM;
int ret = -1;
loc_t ga_loc = {
0,
};
ga_private_t *priv = NULL;
priv = this->private;
/* If stat is on ".gfid" itself, do not wind further,
* return fake stat and return success.
*/
if (__is_gfid_access_dir(loc->gfid))
goto out;
ret = ga_valid_inode_loc_copy(&ga_loc, loc, this);
if (ret < 0)
goto err;
STACK_WIND(frame, default_stat_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->stat, &ga_loc, xdata);
loc_wipe(&ga_loc);
return 0;
err:
STACK_UNWIND_STRICT(stat, frame, -1, op_errno, NULL, xdata);
return 0;
out:
STACK_UNWIND_STRICT(stat, frame, 0, 0, &priv->gfiddir_stbuf, xdata);
return 0;
}
int32_t
ga_setattr(call_frame_t *frame, xlator_t *this, loc_t *loc, struct iatt *stbuf,
int32_t valid, dict_t *xdata)
{
int op_errno = ENOMEM;
int ret = -1;
loc_t ga_loc = {
0,
};
GFID_ACCESS_INODE_OP_CHECK(loc, op_errno, err);
ret = ga_valid_inode_loc_copy(&ga_loc, loc, this);
if (ret < 0)
goto err;
STACK_WIND(frame, default_setattr_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->setattr, &ga_loc, stbuf, valid, xdata);
loc_wipe(&ga_loc);
return 0;
err:
STACK_UNWIND_STRICT(setattr, frame, -1, op_errno, NULL, NULL, xdata);
return 0;
}
int32_t
ga_removexattr(call_frame_t *frame, xlator_t *this, loc_t *loc,
const char *name, dict_t *xdata)
{
int op_errno = ENOMEM;
int ret = -1;
loc_t ga_loc = {
0,
};
GFID_ACCESS_INODE_OP_CHECK(loc, op_errno, err);
ret = ga_valid_inode_loc_copy(&ga_loc, loc, this);
if (ret < 0)
goto err;
STACK_WIND(frame, default_removexattr_cbk, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->removexattr, &ga_loc, name, xdata);
loc_wipe(&ga_loc);
return 0;
err:
STACK_UNWIND_STRICT(removexattr, frame, -1, op_errno, xdata);
return 0;
}
int32_t
mem_acct_init(xlator_t *this)
{
int ret = -1;
if (!this)
return ret;
ret = xlator_mem_acct_init(this, gf_gfid_access_mt_end + 1);
if (ret != 0) {
gf_log(this->name, GF_LOG_WARNING,
"Memory accounting"
" init failed");
return ret;
}
return ret;
}
int32_t
init(xlator_t *this)
{
ga_private_t *priv = NULL;
int ret = -1;
if (!this->children || this->children->next) {
gf_log(this->name, GF_LOG_ERROR,
"not configured with exactly one child. exiting");
goto out;
}
/* This can be the top of graph in certain cases */
if (!this->parents) {
gf_log(this->name, GF_LOG_DEBUG, "dangling volume. check volfile ");
}
/* TODO: define a mem-type structure */
priv = GF_CALLOC(1, sizeof(*priv), gf_gfid_access_mt_priv_t);
if (!priv)
goto out;
priv->newfile_args_pool = mem_pool_new(ga_newfile_args_t, 512);
if (!priv->newfile_args_pool)
goto out;
priv->heal_args_pool = mem_pool_new(ga_heal_args_t, 512);
if (!priv->heal_args_pool)
goto out;
this->local_pool = mem_pool_new(ga_local_t, 16);
if (!this->local_pool) {
gf_log(this->name, GF_LOG_ERROR,
"failed to create local_t's memory pool");
goto out;
}
this->private = priv;
ret = 0;
out:
if (ret && priv) {
if (priv->newfile_args_pool)
mem_pool_destroy(priv->newfile_args_pool);
GF_FREE(priv);
}
return ret;
}
void
fini(xlator_t *this)
{
ga_private_t *priv = NULL;
priv = this->private;
this->private = NULL;
if (priv) {
if (priv->newfile_args_pool)
mem_pool_destroy(priv->newfile_args_pool);
if (priv->heal_args_pool)
mem_pool_destroy(priv->heal_args_pool);
GF_FREE(priv);
}
return;
}
int32_t
ga_dump_inodectx(xlator_t *this, inode_t *inode)
{
int ret = -1;
uint64_t value = 0;
inode_t *tmp_inode = NULL;
char key_prefix[GF_DUMP_MAX_BUF_LEN] = {
0,
};
ret = inode_ctx_get(inode, this, &value);
if (ret == 0) {
tmp_inode = (void *)(uintptr_t)value;
gf_proc_dump_build_key(key_prefix, this->name, "inode");
gf_proc_dump_add_section("%s", key_prefix);
gf_proc_dump_write("real-gfid", "%s", uuid_utoa(tmp_inode->gfid));
}
return 0;
}
struct xlator_fops fops = {
.lookup = ga_lookup,
/* entry fops */
.mkdir = ga_mkdir,
.mknod = ga_mknod,
.create = ga_create,
.symlink = ga_symlink,
.link = ga_link,
.unlink = ga_unlink,
.rmdir = ga_rmdir,
.rename = ga_rename,
/* handle any other directory operations here */
.opendir = ga_opendir,
.stat = ga_stat,
.setattr = ga_setattr,
.getxattr = ga_getxattr,
.removexattr = ga_removexattr,
/* special fop to handle more entry creations */
.setxattr = ga_setxattr,
};
struct xlator_cbks cbks = {
.forget = ga_forget,
};
struct xlator_dumpops dumpops = {
.inodectx = ga_dump_inodectx,
};
struct volume_options options[] = {
/* This translator doesn't take any options, or provide any options */
{.key = {NULL}},
};
xlator_api_t xlator_api = {
.init = init,
.fini = fini,
.mem_acct_init = mem_acct_init,
.op_version = {1},
.fops = &fops,
.cbks = &cbks,
.options = options,
.identifier = "gfid-access",
.category = GF_MAINTAINED,
};