LCOV - code coverage report
Current view: top level - source4/ntvfs/posix - pvfs_notify.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 106 124 85.5 %
Date: 2024-04-21 15:09:00 Functions: 7 7 100.0 %

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

Generated by: LCOV version 1.14