~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source3/modules/onefs_notify.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
 * Unix SMB/CIFS implementation.
 
3
 *
 
4
 * Support for change notify using OneFS's file event notification system
 
5
 *
 
6
 * Copyright (C) Andrew Tridgell, 2006
 
7
 * Copyright (C) Steven Danneman, 2008
 
8
 *
 
9
 * This program is free software; you can redistribute it and/or modify
 
10
 * it under the terms of the GNU General Public License as published by
 
11
 * the Free Software Foundation; either version 3 of the License, or
 
12
 * (at your option) any later version.
 
13
 *
 
14
 * This program is distributed in the hope that it will be useful,
 
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
17
 * GNU General Public License for more details.
 
18
 *
 
19
 * You should have received a copy of the GNU General Public License
 
20
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 
21
 */
 
22
 
 
23
/* Implement handling of change notify requests on files and directories using
 
24
 * Isilon OneFS's "ifs event" file notification system.
 
25
 *
 
26
 * The structure of this file is based off the implementation of change notify
 
27
 * using the inotify system calls in smbd/notify_inotify.c */
 
28
 
 
29
/* TODO: We could reduce the number of file descriptors used by merging
 
30
 * multiple watch requests on the same directory into the same
 
31
 * onefs_notify_watch_context.  To do this we'd need mux/demux routines that
 
32
 * when receiving an event on that watch context would check it against the
 
33
 * CompletionFilter and WatchTree of open SMB requests, and return notify
 
34
 * events back to the proper SMB requests */
 
35
 
 
36
#include "includes.h"
 
37
#include "onefs.h"
 
38
 
 
39
#include <ifs/ifs_types.h>
 
40
#include <ifs/ifs_syscalls.h>
 
41
#include <isi_util/syscalls.h>
 
42
 
 
43
#include <sys/event.h>
 
44
 
 
45
#define ONEFS_IFS_EVENT_MAX_NUM 8
 
46
#define ONEFS_IFS_EVENT_MAX_BYTES (ONEFS_IFS_EVENT_MAX_NUM * \
 
47
                                   sizeof(struct ifs_event))
 
48
 
 
49
struct onefs_notify_watch_context {
 
50
        struct sys_notify_context *ctx;
 
51
        int watch_fd;
 
52
        ino_t watch_lin;
 
53
        const char *path;
 
54
        int ifs_event_fd;
 
55
        uint32_t ifs_filter; /* the ifs event mask */
 
56
        uint32_t smb_filter; /* the windows completion filter */
 
57
        void (*callback)(struct sys_notify_context *ctx,
 
58
                         void *private_data,
 
59
                         struct notify_event *ev);
 
60
        void *private_data;
 
61
};
 
62
 
 
63
/**
 
64
 * Conversion map from a SMB completion filter to an IFS event mask.
 
65
 */
 
66
static const struct {
 
67
        uint32_t smb_filter;
 
68
        uint32_t ifs_filter;
 
69
} onefs_notify_conv[] = {
 
70
        {FILE_NOTIFY_CHANGE_FILE_NAME,
 
71
            NOTE_CREATE | NOTE_DELETE | NOTE_RENAME_FROM | NOTE_RENAME_TO},
 
72
        {FILE_NOTIFY_CHANGE_DIR_NAME,
 
73
            NOTE_CREATE | NOTE_DELETE | NOTE_RENAME_FROM | NOTE_RENAME_TO},
 
74
        {FILE_NOTIFY_CHANGE_ATTRIBUTES,
 
75
            NOTE_CREATE | NOTE_DELETE | NOTE_RENAME_FROM | NOTE_RENAME_TO |
 
76
            NOTE_ATTRIB},
 
77
        {FILE_NOTIFY_CHANGE_SIZE,
 
78
            NOTE_SIZE | NOTE_EXTEND},
 
79
        {FILE_NOTIFY_CHANGE_LAST_WRITE,
 
80
            NOTE_WRITE | NOTE_ATTRIB},
 
81
        /* OneFS doesn't set atime by default, but we can somewhat fake it by
 
82
         * notifying for other events that imply ACCESS */
 
83
        {FILE_NOTIFY_CHANGE_LAST_ACCESS,
 
84
            NOTE_WRITE | NOTE_ATTRIB},
 
85
        /* We don't have an ifs_event for the setting of create time, but we
 
86
         * can fake by notifying when a "new" file is created via rename */
 
87
        {FILE_NOTIFY_CHANGE_CREATION,
 
88
            NOTE_RENAME_TO},
 
89
        {FILE_NOTIFY_CHANGE_SECURITY,
 
90
            NOTE_SECURITY},
 
91
        /* Unsupported bits
 
92
        FILE_NOTIFY_CHANGE_EA            (no EAs in OneFS)
 
93
        FILE_NOTIFY_CHANGE_STREAM_NAME   (no ifs_event equivalent)
 
94
        FILE_NOTIFY_CHANGE_STREAM_SIZE   (no ifs_event equivalent)
 
95
        FILE_NOTIFY_CHANGE_STREAM_WRITE  (no ifs_event equivalent) */
 
96
};
 
