Remove a direct inbuf reference (should have been removed with 8987641d...)
[jra/samba/.git] / source3 / smbd / dmapi.c
index 620baf199e42cf8634af6a8e4f9b50b9b8f640ef..1049c95a391e50c28578a8c72932d928230e2860 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 */
 
@@ -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,145 @@ 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);
+       }
+       samba_dmapi_session = DM_NO_SESSION;
+       become_root();
+       session_num++;
+       dmapi_init_session();
+       unbecome_root();
+       return samba_dmapi_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 (samba_dmapi_session != DM_NO_SESSION) {
+               become_root();
+               if (0 == dm_destroy_session(samba_dmapi_session)) {
+                       session_num--;
+                       samba_dmapi_session = DM_NO_SESSION;
+               } else {
+                       DEBUG(0,("Couldn't destroy DMAPI session: %s\n",
+                                strerror(errno)));
                }
+               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;
 
-       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 +316,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;
        }