~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source3/smbd/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
   change notify handling
 
4
   Copyright (C) Andrew Tridgell 2000
 
5
   Copyright (C) Jeremy Allison 1994-1998
 
6
   Copyright (C) Volker Lendecke 2007
 
7
 
 
8
   This program is free software; you can redistribute it and/or modify
 
9
   it under the terms of the GNU General Public License as published by
 
10
   the Free Software Foundation; either version 3 of the License, or
 
11
   (at your option) any later version.
 
12
 
 
13
   This program is distributed in the hope that it will be useful,
 
14
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
   GNU General Public License for more details.
 
17
 
 
18
   You should have received a copy of the GNU General Public License
 
19
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
20
*/
 
21
 
 
22
#include "includes.h"
 
23
#include "smbd/globals.h"
 
24
 
 
25
struct notify_change_request {
 
26
        struct notify_change_request *prev, *next;
 
27
        struct files_struct *fsp;       /* backpointer for cancel by mid */
 
28
        struct smb_request *req;
 
29
        uint32 filter;
 
30
        uint32 max_param;
 
31
        struct notify_mid_map *mid_map;
 
32
        void *backend_data;
 
33
};
 
34
 
 
35
static void notify_fsp(files_struct *fsp, uint32 action, const char *name);
 
36
 
 
37
/*
 
38
 * For NTCancel, we need to find the notify_change_request indexed by
 
39
 * mid. Separate list here.
 
40
 */
 
41
 
 
42
struct notify_mid_map {
 
43
        struct notify_mid_map *prev, *next;
 
44
        struct notify_change_request *req;
 
45
        uint16 mid;
 
46
};
 
47
 
 
48
static bool notify_change_record_identical(struct notify_change *c1,
 
49
                                        struct notify_change *c2)
 
50
{
 
51
        /* Note this is deliberately case sensitive. */
 
52
        if (c1->action == c2->action &&
 
53
                        strcmp(c1->name, c2->name) == 0) {
 
54
                return True;
 
55
        }
 
56
        return False;
 
57
}
 
58
 
 
59
static bool notify_marshall_changes(int num_changes,
 
60
                                uint32 max_offset,
 
61
                                struct notify_change *changes,
 
62
                                prs_struct *ps)
 
63
{
 
64
        int i;
 
65
        UNISTR uni_name;
 
66
 
 
67
        uni_name.buffer = NULL;
 
68
 
 
69
        for (i=0; i<num_changes; i++) {
 
70
                struct notify_change *c;
 
71
                size_t namelen;
 
72
                uint32 u32_tmp; /* Temp arg to prs_uint32 to avoid
 
73
                                 * signed/unsigned issues */
 
74
 
 
75
                /* Coalesce any identical records. */
 
76
                while (i+1 < num_changes &&
 
77
                        notify_change_record_identical(&changes[i],
 
78
                                                &changes[i+1])) {
 
79
                        i++;
 
80
                }
 
81
 
 
82
                c = &changes[i];
 
83
 
 
84
                if (!convert_string_allocate(NULL, CH_UNIX, CH_UTF16LE,
 
85
                        c->name, strlen(c->name)+1, &uni_name.buffer,
 
86
                        &namelen, True) || (uni_name.buffer == NULL)) {
 
87
                        goto fail;
 
88
                }
 
89
 
 
90
                namelen -= 2;   /* Dump NULL termination */
 
91
 
 
92
                /*
 
93
                 * Offset to next entry, only if there is one
 
94
                 */
 
95
 
 
96
                u32_tmp = (i == num_changes-1) ? 0 : namelen + 12;
 
97
                if (!prs_uint32("offset", ps, 1, &u32_tmp)) goto fail;
 
98
 
 
99
                u32_tmp = c->action;
 
100
                if (!prs_uint32("action", ps, 1, &u32_tmp)) goto fail;
 
101
 
 
102
                u32_tmp = namelen;
 
103
                if (!prs_uint32("namelen", ps, 1, &u32_tmp)) goto fail;
 
104
 
 
105
                if (!prs_unistr("name", ps, 1, &uni_name)) goto fail;
 
106
 
 
107
                /*
 
108
                 * Not NULL terminated, decrease by the 2 UCS2 \0 chars
 
109
                 */
 
110
                prs_set_offset(ps, prs_offset(ps)-2);
 
111
 
 
112
                SAFE_FREE(uni_name.buffer);
 
113
 
 
114
                if (prs_offset(ps) > max_offset) {
 
115
                        /* Too much data for client. */
 
116
                        DEBUG(10, ("Client only wanted %d bytes, trying to "
 
117
                                   "marshall %d bytes\n", (int)max_offset,
 
118
                                   (int)prs_offset(ps)));
 
119
                        return False;
 
120
                }
 
121
        }
 
122
 
 
123
        return True;
 
124
 
 
125
 fail:
 
126
        SAFE_FREE(uni_name.buffer);
 
127
        return False;
 
128
}
 