97
 
 
98
#define ONEFS_NOTIFY_UNSUPPORTED  (FILE_NOTIFY_CHANGE_LAST_ACCESS | \
 
99
                                   FILE_NOTIFY_CHANGE_CREATION    | \
 
100
                                   FILE_NOTIFY_CHANGE_EA          | \
 
101
                                   FILE_NOTIFY_CHANGE_STREAM_NAME | \
 
102
                                   FILE_NOTIFY_CHANGE_STREAM_SIZE | \
 
103
                                   FILE_NOTIFY_CHANGE_STREAM_WRITE)
 
104
 
 
105
/**
 
106
 * Convert Windows/SMB filter/flags to IFS event filter.
 
107
 *
 
108
 * @param[in] smb_filter Windows Completion Filter sent in the SMB
 
109
 *
 
110
 * @return ifs event filter mask
 
111
 */
 
112
static uint32_t
 
113
onefs_notify_smb_filter_to_ifs_filter(uint32_t smb_filter)
 
114
{
 
115
        int i;
 
116
        uint32_t ifs_filter = 0x0;
 
117
 
 
118
        for (i=0;i<ARRAY_SIZE(onefs_notify_conv);i++) {
 
119
                if (onefs_notify_conv[i].smb_filter & smb_filter) {
 
120
                        ifs_filter |= onefs_notify_conv[i].ifs_filter;
 
121
                }
 
122
        }
 
123
 
 
124
        return ifs_filter;
 
125
}
 
126
 
 
127
/**
 
128
 * Convert IFS filter/flags to a Windows notify action.
 
129
 *
 
130
 * Returns Win notification actions, types (1-5).
 
131
 *
 
132
 * @param[in] smb_filter Windows Completion Filter sent in the SMB
 
133
 * @param[in] ifs_filter Returned from the kernel in the ifs_event
 
134
 *
 
135
 * @return 0 if there are no more relevant flags.
 
136
 */
 
137
static int
 
138
onefs_notify_ifs_filter_to_smb_action(uint32_t smb_filter, uint32_t ifs_filter)
 
