~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source4/ntvfs/posix/pvfs_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
   POSIX NTVFS backend - notify
 
5
 
 
6
   Copyright (C) Andrew Tridgell 2006
 
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 "vfs_posix.h"
 
24
#include "lib/messaging/irpc.h"
 
25
#include "messaging/messaging.h"
 
26
#include "../lib/util/dlinklist.h"
 
27
#include "lib/events/events.h"
 
28
 
 
29
/* pending notifies buffer, hung off struct pvfs_file for open directories
 
30
   that have used change notify */
 
31
struct pvfs_notify_buffer {
 
32
        struct pvfs_file *f;
 
33
        uint32_t num_changes;
 
34
        struct notify_changes *changes;
 
35
        uint32_t max_buffer_size;
 
36
        uint32_t current_buffer_size;
 
37
 
 
38
        /* a list of requests waiting for events on this handle */
 
39
        struct notify_pending {
 
40
                struct notify_pending *next, *prev;
 
41
                struct ntvfs_request *req;
 
42
                union smb_notify *info;
 
43
        } *pending;
 
44
};
 
45
 
 
46
/*
 
47
  send a notify on the next event run. 
 
48
*/
 
49
static void pvfs_notify_send_next(struct tevent_context *ev, struct tevent_timer *te, 
 
50
                                  struct timeval t, void *ptr)
 
51
{
 
52
        struct ntvfs_request *req = talloc_get_type(ptr, struct ntvfs_request);
 
53
        req->async_states->send_fn(req);
 
54
}
 
55
 
 
56
 
 
57
/*
 
58
  send a reply to a pending notify request
 
59
*/
 
60
static void pvfs_notify_send(struct pvfs_notify_buffer *notify_buffer, 
 
61
                             NTSTATUS status, bool immediate)
 
62
{
 
63
        struct notify_pending *pending = notify_buffer->pending;
 
64
        struct ntvfs_request *req;
 
65
        union smb_notify *info;
 
66
 
 
67
        if (notify_buffer->current_buffer_size > notify_buffer->max_buffer_size && 
 
68
            notify_buffer->num_changes != 0) {
 
69
                /* on buffer overflow return no changes and destroys the notify buffer */
 
70
                notify_buffer->num_changes = 0;
 
71
                while (notify_buffer->pending) {
 
72
                        pvfs_notify_send(notify_buffer, NT_STATUS_OK, immediate);
 
73
                }
 
74
                talloc_free(notify_buffer);
 
75
                return;
 
76
        }
 
77
 
 
78
        /* see if there is anyone waiting */
 
79
        if (notify_buffer->pending == NULL) {
 
80
                return;
 
81
        }
 
82
 
 
83
        DLIST_REMOVE(notify_buffer->pending, pending);
 
84
 
 
85
        req = pending->req;
 
86
        info = pending->info;
 
87
 
 
88
        info->nttrans.out.num_changes = notify_buffer->num_changes;
 
89
        info->nttrans.out.changes = talloc_steal(req, notify_buffer->changes);
 
90
        notify_buffer->num_changes = 0;
 
91
        notify_buffer->changes = NULL;
 
92
        notify_buffer->current_buffer_size = 0;
 
93
 
 
94
        talloc_free(pending);
 
95
 
 
96
        if (info->nttrans.out.num_changes != 0) {
 
97
                status = NT_STATUS_OK;
 
98
        }
 
99
 
 
100
        req->async_states->status = status;
 
101
 
 
102
        if (immediate) {
 
103
                req->async_states->send_fn(req);
 
104
                return;
 
105
        } 
 
106
 
 
107
        /* we can't call pvfs_notify_send() directly here, as that
 
108
           would free the request, and the ntvfs modules above us
 
109
           could use it, so call it on the next event */
 
110
        event_add_timed(req->ctx->event_ctx, 
 
111
                        req, timeval_zero(), pvfs_notify_send_next, req);
 
112
}
 
113
 
 
114
/*
 
115
  destroy a notify buffer. Called when the handle is closed
 
116
 */
 
117
static int pvfs_notify_destructor(struct pvfs_notify_buffer *n)
 
118
{
 
119
        notify_remove(n->f->pvfs->notify_context, n);
 
120
        n->f->notify_buffer = NULL;
 
121
        pvfs_notify_send(n, NT_STATUS_OK, true);
 
122
        return 0;
 
123
}
 
124
 
 
125
 
 
126
/*
 
127
  called when a async notify event comes in
 
128
*/
 
129
static void pvfs_notify_callback(void *private_data, const struct notify_event *ev)
 
130
{
 
131
        struct pvfs_notify_buffer *n = talloc_get_type(private_data, struct pvfs_notify_buffer);
 
132
        size_t len;
 
133
        struct notify_changes *n2;
 
134
        char *new_path;
 
135
 
 
136
        n2 = talloc_realloc(n, n->changes, struct notify_changes, n->num_changes+1);
 
137
        if (n2 == NULL) {
 
138
                /* nothing much we can do for this */
 
139
                return;
 
140
        }
 
141
        n->changes = n2;
 
142
 
 
143
        new_path = talloc_strdup(n->changes, ev->path);
 
144
        if (new_path == NULL) {
 
145
                return;
 
146
        }
 
147
        string_replace(new_path, '/', '\\');
 
148
 
 
149
        n->changes[n->num_changes].action = ev->action;
 
150
        n->changes[n->num_changes].name.s = new_path;
 
151
        n->num_changes++;
 
152
 
 
153
        /*
 
154
          work out how much room this will take in the buffer
 
155
        */
 
156
        len = 12 + strlen_m(ev->path)*2;
 
157
        if (len & 3) {
 
158
                len += 4 - (len & 3);
 
159
        }
 
160
        n->current_buffer_size += len;
 
161
 
 
162
        /* send what we have, unless its the first part of a rename */
 
163
        if (ev->action != NOTIFY_ACTION_OLD_NAME) {
 
164
                pvfs_notify_send(n, NT_STATUS_OK, true);
 
165
        }
 
166
}
 
