Merge branch 'ctdb-merge' into dmapi-integration
[jra/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
23 #undef DBGC_CLASS
24 #define DBGC_CLASS DBGC_DMAPI
25
26 #ifndef USE_DMAPI
27
28 uint32 dmapi_file_flags(const char * const path) { return 0; }
29 bool dmapi_have_session(void) { return False; }
30 const void * dmapi_get_current_session(void) { return NULL; }
31
32 #else /* USE_DMAPI */
33
34 #ifdef HAVE_XFS_DMAPI_H
35 #include <xfs/dmapi.h>
36 #elif defined(HAVE_SYS_DMI_H)
37 #include <sys/dmi.h>
38 #elif defined(HAVE_SYS_JFSDMAPI_H)
39 #include <sys/jfsdmapi.h>
40 #elif defined(HAVE_SYS_DMAPI_H)
41 #include <sys/dmapi.h>
42 #elif defined(HAVE_DMAPI_H)
43 #include <dmapi.h>
44 #endif
45
46 #define DMAPI_SESSION_NAME "samba"
47 #define DMAPI_TRACE 10
48
49 static dm_sessid_t samba_dmapi_session = DM_NO_SESSION;
50
51
52 /* 
53    Initialise DMAPI session. The session is persistant kernel state, 
54    so it might already exist, in which case we merely want to 
55    reconnect to it. This function should be called as root.
56 */
57 static int dmapi_init_session(void)
58 {
59         char    buf[DM_SESSION_INFO_LEN];
60         size_t  buflen;
61         uint        nsessions = 5;
62         dm_sessid_t *sessions = NULL;
63         char    *version;
64
65         int i, err;
66
67         if (dm_init_service(&version) < 0) {
68                 DEBUG(0, ("dm_init_service failed - disabling DMAPI\n"));
69                 return -1;
70         }
71
72         ZERO_STRUCT(buf);
73
74         do {
75                 dm_sessid_t *new_sessions;
76                 nsessions *= 2;
77                 new_sessions = TALLOC_REALLOC_ARRAY(NULL, sessions, 
78                                                     dm_sessid_t, nsessions);
79                 if (new_sessions == NULL) {
80                         talloc_free(sessions);
81                         return -1;
82                 }
83
84                 sessions = new_sessions;
85                 err = dm_getall_sessions(nsessions, sessions, &nsessions);
86         } while (err == -1 && errno == E2BIG);
87
88         if (err == -1) {
89                 DEBUGADD(DMAPI_TRACE,
90                         ("failed to retrieve DMAPI sessions: %s\n",
91                         strerror(errno)));
92                 talloc_free(sessions);
93                 return -1;
94         }
95
96         for (i = 0; i < nsessions; ++i) {
97                 err = dm_query_session(sessions[i], sizeof(buf), buf, &buflen);
98                 buf[sizeof(buf) - 1] = '\0';
99                 if (err == 0 && strcmp(DMAPI_SESSION_NAME, buf) == 0) {
100                         samba_dmapi_session = sessions[i];
101                         DEBUGADD(DMAPI_TRACE,
102                                 ("attached to existing DMAPI session "
103                                  "named '%s'\n", buf));
104                         break;
105                 }
106         }
107
108         talloc_free(sessions);
109
110         /* No session already defined. */
111         if (samba_dmapi_session == DM_NO_SESSION) {
112                 err = dm_create_session(DM_NO_SESSION, 
113                                         CONST_DISCARD(char *, DMAPI_SESSION_NAME),
114                                         &samba_dmapi_session);
115                 if (err < 0) {
116                         DEBUGADD(DMAPI_TRACE,
117                                 ("failed to create new DMAPI session: %s\n",
118                                 strerror(errno)));
119                         samba_dmapi_session = DM_NO_SESSION;
120                         return -1;
121                 }
122
123                 DEBUG(0, ("created new DMAPI session named '%s' for %s\n",
124                           DMAPI_SESSION_NAME, version));
125         }
126
127         if (samba_dmapi_session != DM_NO_SESSION) {
128                 set_effective_capability(DMAPI_ACCESS_CAPABILITY);
129         }
130
131         /* 
132            Note that we never end the DMAPI session. It gets re-used if possiblie. 
133            DMAPI session is a kernel resource that is usually lives until server reboot
134            and doesn't get destroed when an application finishes.
135          */
136
137         return 0;
138 }
139
140 /*
141   Return a pointer to our DMAPI session, if available.
142   This assumes that you have called dmapi_have_session() first.
143 */
144 const void *dmapi_get_current_session(void)
145 {
146         if (samba_dmapi_session == DM_NO_SESSION) {
147                 return NULL;
148         }
149
150         return (void *)&samba_dmapi_session;
151 }
152         
153 /*
154   dmapi_have_session() must be the first DMAPI call you make in Samba. It will
155   initialize DMAPI, if available, and tell you if you can get a DMAPI session.
156   This should be called in the client-specific child process.
157 */
158
159 bool dmapi_have_session(void)
160 {
161         static bool initialized;
162         if (!initialized) {
163                 initialized = true;
164
165                 become_root();
166                 dmapi_init_session();
167                 unbecome_root();
168
169         }
170
171         return samba_dmapi_session != DM_NO_SESSION;
172 }
173
174 /* 
175    This is default implementation of dmapi_file_flags() that is 
176    called from VFS is_offline() call to know whether file is offline.
177    For GPFS-specific version see modules/vfs_tsmsm.c. It might be
178    that approach on quering existence of a specific attribute that
179    is used in vfs_tsmsm.c will work with other DMAPI-based HSM 
180    implementations as well.
181 */
182 uint32 dmapi_file_flags(const char * const path)
183 {
184         int             err;
185         dm_eventset_t   events = {0};
186         uint            nevents;
187
188         dm_sessid_t dmapi_session;
189         void    *dmapi_session_ptr;
190         void    *dm_handle = NULL;
191         size_t  dm_handle_len = 0;
192
193         uint32  flags = 0;
194
195         dmapi_session_ptr = dmapi_get_current_session();
196         if (dmapi_session_ptr == NULL) {
197                 return 0;
198         }
199
200         dmapi_session = *(dm_sessid_t *)dmapi_session_ptr;
201         if (dmapi_session == DM_NO_SESSION) {
202                 return 0;
203         }
204
205         /* AIX has DMAPI but no POSIX capablities support. In this case,
206          * we need to be root to do DMAPI manipulations.
207          */
208 #ifndef HAVE_POSIX_CAPABILITIES
209         become_root();
210 #endif
211
212         err = dm_path_to_handle(CONST_DISCARD(char *, path),
213                 &dm_handle, &dm_handle_len);
214         if (err < 0) {
215                 DEBUG(DMAPI_TRACE, ("dm_path_to_handle(%s): %s\n",
216                             path, strerror(errno)));
217
218                 if (errno != EPERM) {
219                         goto done;
220                 }
221
222                 /* Linux capabilities are broken in that changing our
223                  * user ID will clobber out effective capabilities irrespective
224                  * of whether we have set PR_SET_KEEPCAPS. Fortunately, the
225                  * capabilities are not removed from our permitted set, so we
226                  * can re-acquire them if necessary.
227                  */
228
229                 set_effective_capability(DMAPI_ACCESS_CAPABILITY);
230
231                 err = dm_path_to_handle(CONST_DISCARD(char *, path),
232                         &dm_handle, &dm_handle_len);
233                 if (err < 0) {
234                         DEBUG(DMAPI_TRACE,
235                             ("retrying dm_path_to_handle(%s): %s\n",
236                             path, strerror(errno)));
237                         goto done;
238                 }
239         }
240
241         err = dm_get_eventlist(dmapi_session, dm_handle, dm_handle_len,
242                 DM_NO_TOKEN, DM_EVENT_MAX, &events, &nevents);
243         if (err < 0) {
244                 DEBUG(DMAPI_TRACE, ("dm_get_eventlist(%s): %s\n",
245                             path, strerror(errno)));
246                 dm_handle_free(dm_handle, dm_handle_len);
247                 goto done;
248         }
249
250         /* We figure that the only reason a DMAPI application would be
251          * interested in trapping read events is that part of the file is
252          * offline.
253          */
254         DEBUG(DMAPI_TRACE, ("DMAPI event list for %s is %#llx\n",
255                     path, events));
256         if (DMEV_ISSET(DM_EVENT_READ, events)) {
257                 flags = FILE_ATTRIBUTE_OFFLINE;
258         }
259
260         dm_handle_free(dm_handle, dm_handle_len);
261
262         if (flags & FILE_ATTRIBUTE_OFFLINE) {
263                 DEBUG(DMAPI_TRACE, ("%s is OFFLINE\n", path));
264         }
265
266 done:
267
268 #ifndef HAVE_POSIX_CAPABILITIES
269         unbecome_root();
270 #endif
271
272         return flags;
273 }
274
275
276 #endif /* USE_DMAPI */