139
{
 
140
        /* Handle Windows special cases, before modifying events bitmask */
 
141
 
 
142
        /* Special case 1: win32api->MoveFile needs to send a modified
 
143
         * notification on the new file, if smb_filter == ATTRIBUTES.
 
144
         * Here we handle the case where two separate ATTR & NAME notifications
 
145
         * have been registered.  We handle the case where both bits are set in
 
146
         * the same registration in onefs_notify_dispatch() */
 
147
        if ((smb_filter & FILE_NOTIFY_CHANGE_ATTRIBUTES) &&
 
148
                !(smb_filter & FILE_NOTIFY_CHANGE_FILE_NAME) &&
 
149
                (ifs_filter & NOTE_FILE) && (ifs_filter & NOTE_RENAME_TO))
 
150
        {
 
151
                return NOTIFY_ACTION_MODIFIED;
 
152
        }
 
153
 
 
154
        /* Special case 2: Writes need to send a modified
 
155
         * notification on the file, if smb_filter = ATTRIBUTES. */
 
156
        if ((smb_filter & FILE_NOTIFY_CHANGE_ATTRIBUTES) &&
 
157
                (ifs_filter & NOTE_FILE) && (ifs_filter & NOTE_WRITE))
 
158
        {
 
159
                return NOTIFY_ACTION_MODIFIED;
 
160
        }
 
161
 
 
162
        /* Loop because some events may be filtered out. Eventually all
 
163
         * relevant events will be taken care of and returned. */
 
164
        while (1) {
 
165
                if (ifs_filter & NOTE_CREATE) {
 
166
                        ifs_filter &= ~NOTE_CREATE;
 
167
                        if ((smb_filter & FILE_NOTIFY_CHANGE_FILE_NAME) &&
 
168
                                (ifs_filter & NOTE_FILE))
 
169
                                return NOTIFY_ACTION_ADDED;
 
170
                        if ((smb_filter & FILE_NOTIFY_CHANGE_DIR_NAME) &&
 
171
                                (ifs_filter & NOTE_DIRECTORY))
 
172
                                return NOTIFY_ACTION_ADDED;
 
173
                }
 
174
                else if (ifs_filter & NOTE_DELETE) {
 
175
                        ifs_filter &= ~NOTE_DELETE;
 
176
                        if ((smb_filter & FILE_NOTIFY_CHANGE_FILE_NAME) &&
 
177
                                (ifs_filter & NOTE_FILE))
 
178
                                return NOTIFY_ACTION_REMOVED;
 
179
                        if ((smb_filter & FILE_NOTIFY_CHANGE_DIR_NAME) &&
 
180
                                (ifs_filter & NOTE_DIRECTORY))
 
181
                                return NOTIFY_ACTION_REMOVED;
 
182
                }
 
183
                else if (ifs_filter & NOTE_WRITE) {
 
184
                        ifs_filter &= ~NOTE_WRITE;
 
185
                        if ((smb_filter & FILE_NOTIFY_CHANGE_LAST_WRITE) ||
 
186
                                (smb_filter & FILE_NOTIFY_CHANGE_LAST_ACCESS))
 
187
                                return NOTIFY_ACTION_MODIFIED;
 
188
                }
 
189
                else if ((ifs_filter & NOTE_SIZE) || (ifs_filter & NOTE_EXTEND)) {
 
190
                        ifs_filter &= ~NOTE_SIZE;
 
191
                        ifs_filter &= ~NOTE_EXTEND;
 
192
 
 
193
                        /* TODO: Do something on NOTE_DIR? */
 
194
                        if ((smb_filter & FILE_NOTIFY_CHANGE_SIZE) &&
 
195
                            (ifs_filter & NOTE_FILE))
 
196
                                return NOTIFY_ACTION_MODIFIED;
 
197
                }
 
198
                else if (ifs_filter & NOTE_ATTRIB) {
 
199
                        ifs_filter &= ~NOTE_ATTRIB;
 
200
                        /* NOTE_ATTRIB needs to be converted to a
 
201
                         * LAST_WRITE as well, because we need to send
 
202
                         * LAST_WRITE when the mtime changes. Looking into
 
203
                         * better alternatives as this causes extra LAST_WRITE
 
204
                         * notifications. We also return LAST_ACCESS as a
 
205
                         * modification to attribs implies this. */
 
206
                        if ((smb_filter & FILE_NOTIFY_CHANGE_ATTRIBUTES) ||
 
207
                                (smb_filter & FILE_NOTIFY_CHANGE_LAST_WRITE) ||
 
208
                                (smb_filter & FILE_NOTIFY_CHANGE_LAST_ACCESS))
 
209
                                return NOTIFY_ACTION_MODIFIED;
 
210
                }
 
211
                else if (ifs_filter & NOTE_LINK) {
 
212
                        ifs_filter &= ~NOTE_LINK;
 
213
                        /* NOTE_LINK will send out NO notifications */
 
214
                }
 
215
                else if (ifs_filter & NOTE_REVOKE) {
 
216
                        ifs_filter &= ~NOTE_REVOKE;
 
217
                        /* NOTE_REVOKE will send out NO notifications */
 
218
                }
 
219
                else if (ifs_filter & NOTE_RENAME_FROM)  {
 
220
                        ifs_filter &= ~NOTE_RENAME_FROM;
 
221
 
 
222
                        if (((smb_filter & FILE_NOTIFY_CHANGE_FILE_NAME) &&
 
223
                                 (ifs_filter & NOTE_FILE)) ||
 
224
                                ((smb_filter & FILE_NOTIFY_CHANGE_DIR_NAME) &&
 
225
                                 (ifs_filter & NOTE_DIRECTORY))) {
 
226
                                /* Check if this is a RENAME, not a MOVE */
 
227
                                if (ifs_filter & NOTE_RENAME_SAMEDIR) {
 
228
                                        /* Remove the NOTE_RENAME_SAMEDIR, IFF
 
229
                                         * RENAME_TO is not in this event */
 
230
                                        if (!(ifs_filter & NOTE_RENAME_TO))
 
231
                                                ifs_filter &=
 
232
                                                    ~NOTE_RENAME_SAMEDIR;
 
233
                                        return NOTIFY_ACTION_OLD_NAME;
 
234
                                }
 
235
                                return NOTIFY_ACTION_REMOVED;
 
236
                        }
 
237
                }
 
238
                else if (ifs_filter & NOTE_RENAME_TO) {
 
239
                        ifs_filter &= ~NOTE_RENAME_TO;
 
240
 
 
241
                        if (((smb_filter & FILE_NOTIFY_CHANGE_FILE_NAME) &&
 
242
                                 (ifs_filter & NOTE_FILE)) ||
 
243
                                ((smb_filter & FILE_NOTIFY_CHANGE_DIR_NAME) &&
 
244
                                 (ifs_filter & NOTE_DIRECTORY))) {
 
245
                                /* Check if this is a RENAME, not a MOVE */
 
246
                                if (ifs_filter & NOTE_RENAME_SAMEDIR) {
 
247
                                        /* Remove the NOTE_RENAME_SAMEDIR, IFF
 
248
                                         * RENAME_FROM is not in this event */
 
249
                                        if (!(ifs_filter & NOTE_RENAME_FROM))
 
250
                                                ifs_filter &=
 
251
                                                    ~NOTE_RENAME_SAMEDIR;
 
252
                                        return NOTIFY_ACTION_NEW_NAME;
 
253
                                }
 
254
                                return NOTIFY_ACTION_ADDED;
 
255
                        }
 
256
                        /* RAW-NOTIFY shows us that a rename triggers a
 
257
                         * creation time change */
 
258
                        if ((smb_filter & FILE_NOTIFY_CHANGE_CREATION) &&
 
259
                                (ifs_filter & NOTE_FILE))
 
260
                                return NOTIFY_ACTION_MODIFIED;
 
261
                }
 
262
                else if (ifs_filter & NOTE_SECURITY) {
 
263
                        ifs_filter &= ~NOTE_SECURITY;
 
264
 
 
265
                        if (smb_filter & FILE_NOTIFY_CHANGE_SECURITY)
 
266
                                return NOTIFY_ACTION_MODIFIED;
 
267
                } else {
 
268
                        /* No relevant flags found */
 
269
                        return 0;
 
270
                }
 
271
        }
 
272
}
 
