r16057: Coalesce the DMAPI configure tests into a single macro. Add
[nivanova/samba-autobuild/.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 2 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, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 #undef DBGC_CLASS
25 #define DBGC_CLASS DBGC_DMAPI
26
27 #ifndef USE_DMAPI
28
29 int dmapi_init_session(void) { return -1; }
30 uint32 dmapi_file_flags(const char * const path) { return 0; }
31 BOOL dmapi_have_session(void) { return False; }
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 #endif
44
45 #define DMAPI_SESSION_NAME "samba"
46 #define DMAPI_TRACE 10
47
48 static dm_sessid_t dmapi_session = DM_NO_SESSION;
49
50 /* Initialise the DMAPI interface. Make sure that we only end up initialising
51  * once per process to avoid resource leaks across different DMAPI
52  * implementations.
53  */
54 static int init_dmapi_service(void)
55 {
56         static pid_t lastpid;
57
58         pid_t mypid;
59
60         mypid = sys_getpid();
61         if (mypid != lastpid) {
62                 char *version;
63
64                 lastpid = mypid;
65                 if (dm_init_service(&version) < 0) {
66                         return -1;
67                 }
68
69                 DEBUG(0, ("Initializing DMAPI: %s\n", version));
70         }
71
72         return 0;
73 }
74
75 BOOL dmapi_have_session(void)
76 {
77         return dmapi_session != DM_NO_SESSION;
78 }
79
80 static dm_sessid_t *realloc_session_list(dm_sessid_t * sessions, int count)
81 {
82         dm_sessid_t *nsessions;
83
84         nsessions = TALLOC_REALLOC_ARRAY(NULL, sessions, dm_sessid_t, count);
85         if (nsessions == NULL) {
86                 TALLOC_FREE(sessions);
87                 return NULL;
88         }
89
90         return nsessions;
91 }
92
93 /* Initialise DMAPI session. The session is persistant kernel state, so it
94  * might already exist, in which case we merely want to reconnect to it. This
95  * function should be called as root.
96  */
97 int dmapi_init_session(void)
98 {
99         char    buf[DM_SESSION_INFO_LEN];
100         size_t  buflen;
101
102         uint        nsessions = 10;
103         dm_sessid_t *sessions = NULL;
104
105         int i, err;
106
107         /* If we aren't root, something in the following will fail due to lack
108          * of privileges. Aborting seems a little extreme.
109          */
110         SMB_WARN(getuid() == 0, "dmapi_init_session must be called as root");
111
112         dmapi_session = DM_NO_SESSION;
113         if (init_dmapi_service() < 0) {
114                 return -1;
115         }
116
117 retry:
118
119         if ((sessions = realloc_session_list(sessions, nsessions)) == NULL) {
120                 return -1;
121         }
122
123         err = dm_getall_sessions(nsessions, sessions, &nsessions);
124         if (err < 0) {
125                 if (errno == E2BIG) {
126                         nsessions *= 2;
127                         goto retry;
128                 }
129
130                 DEBUGADD(DMAPI_TRACE,
131                         ("failed to retrieve DMAPI sessions: %s\n",
132                         strerror(errno)));
133                 TALLOC_FREE(sessions);
134                 return -1;
135         }
136
137         for (i = 0; i < nsessions; ++i) {
138                 err = dm_query_session(sessions[i], sizeof(buf), buf, &buflen);
139                 buf[sizeof(buf) - 1] = '\0';
140                 if (err == 0 && strcmp(DMAPI_SESSION_NAME, buf) == 0) {
141                         dmapi_session = sessions[i];
142                         DEBUGADD(DMAPI_TRACE,
143                                 ("attached to existing DMAPI session "
144                                  "named '%s'\n", buf));
145                         break;
146                 }
147         }
148
149         TALLOC_FREE(sessions);
150
151         /* No session already defined. */
152         if (dmapi_session == DM_NO_SESSION) {
153                 err = dm_create_session(DM_NO_SESSION, DMAPI_SESSION_NAME,
154                                         &dmapi_session);
155                 if (err < 0) {
156                         DEBUGADD(DMAPI_TRACE,
157                                 ("failed to create new DMAPI session: %s\n",
158                                 strerror(errno)));
159                         dmapi_session = DM_NO_SESSION;
160                         return -1;
161                 }
162
163                 DEBUGADD(DMAPI_TRACE,
164                         ("created new DMAPI session named '%s'\n",
165                         DMAPI_SESSION_NAME));
166         }
167
168         /* Note that we never end the DMAPI session. This enables child
169          * processes to continue to use the session after we exit. It also lets
170          * you run a second Samba server on different ports without any
171          * conflict.
172          */
173
174         return 0;
175 }
176
177 /* Reattach to an existing dmapi session. Called from service processes that
178  * might not be running as root.
179  */
180 static int reattach_dmapi_session(void)
181 {
182         char    buf[DM_SESSION_INFO_LEN];
183         size_t  buflen;
184
185         if (dmapi_session != DM_NO_SESSION ) {
186                 become_root();
187
188                 /* NOTE: On Linux, this call opens /dev/dmapi, costing us a
189                  * file descriptor. Ideally, we would close this when we fork.
190                  */
191                 if (init_dmapi_service() < 0) {
192                         dmapi_session = DM_NO_SESSION;
193                         unbecome_root();
194                         return -1;
195                 }
196
197                 if (dm_query_session(dmapi_session, sizeof(buf),
198                             buf, &buflen) < 0) {
199                         /* Session is stale. Disable DMAPI. */
200                         dmapi_session = DM_NO_SESSION;
201                         unbecome_root();
202                         return -1;
203                 }
204
205                 set_effective_capability(DMAPI_ACCESS_CAPABILITY);
206
207                 DEBUG(DMAPI_TRACE, ("reattached DMAPI session\n"));
208                 unbecome_root();
209         }
210
211         return 0;
212 }
213
214 uint32 dmapi_file_flags(const char * const path)
215 {
216         static int attached = 0;
217
218         int             err;
219         dm_eventset_t   events = {0};
220         uint            nevents;
221
222         void    *dm_handle;
223         size_t  dm_handle_len;
224
225         uint32  flags = 0;
226
227         /* If a DMAPI session has been initialised, then we need to make sure
228          * we are attached to it and have the correct privileges. This is
229          * necessary to be able to do DMAPI operations across a fork(2). If
230          * it fails, there is no liklihood of that failure being transient.
231          *
232          * Note that this use of the static attached flag relies on the fact
233          * that dmapi_file_flags() is never called prior to forking the
234          * per-client server process.
235          */
236         if (dmapi_have_session() && !attached) {
237                 attached++;
238                 if (reattach_dmapi_session() < 0) {
239                         return 0;
240                 }
241         }
242
243         err = dm_path_to_handle(CONST_DISCARD(char *, path),
244                 &dm_handle, &dm_handle_len);
245         if (err < 0) {
246                 DEBUG(DMAPI_TRACE, ("dm_path_to_handle(%s): %s\n",
247                             path, strerror(errno)));
248
249                 if (errno != EPERM) {
250                         return 0;
251                 }
252
253                 /* Linux capabilities are broken in that changing our
254                  * user ID will clobber out effective capabilities irrespective
255                  * of whether we have set PR_SET_KEEPCAPS. Fortunately, the
256                  * capabilities are not removed from our permitted set, so we
257                  * can re-acquire them if necessary.
258                  */
259
260                 set_effective_capability(DMAPI_ACCESS_CAPABILITY);
261
262                 err = dm_path_to_handle(CONST_DISCARD(char *, path),
263                         &dm_handle, &dm_handle_len);
264                 if (err < 0) {
265                         DEBUG(DMAPI_TRACE,
266                             ("retrying dm_path_to_handle(%s): %s\n",
267                             path, strerror(errno)));
268                         return 0;
269                 }
270         }
271
272         err = dm_get_eventlist(dmapi_session, dm_handle, dm_handle_len,
273                 DM_NO_TOKEN, DM_EVENT_MAX, &events, &nevents);
274         if (err < 0) {
275                 DEBUG(DMAPI_TRACE, ("dm_get_eventlist(%s): %s\n",
276                             path, strerror(errno)));
277                 dm_handle_free(dm_handle, dm_handle_len);
278                 return 0;
279         }
280
281         /* We figure that the only reason a DMAPI application would be
282          * interested in trapping read events is that part of the file is
283          * offline.
284          */
285         DEBUG(DMAPI_TRACE, ("DMAPI event list for %s is %#llx\n",
286                     path, events));
287         if (DMEV_ISSET(DM_EVENT_READ, events)) {
288                 flags = FILE_ATTRIBUTE_OFFLINE;
289         }
290
291         dm_handle_free(dm_handle, dm_handle_len);
292
293         if (flags & FILE_ATTRIBUTE_OFFLINE) {
294                 DEBUG(DMAPI_TRACE, ("%s is OFFLINE\n", path));
295         }
296
297         return flags;
298 }
299
300 #endif /* USE_DMAPI */