129
 
 
130
/****************************************************************************
 
131
 Setup the common parts of the return packet and send it.
 
132
*****************************************************************************/
 
133
 
 
134
static void change_notify_reply_packet(connection_struct *conn,
 
135
                                       struct smb_request *req,
 
136
                                       NTSTATUS error_code)
 
137
{
 
138
        reply_outbuf(req, 18, 0);
 
139
 
 
140
        if (!NT_STATUS_IS_OK(error_code)) {
 
141
                error_packet_set((char *)req->outbuf, 0, 0, error_code,
 
142
                                 __LINE__,__FILE__);
 
143
        }
 
144
 
 
145
        show_msg((char *)req->outbuf);
 
146
        if (!srv_send_smb(smbd_server_fd(), (char *)req->outbuf,
 
147
                          req->encrypted, &req->pcd)) {
 
148
                exit_server_cleanly("change_notify_reply_packet: srv_send_smb "
 
149
                                    "failed.");
 
150
        }
 
151
        TALLOC_FREE(req->outbuf);
 
152
}
 
153
 
 
154
void change_notify_reply(connection_struct *conn,
 
155
                         struct smb_request *req, uint32 max_param,
 
156
                         struct notify_change_buf *notify_buf)
 
157
{
 
158
        prs_struct ps;
 
159
 
 
160
        if (notify_buf->num_changes == -1) {
 
161
                change_notify_reply_packet(conn, req, NT_STATUS_OK);
 
162
                notify_buf->num_changes = 0;
 
163
                return;
 
164
        }
 
165
 
 
166
        prs_init_empty(&ps, NULL, MARSHALL);
 
167
 
 
168
        if (!notify_marshall_changes(notify_buf->num_changes, max_param,
 
169
                                        notify_buf->changes, &ps)) {
 
170
                /*
 
171
                 * We exceed what the client is willing to accept. Send
 
172
                 * nothing.
 
173
                 */
 
174
                change_notify_reply_packet(conn, req, NT_STATUS_OK);
 
175
                goto done;
 
176
        }
 
177
 
 
178
        send_nt_replies(conn, req, NT_STATUS_OK, prs_data_p(&ps),
 
179
                        prs_offset(&ps), NULL, 0);
 
180
 
 
181
 done:
 
182
        prs_mem_free(&ps);
 
183
 
 
184
        TALLOC_FREE(notify_buf->changes);
 
185
        notify_buf->num_changes = 0;
 
186
}
 
187
 
 
188
static void notify_callback(void *private_data, const struct notify_event *e)
 
189
{
 
190
        files_struct *fsp = (files_struct *)private_data;
 
191
        DEBUG(10, ("notify_callback called for %s\n", fsp->fsp_name));
 
192
        notify_fsp(fsp, e->action, e->path);
 
193
}
 
194
 
 
195
NTSTATUS change_notify_create(struct files_struct *fsp, uint32 filter,
 
196
                              bool recursive)
 