273
 
 
274
/**
 
275
 * Retrieve a directory path of a changed file, relative to the watched dir
 
276
 *
 
277
 * @param[in] wc watch context for the returned event
 
278
 * @param[in] e ifs_event notification returned from the kernel
 
279
 * @param[out] path name relative to the watched dir. This is talloced
 
280
 *                  off of wc and needs to be freed by the caller.
 
281
 *
 
282
 * @return true on success
 
283
 *
 
284
 * TODO: This function currently doesn't handle path names with multiple
 
285
 * encodings.  enc_get_lin_path() should be used in the future to convert
 
286
 * each path segment's encoding to UTF-8
 
287
 */
 
288
static bool
 
289
get_ifs_event_path(struct onefs_notify_watch_context *wc, struct ifs_event *e,
 
290
                   char **path)
 
291
{
 
292
        char *path_buf = NULL;
 
293
        size_t pathlen = 0;
 
294
        int error = 0;
 
295
 
 
296
        SMB_ASSERT(path);
 
297
 
 
298
        /* Lookup the path from watch_dir through our parent dir */
 
299
        if (e->namelen > 0) {
 
300
                error = lin_get_path(wc->watch_lin,
 
301
                                     e->parent_lin,
 
302
                                     HEAD_SNAPID,
 
303
                                     e->parent_parent_lin,
 
304
                                     e->parent_name_hash,
 
305
                                     &pathlen, &path_buf);
 
306
                if (!error) {
 
307
                        /* Only add slash if a path exists in path_buf from
 
308
                         * lin_get_path call. Windows does not expect a
 
309
                         * leading '/' */
 
310
                        if (pathlen > 0)
 
311
                                *path = talloc_asprintf(wc, "%s/%s",
 
312
                                                        path_buf, e->name);
 
313
                        else
 
314
                                *path = talloc_asprintf(wc, "%s", e->name);
 
315
                        SAFE_FREE(path_buf);
 
316
                }
 
317
        }
 
318
 
 
319
        /* If ifs_event didn't return a name, or we errored out of our intial
 
320
         * path lookup, try again using the lin of the changed file */
 
321
        if (!(*path)) {
 
322
                error = lin_get_path(wc->watch_lin,
 
323
                                     e->lin,
 
324
                                     HEAD_SNAPID,
 
325
                                     e->parent_lin,
 
326
                                     e->name_hash,
 
327
                                     &pathlen, &path_buf);
 
328
                if (error) {
 
329
                        /* It's possible that both the lin and the parent lin
 
330
                         * are invalid (or not given) -- we will skip these
 
331
                         * events. */
 
332
                        DEBUG(3,("Path lookup failed. LINS are invalid: "
 
333
                                 "e->lin: 0x%llu, e->parent_lin: 0x%llu, "
 
334
                                 "e->parent_parent_lin: 0x%llu\n",
 
335
                                 e->lin, e->parent_lin, e->parent_parent_lin));
 
336
                        SAFE_FREE(path_buf);
 
337
                        return false;
 
338
                } else {
 
339
                        *path = talloc_asprintf(wc, "%s", path_buf);
 
340
                        DEBUG(5, ("Using path from event LIN = %s\n",
 
341
                            path_buf));
 
342
                        SAFE_FREE(path_buf);
 
343
                }
 
344
        }
 
345
 
 
346
        /* Replacement of UNIX slashes with WIN slashes is handled at a
 
347
         * higher layer. */
 
348
 
 
349
        return true;
 
350
}
 
