blob: 2c8c63a2ad55736c719afbf8a2742da6851d1c43 [file] [log] [blame]
/*
* Copyright © 2012-2014, CohortFS, LLC.
* Author: Adam C. Emerson <aemerson@linuxbox.com>
*
* contributeur : William Allen Simpson <bill@cohortfs.com>
* Marcus Watts <mdw@cohortfs.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 FSAL_CEPH/main.c
* @author Adam C. Emerson <aemerson@linuxbox.com>
* @author William Allen Simpson <bill@cohortfs.com>
* @date Wed Oct 22 13:24:33 2014
*
* @brief Implementation of FSAL module founctions for Ceph
*
* This file implements the module functions for the Ceph FSAL, for
* initialization, teardown, configuration, and creation of exports.
*/
#include <stdlib.h>
#include <assert.h>
#include "fsal.h"
#include "fsal_types.h"
#include "FSAL/fsal_init.h"
#include "FSAL/fsal_commonlib.h"
#include "fsal_api.h"
#include "internal.h"
#include "abstract_mem.h"
#include "nfs_exports.h"
#include "export_mgr.h"
#include "mdcache.h"
/**
* Ceph global module object.
*/
struct ceph_fsal_module CephFSM;
/**
* The name of this module.
*/
static const char *module_name = "Ceph";
static fsal_staticfsinfo_t default_ceph_info = {
/* settable */
#if 0
.umask = 0,
.xattr_access_rights = 0,
#endif
/* fixed */
.symlink_support = true,
.link_support = true,
.cansettime = true,
.no_trunc = true,
.chown_restricted = true,
.case_preserving = true,
#ifdef USE_FSAL_CEPH_SETLK
.lock_support = true,
.lock_support_owner = true,
.lock_support_async_block = false,
#endif
.unique_handles = true,
.homogenous = true,
};
static struct config_item ceph_items[] = {
CONF_ITEM_PATH("ceph_conf", 1, MAXPATHLEN, NULL,
ceph_fsal_module, conf_path),
CONF_ITEM_MODE("umask", 0,
ceph_fsal_module, fs_info.umask),
CONF_ITEM_MODE("xattr_access_rights", 0,
ceph_fsal_module, fs_info.xattr_access_rights),
CONFIG_EOL
};
static struct config_block ceph_block = {
.dbus_interface_name = "org.ganesha.nfsd.config.fsal.ceph",
.blk_desc.name = "Ceph",
.blk_desc.type = CONFIG_BLOCK,
.blk_desc.u.blk.init = noop_conf_init,
.blk_desc.u.blk.params = ceph_items,
.blk_desc.u.blk.commit = noop_conf_commit
};
/* Module methods
*/
/* init_config
* must be called with a reference taken (via lookup_fsal)
*/
static fsal_status_t init_config(struct fsal_module *module_in,
config_file_t config_struct,
struct config_error_type *err_type)
{
struct ceph_fsal_module *myself =
container_of(module_in, struct ceph_fsal_module, fsal);
LogDebug(COMPONENT_FSAL,
"Ceph module setup.");
myself->fs_info = default_ceph_info;
(void) load_config_from_parse(config_struct,
&ceph_block,
myself,
true,
err_type);
if (!config_error_is_harmless(err_type))
return fsalstat(ERR_FSAL_INVAL, 0);
return fsalstat(ERR_FSAL_NO_ERROR, 0);
}
#ifdef CEPH_FS_LOOKUP_ROOT
static fsal_status_t find_cephfs_root(struct ceph_mount_info *cmount,
Inode **pi)
{
return ceph2fsal_error(ceph_ll_lookup_root(cmount, pi));
}
#else /* CEPH_FS_LOOKUP_ROOT */
static fsal_status_t find_cephfs_root(struct ceph_mount_info *cmount,
Inode **pi)
{
Inode *i;
vinodeno_t root;
root.ino.val = CEPH_INO_ROOT;
#ifdef CEPH_NOSNAP
root.snapid.val = CEPH_NOSNAP;
#else
root.snapid.val = 0;
#endif /* CEPH_NOSNAP */
i = ceph_ll_get_inode(cmount, root);
if (!i)
return fsalstat(ERR_FSAL_SERVERFAULT, 0);
*pi = i;
return fsalstat(ERR_FSAL_NO_ERROR, 0);
}
#endif /* CEPH_FS_LOOKUP_ROOT */
/**
* @brief Create a new export under this FSAL
*
* This function creates a new export object for the Ceph FSAL.
*
* @todo ACE: We do not handle re-exports of the same cluster in a
* sane way. Currently we create multiple handles and cache objects
* pointing to the same one. This is not necessarily wrong, but it is
* inefficient. It may also not be something we expect to use enough
* to care about.
*
* @param[in] module_in The supplied module handle
* @param[in] path The path to export
* @param[in] options Export specific options for the FSAL
* @param[in,out] list_entry Our entry in the export list
* @param[in] next_fsal Next stacked FSAL
* @param[out] pub_export Newly created FSAL export object
*
* @return FSAL status.
*/
static fsal_status_t create_export(struct fsal_module *module_in,
void *parse_node,
struct config_error_type *err_type,
const struct fsal_up_vector *up_ops)
{
/* The status code to return */
fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 };
/* A fake argument list for Ceph */
const char *argv[] = { "FSAL_CEPH", op_ctx->ctx_export->fullpath };
/* The internal export object */
struct export *export = gsh_calloc(1, sizeof(struct export));
/* The 'private' root handle */
struct handle *handle = NULL;
/* Root inode */
struct Inode *i = NULL;
/* Stat for root */
struct stat st;
/* Return code */
int rc;
/* Return code from Ceph calls */
int ceph_status;
/* True if we have called fsal_export_init */
bool initialized = false;
fsal_export_init(&export->export);
export_ops_init(&export->export.exp_ops);
initialized = true;
/* allocates ceph_mount_info */
ceph_status = ceph_create(&export->cmount, NULL);
if (ceph_status != 0) {
status.major = ERR_FSAL_SERVERFAULT;
LogCrit(COMPONENT_FSAL,
"Unable to create Ceph handle for %s.",
op_ctx->ctx_export->fullpath);
goto error;
}
ceph_status = ceph_conf_read_file(export->cmount, CephFSM.conf_path);
if (ceph_status != 0) {
status.major = ERR_FSAL_SERVERFAULT;
LogCrit(COMPONENT_FSAL,
"Unable to read Ceph configuration for %s.",
op_ctx->ctx_export->fullpath);
goto error;
}
ceph_status = ceph_conf_parse_argv(export->cmount, 2, argv);
if (ceph_status != 0) {
status.major = ERR_FSAL_SERVERFAULT;
LogCrit(COMPONENT_FSAL,
"Unable to parse Ceph configuration for %s.",
op_ctx->ctx_export->fullpath);
goto error;
}
ceph_status = ceph_mount(export->cmount, NULL);
if (ceph_status != 0) {
status.major = ERR_FSAL_SERVERFAULT;
LogCrit(COMPONENT_FSAL,
"Unable to mount Ceph cluster for %s.",
op_ctx->ctx_export->fullpath);
goto error;
}
if (fsal_attach_export(module_in, &export->export.exports) != 0) {
status.major = ERR_FSAL_SERVERFAULT;
LogCrit(COMPONENT_FSAL,
"Unable to attach export for %s.",
op_ctx->ctx_export->fullpath);
goto error;
}
export->export.fsal = module_in;
LogDebug(COMPONENT_FSAL,
"Ceph module export %s.",
op_ctx->ctx_export->fullpath);
status = find_cephfs_root(export->cmount, &i);
if (FSAL_IS_ERROR(status))
goto error;
rc = ceph_ll_getattr(export->cmount, i, &st, 0, 0);
if (rc < 0) {
status = ceph2fsal_error(rc);
goto error;
}
construct_handle(&st, i, export, &handle);
export->root = handle;
op_ctx->fsal_export = &export->export;
/* Stack MDCACHE on top */
status = mdcache_export_init(up_ops, &export->export.up_ops);
if (FSAL_IS_ERROR(status)) {
LogDebug(COMPONENT_FSAL, "MDCACHE creation failed for CEPH");
goto error;
}
return status;
error:
if (i)
ceph_ll_put(export->cmount, i);
if (export) {
if (export->cmount)
ceph_shutdown(export->cmount);
gsh_free(export);
}
if (initialized)
initialized = false;
return status;
}
/**
* @brief Indicate support for extended operations.
*
* @retval true if extended operations are supported.
*/
bool ceph_support_ex(struct fsal_obj_handle *obj)
{
return true;
}
/**
* @brief Initialize and register the FSAL
*
* This function initializes the FSAL module handle, being called
* before any configuration or even mounting of a Ceph cluster. It
* exists solely to produce a properly constructed FSAL module
* handle.
*/
MODULE_INIT void init(void)
{
struct fsal_module *myself = &CephFSM.fsal;
LogDebug(COMPONENT_FSAL,
"Ceph module registering.");
/* register_fsal seems to expect zeroed memory. */
memset(myself, 0, sizeof(*myself));
if (register_fsal(myself, module_name, FSAL_MAJOR_VERSION,
FSAL_MINOR_VERSION, FSAL_ID_CEPH) != 0) {
/* The register_fsal function prints its own log
message if it fails */
LogCrit(COMPONENT_FSAL,
"Ceph module failed to register.");
}
/* Set up module operations */
#ifdef CEPH_PNFS
myself->m_ops.fsal_pnfs_ds_ops = pnfs_ds_ops_init;
#endif /* CEPH_PNFS */
myself->m_ops.create_export = create_export;
myself->m_ops.init_config = init_config;
myself->m_ops.support_ex = ceph_support_ex;
}
/**
* @brief Release FSAL resources
*
* This function unregisters the FSAL and frees its module handle.
* The Ceph FSAL has no other resources to release on the per-FSAL
* level.
*/
MODULE_FINI void finish(void)
{
LogDebug(COMPONENT_FSAL,
"Ceph module finishing.");
if (unregister_fsal(&CephFSM.fsal) != 0) {
LogCrit(COMPONENT_FSAL,
"Unable to unload Ceph FSAL. Dying with extreme prejudice.");
abort();
}
}