s3:smbldap: add smbldap_talloc_first_attribute()
[ira/wip.git] / source3 / smbd / dmapi.c
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; }
30 bool dmapi_have_session(void) { return False; }
31 const void * dmapi_get_current_session(void) { return NULL; }
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)
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)
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)
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)
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 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)
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 */