351
 
 
352
/**
 
353
 * Dispatch one OneFS notify event to the general Samba code
 
354
 *
 
355
 * @param[in] wc watch context for the returned event
 
356
 * @param[in] e event returned from the kernel
 
357
 *
 
358
 * @return nothing
 
359
 */
 
360
static void
 
361
onefs_notify_dispatch(struct onefs_notify_watch_context *wc,
 
362
                      struct ifs_event *e)
 
363
{
 
364
        char *path = NULL;
 
365
        struct notify_event ne;
 
366
 
 
367
        DEBUG(5, ("Retrieved ifs event from kernel: lin=%#llx, ifs_events=%#x, "
 
368
                 "parent_lin=%#llx, namelen=%d, name=\"%s\"\n",
 
369
                 e->lin, e->events, e->parent_lin, e->namelen, e->name));
 
370
 
 
371
        /* Check validity of event returned from kernel */
 
372
        if (e->lin == 0) {
 
373
                /* The lin == 0 specifies 1 of 2 cases:
 
374
                 * 1) We are out of events. The kernel has a limited
 
375
                 *    amount (somewhere near 90000)
 
376
                 * 2) Split nodes have merged back and had data written
 
377
                 *    to them -- thus we've missed some of those events.  */
 
378
                DEBUG(3, ("We've missed some kernel ifs events!\n"));
 
379
 
 
380
                /* Alert higher level to the problem so it returns catch-all
 
381
                 * response to the client */
 
382
                ne.path = NULL;
 
383
                ne.action = 0;
 
384
                wc->callback(wc->ctx, wc->private_data, &ne);
 
385
        }
 
386
 
 
387
        if (e->lin == wc->watch_lin) {
 
388
                /* Windows doesn't report notifications on root
 
389
                 * watched directory */
 
390
                /* TODO: This should be abstracted out to the general layer
 
391
                 * instead of being handled in every notify provider */
 
392
                DEBUG(5, ("Skipping notification on root of the watched "
 
393
                         "path.\n"));
 
394
                return;
 
395
        }
 
396
 
 
397
        /* Retrieve the full path for the ifs event name */
 
398
        if(!get_ifs_event_path(wc, e, &path)) {
 
399
                DEBUG(3, ("Failed to convert the ifs_event lins to a path. "
 
400
                         "Skipping this event\n"));
 
401
                return;
 
402
        }
 
403
 
 
404
        if (!strncmp(path, ".ifsvar", 7)) {
 
405
                /* Skip notifications on file if its in ifs configuration
 
406
                 * directory */
 
407
                goto clean;
 
408
        }
 
409
 
 
410
        ne.path = path;
 
411
 
 
412
        /* Convert ifs event mask to an smb action mask */
 
413
        ne.action = onefs_notify_ifs_filter_to_smb_action(wc->smb_filter,
 
414
                                                          e->events);
 
415
 
 
416
        DEBUG(5, ("Converted smb_filter=%#x, ifs_events=%#x, to "
 
417
                  "ne.action = %d, for ne.path = %s\n",
 
418
                  wc->smb_filter, e->events, ne.action, ne.path));
 
419
 
 
420
        if (!ne.action)
 
421
                goto clean;
 
422
 
 
423
        /* Return notify_event to higher level */
 
424
        wc->callback(wc->ctx, wc->private_data, &ne);
 
425
 
 
426
        /* SMB expects a file rename/move to generate three actions, a
 
427
         * rename_from/delete on the from file, a rename_to/create on the to
 
428
         * file, and a modify for the rename_to file. If we have two separate
 
429
         * notifications registered for ATTRIBUTES and FILENAME, this case will
 
430
         * be handled by separate ifs_events in
 
431
         * onefs_notify_ifs_filter_to_smb_action(). If both bits are registered
 
432
         * in the same notification, we must send an extra MODIFIED action
 
433
         * here. */
 
434
        if ((wc->smb_filter & FILE_NOTIFY_CHANGE_ATTRIBUTES) &&
 
435
            (wc->smb_filter & FILE_NOTIFY_CHANGE_FILE_NAME) &&
 
436
            (e->events & NOTE_FILE) && (e->events & NOTE_RENAME_TO))
 
437
        {
 
438
                ne.action = NOTIFY_ACTION_MODIFIED;
 
439
                wc->callback(wc->ctx, wc->private_data, &ne);
 
440
        }
 
441
 
 
442
        /* FALLTHROUGH */
 
443
clean:
 
444
        talloc_free(path);
 
445
        return;
 
446
}
 
