~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source3/modules/vfs_notify_fam.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * FAM file notification support.
 
3
 *
 
4
 * Copyright (c) James Peach 2005
 
5
 * Copyright (c) Volker Lendecke 2007
 
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
#include <fam.h>
 
24
 
 
25
#if !defined(HAVE_FAM_H_FAMCODES_TYPEDEF)
 
26
/* Gamin provides this typedef which means we can't use 'enum FAMCodes' as per
 
27
 * every other FAM implementation. Phooey.
 
28
 */
 
29
typedef enum FAMCodes FAMCodes;
 
30
#endif
 
31
 
 
32
/* NOTE: There are multiple versions of FAM floating around the net, each with
 
33
 * slight differences from the original SGI FAM implementation. In this file,
 
34
 * we rely only on the SGI features and do not assume any extensions. For
 
35
 * example, we do not look at FAMErrno, because it is not set by the original
 
36
 * implementation.
 
37
 *
 
38
 * Random FAM links:
 
39
 *      http://oss.sgi.com/projects/fam/
 
40
 *      http://savannah.nongnu.org/projects/fam/
 
41
 *      http://sourceforge.net/projects/bsdfam/
 
42
 */
 
43
 
 
44
/* ------------------------------------------------------------------------- */
 
45
 
 
46
struct fam_watch_context {
 
47
        struct fam_watch_context *prev, *next;
 
48
        FAMConnection *fam_connection;
 
49
        struct FAMRequest fr;
 
50
        struct sys_notify_context *sys_ctx;
 
51
        void (*callback)(struct sys_notify_context *ctx, 
 
52
                         void *private_data,
 
53
                         struct notify_event *ev);
 
54
        void *private_data;
 
55
        uint32_t mask; /* the inotify mask */
 
56
        uint32_t filter; /* the windows completion filter */
 
57
        const char *path;
 
58
};
 
59
 
 
60
 
 
61
/*
 
62
 * We want one FAM connection per smbd, not one per tcon.
 
63
 */
 
64
static FAMConnection fam_connection;
 
65
static bool fam_connection_initialized = False;
 
66
 
 
67
static struct fam_watch_context *fam_notify_list;
 
68
static void fam_handler(struct event_context *event_ctx,
 
69
                        struct fd_event *fd_event,
 
70
                        uint16 flags,
 
71
                        void *private_data);
 
72
 
 
73
static NTSTATUS fam_open_connection(FAMConnection *fam_conn,
 
74
                                    struct event_context *event_ctx)
 
75
{
 
76
        int res;
 
77
        char *name;
 
78
 
 
79
        ZERO_STRUCTP(fam_conn);
 
80
        FAMCONNECTION_GETFD(fam_conn) = -1;
 
81
 
 
82
        if (asprintf(&name, "smbd (%lu)", (unsigned long)sys_getpid()) == -1) {
 
83
                DEBUG(0, ("No memory\n"));
 
84
                return NT_STATUS_NO_MEMORY;
 
85
        }
 
86
 
 
87
        res = FAMOpen2(fam_conn, name);
 
88
        SAFE_FREE(name);
 
89
 
 
90
        if (res < 0) {
 
91
                DEBUG(10, ("FAM file change notifications not available\n"));
 
92
                /*
 
93
                 * No idea how to get NT_STATUS from a FAM result
 
94
                 */
 
95
                FAMCONNECTION_GETFD(fam_conn) = -1;
 
96
                return NT_STATUS_UNEXPECTED_IO_ERROR;
 
97
        }
 
98
 
 
99
        if (event_add_fd(event_ctx, event_ctx,
 
100
                         FAMCONNECTION_GETFD(fam_conn),
 
101
                         EVENT_FD_READ, fam_handler,
 
102
                         (void *)fam_conn) == NULL) {
 
103
                DEBUG(0, ("event_add_fd failed\n"));
 
104
                FAMClose(fam_conn);
 
105
                FAMCONNECTION_GETFD(fam_conn) = -1;
 
106
                return NT_STATUS_NO_MEMORY;
 
107
        }
 
108
 
 
109
        return NT_STATUS_OK;
 
110
}
 
