root/source3/smbd/dmapi.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dmapi_file_flags
  2. dmapi_have_session
  3. dmapi_get_current_session
  4. dmapi_init_session
  5. dmapi_get_current_session
  6. dmapi_have_session
  7. dmapi_new_session
  8. dmapi_destroy_session
  9. dmapi_file_flags

   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/globals.h"
  23 
  24 #undef DBGC_CLASS
  25 #define DBGC_CLASS DBGC_DMAPI
  26 
  27 #ifndef USE_DMAPI
  28 
  29 uint32 dmapi_file_flags(const char * const path) { return 0; }
     /* [<][>][^][v][top][bottom][index][help] */
  30 bool dmapi_have_session(void) { return False; }
     /* [<][>][^][v][top][bottom][index][help] */
  31 const void * dmapi_get_current_session(void) { return NULL; }
     /* [<][>][^][v][top][bottom][index][help] */
  32 
  33 #else /* USE_DMAPI */
  34 
  35 #ifdef HAVE_XFS_DMAPI_H
  36 #include <xfs/dmapi.h>
  37 #elif defined(HAVE_SYS_DMI_H)
  38 #include <sys/dmi.h>
  39 #elif defined(HAVE_SYS_JFSDMAPI_H)
  40 #include <sys/jfsdmapi.h>
  41 #elif defined(HAVE_SYS_DMAPI_H)
  42 #include <sys/dmapi.h>
  43 #elif defined(HAVE_DMAPI_H)
  44 #include <dmapi.h>
  45 #endif
  46 
  47 #define DMAPI_SESSION_NAME "samba"
  48 #define DMAPI_TRACE 10
  49 
  50 struct smbd_dmapi_context {
  51         dm_sessid_t session;
  52         unsigned session_num;
  53 };
  54 
  55 /* 
  56    Initialise DMAPI session. The session is persistant kernel state, 
  57    so it might already exist, in which case we merely want to 
  58    reconnect to it. This function should be called as root.
  59 */
  60 static int dmapi_init_session(struct smbd_dmapi_context *ctx)
     /* [<][>][^][v][top][bottom][index][help] */
  61 {
  62         char    buf[DM_SESSION_INFO_LEN];
  63         size_t  buflen;
  64         uint        nsessions = 5;
  65         dm_sessid_t *sessions = NULL;
  66         char    *version;
  67         char    *session_name;
  68         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
  69 
  70         int i, err;
  71 
  72         if (ctx->session_num == 0) {
  73                 session_name = talloc_strdup(tmp_ctx, DMAPI_SESSION_NAME);
  74         } else {
  75                 session_name = talloc_asprintf(tmp_ctx, "%s%u", DMAPI_SESSION_NAME,
  76                                                ctx->session_num);
  77         }
  78 
  79         if (session_name == NULL) {
  80                 DEBUG(0,("Out of memory in dmapi_init_session\n"));
  81                 talloc_free(tmp_ctx);
  82                 return -1;
  83         }
  84  
  85 
  86         if (dm_init_service(&version) < 0) {
  87                 DEBUG(0, ("dm_init_service failed - disabling DMAPI\n"));
  88                 talloc_free(tmp_ctx);
  89                 return -1;
  90         }
  91 
  92         ZERO_STRUCT(buf);
  93 
  94         /* Fetch kernel DMAPI sessions until we get any of them */
  95         do {
  96                 dm_sessid_t *new_sessions;
  97                 nsessions *= 2;
  98                 new_sessions = TALLOC_REALLOC_ARRAY(tmp_ctx, sessions, 
  99                                                     dm_sessid_t, nsessions);
 100                 if (new_sessions == NULL) {
 101                         talloc_free(tmp_ctx);
 102                         return -1;
 103                 }
 104 
 105                 sessions = new_sessions;
 106                 err = dm_getall_sessions(nsessions, sessions, &nsessions);
 107         } while (err == -1 && errno == E2BIG);
 108 
 109         if (err == -1) {
 110                 DEBUGADD(DMAPI_TRACE,
 111                         ("failed to retrieve DMAPI sessions: %s\n",
 112                         strerror(errno)));
 113                 talloc_free(tmp_ctx);
 114                 return -1;
 115         }
 116 
 117         /* Look through existing kernel DMAPI sessions to find out ours */
 118         for (i = 0; i < nsessions; ++i) {
 119                 err = dm_query_session(sessions[i], sizeof(buf), buf, &buflen);
 120                 buf[sizeof(buf) - 1] = '\0';
 121                 if (err == 0 && strcmp(session_name, buf) == 0) {
 122                         ctx->session = sessions[i];
 123                         DEBUGADD(DMAPI_TRACE,
 124                                 ("attached to existing DMAPI session "
 125                                  "named '%s'\n", buf));
 126                         break;
 127                 }
 128         }
 129 
 130         /* No session already defined. */
 131         if (ctx->session == DM_NO_SESSION) {
 132                 err = dm_create_session(DM_NO_SESSION, 
 133                                         session_name,
 134                                         &ctx->session);
 135                 if (err < 0) {
 136                         DEBUGADD(DMAPI_TRACE,
 137                                 ("failed to create new DMAPI session: %s\n",
 138                                 strerror(errno)));
 139                         ctx->session = DM_NO_SESSION;
 140                         talloc_free(tmp_ctx);
 141                         return -1;
 142                 }
 143 
 144                 DEBUG(0, ("created new DMAPI session named '%s' for %s\n",
 145                           session_name, version));
 146         }
 147 
 148         if (ctx->session != DM_NO_SESSION) {
 149                 set_effective_capability(DMAPI_ACCESS_CAPABILITY);
 150         }
 151 
 152         /* 
 153            Note that we never end the DMAPI session. It gets re-used if possiblie. 
 154            DMAPI session is a kernel resource that is usually lives until server reboot
 155            and doesn't get destroed when an application finishes.
 156 
 157            However, we free list of references to DMAPI sessions we've got from the kernel
 158            as it is not needed anymore once we have found (or created) our session.
 159          */
 160 
 161         talloc_free(tmp_ctx);
 162         return 0;
 163 }
 164 
 165 /*
 166   Return a pointer to our DMAPI session, if available.
 167   This assumes that you have called dmapi_have_session() first.
 168 */
 169 const void *dmapi_get_current_session(void)
     /* [<][>][^][v][top][bottom][index][help] */
 170 {
 171         if (!dmapi_ctx) {
 172                 return NULL;
 173         }
 174 
 175         if (dmapi_ctx->session == DM_NO_SESSION) {
 176                 return NULL;
 177         }
 178 
 179         return (void *)&dmapi_ctx->session;
 180 }
 181         
 182 /*
 183   dmapi_have_session() must be the first DMAPI call you make in Samba. It will
 184   initialize DMAPI, if available, and tell you if you can get a DMAPI session.
 185   This should be called in the client-specific child process.
 186 */
 187 
 188 bool dmapi_have_session(void)
     /* [<][>][^][v][top][bottom][index][help] */
 189 {
 190         if (!dmapi_ctx) {
 191                 dmapi_ctx = talloc(talloc_autofree_context(),
 192                                    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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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 quering 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 dmapi_file_flags(const char * const path)
     /* [<][>][^][v][top][bottom][index][help] */
 263 {
 264         int             err;
 265         dm_eventset_t   events = {0};
 266         uint            nevents;
 267 
 268         dm_sessid_t     dmapi_session;
 269         const void      *dmapi_session_ptr;
 270         void            *dm_handle = NULL;
 271         size_t          dm_handle_len = 0;
 272 
 273         uint32          flags = 0;
 274 
 275         dmapi_session_ptr = dmapi_get_current_session();
 276         if (dmapi_session_ptr == NULL) {
 277                 return 0;
 278         }
 279 
 280         dmapi_session = *(dm_sessid_t *)dmapi_session_ptr;
 281         if (dmapi_session == DM_NO_SESSION) {
 282                 return 0;
 283         }
 284 
 285         /* AIX has DMAPI but no POSIX capablities support. In this case,
 286          * we need to be root to do DMAPI manipulations.
 287          */
 288 #ifndef HAVE_POSIX_CAPABILITIES
 289         become_root();
 290 #endif
 291 
 292         err = dm_path_to_handle(CONST_DISCARD(char *, path),
 293                 &dm_handle, &dm_handle_len);
 294         if (err < 0) {
 295                 DEBUG(DMAPI_TRACE, ("dm_path_to_handle(%s): %s\n",
 296                             path, strerror(errno)));
 297 
 298                 if (errno != EPERM) {
 299                         goto done;
 300                 }
 301 
 302                 /* Linux capabilities are broken in that changing our
 303                  * user ID will clobber out effective capabilities irrespective
 304                  * of whether we have set PR_SET_KEEPCAPS. Fortunately, the
 305                  * capabilities are not removed from our permitted set, so we
 306                  * can re-acquire them if necessary.
 307                  */
 308 
 309                 set_effective_capability(DMAPI_ACCESS_CAPABILITY);
 310 
 311                 err = dm_path_to_handle(CONST_DISCARD(char *, path),
 312                         &dm_handle, &dm_handle_len);
 313                 if (err < 0) {
 314                         DEBUG(DMAPI_TRACE,
 315                             ("retrying dm_path_to_handle(%s): %s\n",
 316                             path, strerror(errno)));
 317                         goto done;
 318                 }
 319         }
 320 
 321         err = dm_get_eventlist(dmapi_session, dm_handle, dm_handle_len,
 322                 DM_NO_TOKEN, DM_EVENT_MAX, &events, &nevents);
 323         if (err < 0) {
 324                 DEBUG(DMAPI_TRACE, ("dm_get_eventlist(%s): %s\n",
 325                             path, strerror(errno)));
 326                 dm_handle_free(dm_handle, dm_handle_len);
 327                 goto done;
 328         }
 329 
 330         /* We figure that the only reason a DMAPI application would be
 331          * interested in trapping read events is that part of the file is
 332          * offline.
 333          */
 334         DEBUG(DMAPI_TRACE, ("DMAPI event list for %s\n", path));
 335         if (DMEV_ISSET(DM_EVENT_READ, events)) {
 336                 flags = FILE_ATTRIBUTE_OFFLINE;
 337         }
 338 
 339         dm_handle_free(dm_handle, dm_handle_len);
 340 
 341         if (flags & FILE_ATTRIBUTE_OFFLINE) {
 342                 DEBUG(DMAPI_TRACE, ("%s is OFFLINE\n", path));
 343         }
 344 
 345 done:
 346 
 347 #ifndef HAVE_POSIX_CAPABILITIES
 348         unbecome_root();
 349 #endif
 350 
 351         return flags;
 352 }
 353 
 354 
 355 #endif /* USE_DMAPI */

/* [<][>][^][v][top][bottom][index][help] */