root/source3/smbd/notify.c

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

DEFINITIONS

This source file includes following definitions.
  1. notify_change_record_identical
  2. notify_marshall_changes
  3. change_notify_reply_packet
  4. change_notify_reply
  5. notify_callback
  6. change_notify_create
  7. change_notify_add_request
  8. change_notify_remove_request
  9. remove_pending_change_notify_requests_by_mid
  10. remove_pending_change_notify_requests_by_fid
  11. notify_fname
  12. notify_fsp
  13. notify_filter_string
  14. sys_notify_context_create
  15. sys_notify_watch

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

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