111
 
 
112
static void fam_reopen(FAMConnection *fam_conn,
 
113
                       struct event_context *event_ctx,
 
114
                       struct fam_watch_context *notify_list)
 
115
{
 
116
        struct fam_watch_context *ctx;
 
117
 
 
118
        DEBUG(5, ("Re-opening FAM connection\n"));
 
119
 
 
120
        FAMClose(fam_conn);
 
121
 
 
122
        if (!NT_STATUS_IS_OK(fam_open_connection(fam_conn, event_ctx))) {
 
123
                DEBUG(5, ("Re-opening fam connection failed\n"));
 
124
                return;
 
125
        }
 
126
 
 
127
        for (ctx = notify_list; ctx; ctx = ctx->next) {
 
128
                FAMMonitorDirectory(fam_conn, ctx->path, &ctx->fr, NULL);
 
129
        }
 
130
}
 
131
 
 
132
static void fam_handler(struct event_context *event_ctx,
 
133
                        struct fd_event *fd_event,
 
134
                        uint16 flags,
 
135
                        void *private_data)
 
136
{
 
137
        FAMConnection *fam_conn = (FAMConnection *)private_data;
 
138
        FAMEvent fam_event;
 
139
        struct fam_watch_context *ctx;
 
140
        struct notify_event ne;
 
141
 
 
142
        if (FAMPending(fam_conn) == 0) {
 
143
                DEBUG(10, ("fam_handler called but nothing pending\n"));
 
144
                return;
 
145
        }
 
146
 
 
147
        if (FAMNextEvent(fam_conn, &fam_event) != 1) {
 
148
                DEBUG(5, ("FAMNextEvent returned an error\n"));
 
149
                TALLOC_FREE(fd_event);
 
150
                fam_reopen(fam_conn, event_ctx, fam_notify_list);
 
151
                return;
 
152
        }
 
153
 
 
154
        DEBUG(10, ("Got FAMCode %d for %s\n", fam_event.code,
 
155
                   fam_event.filename));
 
156
 
 
157
        switch (fam_event.code) {
 
158
        case FAMChanged:
 
159
                ne.action = NOTIFY_ACTION_MODIFIED;
 
160
                break;
 
161
        case FAMCreated:
 
162
                ne.action = NOTIFY_ACTION_ADDED;
 
163
                break;
 
164
        case FAMDeleted:
 
165
                ne.action = NOTIFY_ACTION_REMOVED;
 
166
                break;
 
167
        default:
 
168
                DEBUG(10, ("Ignoring code FAMCode %d for file %s\n",
 
169
                           (int)fam_event.code, fam_event.filename));
 
170
                return;
 
171
        }
 
172
 
 
173
        for (ctx = fam_notify_list; ctx; ctx = ctx->next) {
 
174
                if (memcmp(&fam_event.fr, &ctx->fr, sizeof(FAMRequest)) == 0) {
 
175
                        break;
 
176
                }
 
177
        }
 
178
 
 
179
        if (ctx == NULL) {
 
180
                DEBUG(5, ("Discarding event for file %s\n",
 
181
                          fam_event.filename));
 
182
                return;
 
183
        }
 
184
 
 
185
        if ((ne.path = strrchr_m(fam_event.filename, '\\')) == NULL) {
 
186
                ne.path = fam_event.filename;
 
187
        }
 
188
 
 
189
        ctx->callback(ctx->sys_ctx, ctx->private_data, &ne);
 
190
}
 
191
 
 
192
static int fam_watch_context_destructor(struct fam_watch_context *ctx)
 
193
{
 
194
        if (FAMCONNECTION_GETFD(ctx->fam_connection) != -1) {
 
195
                FAMCancelMonitor(&fam_connection, &ctx->fr);
 
196
        }
 
197
        DLIST_REMOVE(fam_notify_list, ctx);
 
198
        return 0;
 
199
}
 
200
 
 
201
/*
 
202
  add a watch. The watch is removed when the caller calls
 
203
  talloc_free() on *handle
 
204
*/
 
