~vcs-imports/samba/main

« back to all changes in this revision

Viewing changes to source/smbd/dmapi.c

  • Committer: jerry
  • Date: 2006-07-14 21:48:39 UTC
  • Revision ID: vcs-imports@canonical.com-20060714214839-586d8c489a8fcead
gutting trunk to move to svn:externals

Show diffs side-by-side

added added

removed removed

Lines of Context:
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 */