Merge DMAPI fixes from CTDB Samba
authorAlexander Bokovoy <ab@samba.org>
Fri, 18 Jan 2008 14:34:21 +0000 (17:34 +0300)
committerAlexander Bokovoy <ab@samba.org>
Fri, 18 Jan 2008 14:34:21 +0000 (17:34 +0300)
(This used to be commit cf1f90ad7a79dbe5926018790bb50d4e3b36cc7b)

source3/modules/vfs_tsmsm.c
source3/smbd/dmapi.c
source3/smbd/server.c

index 2805488e8b5c3b33d001a38a48154fd42c21fef0..40b8c3adad8d7d724486ed23a75cc3fd8ba07c40 100644 (file)
@@ -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
  */
 /*
   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.
  */
 
 
 /* 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) {
index 620baf199e42cf8634af6a8e4f9b50b9b8f640ef..a410748ccfd0db7d8046769273db36b39915833f 100644 (file)
@@ -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));
+       }
+
+       if (samba_dmapi_session != DM_NO_SESSION) {
+               set_effective_capability(DMAPI_ACCESS_CAPABILITY);
        }
 
-       /* 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.
+       /* 
+          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 ) {
-               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"));
-               unbecome_root();
+       if (samba_dmapi_session == DM_NO_SESSION) {
+               return NULL;
        }
 
-       return 0;
+       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.
+*/
 
-/* 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) 
+bool dmapi_have_session(void)
 {
-       static int attached = 0;
-       if (dmapi_have_session() && !attached) {
-               attached++;
-               if (reattach_dmapi_session() < 0) {
-                       return DM_NO_SESSION;
-               }
+       static bool initialized;
+       if (!initialized) {
+               initialized = true;
+
+               become_root();
+               dmapi_init_session();
+               unbecome_root();
+
        }
-       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;
        }
index db241103ed7a0923acd1df3f500e12319c2e0b39..7116027adfe841502abae99f4d1906d724e789af 100644 (file)
@@ -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);