mirror of
https://github.com/samba-team/samba.git
synced 2025-08-29 13:49:30 +03:00
Merge branch 'ctdb-merge' into dmapi-integration
This commit is contained in:
@ -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_ration > 1.0 ||
|
||||
tsmd->online_ration <= 0.0) {
|
||||
DEBUG(1, ("tsmsm_connect: invalid online ration %f - using %f.\n",
|
||||
tsmd->online_ration, (float)FILE_IS_ONLINE_RATIO));
|
||||
}
|
||||
}
|
||||
|
||||
/* Store the private data. */
|
||||
@ -140,6 +136,7 @@ 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;
|
||||
@ -154,7 +151,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 +177,12 @@ 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));
|
||||
strlcpy((char *)&dmname.an_chars[0], tsmd->attrib_name, sizeof(dmname.an_chars));
|
||||
|
||||
ret = dm_get_dmattr(tsmd->sid, dmhandle, dmhandle_len,
|
||||
ret = dm_get_dmattr(*dmsession_id, dmhandle, dmhandle_len,
|
||||
DM_NO_TOKEN, &dmname, 0, NULL, &rlen);
|
||||
|
||||
/* 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 +284,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) {
|
||||
|
@ -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 */
|
||||
|
||||
@ -48,90 +48,48 @@ bool dmapi_have_session(void) { return False; }
|
||||
|
||||
static dm_sessid_t samba_dmapi_session = DM_NO_SESSION;
|
||||
|
||||
/* 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;
|
||||
|
||||
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");
|
||||
|
||||
samba_dmapi_session = DM_NO_SESSION;
|
||||
if (init_dmapi_service() < 0) {
|
||||
if (dm_init_service(&version) < 0) {
|
||||
DEBUG(0, ("dm_init_service failed - disabling DMAPI\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
retry:
|
||||
ZERO_STRUCT(buf);
|
||||
|
||||
if ((sessions = realloc_session_list(sessions, nsessions)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = dm_getall_sessions(nsessions, sessions, &nsessions);
|
||||
if (err < 0) {
|
||||
if (errno == E2BIG) {
|
||||
nsessions *= 2;
|
||||
goto retry;
|
||||
do {
|
||||
dm_sessid_t *new_sessions;
|
||||
nsessions *= 2;
|
||||
new_sessions = TALLOC_REALLOC_ARRAY(NULL, sessions,
|
||||
dm_sessid_t, nsessions);
|
||||
if (new_sessions == NULL) {
|
||||
talloc_free(sessions);
|
||||
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(sessions);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -147,7 +105,7 @@ retry:
|
||||
}
|
||||
}
|
||||
|
||||
TALLOC_FREE(sessions);
|
||||
talloc_free(sessions);
|
||||
|
||||
/* No session already defined. */
|
||||
if (samba_dmapi_session == DM_NO_SESSION) {
|
||||
@ -162,91 +120,84 @@ retry:
|
||||
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",
|
||||
DMAPI_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.
|
||||
*/
|
||||
|
||||
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) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
bool dmapi_have_session(void)
|
||||
{
|
||||
static bool initialized;
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
|
||||
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 (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;
|
||||
}
|
||||
|
||||
set_effective_capability(DMAPI_ACCESS_CAPABILITY);
|
||||
|
||||
DEBUG(DMAPI_TRACE, ("reattached DMAPI session\n"));
|
||||
dmapi_init_session();
|
||||
unbecome_root();
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
const void * dmapi_get_current_session(void)
|
||||
{
|
||||
static int attached = 0;
|
||||
if (dmapi_have_session() && !attached) {
|
||||
attached++;
|
||||
if (reattach_dmapi_session() < 0) {
|
||||
return DM_NO_SESSION;
|
||||
}
|
||||
}
|
||||
return &samba_dmapi_session;
|
||||
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;
|
||||
|
||||
dm_sessid_t dmapi_session;
|
||||
void *dmapi_session_ptr;
|
||||
void *dm_handle = NULL;
|
||||
size_t dm_handle_len = 0;
|
||||
|
||||
uint32 flags = 0;
|
||||
|
||||
dmapi_session = *(dm_sessid_t*) dmapi_get_current_session();
|
||||
dmapi_session_ptr = dmapi_get_current_session();
|
||||
if (dmapi_session_ptr == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
dmapi_session = *(dm_sessid_t *)dmapi_session_ptr;
|
||||
if (dmapi_session == DM_NO_SESSION) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
Reference in New Issue
Block a user