447
 
 
448
/**
 
449
 * Callback when the kernel has some events for us
 
450
 *
 
451
 * Read events off ifs event fd and pass them to our dispatch function
 
452
 *
 
453
 * @param ev context of all tevents registered in the smbd
 
454
 * @param fde tevent struct specific to one ifs event channel
 
455
 * @param flags tevent flags passed when we added our ifs event channel fd to
 
456
 *              the main loop
 
457
 * @param private_data onefs_notify_watch_context specific to this ifs event
 
458
 *                     channel
 
459
 *
 
460
 * @return nothing
 
461
 */
 
462
static void
 
463
onefs_notify_handler(struct event_context *ev,
 
464
                     struct fd_event *fde,
 
465
                     uint16_t flags,
 
466
                     void *private_data)
 
467
{
 
468
        struct onefs_notify_watch_context *wc = NULL;
 
469
        struct ifs_event ifs_events[ONEFS_IFS_EVENT_MAX_NUM];
 
470
        ssize_t nread = 0;
 
471
        int count = 0;
 
472
        int i = 0;
 
473
 
 
474
        wc = talloc_get_type(private_data, struct onefs_notify_watch_context);
 
475
 
 
476
        /* Read as many ifs events from the notify channel as we can */
 
477
        nread = sys_read(wc->ifs_event_fd, &ifs_events,
 
478
                         ONEFS_IFS_EVENT_MAX_BYTES);
 
479
        if (nread == 0) {
 
480
                DEBUG(0,("No data found while reading ifs event fd?!\n"));
 
481
                return;
 
482
        }
 
483
        if (nread < 0) {
 
484
                DEBUG(0,("Failed to read ifs event data: %s\n",
 
485
                        strerror(errno)));
 
486
                return;
 
487
        }
 
488
 
 
489
        count = nread / sizeof(struct ifs_event);
 
490
 
 
491
        DEBUG(5, ("Got %d notification events in %d bytes.\n", count, nread));
 
492
 
 
493
        /* Dispatch ifs_events one-at-a-time to higher level */
 
494
        for (i=0; i < count; i++) {
 
495
                onefs_notify_dispatch(wc, &ifs_events[i]);
 
496
        }
 
497
}
 
498
 
 
499
/**
 
500
 * Destroy the ifs event channel
 
501
 *
 
502
 * This is called from talloc_free() when the generic Samba notify layer frees
 
503
 * the onefs_notify_watch_context.
 
504
 *
 
505
 * @param[in] wc pointer to watch context which is being destroyed
 
506
 *
 
507
 * return 0 on success
 
508
*/
 
