root/source4/ntvfs/posix/pvfs_notify.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pvfs_notify_send_next
  2. pvfs_notify_send
  3. pvfs_notify_destructor
  4. pvfs_notify_callback
  5. pvfs_notify_setup
  6. pvfs_notify_end
  7. pvfs_notify

   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, 
     /* [<][>][^][v][top][bottom][index][help] */
  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, 
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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, 
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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, 
     /* [<][>][^][v][top][bottom][index][help] */
 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 }

/* [<][>][^][v][top][bottom][index][help] */