197
{
 
198
        char *fullpath;
 
199
        struct notify_entry e;
 
200
        NTSTATUS status;
 
201
 
 
202
        SMB_ASSERT(fsp->notify == NULL);
 
203
 
 
204
        if (!(fsp->notify = TALLOC_ZERO_P(NULL, struct notify_change_buf))) {
 
205
                DEBUG(0, ("talloc failed\n"));
 
206
                return NT_STATUS_NO_MEMORY;
 
207
        }
 
208
 
 
209
        if (asprintf(&fullpath, "%s/%s", fsp->conn->connectpath,
 
210
                     fsp->fsp_name) == -1) {
 
211
                DEBUG(0, ("asprintf failed\n"));
 
212
                TALLOC_FREE(fsp->notify);
 
213
                return NT_STATUS_NO_MEMORY;
 
214
        }
 
215
 
 
216
        ZERO_STRUCT(e);
 
217
        e.path = fullpath;
 
218
        e.dir_fd = fsp->fh->fd;
 
219
        e.dir_id = fsp->file_id;
 
220
        e.filter = filter;
 
221
        e.subdir_filter = 0;
 
222
        if (recursive) {
 
223
                e.subdir_filter = filter;
 
224
        }
 
225
 
 
226
        status = notify_add(fsp->conn->notify_ctx, &e, notify_callback, fsp);
 
227
        SAFE_FREE(fullpath);
 
228
 
 
229
        return status;
 
230
}
 
231
 
 
232
NTSTATUS change_notify_add_request(struct smb_request *req,
 
233
                                uint32 max_param,
 
234
                                uint32 filter, bool recursive,
 
235
                                struct files_struct *fsp)
 
236
{
 
237
        struct notify_change_request *request = NULL;
 
238
        struct notify_mid_map *map = NULL;
 
239
 
 
240
        DEBUG(10, ("change_notify_add_request: Adding request for %s: "
 
241
                   "max_param = %d\n", fsp->fsp_name, (int)max_param));
 
242
 
 
243
        if (!(request = talloc(NULL, struct notify_change_request))
 
244
            || !(map = talloc(request, struct notify_mid_map))) {
 
245
                TALLOC_FREE(request);
 
246
                return NT_STATUS_NO_MEMORY;
 
247
        }
 
248
 
 
249
        request->mid_map = map;
 
250
        map->req = request;
 
251
 
 
252
        request->req = talloc_move(request, &req);
 
253
        request->max_param = max_param;
 
254
        request->filter = filter;
 
255
        request->fsp = fsp;
 
256
        request->backend_data = NULL;
 
257
 
 
258
        DLIST_ADD_END(fsp->notify->requests, request,
 
259
                      struct notify_change_request *);
 
260
 
 
261
        map->mid = request->req->mid;
 
262
        DLIST_ADD(notify_changes_by_mid, map);
 
263
 
 
264
        /* Push the MID of this packet on the signing queue. */
 
265
        srv_defer_sign_response(request->req->mid);
 
266
 
 
267
        return NT_STATUS_OK;
 
268
}
 
269
 
 
270
static void change_notify_remove_request(struct notify_change_request *remove_req)
 
271
{
 
272
        files_struct *fsp;
 
273
        struct notify_change_request *req;
 
274
 
 
275
        /*
 
276
         * Paranoia checks, the fsp referenced must must have the request in
 
277
         * its list of pending requests
 
278
         */
 
279
 
 
280
        fsp = remove_req->fsp;
 
281
        SMB_ASSERT(fsp->notify != NULL);
 
282
 
 
283
        for (req = fsp->notify->requests; req; req = req->next) {
 
284
                if (req == remove_req) {
 
285
                        break;
 
286
                }
 
287
        }
 
288
 
 
289
        if (req == NULL) {
 
290
                smb_panic("notify_req not found in fsp's requests");
 
291
        }
 
292
 
 
293
        DLIST_REMOVE(fsp->notify->requests, req);
 
294
        DLIST_REMOVE(notify_changes_by_mid, req->mid_map);
 
295
        TALLOC_FREE(req);
 
296
}
 
297
 
 
298
/****************************************************************************
 
299
 Delete entries by mid from the change notify pending queue. Always send reply.
 
300
*****************************************************************************/
 
301
 
 
302
void remove_pending_change_notify_requests_by_mid(uint16 mid)
 
303
{
 
304
        struct notify_mid_map *map;
 
305
 
 
306
        for (map = notify_changes_by_mid; map; map = map->next) {
 
307
                if (map->mid == mid) {
 
308
                        break;
 
309
                }
 
310
        }
 
311
 
 
312
        if (map == NULL) {
 
313
                return;
 
314
        }
 
315
 
 
316
        change_notify_reply_packet(map->req->fsp->conn, map->req->req,
 
317
                                   NT_STATUS_CANCELLED);
 
318
        change_notify_remove_request(map->req);
 
319
}
 