509
static int
 
510
onefs_watch_destructor(struct onefs_notify_watch_context *wc)
 
511
{
 
512
        /* The ifs_event_fd will re de-registered from the event loop by
 
513
         * another destructor triggered from the freeing of this wc */
 
514
        close(wc->ifs_event_fd);
 
515
        return 0;
 
516
}
 
517
 
 
518
/**
 
519
 * Register a single change notify watch request.
 
520
 *
 
521
 * Open an event listener on a directory to watch for modifications. This
 
522
 * channel is closed by a destructor when the caller calls talloc_free()
 
523
 * on *handle.
 
524
 *
 
525
 * @param[in] vfs_handle handle given to most VFS operations
 
526
 * @param[in] ctx context (conn and tevent) for this connection
 
527
 * @param[in] e filter and path of client's notify request
 
528
 * @param[in] callback function to call when file notification event is received
 
529
 *                     from the kernel, passing that event up to Samba's
 
530
 *                     generalized change notify layer
 
531
 * @param[in] private_data opaque data given to us by the general change notify
 
532
 *                         layer which must be returned in the callback function
 
533
 * @param[out] handle_p handle returned to generalized change notify layer used
 
534
 *                      to close the event channel when notification is
 
535
 *                      cancelled
 
536
 *
 
537
 * @return NT_STATUS_OK unless error
 
538
 */
 
539
NTSTATUS
 
540
onefs_notify_watch(vfs_handle_struct *vfs_handle,
 
541
                   struct sys_notify_context *ctx,
 
542
                   struct notify_entry *e,
 
543
                   void (*callback)(struct sys_notify_context *ctx,
 
544
                                    void *private_data,
 
545
                                    struct notify_event *ev),
 
546
                   void *private_data,
 
547
                   void *handle_p)
 
