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