Coverage Report

Created: 2026-04-01 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/smbd/dmapi.c
Line
Count
Source
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_dmapi_capability(true);
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_dmapi_capability(true);
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 */