blob: 2d8f48cac360edead821963036537621937fb328 [file] [log] [blame]
/*
* vim:noexpandtab:shiftwidth=8:tabstop=8:
*
* Copyright (C) Red Hat Inc., 2013
* Author: Anand Subramanian anands@redhat.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* -------------
*/
/**
* @file export.c
* @author Shyamsundar R <srangana@redhat.com>
* @author Anand Subramanian <anands@redhat.com>
*
* @brief GLUSTERFS FSAL export object
*/
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#include "fsal.h"
#include "FSAL/fsal_commonlib.h"
#include "FSAL/fsal_config.h"
#include "fsal_convert.h"
#include "config_parsing.h"
#include "gluster_internal.h"
#include "nfs_exports.h"
#include "export_mgr.h"
#include "pnfs_utils.h"
#include "mdcache.h"
#include "sal_data.h"
/* The default location of gfapi log
* if glfs_log param is not defined in
* the export file */
#define GFAPI_LOG_LOCATION "/var/log/ganesha-gfapi.log"
/**
* @brief Implements GLUSTER FSAL exportoperation release
*/
static void export_release(struct fsal_export *exp_hdl)
{
struct glusterfs_export *glfs_export =
container_of(exp_hdl, struct glusterfs_export, export);
int *retval = NULL;
int err = 0;
/* check activity on the export */
/* detach the export */
fsal_detach_export(glfs_export->export.fsal,
&glfs_export->export.exports);
free_export_ops(&glfs_export->export);
atomic_add_int8_t (&glfs_export->destroy_mode, 1);
/* Wait for up_thread to exit */
err = pthread_join(glfs_export->up_thread, (void **)&retval);
if (retval && *retval) {
LogDebug(COMPONENT_FSAL, "Up_thread join returned value %d",
*retval);
}
if (err) {
LogCrit(COMPONENT_FSAL, "Up_thread join failed (%s)",
strerror(err));
return;
}
/* Gluster and memory cleanup */
glfs_fini(glfs_export->gl_fs);
glfs_export->gl_fs = NULL;
gsh_free(glfs_export->export_path);
glfs_export->export_path = NULL;
gsh_free(glfs_export);
glfs_export = NULL;
}
/**
* @brief Implements GLUSTER FSAL exportoperation lookup_path
*/
static fsal_status_t lookup_path(struct fsal_export *export_pub,
const char *path,
struct fsal_obj_handle **pub_handle,
struct attrlist *attrs_out)
{
int rc = 0;
fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 };
char *realpath = NULL;
struct stat sb;
struct glfs_object *glhandle = NULL;
unsigned char globjhdl[GFAPI_HANDLE_LENGTH] = {'\0'};
struct glusterfs_handle *objhandle = NULL;
struct glusterfs_export *glfs_export =
container_of(export_pub, struct glusterfs_export, export);
char vol_uuid[GLAPI_UUID_LENGTH] = {'\0'};
LogFullDebug(COMPONENT_FSAL, "In args: path = %s", path);
*pub_handle = NULL;
if (strcmp(path, glfs_export->mount_path) == 0) {
realpath = gsh_strdup(glfs_export->export_path);
} else {
/*
* mount path is not same as the exported one. Should be subdir
* then.
*/
/** @todo: How do we handle symlinks if present in the path.
*/
realpath = gsh_malloc(strlen(glfs_export->export_path) +
strlen(path) + 1);
/*
* Handle the case wherein glfs_export->export_path
* is root i.e, '/' separately.
*/
if (strlen(glfs_export->export_path) != 1) {
strcpy(realpath, glfs_export->export_path);
strcpy((realpath +
strlen(glfs_export->export_path)),
&path[strlen(glfs_export->mount_path)]);
} else {
strcpy(realpath,
&path[strlen(glfs_export->mount_path)]);
}
}
glhandle = glfs_h_lookupat(glfs_export->gl_fs, NULL, realpath,
&sb, 1);
if (glhandle == NULL) {
status = gluster2fsal_error(errno);
goto out;
}
rc = glfs_h_extract_handle(glhandle, globjhdl, GFAPI_HANDLE_LENGTH);
if (rc < 0) {
status = gluster2fsal_error(errno);
goto out;
}
rc = glfs_get_volumeid(glfs_export->gl_fs, vol_uuid, GLAPI_UUID_LENGTH);
if (rc < 0) {
status = gluster2fsal_error(rc);
goto out;
}
construct_handle(glfs_export, &sb, glhandle, globjhdl,
GLAPI_HANDLE_LENGTH, &objhandle, vol_uuid);
if (attrs_out != NULL) {
posix2fsal_attributes(&sb, attrs_out);
/* Make sure ATTR_RDATTR_ERR is cleared on success. */
attrs_out->mask &= ~ATTR_RDATTR_ERR;
}
*pub_handle = &objhandle->handle;
gsh_free(realpath);
return status;
out:
gluster_cleanup_vars(glhandle);
gsh_free(realpath);
return status;
}
/**
* @brief Implements GLUSTER FSAL exportoperation extract_handle
*/
static fsal_status_t extract_handle(struct fsal_export *exp_hdl,
fsal_digesttype_t in_type,
struct gsh_buffdesc *fh_desc,
int flags)
{
size_t fh_size;
#ifdef GLTIMING
struct timespec s_time, e_time;
now(&s_time);
#endif
/* sanity checks */
if (!fh_desc || !fh_desc->addr)
return fsalstat(ERR_FSAL_FAULT, 0);
fh_size = GLAPI_HANDLE_LENGTH;
if (fh_desc->len != fh_size) {
LogMajor(COMPONENT_FSAL,
"Size mismatch for handle. should be %zu, got %zu",
fh_size, fh_desc->len);
return fsalstat(ERR_FSAL_SERVERFAULT, 0);
}
fh_desc->len = fh_size; /* pass back the actual size */
#ifdef GLTIMING
now(&e_time);
latency_update(&s_time, &e_time, lat_extract_handle);
#endif
return fsalstat(ERR_FSAL_NO_ERROR, 0);
}
/**
* @brief Implements GLUSTER FSAL exportoperation create_handle
*/
static fsal_status_t create_handle(struct fsal_export *export_pub,
struct gsh_buffdesc *fh_desc,
struct fsal_obj_handle **pub_handle,
struct attrlist *attrs_out)
{
int rc = 0;
fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 };
struct stat sb;
struct glfs_object *glhandle = NULL;
unsigned char globjhdl[GFAPI_HANDLE_LENGTH] = {'\0'};
struct glusterfs_handle *objhandle = NULL;
struct glusterfs_export *glfs_export =
container_of(export_pub, struct glusterfs_export, export);
char vol_uuid[GLAPI_UUID_LENGTH] = {'\0'};
#ifdef GLTIMING
struct timespec s_time, e_time;
now(&s_time);
#endif
*pub_handle = NULL;
if (fh_desc->len != GLAPI_HANDLE_LENGTH) {
status.major = ERR_FSAL_INVAL;
goto out;
}
/* First 16bytes contain volume UUID. globjhdl is in the second */
/* half(16bytes) of the fs_desc->addr. */
memcpy(globjhdl, fh_desc->addr+GLAPI_UUID_LENGTH, GFAPI_HANDLE_LENGTH);
glhandle =
glfs_h_create_from_handle(glfs_export->gl_fs, globjhdl,
GFAPI_HANDLE_LENGTH, &sb);
if (glhandle == NULL) {
status = gluster2fsal_error(errno);
goto out;
}
rc = glfs_get_volumeid(glfs_export->gl_fs, vol_uuid, GLAPI_UUID_LENGTH);
if (rc < 0) {
status = gluster2fsal_error(rc);
goto out;
}
construct_handle(glfs_export, &sb, glhandle, globjhdl,
GLAPI_HANDLE_LENGTH, &objhandle, vol_uuid);
if (attrs_out != NULL) {
posix2fsal_attributes(&sb, attrs_out);
/* Make sure ATTR_RDATTR_ERR is cleared on success. */
attrs_out->mask &= ~ATTR_RDATTR_ERR;
}
*pub_handle = &objhandle->handle;
out:
if (status.major != ERR_FSAL_NO_ERROR)
gluster_cleanup_vars(glhandle);
#ifdef GLTIMING
now(&e_time);
latency_update(&s_time, &e_time, lat_create_handle);
#endif
return status;
}
/**
* @brief Implements GLUSTER FSAL exportoperation get_fs_dynamic_info
*/
static fsal_status_t get_dynamic_info(struct fsal_export *exp_hdl,
struct fsal_obj_handle *obj_hdl,
fsal_dynamicfsinfo_t *infop)
{
int rc = 0;
fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 };
struct statvfs vfssb;
struct glusterfs_export *glfs_export =
container_of(exp_hdl, struct glusterfs_export, export);
rc = glfs_statvfs(glfs_export->gl_fs, glfs_export->export_path, &vfssb);
if (rc != 0)
return gluster2fsal_error(rc);
memset(infop, 0, sizeof(fsal_dynamicfsinfo_t));
infop->total_bytes = vfssb.f_frsize * vfssb.f_blocks;
infop->free_bytes = vfssb.f_frsize * vfssb.f_bfree;
infop->avail_bytes = vfssb.f_frsize * vfssb.f_bavail;
infop->total_files = vfssb.f_files;
infop->free_files = vfssb.f_ffree;
infop->avail_files = vfssb.f_favail;
infop->time_delta.tv_sec = 1;
infop->time_delta.tv_nsec = 0;
return status;
}
/**
* @brief Allocate a state_t structure
*
* Note that this is not expected to fail since memory allocation is
* expected to abort on failure.
*
* @param[in] exp_hdl Export state_t will be associated with
* @param[in] state_type Type of state to allocate
* @param[in] related_state Related state if appropriate
*
* @returns a state structure.
*/
struct state_t *glusterfs_alloc_state(struct fsal_export *exp_hdl,
enum state_type state_type,
struct state_t *related_state)
{
struct state_t *state;
struct glusterfs_fd *my_fd;
state = init_state(gsh_calloc(1, sizeof(struct state_t)
+ sizeof(struct glusterfs_fd)),
exp_hdl, state_type, related_state);
my_fd = (struct glusterfs_fd *)(state + 1);
my_fd->glfd = NULL;
return state;
}
/** @todo: We have gone POSIX way for the APIs below, can consider the CEPH way
* in case all are constants across all volumes etc.
*/
/**
* @brief Implements GLUSTER FSAL exportoperation fs_supports
*/
static bool fs_supports(struct fsal_export *exp_hdl,
fsal_fsinfo_options_t option)
{
struct fsal_staticfsinfo_t *info;
info = gluster_staticinfo(exp_hdl->fsal);
return fsal_supports(info, option);
}
/**
* @brief Implements GLUSTER FSAL exportoperation fs_maxfilesize
*/
static uint64_t fs_maxfilesize(struct fsal_export *exp_hdl)
{
struct fsal_staticfsinfo_t *info;
info = gluster_staticinfo(exp_hdl->fsal);
return fsal_maxfilesize(info);
}
/**
* @brief Implements GLUSTER FSAL exportoperation fs_maxread
*/
static uint32_t fs_maxread(struct fsal_export *exp_hdl)
{
struct fsal_staticfsinfo_t *info;
info = gluster_staticinfo(exp_hdl->fsal);
return fsal_maxread(info);
}
/**
* @brief Implements GLUSTER FSAL exportoperation fs_maxwrite
*/
static uint32_t fs_maxwrite(struct fsal_export *exp_hdl)
{
struct fsal_staticfsinfo_t *info;
info = gluster_staticinfo(exp_hdl->fsal);
return fsal_maxwrite(info);
}
/**
* @brief Implements GLUSTER FSAL exportoperation fs_maxlink
*/
static uint32_t fs_maxlink(struct fsal_export *exp_hdl)
{
struct fsal_staticfsinfo_t *info;
info = gluster_staticinfo(exp_hdl->fsal);
return fsal_maxlink(info);
}
/**
* @brief Implements GLUSTER FSAL exportoperation fs_maxnamelen
*/
static uint32_t fs_maxnamelen(struct fsal_export *exp_hdl)
{
struct fsal_staticfsinfo_t *info;
info = gluster_staticinfo(exp_hdl->fsal);
return fsal_maxnamelen(info);
}
/**
* @brief Implements GLUSTER FSAL exportoperation fs_maxpathlen
*/
static uint32_t fs_maxpathlen(struct fsal_export *exp_hdl)
{
struct fsal_staticfsinfo_t *info;
info = gluster_staticinfo(exp_hdl->fsal);
return fsal_maxpathlen(info);
}
/**
* @brief Implements GLUSTER FSAL exportoperation fs_lease_time
*/
static struct timespec fs_lease_time(struct fsal_export *exp_hdl)
{
struct fsal_staticfsinfo_t *info;
info = gluster_staticinfo(exp_hdl->fsal);
return fsal_lease_time(info);
}
/**
* @brief Implements GLUSTER FSAL exportoperation fs_acl_support
*/
static fsal_aclsupp_t fs_acl_support(struct fsal_export *exp_hdl)
{
struct fsal_staticfsinfo_t *info;
info = gluster_staticinfo(exp_hdl->fsal);
return fsal_acl_support(info);
}
/**
* @brief Implements GLUSTER FSAL exportoperation fs_supported_attrs
*/
static attrmask_t fs_supported_attrs(struct fsal_export *exp_hdl)
{
struct fsal_staticfsinfo_t *info;
info = gluster_staticinfo(exp_hdl->fsal);
return fsal_supported_attrs(info);
}
/**
* @brief Implements GLUSTER FSAL exportoperation fs_umask
*/
static uint32_t fs_umask(struct fsal_export *exp_hdl)
{
struct fsal_staticfsinfo_t *info;
info = gluster_staticinfo(exp_hdl->fsal);
return fsal_umask(info);
}
/**
* @brief Implements GLUSTER FSAL exportoperation fs_xattr_access_rights
*/
static uint32_t fs_xattr_access_rights(struct fsal_export *exp_hdl)
{
struct fsal_staticfsinfo_t *info;
info = gluster_staticinfo(exp_hdl->fsal);
return fsal_xattr_access_rights(info);
}
/**
* @brief Implements GLUSTER FSAL exportoperation check_quota
*/
/*
static fsal_status_t check_quota(struct fsal_export *exp_hdl,
const char * filepath,
int quota_type,
struct req_op_context *req_ctx)
{
return fsalstat(ERR_FSAL_NO_ERROR, 0) ;
}
*/
/**
* @brief Implements GLUSTER FSAL exportoperation get_quota
*/
/*
static fsal_status_t get_quota(struct fsal_export *exp_hdl,
const char * filepath,
int quota_type,
struct req_op_context *req_ctx,
fsal_quota_t *pquota)
{
return fsalstat(ERR_FSAL_NOTSUPP, 0) ;
}
*/
/**
* @brief Implements GLUSTER FSAL exportoperation set_quota
*/
/*
static fsal_status_t set_quota(struct fsal_export *exp_hdl,
const char *filepath,
int quota_type,
struct req_op_context *req_ctx,
fsal_quota_t * pquota,
fsal_quota_t * presquota)
{
return fsalstat(ERR_FSAL_NOTSUPP, 0) ;
}
*/
/**
* @brief Registers GLUSTER FSAL exportoperation vector
*
* This function overrides operations that we've implemented, leaving
* the rest for the default.
*
* @param[in,out] ops Operations vector
*/
void export_ops_init(struct export_ops *ops)
{
ops->release = export_release;
ops->lookup_path = lookup_path;
ops->extract_handle = extract_handle;
ops->create_handle = create_handle;
ops->get_fs_dynamic_info = get_dynamic_info;
ops->fs_supports = fs_supports;
ops->fs_maxfilesize = fs_maxfilesize;
ops->fs_maxread = fs_maxread;
ops->fs_maxwrite = fs_maxwrite;
ops->fs_maxlink = fs_maxlink;
ops->fs_maxnamelen = fs_maxnamelen;
ops->fs_maxpathlen = fs_maxpathlen;
ops->fs_lease_time = fs_lease_time;
ops->fs_acl_support = fs_acl_support;
ops->fs_supported_attrs = fs_supported_attrs;
ops->fs_umask = fs_umask;
ops->fs_xattr_access_rights = fs_xattr_access_rights;
ops->alloc_state = glusterfs_alloc_state;
}
struct glexport_params {
char *glvolname;
char *glhostname;
char *glvolpath;
char *glfs_log;
};
static struct config_item export_params[] = {
CONF_ITEM_NOOP("name"),
CONF_MAND_STR("volume", 1, MAXPATHLEN, NULL,
glexport_params, glvolname),
CONF_MAND_STR("hostname", 1, MAXPATHLEN, NULL,
glexport_params, glhostname),
CONF_ITEM_PATH("volpath", 1, MAXPATHLEN, "/",
glexport_params, glvolpath),
CONF_ITEM_PATH("glfs_log", 1, MAXPATHLEN, GFAPI_LOG_LOCATION,
glexport_params, glfs_log),
CONFIG_EOL
};
static struct config_block export_param = {
.dbus_interface_name = "org.ganesha.nfsd.config.fsal.gluster-export%d",
.blk_desc.name = "FSAL",
.blk_desc.type = CONFIG_BLOCK,
.blk_desc.u.blk.init = noop_conf_init,
.blk_desc.u.blk.params = export_params,
.blk_desc.u.blk.commit = noop_conf_commit
};
/**
* @brief Implements GLUSTER FSAL moduleoperation create_export
*/
fsal_status_t glusterfs_create_export(struct fsal_module *fsal_hdl,
void *parse_node,
struct config_error_type *err_type,
const struct fsal_up_vector *up_ops)
{
int rc = 0;
fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 };
struct glusterfs_export *glfsexport = NULL;
glfs_t *fs = NULL;
struct glexport_params params = {
.glvolname = NULL,
.glhostname = NULL,
.glvolpath = NULL,
.glfs_log = NULL};
LogDebug(COMPONENT_FSAL, "In args: export path = %s",
op_ctx->ctx_export->fullpath);
rc = load_config_from_node(parse_node,
&export_param,
&params,
true,
err_type);
if (rc != 0) {
LogCrit(COMPONENT_FSAL,
"Incorrect or missing parameters for export %s",
op_ctx->ctx_export->fullpath);
status.major = ERR_FSAL_INVAL;
goto out;
}
LogEvent(COMPONENT_FSAL, "Volume %s exported at : '%s'",
params.glvolname, params.glvolpath);
glfsexport = gsh_calloc(1, sizeof(struct glusterfs_export));
fsal_export_init(&glfsexport->export);
export_ops_init(&glfsexport->export.exp_ops);
fs = glfs_new(params.glvolname);
if (!fs) {
status.major = ERR_FSAL_SERVERFAULT;
LogCrit(COMPONENT_FSAL,
"Unable to create new glfs. Export: %s",
op_ctx->ctx_export->fullpath);
goto out;
}
rc = glfs_set_volfile_server(fs, "tcp", params.glhostname, 24007);
if (rc != 0) {
status.major = ERR_FSAL_SERVERFAULT;
LogCrit(COMPONENT_FSAL,
"Unable to set volume file. Export: %s",
op_ctx->ctx_export->fullpath);
goto out;
}
rc = glfs_set_logging(fs, params.glfs_log, 7);
if (rc != 0) {
status.major = ERR_FSAL_SERVERFAULT;
LogCrit(COMPONENT_FSAL,
"Unable to set logging. Export: %s",
op_ctx->ctx_export->fullpath);
goto out;
}
rc = glfs_init(fs);
if (rc != 0) {
status.major = ERR_FSAL_SERVERFAULT;
LogCrit(COMPONENT_FSAL,
"Unable to initialize volume. Export: %s",
op_ctx->ctx_export->fullpath);
goto out;
}
rc = fsal_attach_export(fsal_hdl, &glfsexport->export.exports);
if (rc != 0) {
status.major = ERR_FSAL_SERVERFAULT;
LogCrit(COMPONENT_FSAL, "Unable to attach export. Export: %s",
op_ctx->ctx_export->fullpath);
goto out;
}
glfsexport->mount_path = op_ctx->ctx_export->fullpath;
glfsexport->export_path = params.glvolpath;
glfsexport->gl_fs = fs;
glfsexport->saveduid = geteuid();
glfsexport->savedgid = getegid();
glfsexport->export.fsal = fsal_hdl;
glfsexport->acl_enable =
!op_ctx_export_has_option(EXPORT_OPTION_DISABLE_ACL);
glfsexport->destroy_mode = 0;
op_ctx->fsal_export = &glfsexport->export;
glfsexport->pnfs_ds_enabled =
glfsexport->export.exp_ops.fs_supports(&glfsexport->export,
fso_pnfs_ds_supported);
if (glfsexport->pnfs_ds_enabled) {
struct fsal_pnfs_ds *pds = NULL;
status = fsal_hdl->m_ops.
fsal_pnfs_ds(fsal_hdl, parse_node, &pds);
if (status.major != ERR_FSAL_NO_ERROR)
goto out;
/* special case: server_id matches export_id */
pds->id_servers = op_ctx->ctx_export->export_id;
pds->mds_export = op_ctx->ctx_export;
if (!pnfs_ds_insert(pds)) {
LogCrit(COMPONENT_CONFIG,
"Server id %d already in use.",
pds->id_servers);
status.major = ERR_FSAL_EXIST;
goto out;
}
LogDebug(COMPONENT_PNFS,
"glusterfs_fsal_create: pnfs ds was enabled for [%s]",
op_ctx->ctx_export->fullpath);
}
glfsexport->pnfs_mds_enabled =
glfsexport->export.exp_ops.fs_supports(&glfsexport->export,
fso_pnfs_mds_supported);
if (glfsexport->pnfs_mds_enabled) {
LogDebug(COMPONENT_PNFS,
"glusterfs_fsal_create: pnfs mds was enabled for [%s]",
op_ctx->ctx_export->fullpath);
export_ops_pnfs(&glfsexport->export.exp_ops);
fsal_ops_pnfs(&glfsexport->export.fsal->m_ops);
}
status = mdcache_export_init(up_ops, &glfsexport->export.up_ops);
if (FSAL_IS_ERROR(status)) {
LogDebug(COMPONENT_FSAL, "MDCACHE creation failed for GLUSTER");
goto out;
}
rc = initiate_up_thread(glfsexport);
if (rc != 0) {
LogCrit(COMPONENT_FSAL,
"Unable to create GLUSTERFSAL_UP_Thread. Export: %s",
op_ctx->ctx_export->fullpath);
status.major = ERR_FSAL_FAULT;
goto out;
}
out:
if (params.glvolname)
gsh_free(params.glvolname);
if (params.glhostname)
gsh_free(params.glhostname);
if (params.glfs_log)
gsh_free(params.glfs_log);
if (status.major != ERR_FSAL_NO_ERROR) {
if (params.glvolpath)
gsh_free(params.glvolpath);
if (fs)
glfs_fini(fs);
if (glfsexport)
gsh_free(glfsexport);
}
return status;
}