320
 
 
321
/****************************************************************************
 
322
 Delete entries by fnum from the change notify pending queue.
 
323
*****************************************************************************/
 
324
 
 
325
void remove_pending_change_notify_requests_by_fid(files_struct *fsp,
 
326
                                                  NTSTATUS status)
 
327
{
 
328
        if (fsp->notify == NULL) {
 
329
                return;
 
330
        }
 
331
 
 
332
        while (fsp->notify->requests != NULL) {
 
333
                change_notify_reply_packet(
 
334
                        fsp->conn, fsp->notify->requests->req, status);
 
335
                change_notify_remove_request(fsp->notify->requests);
 
336
        }
 
337
}
 
338
 
 
339
void notify_fname(connection_struct *conn, uint32 action, uint32 filter,
 
340
                  const char *path)
 
341
{
 
342
        char *fullpath;
 
343
 
 
344
        if (path[0] == '.' && path[1] == '/') {
 
345
                path += 2;
 
346
        }
 
347
        if (asprintf(&fullpath, "%s/%s", conn->connectpath, path) == -1) {
 
348
                DEBUG(0, ("asprintf failed\n"));
 
349
                return;
 
350
        }
 
351
 
 
352
        notify_trigger(conn->notify_ctx, action, filter, fullpath);
 
353
        SAFE_FREE(fullpath);
 
354
}
 
355
 
 
356
static void notify_fsp(files_struct *fsp, uint32 action, const char *name)
 
357
{
 
358
        struct notify_change *change, *changes;
 
359
        char *tmp;
 
360
 
 
361
        if (fsp->notify == NULL) {
 
362
                /*
 
363
                 * Nobody is waiting, don't queue
 
364
                 */
 
365
                return;
 
366
        }
 
367
 
 
368
        /*
 
369
         * Someone has triggered a notify previously, queue the change for
 
370
         * later.
 
371
         */
 
372
 
 
373
        if ((fsp->notify->num_changes > 1000) || (name == NULL)) {
 
374
                /*
 
375
                 * The real number depends on the client buf, just provide a
 
376
                 * guard against a DoS here.  If name == NULL the CN backend is
 
377
                 * alerting us to a problem.  Possibly dropped events.  Clear
 
378
                 * queued changes and send the catch-all response to the client
 
379
                 * if a request is pending.
 
380
                 */
 
381
                TALLOC_FREE(fsp->notify->changes);
 
382
                fsp->notify->num_changes = -1;
 
383
                if (fsp->notify->requests != NULL) {
 
384
                        change_notify_reply(fsp->conn,
 
385
                                            fsp->notify->requests->req,
 
386
                                            fsp->notify->requests->max_param,
 
387
                                            fsp->notify);
 
388
                        change_notify_remove_request(fsp->notify->requests);
 
389
                }
 
390
                return;
 
391
        }
 
392
 
 
393
        /* If we've exceeded the server side queue or received a NULL name
 
394
         * from the underlying CN implementation, don't queue up any more
 
395
         * requests until we can send a catch-all response to the client */
 
396
        if (fsp->notify->num_changes == -1) {
 
397
                return;
 
398
        }
 
399
 
 
400
        if (!(changes = TALLOC_REALLOC_ARRAY(
 
401
                      fsp->notify, fsp->notify->changes,
 
402
                      struct notify_change, fsp->notify->num_changes+1))) {
 
403
                DEBUG(0, ("talloc_realloc failed\n"));
 
404
                return;
 
405
        }
 
406
 
 
407
        fsp->notify->changes = changes;
 
408
 
 
409
        change = &(fsp->notify->changes[fsp->notify->num_changes]);
 
410
 
 
411
        if (!(tmp = talloc_strdup(changes, name))) {
 
412
                DEBUG(0, ("talloc_strdup failed\n"));
 
413
                return;
 
414
        }
 
415
 
 
416
        string_replace(tmp, '/', '\\');
 
417
        change->name = tmp;     
 
418
 
 
419
        change->action = action;
 
420
        fsp->notify->num_changes += 1;
 
421
 
 
422
        if (fsp->notify->requests == NULL) {
 
423
                /*
 
424
                 * Nobody is waiting, so don't send anything. The ot
 
425
                 */
 
426
                return;
 
427
        }
 
428
 
 
429
        if (action == NOTIFY_ACTION_OLD_NAME) {
 
430
                /*
 
431
                 * We have to send the two rename events in one reply. So hold
 
432
                 * the first part back.
 
433
                 */
 
434
                return;
 
435
        }
 
436
 
 
437
        /*
 
438
         * Someone is waiting for the change, trigger the reply immediately.
 
439
         *
 
440
         * TODO: do we have to walk the lists of requests pending?
 
441
         */
 
442
 
 
443
        change_notify_reply(fsp->conn,
 
444
                            fsp->notify->requests->req,
 
445
                            fsp->notify->requests->max_param,
 
446
                            fsp->notify);
 
447
 
 
448
        change_notify_remove_request(fsp->notify->requests);
 
449
}
 