205
static NTSTATUS fam_watch(vfs_handle_struct *vfs_handle,
 
206
                          struct sys_notify_context *ctx,
 
207
                          struct notify_entry *e,
 
208
                          void (*callback)(struct sys_notify_context *ctx, 
 
209
                                           void *private_data,
 
210
                                           struct notify_event *ev),
 
211
                          void *private_data, 
 
212
                          void *handle_p)
 
213
{
 
214
        const uint32 fam_mask = (FILE_NOTIFY_CHANGE_FILE_NAME|
 
215
                                 FILE_NOTIFY_CHANGE_DIR_NAME);
 
216
        struct fam_watch_context *watch;
 
217
        void **handle = (void **)handle_p;
 
218
 
 
219
        if ((e->filter & fam_mask) == 0) {
 
220
                DEBUG(10, ("filter = %u, ignoring in FAM\n", e->filter));
 
221
                return NT_STATUS_OK;
 
222
        }
 
223
 
 
224
        if (!fam_connection_initialized) {
 
225
                if (!NT_STATUS_IS_OK(fam_open_connection(&fam_connection,
 
226
                                                         ctx->ev))) {
 
227
                        /*
 
228
                         * Just let smbd do all the work itself
 
229
                         */
 
230
                        return NT_STATUS_OK;
 
231
                }
 
232
                fam_connection_initialized = True;
 
233
        }
 
234
 
 
235
        if (!(watch = TALLOC_P(ctx, struct fam_watch_context))) {
 
236
                return NT_STATUS_NO_MEMORY;
 
237
        }
 
238
 
 
239
        watch->fam_connection = &fam_connection;
 
240
 
 
241
        watch->callback = callback;
 
242
        watch->private_data = private_data;
 
243
        watch->sys_ctx = ctx;
 
244
 
 
245
        if (!(watch->path = talloc_strdup(watch, e->path))) {
 
246
                DEBUG(0, ("talloc_asprintf failed\n"));
 
247
                TALLOC_FREE(watch);
 
248
                return NT_STATUS_NO_MEMORY;
 
249
        }
 
250
 
 
251
        /*
 
252
         * The FAM module in this early state will only take care of
 
253
         * FAMCreated and FAMDeleted events, Leave the rest to
 
254
         * notify_internal.c
 
255
         */
 
256
 
 
257
        watch->filter = fam_mask;
 
258
        e->filter &= ~fam_mask;
 
259
 
 
260
        DLIST_ADD(fam_notify_list, watch);
 
261
        talloc_set_destructor(watch, fam_watch_context_destructor);
 
262
 
 
263
        /*
 
264
         * Only directories monitored so far
 
265
         */
 
266
 
 
267
        if (FAMCONNECTION_GETFD(watch->fam_connection) != -1) {
 
268
                FAMMonitorDirectory(watch->fam_connection, watch->path,
 
269
                                    &watch->fr, NULL);
 
270
        }
 
271
        else {
 
272
                /*
 
273
                 * If the re-open is successful, this will establish the
 
274
                 * FAMMonitor from the list
 
275
                 */
 
276
                fam_reopen(watch->fam_connection, ctx->ev, fam_notify_list);
 
277
        }
 
278
 
 
279
        *handle = (void *)watch;
 
280
 
 
281
        return NT_STATUS_OK;
 
282
}
 
283
 
 
284
/* VFS operations structure */
 
285
 
 
286
static vfs_op_tuple notify_fam_op_tuples[] = {
 
287
 
 
288
        {SMB_VFS_OP(fam_watch),
 
289
         SMB_VFS_OP_NOTIFY_WATCH,
 
290
         SMB_VFS_LAYER_OPAQUE},
 
291
 
 
292
        {SMB_VFS_OP(NULL),
 
293
         SMB_VFS_OP_NOOP,
 
294
         SMB_VFS_LAYER_NOOP}
 
295
 
 
296
};
 
297
 
 
298
 
 
299
NTSTATUS vfs_notify_fam_init(void);
 
300
NTSTATUS vfs_notify_fam_init(void)
 
301
{
 
302
        return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "notify_fam",
 
303
                                notify_fam_op_tuples);
 
304
}