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 2 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, write to the Free Software 20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21*/ 22 23#include "includes.h" 24 25struct notify_change_request { 26 struct notify_change_request *prev, *next; 27 struct files_struct *fsp; /* backpointer for cancel by mid */ 28 char request_buf[smb_size]; 29 uint32 filter; 30 uint32 current_bufsize; 31 struct notify_mid_map *mid_map; 32 void *backend_data; 33}; 34 35static void notify_fsp(files_struct *fsp, uint32 action, const char *name); 36 37static struct notify_mid_map *notify_changes_by_mid; 38 39/* 40 * For NTCancel, we need to find the notify_change_request indexed by 41 * mid. Separate list here. 42 */ 43 44struct notify_mid_map { 45 struct notify_mid_map *prev, *next; 46 struct notify_change_request *req; 47 uint16 mid; 48}; 49 50static BOOL notify_marshall_changes(int num_changes, 51 struct notify_change *changes, 52 prs_struct *ps) 53{ 54 int i; 55 UNISTR uni_name; 56 57 for (i=0; i<num_changes; i++) { 58 struct notify_change *c = &changes[i]; 59 size_t namelen; 60 uint32 u32_tmp; /* Temp arg to prs_uint32 to avoid 61 * signed/unsigned issues */ 62 63 namelen = convert_string_allocate( 64 NULL, CH_UNIX, CH_UTF16LE, c->name, strlen(c->name)+1, 65 &uni_name.buffer, True); 66 if ((namelen == -1) || (uni_name.buffer == NULL)) { 67 goto fail; 68 } 69 70 namelen -= 2; /* Dump NULL termination */ 71 72 /* 73 * Offset to next entry, only if there is one 74 */ 75 76 u32_tmp = (i == num_changes-1) ? 0 : namelen + 12; 77 if (!prs_uint32("offset", ps, 1, &u32_tmp)) goto fail; 78 79 u32_tmp = c->action; 80 if (!prs_uint32("action", ps, 1, &u32_tmp)) goto fail; 81 82 u32_tmp = namelen; 83 if (!prs_uint32("namelen", ps, 1, &u32_tmp)) goto fail; 84 85 if (!prs_unistr("name", ps, 1, &uni_name)) goto fail; 86 87 /* 88 * Not NULL terminated, decrease by the 2 UCS2 \0 chars 89 */ 90 prs_set_offset(ps, prs_offset(ps)-2); 91 92 SAFE_FREE(uni_name.buffer); 93 } 94 95 return True; 96 97 fail: 98 SAFE_FREE(uni_name.buffer); 99 return False; 100} 101 102/**************************************************************************** 103 Setup the common parts of the return packet and send it. 104*****************************************************************************/ 105 106static void change_notify_reply_packet(const char *request_buf, 107 NTSTATUS error_code) 108{ 109 char outbuf[smb_size+38]; 110 111 memset(outbuf, '\0', sizeof(outbuf)); 112 construct_reply_common(request_buf, outbuf); 113 114 ERROR_NT(error_code); 115 116 /* 117 * Seems NT needs a transact command with an error code 118 * in it. This is a longer packet than a simple error. 119 */ 120 set_message(outbuf,18,0,False); 121 122 show_msg(outbuf); 123 if (!send_smb(smbd_server_fd(),outbuf)) 124 exit_server_cleanly("change_notify_reply_packet: send_smb " 125 "failed."); 126} 127 128void change_notify_reply(const char *request_buf, 129 struct notify_change_buf *notify_buf) 130{ 131 char *outbuf = NULL; 132 prs_struct ps; 133 size_t buflen; 134 135 if (notify_buf->num_changes == -1) { 136 change_notify_reply_packet(request_buf, NT_STATUS_OK); 137 return; 138 } 139 140 if (!prs_init(&ps, 0, NULL, False) 141 || !notify_marshall_changes(notify_buf->num_changes, 142 notify_buf->changes, &ps)) { 143 change_notify_reply_packet(request_buf, NT_STATUS_NO_MEMORY); 144 goto done; 145 } 146 147 buflen = smb_size+38+prs_offset(&ps) + 4 /* padding */; 148 149 if (!(outbuf = SMB_MALLOC_ARRAY(char, buflen))) { 150 change_notify_reply_packet(request_buf, NT_STATUS_NO_MEMORY); 151 goto done; 152 } 153 154 construct_reply_common(request_buf, outbuf); 155 156 if (send_nt_replies(outbuf, buflen, NT_STATUS_OK, prs_data_p(&ps), 157 prs_offset(&ps), NULL, 0) == -1) { 158 exit_server("change_notify_reply_packet: send_smb failed."); 159 } 160 161 done: 162 SAFE_FREE(outbuf); 163 prs_mem_free(&ps); 164 165 TALLOC_FREE(notify_buf->changes); 166 notify_buf->num_changes = 0; 167} 168 169static void notify_callback(void *private_data, const struct notify_event *e) 170{ 171 files_struct *fsp = (files_struct *)private_data; 172 DEBUG(10, ("notify_callback called for %s\n", fsp->fsp_name)); 173 notify_fsp(fsp, e->action, e->path); 174} 175 176NTSTATUS change_notify_create(struct files_struct *fsp, uint32 filter, 177 BOOL recursive) 178{ 179 char *fullpath; 180 struct notify_entry e; 181 NTSTATUS status; 182 183 SMB_ASSERT(fsp->notify == NULL); 184 185 if (!(fsp->notify = TALLOC_ZERO_P(NULL, struct notify_change_buf))) { 186 DEBUG(0, ("talloc failed\n")); 187 return NT_STATUS_NO_MEMORY; 188 } 189 190 if (asprintf(&fullpath, "%s/%s", fsp->conn->connectpath, 191 fsp->fsp_name) == -1) { 192 DEBUG(0, ("asprintf failed\n")); 193 return NT_STATUS_NO_MEMORY; 194 } 195 196 e.path = fullpath; 197 e.filter = filter; 198 e.subdir_filter = 0; 199 if (recursive) { 200 e.subdir_filter = filter; 201 } 202 203 status = notify_add(fsp->conn->notify_ctx, &e, notify_callback, fsp); 204 SAFE_FREE(fullpath); 205 206 return status; 207} 208 209NTSTATUS change_notify_add_request(const char *inbuf, 210 uint32 filter, BOOL recursive, 211 struct files_struct *fsp) 212{ 213 struct notify_change_request *request = NULL; 214 struct notify_mid_map *map = NULL; 215 216 if (!(request = SMB_MALLOC_P(struct notify_change_request)) 217 || !(map = SMB_MALLOC_P(struct notify_mid_map))) { 218 SAFE_FREE(request); 219 return NT_STATUS_NO_MEMORY; 220 } 221 222 request->mid_map = map; 223 map->req = request; 224 225 memcpy(request->request_buf, inbuf, sizeof(request->request_buf)); 226 request->current_bufsize = 0; 227 request->filter = filter; 228 request->fsp = fsp; 229 request->backend_data = NULL; 230 231 DLIST_ADD_END(fsp->notify->requests, request, 232 struct notify_change_request *); 233 234 map->mid = SVAL(inbuf, smb_mid); 235 DLIST_ADD(notify_changes_by_mid, map); 236 237 /* Push the MID of this packet on the signing queue. */ 238 srv_defer_sign_response(SVAL(inbuf,smb_mid)); 239 240 return NT_STATUS_OK; 241} 242 243static void change_notify_remove_request(struct notify_change_request *remove_req) 244{ 245 files_struct *fsp; 246 struct notify_change_request *req; 247 248 /* 249 * Paranoia checks, the fsp referenced must must have the request in 250 * its list of pending requests 251 */ 252 253 fsp = remove_req->fsp; 254 SMB_ASSERT(fsp->notify != NULL); 255 256 for (req = fsp->notify->requests; req; req = req->next) { 257 if (req == remove_req) { 258 break; 259 } 260 } 261 262 if (req == NULL) { 263 smb_panic("notify_req not found in fsp's requests\n"); 264 } 265 266 DLIST_REMOVE(fsp->notify->requests, req); 267 DLIST_REMOVE(notify_changes_by_mid, req->mid_map); 268 SAFE_FREE(req->mid_map); 269 TALLOC_FREE(req->backend_data); 270 SAFE_FREE(req); 271} 272 273/**************************************************************************** 274 Delete entries by mid from the change notify pending queue. Always send reply. 275*****************************************************************************/ 276 277void remove_pending_change_notify_requests_by_mid(uint16 mid) 278{ 279 struct notify_mid_map *map; 280 281 for (map = notify_changes_by_mid; map; map = map->next) { 282 if (map->mid == mid) { 283 break; 284 } 285 } 286 287 if (map == NULL) { 288 return; 289 } 290 291 change_notify_reply_packet(map->req->request_buf, NT_STATUS_CANCELLED); 292 change_notify_remove_request(map->req); 293} 294 295/**************************************************************************** 296 Delete entries by fnum from the change notify pending queue. 297*****************************************************************************/ 298 299void remove_pending_change_notify_requests_by_fid(files_struct *fsp, 300 NTSTATUS status) 301{ 302 if (fsp->notify == NULL) { 303 return; 304 } 305 306 while (fsp->notify->requests != NULL) { 307 change_notify_reply_packet( 308 fsp->notify->requests->request_buf, status); 309 change_notify_remove_request(fsp->notify->requests); 310 } 311} 312 313void notify_fname(connection_struct *conn, uint32 action, uint32 filter, 314 const char *path) 315{ 316 char *fullpath; 317 318 if (asprintf(&fullpath, "%s/%s", conn->connectpath, path) == -1) { 319 DEBUG(0, ("asprintf failed\n")); 320 return; 321 } 322 323 notify_trigger(conn->notify_ctx, action, filter, fullpath); 324 SAFE_FREE(fullpath); 325} 326 327static void notify_fsp(files_struct *fsp, uint32 action, const char *name) 328{ 329 struct notify_change *change, *changes; 330 pstring name2; 331 332 if (fsp->notify == NULL) { 333 /* 334 * Nobody is waiting, don't queue 335 */ 336 return; 337 } 338 339 pstrcpy(name2, name); 340 string_replace(name2, '/', '\\'); 341 342 /* 343 * Someone has triggered a notify previously, queue the change for 344 * later. 345 */ 346 347 if ((fsp->notify->num_changes > 1000) || (name == NULL)) { 348 /* 349 * The real number depends on the client buf, just provide a 350 * guard against a DoS here. 351 */ 352 TALLOC_FREE(fsp->notify->changes); 353 fsp->notify->num_changes = -1; 354 return; 355 } 356 357 if (fsp->notify->num_changes == -1) { 358 return; 359 } 360 361 if (!(changes = TALLOC_REALLOC_ARRAY( 362 fsp->notify, fsp->notify->changes, 363 struct notify_change, fsp->notify->num_changes+1))) { 364 DEBUG(0, ("talloc_realloc failed\n")); 365 return; 366 } 367 368 fsp->notify->changes = changes; 369 370 change = &(fsp->notify->changes[fsp->notify->num_changes]); 371 372 if (!(change->name = talloc_strdup(changes, name2))) { 373 DEBUG(0, ("talloc_strdup failed\n")); 374 return; 375 } 376 377 change->action = action; 378 fsp->notify->num_changes += 1; 379 380 if (fsp->notify->requests == NULL) { 381 /* 382 * Nobody is waiting, so don't send anything. The ot 383 */ 384 return; 385 } 386 387 if (action == NOTIFY_ACTION_OLD_NAME) { 388 /* 389 * We have to send the two rename events in one reply. So hold 390 * the first part back. 391 */ 392 return; 393 } 394 395 /* 396 * Someone is waiting for the change, trigger the reply immediately. 397 * 398 * TODO: do we have to walk the lists of requests pending? 399 */ 400 401 change_notify_reply(fsp->notify->requests->request_buf, 402 fsp->notify); 403 404 change_notify_remove_request(fsp->notify->requests); 405} 406 407char *notify_filter_string(TALLOC_CTX *mem_ctx, uint32 filter) 408{ 409 char *result = NULL; 410 411 result = talloc_strdup(mem_ctx, ""); 412 413 if (filter & FILE_NOTIFY_CHANGE_FILE_NAME) 414 result = talloc_asprintf_append(result, "FILE_NAME|"); 415 if (filter & FILE_NOTIFY_CHANGE_DIR_NAME) 416 result = talloc_asprintf_append(result, "DIR_NAME|"); 417 if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES) 418 result = talloc_asprintf_append(result, "ATTRIBUTES|"); 419 if (filter & FILE_NOTIFY_CHANGE_SIZE) 420 result = talloc_asprintf_append(result, "SIZE|"); 421 if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE) 422 result = talloc_asprintf_append(result, "LAST_WRITE|"); 423 if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS) 424 result = talloc_asprintf_append(result, "LAST_ACCESS|"); 425 if (filter & FILE_NOTIFY_CHANGE_CREATION) 426 result = talloc_asprintf_append(result, "CREATION|"); 427 if (filter & FILE_NOTIFY_CHANGE_EA) 428 result = talloc_asprintf_append(result, "EA|"); 429 if (filter & FILE_NOTIFY_CHANGE_SECURITY) 430 result = talloc_asprintf_append(result, "SECURITY|"); 431 if (filter & FILE_NOTIFY_CHANGE_STREAM_NAME) 432 result = talloc_asprintf_append(result, "STREAM_NAME|"); 433 if (filter & FILE_NOTIFY_CHANGE_STREAM_SIZE) 434 result = talloc_asprintf_append(result, "STREAM_SIZE|"); 435 if (filter & FILE_NOTIFY_CHANGE_STREAM_WRITE) 436 result = talloc_asprintf_append(result, "STREAM_WRITE|"); 437 438 if (result == NULL) return NULL; 439 if (*result == '\0') return result; 440 441 result[strlen(result)-1] = '\0'; 442 return result; 443} 444 445struct sys_notify_context *sys_notify_context_create(connection_struct *conn, 446 TALLOC_CTX *mem_ctx, 447 struct event_context *ev) 448{ 449 struct sys_notify_context *ctx; 450 451 if (!(ctx = TALLOC_P(mem_ctx, struct sys_notify_context))) { 452 DEBUG(0, ("talloc failed\n")); 453 return NULL; 454 } 455 456 ctx->ev = ev; 457 ctx->conn = conn; 458 ctx->private_data = NULL; 459 return ctx; 460} 461 462NTSTATUS sys_notify_watch(struct sys_notify_context *ctx, 463 struct notify_entry *e, 464 void (*callback)(struct sys_notify_context *ctx, 465 void *private_data, 466 struct notify_event *ev), 467 void *private_data, void *handle) 468{ 469 return SMB_VFS_NOTIFY_WATCH(ctx->conn, ctx, e, callback, private_data, 470 handle); 471} 472 473