s3:idmap_rid: force mapping type to ID_TYPE_BOTH for sid->unixid mapping
[kai/samba.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/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 uint32 dmapi_file_flags(const char * const path) { return 0; }
31 bool dmapi_have_session(void) { return False; }
32 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 persistant 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 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(discard_const_p(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(discard_const_p(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 */