Samba 3: added Samba 3.0.24 sources
[tomato.git] / release / src / router / samba3 / source / smbd / dmapi.c
bloba9d83c782bb7fd4c33ccdafe1d419e0453f65b89
1 /*
2 Unix SMB/CIFS implementation.
3 DMAPI Support routines
5 Copyright (C) James Peach 2006
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.
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.
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.
22 #include "includes.h"
24 #undef DBGC_CLASS
25 #define DBGC_CLASS DBGC_DMAPI
27 #ifndef USE_DMAPI
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; }
33 #else /* USE_DMAPI */
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
45 #define DMAPI_SESSION_NAME "samba"
46 #define DMAPI_TRACE 10
48 static dm_sessid_t dmapi_session = DM_NO_SESSION;
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.
54 static int init_dmapi_service(void)
56 static pid_t lastpid;
58 pid_t mypid;
60 mypid = sys_getpid();
61 if (mypid != lastpid) {
62 char *version;
64 lastpid = mypid;
65 if (dm_init_service(&version) < 0) {
66 return -1;
69 DEBUG(0, ("Initializing DMAPI: %s\n", version));
72 return 0;
75 BOOL dmapi_have_session(void)
77 return dmapi_session != DM_NO_SESSION;
80 static dm_sessid_t *realloc_session_list(dm_sessid_t * sessions, int count)
82 dm_sessid_t *nsessions;
84 nsessions = TALLOC_REALLOC_ARRAY(NULL, sessions, dm_sessid_t, count);
85 if (nsessions == NULL) {
86 TALLOC_FREE(sessions);
87 return NULL;
90 return nsessions;
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.
97 int dmapi_init_session(void)
99 char buf[DM_SESSION_INFO_LEN];
100 size_t buflen;
102 uint nsessions = 10;
103 dm_sessid_t *sessions = NULL;
105 int i, err;
107 /* If we aren't root, something in the following will fail due to lack
108 * of privileges. Aborting seems a little extreme.
110 SMB_WARN(getuid() == 0, "dmapi_init_session must be called as root");
112 dmapi_session = DM_NO_SESSION;
113 if (init_dmapi_service() < 0) {
114 return -1;
117 retry:
119 if ((sessions = realloc_session_list(sessions, nsessions)) == NULL) {
120 return -1;
123 err = dm_getall_sessions(nsessions, sessions, &nsessions);
124 if (err < 0) {
125 if (errno == E2BIG) {
126 nsessions *= 2;
127 goto retry;
130 DEBUGADD(DMAPI_TRACE,
131 ("failed to retrieve DMAPI sessions: %s\n",
132 strerror(errno)));
133 TALLOC_FREE(sessions);
134 return -1;
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;
149 TALLOC_FREE(sessions);
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;
163 DEBUGADD(DMAPI_TRACE,
164 ("created new DMAPI session named '%s'\n",
165 DMAPI_SESSION_NAME));
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.
174 return 0;
177 /* Reattach to an existing dmapi session. Called from service processes that
178 * might not be running as root.
180 static int reattach_dmapi_session(void)
182 char buf[DM_SESSION_INFO_LEN];
183 size_t buflen;
185 if (dmapi_session != DM_NO_SESSION ) {
186 become_root();
188 /* NOTE: On Linux, this call opens /dev/dmapi, costing us a
189 * file descriptor. Ideally, we would close this when we fork.
191 if (init_dmapi_service() < 0) {
192 dmapi_session = DM_NO_SESSION;
193 unbecome_root();
194 return -1;
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;
205 set_effective_capability(DMAPI_ACCESS_CAPABILITY);
207 DEBUG(DMAPI_TRACE, ("reattached DMAPI session\n"));
208 unbecome_root();
211 return 0;
214 uint32 dmapi_file_flags(const char * const path)
216 static int attached = 0;
218 int err;
219 dm_eventset_t events = {0};
220 uint nevents;
222 void *dm_handle;
223 size_t dm_handle_len;
225 uint32 flags = 0;
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.
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.
236 if (dmapi_have_session() && !attached) {
237 attached++;
238 if (reattach_dmapi_session() < 0) {
239 return 0;
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)));
249 if (errno != EPERM) {
250 return 0;
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.
260 set_effective_capability(DMAPI_ACCESS_CAPABILITY);
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;
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;
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.
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;
291 dm_handle_free(dm_handle, dm_handle_len);
293 if (flags & FILE_ATTRIBUTE_OFFLINE) {
294 DEBUG(DMAPI_TRACE, ("%s is OFFLINE\n", path));
297 return flags;
300 #endif /* USE_DMAPI */