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 */
|