450
 
 
451
char *notify_filter_string(TALLOC_CTX *mem_ctx, uint32 filter)
 
452
{
 
453
        char *result = NULL;
 
454
 
 
455
        result = talloc_strdup(mem_ctx, "");
 
456
 
 
457
        if (filter & FILE_NOTIFY_CHANGE_FILE_NAME)
 
458
                result = talloc_asprintf_append(result, "FILE_NAME|");
 
459
        if (filter & FILE_NOTIFY_CHANGE_DIR_NAME)
 
460
                result = talloc_asprintf_append(result, "DIR_NAME|");
 
461
        if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)
 
462
                result = talloc_asprintf_append(result, "ATTRIBUTES|");
 
463
        if (filter & FILE_NOTIFY_CHANGE_SIZE)
 
464
                result = talloc_asprintf_append(result, "SIZE|");
 
465
        if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE)
 
466
                result = talloc_asprintf_append(result, "LAST_WRITE|");
 
467
        if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS)
 
468
                result = talloc_asprintf_append(result, "LAST_ACCESS|");
 
469
        if (filter & FILE_NOTIFY_CHANGE_CREATION)
 
470
                result = talloc_asprintf_append(result, "CREATION|");
 
471
        if (filter & FILE_NOTIFY_CHANGE_EA)
 
472
                result = talloc_asprintf_append(result, "EA|");
 
473
        if (filter & FILE_NOTIFY_CHANGE_SECURITY)
 
474
                result = talloc_asprintf_append(result, "SECURITY|");
 
475
        if (filter & FILE_NOTIFY_CHANGE_STREAM_NAME)
 
476
                result = talloc_asprintf_append(result, "STREAM_NAME|");
 
477
        if (filter & FILE_NOTIFY_CHANGE_STREAM_SIZE)
 
478
                result = talloc_asprintf_append(result, "STREAM_SIZE|");
 
479
        if (filter & FILE_NOTIFY_CHANGE_STREAM_WRITE)
 
480
                result = talloc_asprintf_append(result, "STREAM_WRITE|");
 
481
 
 
482
        if (result == NULL) return NULL;
 
483
        if (*result == '\0') return result;
 
484
 
 
485
        result[strlen(result)-1] = '\0';
 
486
        return result;
 
487
}
 
488
 
 
489
struct sys_notify_context *sys_notify_context_create(connection_struct *conn,
 
490
                                                     TALLOC_CTX *mem_ctx, 
 
491
                                                     struct event_context *ev)
 
492
{
 
493
        struct sys_notify_context *ctx;
 
494
 
 
495
        if (!(ctx = TALLOC_P(mem_ctx, struct sys_notify_context))) {
 
496
                DEBUG(0, ("talloc failed\n"));
 
497
                return NULL;
 
498
        }
 
499
 
 
500
        ctx->ev = ev;
 
501
        ctx->conn = conn;
 
502
        ctx->private_data = NULL;
 
503
        return ctx;
 
504
}
 
505
 
 
506
NTSTATUS sys_notify_watch(struct sys_notify_context *ctx,
 
507
                          struct notify_entry *e,
 
508
                          void (*callback)(struct sys_notify_context *ctx, 
 
509
                                           void *private_data,
 
510
                                           struct notify_event *ev),
 
511
                          void *private_data, void *handle)
 
512
{
 
513
        return SMB_VFS_NOTIFY_WATCH(ctx->conn, ctx, e, callback, private_data,
 
514
                                    handle);
 
515
}
 
516