summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/modules/vfs_tsmsm.c128
-rw-r--r--source3/smbd/dmapi.c247
-rw-r--r--source3/smbd/server.c6
3 files changed, 195 insertions, 186 deletions
diff --git a/source3/modules/vfs_tsmsm.c b/source3/modules/vfs_tsmsm.c
index 2805488e8b..791e8cfcd0 100644
--- a/source3/modules/vfs_tsmsm.c
+++ b/source3/modules/vfs_tsmsm.c
@@ -3,8 +3,8 @@
Samba VFS module for handling offline files
with Tivoli Storage Manager Space Management
- (c) Alexander Bokovoy, 2007
- (c) Andrew Tridgell, 2007
+ (c) Alexander Bokovoy, 2007, 2008
+ (c) Andrew Tridgell, 2007, 2008
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,18 +21,20 @@
*/
/*
This VFS module accepts following options:
- tsmsm: hsm script = <path to hsm script> (/bin/true by default, i.e. does nothing)
+ tsmsm: hsm script = <path to hsm script> (default does nothing)
hsm script should point to a shell script which accepts two arguments:
<operation> <filepath>
where <operation> is currently 'offline' to set offline status of the <filepath>
tsmsm: online ratio = ratio to check reported size against actual file size (0.5 by default)
+ tsmsm: attribute name = name of DMAPI attribute that is present when a file is offline.
+ Default is "IBMobj" (which is what GPFS uses)
The TSMSM VFS module tries to avoid calling expensive DMAPI calls with some heuristics
based on the fact that number of blocks reported of a file multiplied by 512 will be
bigger than 'online ratio' of actual size for online (non-migrated) files.
- If checks fail, we call DMAPI and ask for specific IBM attribute which present for
+ If checks fail, we call DMAPI and ask for specific attribute which present for
offline (migrated) files. If this attribute presents, we consider file offline.
*/
@@ -62,72 +64,66 @@
/* optimisation tunables - used to avoid the DMAPI slow path */
#define FILE_IS_ONLINE_RATIO 0.5
+
+/* default attribute name to look for */
#define DM_ATTRIB_OBJECT "IBMObj"
-#define DM_ATTRIB_MIGRATED "IBMMig"
struct tsmsm_struct {
- dm_sessid_t sid;
float online_ratio;
char *hsmscript;
+ const char *attrib_name;
};
-#define TSM_STRINGIFY(a) #a
-#define TSM_TOSTRING(a) TSM_STRINGIFY(a)
-
static void tsmsm_free_data(void **pptr) {
struct tsmsm_struct **tsmd = (struct tsmsm_struct **)pptr;
if(!tsmd) return;
TALLOC_FREE(*tsmd);
}
+/*
+ called when a client connects to a share
+*/
static int tsmsm_connect(struct vfs_handle_struct *handle,
const char *service,
const char *user) {
struct tsmsm_struct *tsmd = TALLOC_ZERO_P(handle, struct tsmsm_struct);
- const char *hsmscript, *tsmname;
const char *fres;
+ const char *tsmname;
if (!tsmd) {
DEBUG(0,("tsmsm_connect: out of memory!\n"));
return -1;
}
- tsmd->sid = *(dm_sessid_t*) dmapi_get_current_session();
-
- if (tsmd->sid == DM_NO_SESSION) {
+ if (!dmapi_have_session()) {
DEBUG(0,("tsmsm_connect: no DMAPI session for Samba is available!\n"));
TALLOC_FREE(tsmd);
return -1;
}
tsmname = (handle->param ? handle->param : "tsmsm");
- hsmscript = lp_parm_const_string(SNUM(handle->conn), tsmname,
- "hsm script", NULL);
- if (hsmscript) {
- tsmd->hsmscript = talloc_strdup(tsmd, hsmscript);
- if(!tsmd->hsmscript) {
- DEBUG(1, ("tsmsm_connect: can't allocate memory for hsm script path"));
- TALLOC_FREE(tsmd);
- return -1;
- }
- } else {
- DEBUG(1, ("tsmsm_connect: can't call hsm script because it "
- "is not set to anything in the smb.conf\n"
- "Use %s: 'hsm script = path' to set it\n",
- tsmname));
- TALLOC_FREE(tsmd);
- return -1;
- }
+
+ /* Get 'hsm script' and 'dmapi attribute' parameters to tsmd context */
+ tsmd->hsmscript = lp_parm_talloc_string(SNUM(handle->conn), tsmname,
+ "hsm script", NULL);
+ talloc_steal(tsmd, tsmd->hsmscript);
+
+ tsmd->attrib_name = lp_parm_talloc_string(SNUM(handle->conn), tsmname,
+ "dmapi attribute", DM_ATTRIB_OBJECT);
+ talloc_steal(tsmd, tsmd->attrib_name);
+ /* retrieve 'online ratio'. In case of error default to FILE_IS_ONLINE_RATIO */
fres = lp_parm_const_string(SNUM(handle->conn), tsmname,
- "online ratio", TSM_TOSTRING(FILE_IS_ONLINE_RATIO));
- tsmd->online_ratio = strtof(fres, NULL);
- if((tsmd->online_ratio == (float)0) || ((errno == ERANGE) &&
- ((tsmd->online_ratio == HUGE_VALF) ||
- (tsmd->online_ratio == HUGE_VALL)))) {
- DEBUG(1, ("tsmsm_connect: error while getting online ratio from smb.conf."
- "Default to %s.\n", TSM_TOSTRING(FILE_IS_ONLINE_RATIO)));
+ "online ratio", NULL);
+ if (fres == NULL) {
tsmd->online_ratio = FILE_IS_ONLINE_RATIO;
+ } else {
+ tsmd->online_ratio = strtof(fres, NULL);
+ if (tsmd->online_ratio > 1.0 ||
+ tsmd->online_ratio <= 0.0) {
+ DEBUG(1, ("tsmsm_connect: invalid online ration %f - using %f.\n",
+ tsmd->online_ratio, (float)FILE_IS_ONLINE_RATIO));
+ }
}
/* Store the private data. */
@@ -140,12 +136,14 @@ static bool tsmsm_is_offline(struct vfs_handle_struct *handle,
const char *path,
SMB_STRUCT_STAT *stbuf) {
struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
+ const dm_sessid_t *dmsession_id;
void *dmhandle = NULL;
size_t dmhandle_len = 0;
size_t rlen;
dm_attrname_t dmname;
- int ret;
+ int ret, lerrno;
bool offline;
+ char buf[1];
/* if the file has more than FILE_IS_ONLINE_RATIO of blocks available,
then assume it is not offline (it may not be 100%, as it could be sparse) */
@@ -154,7 +152,14 @@ static bool tsmsm_is_offline(struct vfs_handle_struct *handle,
path, stbuf->st_blocks, stbuf->st_size, tsmd->online_ratio));
return false;
}
-
+
+ dmsession_id = dmapi_get_current_session();
+ if (dmsession_id == NULL) {
+ DEBUG(2, ("tsmsm_is_offline: no DMAPI session available? "
+ "Assume file is online.\n"));
+ return false;
+ }
+
/* using POSIX capabilities does not work here. It's a slow path, so
* become_root() is just as good anyway (tridge)
*/
@@ -173,12 +178,30 @@ static bool tsmsm_is_offline(struct vfs_handle_struct *handle,
}
memset(&dmname, 0, sizeof(dmname));
- strlcpy((char *)&dmname.an_chars[0], DM_ATTRIB_OBJECT, sizeof(dmname.an_chars));
-
- ret = dm_get_dmattr(tsmd->sid, dmhandle, dmhandle_len,
- DM_NO_TOKEN, &dmname, 0, NULL, &rlen);
+ strlcpy((char *)&dmname.an_chars[0], tsmd->attrib_name, sizeof(dmname.an_chars));
+
+ lerrno = 0;
+
+ do {
+ ret = dm_get_dmattr(*dmsession_id, dmhandle, dmhandle_len,
+ DM_NO_TOKEN, &dmname, sizeof(buf), buf, &rlen);
+ if (ret == -1 && errno == EINVAL) {
+ DEBUG(0, ("Stale DMAPI session, re-creating it.\n"));
+ lerrno = EINVAL;
+ if (dmapi_new_session()) {
+ dmsession_id = dmapi_get_current_session();
+ } else {
+ DEBUG(0,
+ ("Unable to re-create DMAPI session, assuming offline (%s) - %s\n",
+ path, strerror(errno)));
+ offline = true;
+ dm_handle_free(dmhandle, dmhandle_len);
+ goto done;
+ }
+ }
+ } while (ret == -1 && lerrno == EINVAL);
- /* its offline if the IBMObj attribute exists */
+ /* its offline if the specified DMAPI attribute exists */
offline = (ret == 0 || (ret == -1 && errno == E2BIG));
DEBUG(10,("dm_get_dmattr %s ret=%d (%s)\n", path, ret, strerror(errno)));
@@ -280,6 +303,12 @@ static int tsmsm_set_offline(struct vfs_handle_struct *handle,
int result = 0;
char *command;
+ if (tsmd->hsmscript == NULL) {
+ /* no script enabled */
+ DEBUG(1, ("tsmsm_set_offline: No tsmsm:hsmscript configured\n"));
+ return 0;
+ }
+
/* Now, call the script */
command = talloc_asprintf(tsmd, "%s offline \"%s\"", tsmd->hsmscript, path);
if(!command) {
@@ -294,14 +323,9 @@ static int tsmsm_set_offline(struct vfs_handle_struct *handle,
return result;
}
-static bool tsmsm_statvfs(struct vfs_handle_struct *handle, const char *path, vfs_statvfs_struct *statbuf)
+static uint32_t tsmsm_fs_capabilities(struct vfs_handle_struct *handle)
{
- bool result;
-
- result = SMB_VFS_NEXT_STATVFS(handle, path, statbuf);
- statbuf->FsCapabilities | = FILE_SUPPORTS_REMOTE_STORAGE | FILE_SUPPORTS_REPARSE_POINTS;
-
- return result;
+ return SMB_VFS_NEXT_FS_CAPABILITIES(handle) | FILE_SUPPORTS_REMOTE_STORAGE | FILE_SUPPORTS_REPARSE_POINTS;
}
static vfs_op_tuple vfs_tsmsm_ops[] = {
@@ -310,7 +334,7 @@ static vfs_op_tuple vfs_tsmsm_ops[] = {
{SMB_VFS_OP(tsmsm_connect), SMB_VFS_OP_CONNECT,
SMB_VFS_LAYER_TRANSPARENT},
- {SMB_VFS_OP(tsmsm_statvfs), SMB_VFS_OP_STATVFS,
+ {SMB_VFS_OP(tsmsm_fs_capabilities), SMB_VFS_OP_FS_CAPABILITIES,
SMB_VFS_LAYER_TRANSPARENT},
{SMB_VFS_OP(tsmsm_aio_force), SMB_VFS_OP_AIO_FORCE,
SMB_VFS_LAYER_TRANSPARENT},
diff --git a/source3/smbd/dmapi.c b/source3/smbd/dmapi.c
index 620baf199e..fab0d5f9ef 100644
--- a/source3/smbd/dmapi.c
+++ b/source3/smbd/dmapi.c
@@ -25,9 +25,9 @@
#ifndef USE_DMAPI
-int dmapi_init_session(void) { return -1; }
uint32 dmapi_file_flags(const char * const path) { return 0; }
bool dmapi_have_session(void) { return False; }
+const void * dmapi_get_current_session(void) { return NULL; }
#else /* USE_DMAPI */
@@ -47,98 +47,75 @@ bool dmapi_have_session(void) { return False; }
#define DMAPI_TRACE 10
static dm_sessid_t samba_dmapi_session = DM_NO_SESSION;
+static unsigned session_num;
-/* Initialise the DMAPI interface. Make sure that we only end up initialising
- * once per process to avoid resource leaks across different DMAPI
- * implementations.
- */
-static int init_dmapi_service(void)
-{
- static pid_t lastpid;
-
- pid_t mypid;
-
- mypid = sys_getpid();
- if (mypid != lastpid) {
- char *version;
-
- lastpid = mypid;
- if (dm_init_service(&version) < 0) {
- return -1;
- }
-
- DEBUG(0, ("Initializing DMAPI: %s\n", version));
- }
-
- return 0;
-}
-
-bool dmapi_have_session(void)
-{
- return samba_dmapi_session != DM_NO_SESSION;
-}
-
-static dm_sessid_t *realloc_session_list(dm_sessid_t * sessions, int count)
-{
- dm_sessid_t *nsessions;
-
- nsessions = TALLOC_REALLOC_ARRAY(NULL, sessions, dm_sessid_t, count);
- if (nsessions == NULL) {
- TALLOC_FREE(sessions);
- return NULL;
- }
-
- return nsessions;
-}
-
-/* Initialise DMAPI session. The session is persistant kernel state, so it
- * might already exist, in which case we merely want to reconnect to it. This
- * function should be called as root.
- */
-int dmapi_init_session(void)
+/*
+ Initialise DMAPI session. The session is persistant kernel state,
+ so it might already exist, in which case we merely want to
+ reconnect to it. This function should be called as root.
+*/
+static int dmapi_init_session(void)
{
char buf[DM_SESSION_INFO_LEN];
size_t buflen;
-
- uint nsessions = 10;
+ uint nsessions = 5;
dm_sessid_t *sessions = NULL;
+ char *version;
+ char *session_name;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
int i, err;
- /* If we aren't root, something in the following will fail due to lack
- * of privileges. Aborting seems a little extreme.
- */
- SMB_WARN(getuid() == 0, "dmapi_init_session must be called as root");
+ if (session_num == 0) {
+ session_name = DMAPI_SESSION_NAME;
+ } else {
+ session_name = talloc_asprintf(tmp_ctx, "%s%u", DMAPI_SESSION_NAME,
+ session_num);
+ }
- samba_dmapi_session = DM_NO_SESSION;
- if (init_dmapi_service() < 0) {
+ if (session_name == NULL) {
+ DEBUG(0,("Out of memory in dmapi_init_session\n"));
+ talloc_free(tmp_ctx);
return -1;
}
+
-retry:
-
- if ((sessions = realloc_session_list(sessions, nsessions)) == NULL) {
+ if (dm_init_service(&version) < 0) {
+ DEBUG(0, ("dm_init_service failed - disabling DMAPI\n"));
+ talloc_free(tmp_ctx);
return -1;
}
- err = dm_getall_sessions(nsessions, sessions, &nsessions);
- if (err < 0) {
- if (errno == E2BIG) {
- nsessions *= 2;
- goto retry;
+ ZERO_STRUCT(buf);
+
+ /* Fetch kernel DMAPI sessions until we get any of them */
+ do {
+ dm_sessid_t *new_sessions;
+ nsessions *= 2;
+ new_sessions = TALLOC_REALLOC_ARRAY(tmp_ctx, sessions,
+ dm_sessid_t, nsessions);
+ if (new_sessions == NULL) {
+ talloc_free(tmp_ctx);
+ return -1;
}
+ sessions = new_sessions;
+ err = dm_getall_sessions(nsessions, sessions, &nsessions);
+ } while (err == -1 && errno == E2BIG);
+
+ if (err == -1) {
DEBUGADD(DMAPI_TRACE,
("failed to retrieve DMAPI sessions: %s\n",
strerror(errno)));
- TALLOC_FREE(sessions);
+ talloc_free(tmp_ctx);
return -1;
}
+ /* Look through existing kernel DMAPI sessions to find out ours */
for (i = 0; i < nsessions; ++i) {
err = dm_query_session(sessions[i], sizeof(buf), buf, &buflen);
buf[sizeof(buf) - 1] = '\0';
- if (err == 0 && strcmp(DMAPI_SESSION_NAME, buf) == 0) {
+ if (err == 0 && strcmp(session_name, buf) == 0) {
samba_dmapi_session = sessions[i];
DEBUGADD(DMAPI_TRACE,
("attached to existing DMAPI session "
@@ -147,106 +124,121 @@ retry:
}
}
- TALLOC_FREE(sessions);
-
/* No session already defined. */
if (samba_dmapi_session == DM_NO_SESSION) {
err = dm_create_session(DM_NO_SESSION,
- CONST_DISCARD(char *, DMAPI_SESSION_NAME),
+ session_name,
&samba_dmapi_session);
if (err < 0) {
DEBUGADD(DMAPI_TRACE,
("failed to create new DMAPI session: %s\n",
strerror(errno)));
samba_dmapi_session = DM_NO_SESSION;
+ talloc_free(tmp_ctx);
return -1;
}
- DEBUGADD(DMAPI_TRACE,
- ("created new DMAPI session named '%s'\n",
- DMAPI_SESSION_NAME));
+ DEBUG(0, ("created new DMAPI session named '%s' for %s\n",
+ session_name, version));
}
- /* Note that we never end the DMAPI session. This enables child
- * processes to continue to use the session after we exit. It also lets
- * you run a second Samba server on different ports without any
- * conflict.
+ if (samba_dmapi_session != DM_NO_SESSION) {
+ set_effective_capability(DMAPI_ACCESS_CAPABILITY);
+ }
+
+ /*
+ Note that we never end the DMAPI session. It gets re-used if possiblie.
+ DMAPI session is a kernel resource that is usually lives until server reboot
+ and doesn't get destroed when an application finishes.
+
+ However, we free list of references to DMAPI sessions we've got from the kernel
+ as it is not needed anymore once we have found (or created) our session.
*/
+ talloc_free(tmp_ctx);
return 0;
}
-/* Reattach to an existing dmapi session. Called from service processes that
- * might not be running as root.
- */
-static int reattach_dmapi_session(void)
+/*
+ Return a pointer to our DMAPI session, if available.
+ This assumes that you have called dmapi_have_session() first.
+*/
+const void *dmapi_get_current_session(void)
{
- char buf[DM_SESSION_INFO_LEN];
- size_t buflen;
-
- if (samba_dmapi_session != DM_NO_SESSION ) {
- become_root();
-
- /* NOTE: On Linux, this call opens /dev/dmapi, costing us a
- * file descriptor. Ideally, we would close this when we fork.
- */
- if (init_dmapi_service() < 0) {
- samba_dmapi_session = DM_NO_SESSION;
- unbecome_root();
- return -1;
- }
+ if (samba_dmapi_session == DM_NO_SESSION) {
+ return NULL;
+ }
- if (dm_query_session(samba_dmapi_session, sizeof(buf),
- buf, &buflen) < 0) {
- /* Session is stale. Disable DMAPI. */
- samba_dmapi_session = DM_NO_SESSION;
- unbecome_root();
- return -1;
- }
+ return (void *)&samba_dmapi_session;
+}
+
+/*
+ dmapi_have_session() must be the first DMAPI call you make in Samba. It will
+ initialize DMAPI, if available, and tell you if you can get a DMAPI session.
+ This should be called in the client-specific child process.
+*/
- set_effective_capability(DMAPI_ACCESS_CAPABILITY);
+bool dmapi_have_session(void)
+{
+ static bool initialized;
+ if (!initialized) {
+ initialized = true;
- DEBUG(DMAPI_TRACE, ("reattached DMAPI session\n"));
+ become_root();
+ dmapi_init_session();
unbecome_root();
+
}
- return 0;
+ return samba_dmapi_session != DM_NO_SESSION;
}
-/* If a DMAPI session has been initialised, then we need to make sure
- * we are attached to it and have the correct privileges. This is
- * necessary to be able to do DMAPI operations across a fork(2). If
- * it fails, there is no likelihood of that failure being transient.
- *
- * Note that this use of the static attached flag relies on the fact
- * that dmapi_file_flags() is never called prior to forking the
- * per-client server process.
+/*
+ only call this when you get back an EINVAL error indicating that the
+ session you are using is invalid. This destroys the existing session
+ and creates a new one.
*/
-const void * dmapi_get_current_session(void)
+bool dmapi_new_session(void)
{
- static int attached = 0;
- if (dmapi_have_session() && !attached) {
- attached++;
- if (reattach_dmapi_session() < 0) {
- return DM_NO_SESSION;
- }
+ if (dmapi_have_session()) {
+ /* try to destroy the old one - this may not succeed */
+ dm_destroy_session(samba_dmapi_session);
}
- return &samba_dmapi_session;
+ samba_dmapi_session = DM_NO_SESSION;
+ become_root();
+ session_num++;
+ dmapi_init_session();
+ unbecome_root();
+ return samba_dmapi_session != DM_NO_SESSION;
}
+/*
+ This is default implementation of dmapi_file_flags() that is
+ called from VFS is_offline() call to know whether file is offline.
+ For GPFS-specific version see modules/vfs_tsmsm.c. It might be
+ that approach on quering existence of a specific attribute that
+ is used in vfs_tsmsm.c will work with other DMAPI-based HSM
+ implementations as well.
+*/
uint32 dmapi_file_flags(const char * const path)
{
- dm_sessid_t dmapi_session;
int err;
dm_eventset_t events = {0};
uint nevents;
- void *dm_handle = NULL;
- size_t dm_handle_len = 0;
+ dm_sessid_t dmapi_session;
+ const void *dmapi_session_ptr;
+ void *dm_handle = NULL;
+ size_t dm_handle_len = 0;
+
+ uint32 flags = 0;
- uint32 flags = 0;
+ dmapi_session_ptr = dmapi_get_current_session();
+ if (dmapi_session_ptr == NULL) {
+ return 0;
+ }
- dmapi_session = *(dm_sessid_t*) dmapi_get_current_session();
+ dmapi_session = *(dm_sessid_t *)dmapi_session_ptr;
if (dmapi_session == DM_NO_SESSION) {
return 0;
}
@@ -300,8 +292,7 @@ uint32 dmapi_file_flags(const char * const path)
* interested in trapping read events is that part of the file is
* offline.
*/
- DEBUG(DMAPI_TRACE, ("DMAPI event list for %s is %#llx\n",
- path, events));
+ DEBUG(DMAPI_TRACE, ("DMAPI event list for %s\n", path));
if (DMEV_ISSET(DM_EVENT_READ, events)) {
flags = FILE_ATTRIBUTE_OFFLINE;
}
diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index db241103ed..7116027adf 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -1313,12 +1313,6 @@ extern void build_options(bool screen);
if ( is_daemon && !interactive )
start_background_queue();
- /* Always attempt to initialize DMAPI. We will only use it later if
- * lp_dmapi_support is set on the share, but we need a single global
- * session to work with.
- */
- dmapi_init_session();
-
if (!open_sockets_smbd(is_daemon, interactive, ports))
exit(1);