167
 
 
168
/*
 
169
  setup a notify buffer on a directory handle
 
170
*/
 
171
static NTSTATUS pvfs_notify_setup(struct pvfs_state *pvfs, struct pvfs_file *f, 
 
172
                                  uint32_t buffer_size, uint32_t filter, bool recursive)
 
173
{
 
174
        NTSTATUS status;
 
175
        struct notify_entry e;
 
176
 
 
177
        f->notify_buffer = talloc_zero(f, struct pvfs_notify_buffer);
 
178
        NT_STATUS_HAVE_NO_MEMORY(f->notify_buffer);
 
179
 
 
180
        f->notify_buffer->max_buffer_size = buffer_size;
 
181
        f->notify_buffer->f = f;
 
182
 
 
183
        e.filter    = filter;
 
184
        e.path      = f->handle->name->full_name;
 
185
        if (recursive) {
 
186
                e.subdir_filter = filter;
 
187
        } else {
 
188
                e.subdir_filter = 0;
 
189
        }
 
190
 
 
191
        status = notify_add(pvfs->notify_context, &e, 
 
192
                            pvfs_notify_callback, f->notify_buffer);
 
193
        NT_STATUS_NOT_OK_RETURN(status);
 
194
 
 
195
        talloc_set_destructor(f->notify_buffer, pvfs_notify_destructor);
 
196
 
 
197
        return NT_STATUS_OK;
 
198
}
 
199
 
 
200
/*
 
201
  called from the pvfs_wait code when either an event has come in, or
 
202
  the notify request has been cancelled
 
203
*/
 
204
static void pvfs_notify_end(void *private_data, enum pvfs_wait_notice reason)
 
205
{
 
206
        struct pvfs_notify_buffer *notify_buffer = talloc_get_type(private_data,
 
207
                                                                   struct pvfs_notify_buffer);
 
208
        if (reason == PVFS_WAIT_CANCEL) {
 
209
                pvfs_notify_send(notify_buffer, NT_STATUS_CANCELLED, false);
 
210
        } else {
 
211
                pvfs_notify_send(notify_buffer, NT_STATUS_OK, true);
 
212
        }
 
213
}
 
214
 
 
215
/* change notify request - always async. This request blocks until the
 
216
   event buffer is non-empty */
 
217
NTSTATUS pvfs_notify(struct ntvfs_module_context *ntvfs, 
 
218
                     struct ntvfs_request *req,
 
219
                     union smb_notify *info)
 
220
{
 
221
        struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data, 
 
222
                                                  struct pvfs_state);
 
223
        struct pvfs_file *f;
 
224
        NTSTATUS status;
 
225
        struct notify_pending *pending;
 
226
 
 
227
        if (info->nttrans.level != RAW_NOTIFY_NTTRANS) {
 
228
                return ntvfs_map_notify(ntvfs, req, info);
 
229
        }
 
230
 
 
231
        f = pvfs_find_fd(pvfs, req, info->nttrans.in.file.ntvfs);
 
232
        if (!f) {
 
233
                return NT_STATUS_INVALID_HANDLE;
 
234
        }
 
235
 
 
236
        /* this request doesn't make sense unless its async */
 
237
        if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
 
238
                return NT_STATUS_INVALID_PARAMETER;
 
239
        }
 
240
 
 
241
        /* its only valid for directories */
 
242
        if (f->handle->fd != -1) {
 
243
                return NT_STATUS_INVALID_PARAMETER;
 
244
        }
 
245
 
 
246
        /* if the handle doesn't currently have a notify buffer then
 
247
           create one */
 
248
        if (f->notify_buffer == NULL) {
 
249
                status = pvfs_notify_setup(pvfs, f, 
 
250
                                           info->nttrans.in.buffer_size, 
 
251
                                           info->nttrans.in.completion_filter,
 
252
                                           info->nttrans.in.recursive);
 
253
                NT_STATUS_NOT_OK_RETURN(status);
 
254
        }
 
255
 
 
256
        /* we update the max_buffer_size on each call, but we do not
 
257
           update the recursive flag or filter */
 
258
        f->notify_buffer->max_buffer_size = info->nttrans.in.buffer_size;
 
259
 
 
260
        pending = talloc(f->notify_buffer, struct notify_pending);
 
261
        NT_STATUS_HAVE_NO_MEMORY(pending);
 
262
 
 
263
        pending->req = talloc_reference(pending, req);
 
264
        NT_STATUS_HAVE_NO_MEMORY(pending->req); 
 
265
        pending->info = info;
 
266
 
 
267
        DLIST_ADD_END(f->notify_buffer->pending, pending, struct notify_pending *);
 
268
 
 
269
        /* if the buffer is empty then start waiting */
 
270
        if (f->notify_buffer->num_changes == 0) {
 
271
                struct pvfs_wait *wait_handle;
 
272
                wait_handle = pvfs_wait_message(pvfs, req, -1,
 
273
                                                timeval_zero(),
 
274
                                                pvfs_notify_end,
 
275
                                                f->notify_buffer);
 
276
                NT_STATUS_HAVE_NO_MEMORY(wait_handle);
 
277
                talloc_steal(req, wait_handle);
 
278
                return NT_STATUS_OK;
 
279
        }
 
280
 
 
281
        req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
 
282
        pvfs_notify_send(f->notify_buffer, NT_STATUS_OK, false);
 
283
 
 
284
        return NT_STATUS_OK;
 
285
}