LCOV - code coverage report
Current view: top level - source3/smbd - dmapi.c (source / functions) Hit Total Coverage
Test: coverage report for master 2b515b7d Lines: 0 3 0.0 %
Date: 2024-02-28 12:06:22 Functions: 0 3 0.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    DMAPI Support routines
       4             : 
       5             :    Copyright (C) James Peach 2006
       6             : 
       7             :    This program is free software; you can redistribute it and/or modify
       8             :    it under the terms of the GNU General Public License as published by
       9             :    the Free Software Foundation; either version 3 of the License, or
      10             :    (at your option) any later version.
      11             : 
      12             :    This program is distributed in the hope that it will be useful,
      13             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :    GNU General Public License for more details.
      16             : 
      17             :    You should have received a copy of the GNU General Public License
      18             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      19             : */
      20             : 
      21             : #include "includes.h"
      22             : #include "smbd/smbd.h"
      23             : #include "smbd/globals.h"
      24             : 
      25             : #undef DBGC_CLASS
      26             : #define DBGC_CLASS DBGC_DMAPI
      27             : 
      28             : #ifndef USE_DMAPI
      29             : 
      30           0 : uint32_t dmapi_file_flags(const char * const path) { return 0; }
      31           0 : bool dmapi_have_session(void) { return False; }
      32           0 : const void * dmapi_get_current_session(void) { return NULL; }
      33             : 
      34             : #else /* USE_DMAPI */
      35             : 
      36             : #ifdef HAVE_XFS_DMAPI_H
      37             : #include <xfs/dmapi.h>
      38             : #elif defined(HAVE_SYS_DMI_H)
      39             : #include <sys/dmi.h>
      40             : #elif defined(HAVE_SYS_JFSDMAPI_H)
      41             : #include <sys/jfsdmapi.h>
      42             : #elif defined(HAVE_SYS_DMAPI_H)
      43             : #include <sys/dmapi.h>
      44             : #elif defined(HAVE_DMAPI_H)
      45             : #include <dmapi.h>
      46             : #endif
      47             : 
      48             : #define DMAPI_SESSION_NAME "samba"
      49             : #define DMAPI_TRACE 10
      50             : 
      51             : struct smbd_dmapi_context {
      52             :         dm_sessid_t session;
      53             :         unsigned session_num;
      54             : };
      55             : 
      56             : /*
      57             :    Initialise DMAPI session. The session is persistent kernel state,
      58             :    so it might already exist, in which case we merely want to
      59             :    reconnect to it. This function should be called as root.
      60             : */
      61             : static int dmapi_init_session(struct smbd_dmapi_context *ctx)
      62             : {
      63             :         char    buf[DM_SESSION_INFO_LEN];
      64             :         size_t  buflen;
      65             :         uint        nsessions = 5;
      66             :         dm_sessid_t *sessions = NULL;
      67             :         char    *version;
      68             :         char    *session_name;
      69             :         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
      70             : 
      71             :         int i, err;
      72             : 
      73             :         if (ctx->session_num == 0) {
      74             :                 session_name = talloc_strdup(tmp_ctx, DMAPI_SESSION_NAME);
      75             :         } else {
      76             :                 session_name = talloc_asprintf(tmp_ctx, "%s%u", DMAPI_SESSION_NAME,
      77             :                                                ctx->session_num);
      78             :         }
      79             : 
      80             :         if (session_name == NULL) {
      81             :                 DEBUG(0,("Out of memory in dmapi_init_session\n"));
      82             :                 talloc_free(tmp_ctx);
      83             :                 return -1;
      84             :         }
      85             : 
      86             : 
      87             :         if (dm_init_service(&version) < 0) {
      88             :                 DEBUG(0, ("dm_init_service failed - disabling DMAPI\n"));
      89             :                 talloc_free(tmp_ctx);
      90             :                 return -1;
      91             :         }
      92             : 
      93             :         ZERO_STRUCT(buf);
      94             : 
      95             :         /* Fetch kernel DMAPI sessions until we get any of them */
      96             :         do {
      97             :                 dm_sessid_t *new_sessions;
      98             :                 nsessions *= 2;
      99             :                 new_sessions = talloc_realloc(tmp_ctx, sessions,
     100             :                                                     dm_sessid_t, nsessions);
     101             :                 if (new_sessions == NULL) {
     102             :                         talloc_free(tmp_ctx);
     103             :                         return -1;
     104             :                 }
     105             : 
     106             :                 sessions = new_sessions;
     107             :                 err = dm_getall_sessions(nsessions, sessions, &nsessions);
     108             :         } while (err == -1 && errno == E2BIG);
     109             : 
     110             :         if (err == -1) {
     111             :                 DEBUGADD(DMAPI_TRACE,
     112             :                         ("failed to retrieve DMAPI sessions: %s\n",
     113             :                         strerror(errno)));
     114             :                 talloc_free(tmp_ctx);
     115             :                 return -1;
     116             :         }
     117             : 
     118             :         /* Look through existing kernel DMAPI sessions to find out ours */
     119             :         for (i = 0; i < nsessions; ++i) {
     120             :                 err = dm_query_session(sessions[i], sizeof(buf), buf, &buflen);
     121             :                 buf[sizeof(buf) - 1] = '\0';
     122             :                 if (err == 0 && strcmp(session_name, buf) == 0) {
     123             :                         ctx->session = sessions[i];
     124             :                         DEBUGADD(DMAPI_TRACE,
     125             :                                 ("attached to existing DMAPI session "
     126             :                                  "named '%s'\n", buf));
     127             :                         break;
     128             :                 }
     129             :         }
     130             : 
     131             :         /* No session already defined. */
     132             :         if (ctx->session == DM_NO_SESSION) {
     133             :                 err = dm_create_session(DM_NO_SESSION,
     134             :                                         session_name,
     135             :                                         &ctx->session);
     136             :                 if (err < 0) {
     137             :                         DEBUGADD(DMAPI_TRACE,
     138             :                                 ("failed to create new DMAPI session: %s\n",
     139             :                                 strerror(errno)));
     140             :                         ctx->session = DM_NO_SESSION;
     141             :                         talloc_free(tmp_ctx);
     142             :                         return -1;
     143             :                 }
     144             : 
     145             :                 DEBUG(0, ("created new DMAPI session named '%s' for %s\n",
     146             :                           session_name, version));
     147             :         }
     148             : 
     149             :         if (ctx->session != DM_NO_SESSION) {
     150             :                 set_effective_capability(DMAPI_ACCESS_CAPABILITY);
     151             :         }
     152             : 
     153             :         /*
     154             :            Note that we never end the DMAPI session. It gets re-used if possiblie.
     155             :            DMAPI session is a kernel resource that is usually lives until server reboot
     156             :            and doesn't get destroed when an application finishes.
     157             : 
     158             :            However, we free list of references to DMAPI sessions we've got from the kernel
     159             :            as it is not needed anymore once we have found (or created) our session.
     160             :          */
     161             : 
     162             :         talloc_free(tmp_ctx);
     163             :         return 0;
     164             : }
     165             : 
     166             : /*
     167             :   Return a pointer to our DMAPI session, if available.
     168             :   This assumes that you have called dmapi_have_session() first.
     169             : */
     170             : const void *dmapi_get_current_session(void)
     171             : {
     172             :         if (!dmapi_ctx) {
     173             :                 return NULL;
     174             :         }
     175             : 
     176             :         if (dmapi_ctx->session == DM_NO_SESSION) {
     177             :                 return NULL;
     178             :         }
     179             : 
     180             :         return (void *)&dmapi_ctx->session;
     181             : }
     182             : 
     183             : /*
     184             :   dmapi_have_session() must be the first DMAPI call you make in Samba. It will
     185             :   initialize DMAPI, if available, and tell you if you can get a DMAPI session.
     186             :   This should be called in the client-specific child process.
     187             : */
     188             : 
     189             : bool dmapi_have_session(void)
     190             : {
     191             :         if (!dmapi_ctx) {
     192             :                 dmapi_ctx = talloc(NULL, struct smbd_dmapi_context);
     193             :                 if (!dmapi_ctx) {
     194             :                         exit_server("unable to allocate smbd_dmapi_context");
     195             :                 }
     196             :                 dmapi_ctx->session = DM_NO_SESSION;
     197             :                 dmapi_ctx->session_num = 0;
     198             : 
     199             :                 become_root();
     200             :                 dmapi_init_session(dmapi_ctx);
     201             :                 unbecome_root();
     202             : 
     203             :         }
     204             : 
     205             :         return dmapi_ctx->session != DM_NO_SESSION;
     206             : }
     207             : 
     208             : /*
     209             :   only call this when you get back an EINVAL error indicating that the
     210             :   session you are using is invalid. This destroys the existing session
     211             :   and creates a new one.
     212             :  */
     213             : bool dmapi_new_session(void)
     214             : {
     215             :         if (dmapi_have_session()) {
     216             :                 /* try to destroy the old one - this may not succeed */
     217             :                 dm_destroy_session(dmapi_ctx->session);
     218             :         }
     219             :         dmapi_ctx->session = DM_NO_SESSION;
     220             :         become_root();
     221             :         dmapi_ctx->session_num++;
     222             :         dmapi_init_session(dmapi_ctx);
     223             :         unbecome_root();
     224             :         return dmapi_ctx->session != DM_NO_SESSION;
     225             : }
     226             : 
     227             : /*
     228             :     only call this when exiting from master smbd process. DMAPI sessions
     229             :     are long-lived kernel resources we ought to share across smbd processes.
     230             :     However, we must free them when all smbd processes are finished to
     231             :     allow other subsystems clean up properly. Not freeing DMAPI session
     232             :     blocks certain HSM implementations from proper shutdown.
     233             : */
     234             : bool dmapi_destroy_session(void)
     235             : {
     236             :         if (!dmapi_ctx) {
     237             :                 return true;
     238             :         }
     239             :         if (dmapi_ctx->session != DM_NO_SESSION) {
     240             :                 become_root();
     241             :                 if (0 == dm_destroy_session(dmapi_ctx->session)) {
     242             :                         dmapi_ctx->session_num--;
     243             :                         dmapi_ctx->session = DM_NO_SESSION;
     244             :                 } else {
     245             :                         DEBUG(0,("Couldn't destroy DMAPI session: %s\n",
     246             :                                  strerror(errno)));
     247             :                 }
     248             :                 unbecome_root();
     249             :         }
     250             :         return dmapi_ctx->session == DM_NO_SESSION;
     251             : }
     252             : 
     253             : 
     254             : /*
     255             :    This is default implementation of dmapi_file_flags() that is
     256             :    called from VFS is_offline() call to know whether file is offline.
     257             :    For GPFS-specific version see modules/vfs_tsmsm.c. It might be
     258             :    that approach on querying existence of a specific attribute that
     259             :    is used in vfs_tsmsm.c will work with other DMAPI-based HSM
     260             :    implementations as well.
     261             : */
     262             : uint32_t dmapi_file_flags(const char * const path)
     263             : {
     264             :         int             err;
     265             :         dm_eventset_t   events = {0};
     266             :         uint            nevents;
     267             : 
     268             :         dm_sessid_t     dmapi_session;
     269             :         dm_sessid_t     *dmapi_session_ptr;
     270             :         const void      *_dmapi_session_ptr;
     271             :         void            *dm_handle = NULL;
     272             :         size_t          dm_handle_len = 0;
     273             : 
     274             :         uint32_t        flags = 0;
     275             : 
     276             :         _dmapi_session_ptr = dmapi_get_current_session();
     277             :         if (_dmapi_session_ptr == NULL) {
     278             :                 return 0;
     279             :         }
     280             : 
     281             :         dmapi_session_ptr = discard_const_p(dm_sessid_t, _dmapi_session_ptr);
     282             :         dmapi_session = *dmapi_session_ptr;
     283             :         if (dmapi_session == DM_NO_SESSION) {
     284             :                 return 0;
     285             :         }
     286             : 
     287             :         /* AIX has DMAPI but no POSIX capabilities support. In this case,
     288             :          * we need to be root to do DMAPI manipulations.
     289             :          */
     290             : #ifndef HAVE_POSIX_CAPABILITIES
     291             :         become_root();
     292             : #endif
     293             : 
     294             :         err = dm_path_to_handle(discard_const_p(char, path),
     295             :                 &dm_handle, &dm_handle_len);
     296             :         if (err < 0) {
     297             :                 DEBUG(DMAPI_TRACE, ("dm_path_to_handle(%s): %s\n",
     298             :                             path, strerror(errno)));
     299             : 
     300             :                 if (errno != EPERM) {
     301             :                         goto done;
     302             :                 }
     303             : 
     304             :                 /* Linux capabilities are broken in that changing our
     305             :                  * user ID will clobber out effective capabilities irrespective
     306             :                  * of whether we have set PR_SET_KEEPCAPS. Fortunately, the
     307             :                  * capabilities are not removed from our permitted set, so we
     308             :                  * can re-acquire them if necessary.
     309             :                  */
     310             : 
     311             :                 set_effective_capability(DMAPI_ACCESS_CAPABILITY);
     312             : 
     313             :                 err = dm_path_to_handle(discard_const_p(char, path),
     314             :                         &dm_handle, &dm_handle_len);
     315             :                 if (err < 0) {
     316             :                         DEBUG(DMAPI_TRACE,
     317             :                             ("retrying dm_path_to_handle(%s): %s\n",
     318             :                             path, strerror(errno)));
     319             :                         goto done;
     320             :                 }
     321             :         }
     322             : 
     323             :         err = dm_get_eventlist(dmapi_session, dm_handle, dm_handle_len,
     324             :                 DM_NO_TOKEN, DM_EVENT_MAX, &events, &nevents);
     325             :         if (err < 0) {
     326             :                 DEBUG(DMAPI_TRACE, ("dm_get_eventlist(%s): %s\n",
     327             :                             path, strerror(errno)));
     328             :                 dm_handle_free(dm_handle, dm_handle_len);
     329             :                 goto done;
     330             :         }
     331             : 
     332             :         /* We figure that the only reason a DMAPI application would be
     333             :          * interested in trapping read events is that part of the file is
     334             :          * offline.
     335             :          */
     336             :         DEBUG(DMAPI_TRACE, ("DMAPI event list for %s\n", path));
     337             :         if (DMEV_ISSET(DM_EVENT_READ, events)) {
     338             :                 flags = FILE_ATTRIBUTE_OFFLINE;
     339             :         }
     340             : 
     341             :         dm_handle_free(dm_handle, dm_handle_len);
     342             : 
     343             :         if (flags & FILE_ATTRIBUTE_OFFLINE) {
     344             :                 DEBUG(DMAPI_TRACE, ("%s is OFFLINE\n", path));
     345             :         }
     346             : 
     347             : done:
     348             : 
     349             : #ifndef HAVE_POSIX_CAPABILITIES
     350             :         unbecome_root();
     351             : #endif
     352             : 
     353             :         return flags;
     354             : }
     355             : 
     356             : 
     357             : #endif /* USE_DMAPI */

Generated by: LCOV version 1.14