548
{
 
549
        int ifs_event_fd = -1;
 
550
        uint32_t ifs_filter = 0;
 
551
        uint32_t smb_filter = e->filter;
 
552
        bool watch_tree = !!e->subdir_filter;
 
553
        struct onefs_notify_watch_context *wc = NULL;
 
554
        void **handle = (void **)handle_p;
 
555
        SMB_STRUCT_STAT sbuf;
 
556
        NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
 
557
 
 
558
        /* Fallback to default Samba implementation if kernel CN is disabled */
 
559
        if (!lp_kernel_change_notify(vfs_handle->conn->params)) {
 
560
                (*handle) = NULL;
 
561
                return NT_STATUS_OK;
 
562
        }
 
563
 
 
564
        /* The OneFS open path should always give us a valid fd on a directory*/
 
565
        SMB_ASSERT(e->dir_fd >= 0);
 
566
 
 
567
        /* Always set e->filter to 0 so we don't fallback on the default change
 
568
         * notify backend. It's not cluster coherent or cross-protocol so we
 
569
         * can't guarantee correctness using it. */
 
570
        e->filter = 0;
 
571
        e->subdir_filter = 0;
 
572
 
 
573
        /* Snapshots do not currently allow event listeners. See Isilon
 
574
         * bug 33476 for an example of .snapshot debug spew that can occur. */
 
575
        if (e->dir_id.extid != HEAD_SNAPID)
 
576
                return NT_STATUS_INVALID_PARAMETER;
 
577
 
 
578
        /* Convert Completion Filter mask to IFS Event mask */
 
579
        ifs_filter = onefs_notify_smb_filter_to_ifs_filter(smb_filter);
 
580
 
 
581
        if (smb_filter & ONEFS_NOTIFY_UNSUPPORTED) {
 
582
                /* One or more of the filter bits could not be fully handled by
 
583
                 * the ifs_event system. To be correct, if we cannot service a
 
584
                 * bit in the completion filter we should return
 
585
                 * NT_STATUS_NOT_IMPLEMENTED to let the client know that they
 
586
                 * won't be receiving all the notify events that they asked for.
 
587
                 * Unfortunately, WinXP clients cache this error message, stop
 
588
                 * trying to send any notify requests at all, and instead return
 
589
                 * NOT_IMPLEMENTED to all requesting apps without ever sending a
 
590
                 * message to us. Thus we lie, and say we can service all bits,
 
591
                 * but simply don't return actions for the filter bits we can't
 
592
                 * detect or fully implement. */
 
593
                DEBUG(3,("One or more of the Windows completion filter bits "
 
594
                         "for \"%s\" could not be fully handled by the "
 
595
                         "ifs_event system. The failed bits are "
 
596
                         "smb_filter=%#x\n",
 
597
                         e->path, smb_filter & ONEFS_NOTIFY_UNSUPPORTED));
 
598
        }
 
599
 
 
600
        if (ifs_filter == 0) {
 
601
                /* None of the filter bits given are supported by the ifs_event
 
602
                 * system. Don't create a kernel notify channel, but mock
 
603
                 * up a fake handle for the caller. */
 
604
                DEBUG(3,("No bits in the Windows completion filter could be "
 
605
                         "translated to ifs_event mask for \"%s\", "
 
606
                         "smb_filter=%#x\n", e->path, smb_filter));
 
607
                (*handle) = NULL;
 
608
                return NT_STATUS_OK;
 
609
        }
 
610
 
 
611
        /* Register an ifs event channel for this watch request */
 
612
        ifs_event_fd = ifs_create_listener(watch_tree ?
 
613
                                           EVENT_RECURSIVE :
 
614
                                           EVENT_CHILDREN,
 
615
                                           ifs_filter,
 
616
                                           e->dir_fd);
 
617
        if (ifs_event_fd < 0) {
 
618
                DEBUG(0,("Failed to create listener for %s with \"%s\". "
 
619
                        "smb_filter=0x%x, ifs_filter=0x%x, watch_tree=%u\n",
 
620
                        strerror(errno), e->path, smb_filter, ifs_filter,
 
621
                        watch_tree));
 
622
                return map_nt_error_from_unix(errno);
 
623
        }
 
624
 
 
625
        /* Create a watch context to track this change notify request */
 
626
        wc = talloc(ctx, struct onefs_notify_watch_context);
 
627
        if (wc == NULL) {
 
628
                status = NT_STATUS_NO_MEMORY;
 
629
                goto err;
 
630
        }
 
631
 
 
632
        /* Get LIN for directory */
 
633
        if (sys_fstat(e->dir_fd, &sbuf)) {
 
634
                DEBUG(0, ("stat on directory fd failed: %s\n",
 
635
                          strerror(errno)));
 
636
                status = map_nt_error_from_unix(errno);
 
637
                goto err;
 
638
        }
 
639
 
 
640
        if (sbuf.st_ino == 0) {
 
641
                DEBUG(0, ("0 LIN found!\n"));
 
642
                goto err;
 
643
        }
 
644
 
 
645
        wc->ctx = ctx;
 
646
        wc->watch_fd = e->dir_fd;
 
647
        wc->watch_lin = sbuf.st_ino;
 
648
        wc->ifs_event_fd = ifs_event_fd;
 
649
        wc->ifs_filter = ifs_filter;
 
650
        wc->smb_filter = smb_filter;
 
651
        wc->callback = callback;
 
652
        wc->private_data = private_data;
 
653
        wc->path = talloc_strdup(wc, e->path);
 
654
        if (wc->path == NULL) {
 
655
                status = NT_STATUS_NO_MEMORY;
 
656
                goto err;
 
657
        }
 
658
 
 
659
        (*handle) = wc;
 
660
 
 
661
        /* The caller frees the handle to stop watching */
 
662
        talloc_set_destructor(wc, onefs_watch_destructor);
 
663
 
 
664
        /* Add a tevent waiting for the ifs event fd to be readable */
 
665
        event_add_fd(ctx->ev, wc, wc->ifs_event_fd, EVENT_FD_READ,
 
666
                     onefs_notify_handler, wc);
 
667
 
 
668
        DEBUG(10, ("Watching for changes on \"%s\" smb_filter=0x%x, "
 
669
                   "ifs_filter=0x%x, watch_tree=%d, ifs_event_fd=%d, "
 
670
                   "dir_fd=%d, dir_lin=0x%llx\n",
 
671
                   e->path, smb_filter, ifs_filter, watch_tree,
 
672
                   ifs_event_fd, e->dir_fd, sbuf.st_ino));
 
673
 
 
674
        return NT_STATUS_OK;
 
675
 
 
676
err:
 
677
        talloc_free(wc);
 
678
        SMB_ASSERT(ifs_event_fd >= 0);
 
679
        close(ifs_event_fd);
 
680
        return status;
 
681
}