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