diff options
Diffstat (limited to 'source3/smbd/dmapi.c')
-rw-r--r-- | source3/smbd/dmapi.c | 357 |
1 files changed, 357 insertions, 0 deletions
diff --git a/source3/smbd/dmapi.c b/source3/smbd/dmapi.c new file mode 100644 index 0000000..7e82445 --- /dev/null +++ b/source3/smbd/dmapi.c @@ -0,0 +1,357 @@ +/* + Unix SMB/CIFS implementation. + DMAPI Support routines + + Copyright (C) James Peach 2006 + + 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 + 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "smbd/smbd.h" +#include "smbd/globals.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_DMAPI + +#ifndef USE_DMAPI + +uint32_t 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 */ + +#ifdef HAVE_XFS_DMAPI_H +#include <xfs/dmapi.h> +#elif defined(HAVE_SYS_DMI_H) +#include <sys/dmi.h> +#elif defined(HAVE_SYS_JFSDMAPI_H) +#include <sys/jfsdmapi.h> +#elif defined(HAVE_SYS_DMAPI_H) +#include <sys/dmapi.h> +#elif defined(HAVE_DMAPI_H) +#include <dmapi.h> +#endif + +#define DMAPI_SESSION_NAME "samba" +#define DMAPI_TRACE 10 + +struct smbd_dmapi_context { + dm_sessid_t session; + unsigned session_num; +}; + +/* + 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(struct smbd_dmapi_context *ctx) +{ + char buf[DM_SESSION_INFO_LEN]; + size_t buflen; + uint nsessions = 5; + dm_sessid_t *sessions = NULL; + char *version; + char *session_name; + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + + int i, err; + + if (ctx->session_num == 0) { + session_name = talloc_strdup(tmp_ctx, DMAPI_SESSION_NAME); + } else { + session_name = talloc_asprintf(tmp_ctx, "%s%u", DMAPI_SESSION_NAME, + ctx->session_num); + } + + if (session_name == NULL) { + DEBUG(0,("Out of memory in dmapi_init_session\n")); + talloc_free(tmp_ctx); + return -1; + } + + + if (dm_init_service(&version) < 0) { + DEBUG(0, ("dm_init_service failed - disabling DMAPI\n")); + talloc_free(tmp_ctx); + return -1; + } + + 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(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(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(session_name, buf) == 0) { + ctx->session = sessions[i]; + DEBUGADD(DMAPI_TRACE, + ("attached to existing DMAPI session " + "named '%s'\n", buf)); + break; + } + } + + /* No session already defined. */ + if (ctx->session == DM_NO_SESSION) { + err = dm_create_session(DM_NO_SESSION, + session_name, + &ctx->session); + if (err < 0) { + DEBUGADD(DMAPI_TRACE, + ("failed to create new DMAPI session: %s\n", + strerror(errno))); + ctx->session = DM_NO_SESSION; + talloc_free(tmp_ctx); + return -1; + } + + DEBUG(0, ("created new DMAPI session named '%s' for %s\n", + session_name, version)); + } + + if (ctx->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; +} + +/* + 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) +{ + if (!dmapi_ctx) { + return NULL; + } + + if (dmapi_ctx->session == DM_NO_SESSION) { + return NULL; + } + + return (void *)&dmapi_ctx->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) +{ + if (!dmapi_ctx) { + dmapi_ctx = talloc(NULL, struct smbd_dmapi_context); + if (!dmapi_ctx) { + exit_server("unable to allocate smbd_dmapi_context"); + } + dmapi_ctx->session = DM_NO_SESSION; + dmapi_ctx->session_num = 0; + + become_root(); + dmapi_init_session(dmapi_ctx); + unbecome_root(); + + } + + return dmapi_ctx->session != DM_NO_SESSION; +} + +/* + 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. + */ +bool dmapi_new_session(void) +{ + if (dmapi_have_session()) { + /* try to destroy the old one - this may not succeed */ + dm_destroy_session(dmapi_ctx->session); + } + dmapi_ctx->session = DM_NO_SESSION; + become_root(); + dmapi_ctx->session_num++; + dmapi_init_session(dmapi_ctx); + unbecome_root(); + return dmapi_ctx->session != DM_NO_SESSION; +} + +/* + only call this when exiting from master smbd process. DMAPI sessions + are long-lived kernel resources we ought to share across smbd processes. + However, we must free them when all smbd processes are finished to + allow other subsystems clean up properly. Not freeing DMAPI session + blocks certain HSM implementations from proper shutdown. +*/ +bool dmapi_destroy_session(void) +{ + if (!dmapi_ctx) { + return true; + } + if (dmapi_ctx->session != DM_NO_SESSION) { + become_root(); + if (0 == dm_destroy_session(dmapi_ctx->session)) { + dmapi_ctx->session_num--; + dmapi_ctx->session = DM_NO_SESSION; + } else { + DEBUG(0,("Couldn't destroy DMAPI session: %s\n", + strerror(errno))); + } + unbecome_root(); + } + return dmapi_ctx->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_t dmapi_file_flags(const char * const path) +{ + int err; + dm_eventset_t events = {0}; + uint nevents; + + dm_sessid_t dmapi_session; + dm_sessid_t *dmapi_session_ptr; + const void *_dmapi_session_ptr; + void *dm_handle = NULL; + size_t dm_handle_len = 0; + + uint32_t flags = 0; + + _dmapi_session_ptr = dmapi_get_current_session(); + if (_dmapi_session_ptr == NULL) { + return 0; + } + + dmapi_session_ptr = discard_const_p(dm_sessid_t, _dmapi_session_ptr); + dmapi_session = *dmapi_session_ptr; + if (dmapi_session == DM_NO_SESSION) { + return 0; + } + + /* AIX has DMAPI but no POSIX capablities support. In this case, + * we need to be root to do DMAPI manipulations. + */ +#ifndef HAVE_POSIX_CAPABILITIES + become_root(); +#endif + + err = dm_path_to_handle(discard_const_p(char, path), + &dm_handle, &dm_handle_len); + if (err < 0) { + DEBUG(DMAPI_TRACE, ("dm_path_to_handle(%s): %s\n", + path, strerror(errno))); + + if (errno != EPERM) { + goto done; + } + + /* Linux capabilities are broken in that changing our + * user ID will clobber out effective capabilities irrespective + * of whether we have set PR_SET_KEEPCAPS. Fortunately, the + * capabilities are not removed from our permitted set, so we + * can re-acquire them if necessary. + */ + + set_effective_capability(DMAPI_ACCESS_CAPABILITY); + + err = dm_path_to_handle(discard_const_p(char, path), + &dm_handle, &dm_handle_len); + if (err < 0) { + DEBUG(DMAPI_TRACE, + ("retrying dm_path_to_handle(%s): %s\n", + path, strerror(errno))); + goto done; + } + } + + err = dm_get_eventlist(dmapi_session, dm_handle, dm_handle_len, + DM_NO_TOKEN, DM_EVENT_MAX, &events, &nevents); + if (err < 0) { + DEBUG(DMAPI_TRACE, ("dm_get_eventlist(%s): %s\n", + path, strerror(errno))); + dm_handle_free(dm_handle, dm_handle_len); + goto done; + } + + /* We figure that the only reason a DMAPI application would be + * interested in trapping read events is that part of the file is + * offline. + */ + DEBUG(DMAPI_TRACE, ("DMAPI event list for %s\n", path)); + if (DMEV_ISSET(DM_EVENT_READ, events)) { + flags = FILE_ATTRIBUTE_OFFLINE; + } + + dm_handle_free(dm_handle, dm_handle_len); + + if (flags & FILE_ATTRIBUTE_OFFLINE) { + DEBUG(DMAPI_TRACE, ("%s is OFFLINE\n", path)); + } + +done: + +#ifndef HAVE_POSIX_CAPABILITIES + unbecome_root(); +#endif + + return flags; +} + + +#endif /* USE_DMAPI */ |