LCOV - code coverage report
Current view: top level - source4/ntvfs/common - notify.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 206 314 65.6 %
Date: 2024-04-21 15:09:00 Functions: 14 15 93.3 %

          Line data    Source code
       1             : /* 
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    Copyright (C) Andrew Tridgell 2006
       5             :    
       6             :    This program is free software; you can redistribute it and/or modify
       7             :    it under the terms of the GNU General Public License as published by
       8             :    the Free Software Foundation; either version 3 of the License, or
       9             :    (at your option) any later version.
      10             :    
      11             :    This program is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU General Public License for more details.
      15             :    
      16             :    You should have received a copy of the GNU General Public License
      17             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18             : */
      19             : 
      20             : /*
      21             :   this is the change notify database. It implements mechanisms for
      22             :   storing current change notify waiters in a tdb, and checking if a
      23             :   given event matches any of the stored notify waiiters.
      24             : */
      25             : 
      26             : #include "includes.h"
      27             : #include "system/filesys.h"
      28             : #include "messaging/messaging.h"
      29             : #include "lib/messaging/irpc.h"
      30             : #include "librpc/gen_ndr/ndr_notify.h"
      31             : #include "../lib/util/dlinklist.h"
      32             : #include "ntvfs/common/ntvfs_common.h"
      33             : #include "ntvfs/sysdep/sys_notify.h"
      34             : #include "cluster/cluster.h"
      35             : #include "param/param.h"
      36             : #include "lib/util/tsort.h"
      37             : #include "lib/dbwrap/dbwrap.h"
      38             : #include "../lib/util/util_tdb.h"
      39             : 
      40             : struct notify_context {
      41             :         struct db_context *db;
      42             :         struct server_id server;
      43             :         struct imessaging_context *imessaging_ctx;
      44             :         struct notify_list *list;
      45             :         struct notify_array *array;
      46             :         int64_t seqnum;
      47             :         struct sys_notify_context *sys_notify_ctx;
      48             : };
      49             : 
      50             : 
      51             : struct notify_list {
      52             :         struct notify_list *next, *prev;
      53             :         void *private_data;
      54             :         void (*callback)(void *, const struct notify_event *);
      55             :         void *sys_notify_handle;
      56             :         int depth;
      57             : };
      58             : 
      59             : #define NOTIFY_KEY "notify array"
      60             : 
      61             : #define NOTIFY_ENABLE           "notify:enable"
      62             : #define NOTIFY_ENABLE_DEFAULT   true
      63             : 
      64             : static NTSTATUS notify_remove_all(struct notify_context *notify);
      65             : static void notify_handler(struct imessaging_context *msg_ctx,
      66             :                            void *private_data,
      67             :                            uint32_t msg_type,
      68             :                            struct server_id server_id,
      69             :                            size_t num_fds,
      70             :                            int *fds,
      71             :                            DATA_BLOB *data);
      72             : 
      73             : /*
      74             :   destroy the notify context
      75             : */
      76        1328 : static int notify_destructor(struct notify_context *notify)
      77             : {
      78        1328 :         imessaging_deregister(notify->imessaging_ctx, MSG_PVFS_NOTIFY, notify);
      79        1328 :         notify_remove_all(notify);
      80        1328 :         return 0;
      81             : }
      82             : 
      83             : /*
      84             :   Open up the notify.tdb database. You should close it down using
      85             :   talloc_free(). We need the imessaging_ctx to allow for notifications
      86             :   via internal messages
      87             : */
      88        1328 : struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server, 
      89             :                                    struct imessaging_context *imessaging_ctx,
      90             :                                    struct loadparm_context *lp_ctx,
      91             :                                    struct tevent_context *ev,
      92             :                                    struct share_config *scfg)
      93             : {
      94           0 :         struct notify_context *notify;
      95             : 
      96        1328 :         if (share_bool_option(scfg, NOTIFY_ENABLE, NOTIFY_ENABLE_DEFAULT) != true) {
      97           0 :                 return NULL;
      98             :         }
      99             : 
     100        1328 :         if (ev == NULL) {
     101           0 :                 return NULL;
     102             :         }
     103             : 
     104        1328 :         notify = talloc(mem_ctx, struct notify_context);
     105        1328 :         if (notify == NULL) {
     106           0 :                 return NULL;
     107             :         }
     108             : 
     109        1328 :         notify->db = cluster_db_tmp_open(notify, lp_ctx, "notify", TDB_SEQNUM);
     110        1328 :         if (notify->db == NULL) {
     111           0 :                 talloc_free(notify);
     112           0 :                 return NULL;
     113             :         }
     114             : 
     115        1328 :         notify->server = server;
     116        1328 :         notify->imessaging_ctx = imessaging_ctx;
     117        1328 :         notify->list = NULL;
     118        1328 :         notify->array = NULL;
     119        1328 :         notify->seqnum = dbwrap_get_seqnum(notify->db);
     120             : 
     121        1328 :         talloc_set_destructor(notify, notify_destructor);
     122             : 
     123             :         /* register with the messaging subsystem for the notify
     124             :            message type */
     125        1328 :         imessaging_register(notify->imessaging_ctx, notify,
     126             :                            MSG_PVFS_NOTIFY, notify_handler);
     127             : 
     128        1328 :         notify->sys_notify_ctx = sys_notify_context_create(scfg, notify, ev);
     129             : 
     130        1328 :         return notify;
     131             : }
     132             : 
     133             : 
     134             : /*
     135             :   lock the notify db
     136             : */
     137        1026 : static struct db_record *notify_lock(struct notify_context *notify)
     138             : {
     139        1026 :         TDB_DATA key = string_term_tdb_data(NOTIFY_KEY);
     140             : 
     141        1026 :         return dbwrap_fetch_locked(notify->db, notify, key);
     142             : }
     143             : 
     144        1026 : static void notify_unlock(struct db_record *lock)
     145             : {
     146        1026 :         talloc_free(lock);
     147        1026 : }
     148             : 
     149             : /*
     150             :   load the notify array
     151             : */
     152      211386 : static NTSTATUS notify_load(struct notify_context *notify)
     153             : {
     154           0 :         TDB_DATA dbuf;
     155           0 :         DATA_BLOB blob;
     156           0 :         enum ndr_err_code ndr_err;
     157           0 :         int seqnum;
     158           0 :         NTSTATUS status;
     159             : 
     160      211386 :         seqnum = dbwrap_get_seqnum(notify->db);
     161             : 
     162      211386 :         if (seqnum == notify->seqnum && notify->array != NULL) {
     163      209165 :                 return NT_STATUS_OK;
     164             :         }
     165             : 
     166        2221 :         notify->seqnum = seqnum;
     167             : 
     168        2221 :         talloc_free(notify->array);
     169        2221 :         notify->array = talloc_zero(notify, struct notify_array);
     170        2221 :         NT_STATUS_HAVE_NO_MEMORY(notify->array);
     171             : 
     172        2221 :         status = dbwrap_fetch_bystring(notify->db, notify, NOTIFY_KEY, &dbuf);
     173        2221 :         if (!NT_STATUS_IS_OK(status)) {
     174        1364 :                 return NT_STATUS_OK;
     175             :         }
     176             : 
     177         857 :         blob.data = dbuf.dptr;
     178         857 :         blob.length = dbuf.dsize;
     179             : 
     180         857 :         ndr_err = ndr_pull_struct_blob(&blob, notify->array, notify->array,
     181             :                                        (ndr_pull_flags_fn_t)ndr_pull_notify_array);
     182         857 :         talloc_free(dbuf.dptr);
     183         857 :         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
     184           0 :                 return ndr_map_error2ntstatus(ndr_err);
     185             :         }
     186             : 
     187         857 :         return NT_STATUS_OK;
     188             : }
     189             : 
     190             : /*
     191             :   compare notify entries for sorting
     192             : */
     193          89 : static int notify_compare(const void *p1, const void *p2)
     194             : {
     195          89 :         const struct notify_entry *e1 = p1, *e2 = p2;
     196          89 :         return strcmp(e1->path, e2->path);
     197             : }
     198             : 
     199             : /*
     200             :   save the notify array
     201             : */
     202        1026 : static NTSTATUS notify_save(struct notify_context *notify)
     203             : {
     204           0 :         TDB_DATA dbuf;
     205           0 :         DATA_BLOB blob;
     206           0 :         enum ndr_err_code ndr_err;
     207           0 :         TALLOC_CTX *tmp_ctx;
     208           0 :         NTSTATUS status;
     209             : 
     210             :         /* if possible, remove some depth arrays */
     211        5466 :         while (notify->array->num_depths > 0 &&
     212        4973 :                notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
     213        4440 :                 notify->array->num_depths--;
     214             :         }
     215             : 
     216             :         /* we might just be able to delete the record */
     217        1026 :         if (notify->array->num_depths == 0) {
     218         493 :                 return dbwrap_delete_bystring(notify->db, NOTIFY_KEY);
     219             :         }
     220             : 
     221         533 :         tmp_ctx = talloc_new(notify);
     222         533 :         NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
     223             : 
     224         533 :         ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
     225             :                                        (ndr_push_flags_fn_t)ndr_push_notify_array);
     226         533 :         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
     227           0 :                 talloc_free(tmp_ctx);
     228           0 :                 return ndr_map_error2ntstatus(ndr_err);
     229             :         }
     230             : 
     231         533 :         dbuf.dptr = blob.data;
     232         533 :         dbuf.dsize = blob.length;
     233             : 
     234         533 :         status = dbwrap_store_bystring(notify->db, NOTIFY_KEY, dbuf,
     235             :                                        TDB_REPLACE);
     236         533 :         talloc_free(tmp_ctx);
     237         533 :         return status;
     238             : }
     239             : 
     240             : 
     241             : /*
     242             :   handle incoming notify messages
     243             : */
     244         431 : static void notify_handler(struct imessaging_context *msg_ctx,
     245             :                            void *private_data,
     246             :                            uint32_t msg_type,
     247             :                            struct server_id server_id,
     248             :                            size_t num_fds,
     249             :                            int *fds,
     250             :                            DATA_BLOB *data)
     251             : {
     252         431 :         struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
     253           0 :         enum ndr_err_code ndr_err;
     254           0 :         struct notify_event ev;
     255         431 :         TALLOC_CTX *tmp_ctx = talloc_new(notify);
     256           0 :         struct notify_list *listel;
     257             : 
     258         431 :         if (num_fds != 0) {
     259           0 :                 DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
     260           0 :                 return;
     261             :         }
     262             : 
     263         431 :         if (tmp_ctx == NULL) {
     264           0 :                 return;
     265             :         }
     266             : 
     267         431 :         ndr_err = ndr_pull_struct_blob(data, tmp_ctx, &ev,
     268             :                                       (ndr_pull_flags_fn_t)ndr_pull_notify_event);
     269         431 :         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
     270           0 :                 talloc_free(tmp_ctx);
     271           0 :                 return;
     272             :         }
     273             : 
     274        2193 :         for (listel=notify->list;listel;listel=listel->next) {
     275        2191 :                 if (listel->private_data == ev.private_data) {
     276         429 :                         listel->callback(listel->private_data, &ev);
     277         429 :                         break;
     278             :                 }
     279             :         }
     280             : 
     281         431 :         talloc_free(tmp_ctx);   
     282             : }
     283             : 
     284             : /*
     285             :   callback from sys_notify telling us about changes from the OS
     286             : */
     287           0 : static void sys_notify_callback(struct sys_notify_context *ctx, 
     288             :                                 void *ptr, struct notify_event *ev)
     289             : {
     290           0 :         struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
     291           0 :         ev->private_data = listel;
     292           0 :         listel->callback(listel->private_data, ev);
     293           0 : }
     294             : 
     295             : /*
     296             :   add an entry to the notify array
     297             : */
     298         513 : static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_entry *e,
     299             :                                  void *private_data, int depth)
     300             : {
     301           0 :         int i;
     302           0 :         struct notify_depth *d;
     303           0 :         struct notify_entry *ee;
     304             : 
     305             :         /* possibly expand the depths array */
     306         513 :         if (depth >= notify->array->num_depths) {
     307         495 :                 d = talloc_realloc(notify->array, notify->array->depth, 
     308             :                                    struct notify_depth, depth+1);
     309         495 :                 NT_STATUS_HAVE_NO_MEMORY(d);
     310        4935 :                 for (i=notify->array->num_depths;i<=depth;i++) {
     311        4440 :                         ZERO_STRUCT(d[i]);
     312             :                 }
     313         495 :                 notify->array->depth = d;
     314         495 :                 notify->array->num_depths = depth+1;
     315             :         }
     316         513 :         d = &notify->array->depth[depth];
     317             : 
     318             :         /* expand the entries array */
     319         513 :         ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
     320             :                             d->num_entries+1);
     321         513 :         NT_STATUS_HAVE_NO_MEMORY(ee);
     322         513 :         d->entries = ee;
     323             : 
     324         513 :         d->entries[d->num_entries] = *e;
     325         513 :         d->entries[d->num_entries].private_data = private_data;
     326         513 :         d->entries[d->num_entries].server = notify->server;
     327         513 :         d->entries[d->num_entries].path_len = strlen(e->path);
     328         513 :         d->num_entries++;
     329             : 
     330         513 :         d->max_mask |= e->filter;
     331         513 :         d->max_mask_subdir |= e->subdir_filter;
     332             : 
     333         513 :         TYPESAFE_QSORT(d->entries, d->num_entries, notify_compare);
     334             : 
     335             :         /* recalculate the maximum masks */
     336         513 :         d->max_mask = 0;
     337         513 :         d->max_mask_subdir = 0;
     338             : 
     339        1076 :         for (i=0;i<d->num_entries;i++) {
     340         563 :                 d->max_mask |= d->entries[i].filter;
     341         563 :                 d->max_mask_subdir |= d->entries[i].subdir_filter;
     342             :         }
     343             : 
     344         513 :         return notify_save(notify);
     345             : }
     346             : 
     347             : /*
     348             :   add a notify watch. This is called when a notify is first setup on a open
     349             :   directory handle.
     350             : */
     351         513 : NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
     352             :                     void (*callback)(void *, const struct notify_event *), 
     353             :                     void *private_data)
     354             : {
     355         513 :         struct notify_entry e = *e0;
     356           0 :         NTSTATUS status;
     357         513 :         char *tmp_path = NULL;
     358           0 :         struct notify_list *listel;
     359           0 :         size_t len;
     360           0 :         int depth;
     361           0 :         struct db_record *locked;
     362             : 
     363             :         /* see if change notify is enabled at all */
     364         513 :         if (notify == NULL) {
     365           0 :                 return NT_STATUS_NOT_IMPLEMENTED;
     366             :         }
     367             : 
     368         513 :         locked = notify_lock(notify);
     369         513 :         if (!locked) {
     370           0 :                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
     371             :         }
     372             : 
     373         513 :         status = notify_load(notify);
     374         513 :         if (!NT_STATUS_IS_OK(status)) {
     375           0 :                 goto done;
     376             :         }
     377             : 
     378             :         /* cope with /. on the end of the path */
     379         513 :         len = strlen(e.path);
     380         513 :         if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
     381           0 :                 tmp_path = talloc_strndup(notify, e.path, len-2);
     382           0 :                 if (tmp_path == NULL) {
     383           0 :                         status = NT_STATUS_NO_MEMORY;
     384           0 :                         goto done;
     385             :                 }
     386           0 :                 e.path = tmp_path;
     387             :         }
     388             : 
     389         513 :         depth = count_chars(e.path, '/');
     390             : 
     391         513 :         listel = talloc_zero(notify, struct notify_list);
     392         513 :         if (listel == NULL) {
     393           0 :                 status = NT_STATUS_NO_MEMORY;
     394           0 :                 goto done;
     395             :         }
     396             : 
     397         513 :         listel->private_data = private_data;
     398         513 :         listel->callback = callback;
     399         513 :         listel->depth = depth;
     400         513 :         DLIST_ADD(notify->list, listel);
     401             : 
     402             :         /* ignore failures from sys_notify */
     403         513 :         if (notify->sys_notify_ctx != NULL) {
     404             :                 /*
     405             :                   this call will modify e.filter and e.subdir_filter
     406             :                   to remove bits handled by the backend
     407             :                 */
     408         513 :                 status = sys_notify_watch(notify->sys_notify_ctx, &e,
     409             :                                           sys_notify_callback, listel, 
     410         513 :                                           &listel->sys_notify_handle);
     411         513 :                 if (NT_STATUS_IS_OK(status)) {
     412           0 :                         talloc_steal(listel, listel->sys_notify_handle);
     413             :                 }
     414             :         }
     415             : 
     416             :         /* if the system notify handler couldn't handle some of the
     417             :            filter bits, or couldn't handle a request for recursion
     418             :            then we need to install it in the array used for the
     419             :            intra-samba notify handling */
     420         513 :         if (e.filter != 0 || e.subdir_filter != 0) {
     421         513 :                 status = notify_add_array(notify, &e, private_data, depth);
     422             :         }
     423             : 
     424           0 : done:
     425         513 :         notify_unlock(locked);
     426         513 :         talloc_free(tmp_path);
     427             : 
     428         513 :         return status;
     429             : }
     430             : 
     431             : /*
     432             :   remove a notify watch. Called when the directory handle is closed
     433             : */
     434         513 : NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
     435             : {
     436           0 :         NTSTATUS status;
     437           0 :         struct notify_list *listel;
     438           0 :         int i, depth;
     439           0 :         struct notify_depth *d;
     440           0 :         struct db_record *locked;
     441             : 
     442             :         /* see if change notify is enabled at all */
     443         513 :         if (notify == NULL) {
     444           0 :                 return NT_STATUS_NOT_IMPLEMENTED;
     445             :         }
     446             : 
     447         514 :         for (listel=notify->list;listel;listel=listel->next) {
     448         514 :                 if (listel->private_data == private_data) {
     449         513 :                         DLIST_REMOVE(notify->list, listel);
     450         513 :                         break;
     451             :                 }
     452             :         }
     453         513 :         if (listel == NULL) {
     454           0 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     455             :         }
     456             : 
     457         513 :         depth = listel->depth;
     458             : 
     459         513 :         talloc_free(listel);
     460             : 
     461         513 :         locked = notify_lock(notify);
     462         513 :         if (!locked) {
     463           0 :                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
     464             :         }
     465             : 
     466         513 :         status = notify_load(notify);
     467         513 :         if (!NT_STATUS_IS_OK(status)) {
     468           0 :                 notify_unlock(locked);
     469           0 :                 return status;
     470             :         }
     471             : 
     472         513 :         if (depth >= notify->array->num_depths) {
     473           0 :                 notify_unlock(locked);
     474           0 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     475             :         }
     476             : 
     477             :         /* we only have to search at the depth of this element */
     478         513 :         d = &notify->array->depth[depth];
     479             : 
     480         546 :         for (i=0;i<d->num_entries;i++) {
     481         546 :                 if (private_data == d->entries[i].private_data &&
     482         513 :                     cluster_id_equal(&notify->server, &d->entries[i].server)) {
     483         513 :                         break;
     484             :                 }
     485             :         }
     486         513 :         if (i == d->num_entries) {
     487           0 :                 notify_unlock(locked);
     488           0 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     489             :         }
     490             : 
     491         513 :         if (i < d->num_entries-1) {
     492           9 :                 memmove(&d->entries[i], &d->entries[i+1], 
     493           9 :                         sizeof(d->entries[i])*(d->num_entries-(i+1)));
     494             :         }
     495         513 :         d->num_entries--;
     496             : 
     497         513 :         status = notify_save(notify);
     498             : 
     499         513 :         notify_unlock(locked);
     500             : 
     501         513 :         return status;
     502             : }
     503             : 
     504             : /*
     505             :   remove all notify watches for this messaging server
     506             : */
     507        1328 : static NTSTATUS notify_remove_all(struct notify_context *notify)
     508             : {
     509           0 :         NTSTATUS status;
     510        1328 :         int i, depth, del_count=0;
     511           0 :         struct db_record *locked;
     512             : 
     513        1328 :         if (notify->list == NULL) {
     514        1328 :                 return NT_STATUS_OK;
     515             :         }
     516             : 
     517           0 :         locked = notify_lock(notify);
     518           0 :         if (!locked) {
     519           0 :                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
     520             :         }
     521             : 
     522           0 :         status = notify_load(notify);
     523           0 :         if (!NT_STATUS_IS_OK(status)) {
     524           0 :                 notify_unlock(locked);
     525           0 :                 return status;
     526             :         }
     527             : 
     528             :         /* we have to search for all entries across all depths, looking for matches
     529             :            for our server id */
     530           0 :         for (depth=0;depth<notify->array->num_depths;depth++) {
     531           0 :                 struct notify_depth *d = &notify->array->depth[depth];
     532           0 :                 for (i=0;i<d->num_entries;i++) {
     533           0 :                         if (cluster_id_equal(&notify->server, &d->entries[i].server)) {
     534           0 :                                 if (i < d->num_entries-1) {
     535           0 :                                         memmove(&d->entries[i], &d->entries[i+1], 
     536           0 :                                                 sizeof(d->entries[i])*(d->num_entries-(i+1)));
     537             :                                 }
     538           0 :                                 i--;
     539           0 :                                 d->num_entries--;
     540           0 :                                 del_count++;
     541             :                         }
     542             :                 }
     543             :         }
     544             : 
     545           0 :         if (del_count > 0) {
     546           0 :                 status = notify_save(notify);
     547             :         }
     548             : 
     549           0 :         notify_unlock(locked);
     550             : 
     551           0 :         return status;
     552             : }
     553             : 
     554             : 
     555             : /*
     556             :   send a notify message to another messaging server
     557             : */
     558         429 : static void notify_send(struct notify_context *notify, struct notify_entry *e,
     559             :                         const char *path, uint32_t action)
     560             : {
     561           0 :         struct notify_event ev;
     562           0 :         DATA_BLOB data;
     563           0 :         NTSTATUS status;
     564           0 :         enum ndr_err_code ndr_err;
     565           0 :         TALLOC_CTX *tmp_ctx;
     566             : 
     567         429 :         ev.action = action;
     568         429 :         ev.dir = discard_const_p(char, "");
     569         429 :         ev.path = path;
     570         429 :         ev.private_data = e->private_data;
     571             : 
     572         429 :         tmp_ctx = talloc_new(notify);
     573             : 
     574         429 :         ndr_err = ndr_push_struct_blob(&data, tmp_ctx, &ev, (ndr_push_flags_fn_t)ndr_push_notify_event);
     575         429 :         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
     576           0 :                 talloc_free(tmp_ctx);
     577           0 :                 return;
     578             :         }
     579             : 
     580         429 :         status = imessaging_send(notify->imessaging_ctx, e->server,
     581             :                                 MSG_PVFS_NOTIFY, &data);
     582         429 :         if (!NT_STATUS_IS_OK(status)) {
     583           0 :                 talloc_free(tmp_ctx);
     584           0 :                 return;
     585             :         }
     586             : 
     587         429 :         talloc_free(tmp_ctx);
     588             : }
     589             : 
     590             : 
     591             : /*
     592             :   trigger a notify message for anyone waiting on a matching event
     593             : 
     594             :   This function is called a lot, and needs to be very fast. The unusual data structure
     595             :   and traversal is designed to be fast in the average case, even for large numbers of
     596             :   notifies
     597             : */
     598      210360 : void notify_trigger(struct notify_context *notify,
     599             :                     uint32_t action, uint32_t filter, const char *path)
     600             : {
     601           0 :         NTSTATUS status;
     602           0 :         int depth;
     603           0 :         const char *p, *next_p;
     604             : 
     605             :         /* see if change notify is enabled at all */
     606      210360 :         if (notify == NULL) {
     607           0 :                 return;
     608             :         }
     609             : 
     610      210360 :         status = notify_load(notify);
     611      210360 :         if (!NT_STATUS_IS_OK(status)) {
     612           0 :                 return;
     613             :         }
     614             : 
     615             :         /* loop along the given path, working with each directory depth separately */
     616      210360 :         for (depth=0,p=path;
     617      221167 :              p && depth < notify->array->num_depths;
     618       10807 :              p=next_p,depth++) {
     619       10807 :                 int p_len = p - path;
     620           0 :                 int min_i, max_i, i;
     621       10807 :                 struct notify_depth *d = &notify->array->depth[depth];
     622       10807 :                 next_p = strchr(p+1, '/');
     623             : 
     624             :                 /* see if there are any entries at this depth */
     625       10807 :                 if (d->num_entries == 0) continue;
     626             :                 
     627             :                 /* try to skip based on the maximum mask. If next_p is
     628             :                  NULL then we know it will be a 'this directory'
     629             :                  match, otherwise it must be a subdir match */
     630        1252 :                 if (next_p != NULL) {
     631          76 :                         if (0 == (filter & d->max_mask_subdir)) {
     632           6 :                                 continue;
     633             :                         }
     634             :                 } else {
     635        1176 :                         if (0 == (filter & d->max_mask)) {
     636         937 :                                 continue;
     637             :                         }
     638             :                 }
     639             : 
     640             :                 /* we know there is an entry here worth looking
     641             :                  for. Use a bisection search to find the first entry
     642             :                  with a matching path */
     643         309 :                 min_i = 0;
     644         309 :                 max_i = d->num_entries-1;
     645             : 
     646         577 :                 while (min_i < max_i) {
     647           0 :                         struct notify_entry *e;
     648           0 :                         int cmp;
     649         268 :                         i = (min_i+max_i)/2;
     650         268 :                         e = &d->entries[i];
     651         268 :                         cmp = strncmp(path, e->path, p_len);
     652         268 :                         if (cmp == 0) {
     653         196 :                                 if (p_len == e->path_len) {
     654         196 :                                         max_i = i;
     655             :                                 } else {
     656           0 :                                         max_i = i-1;
     657             :                                 }
     658          72 :                         } else if (cmp < 0) {
     659          26 :                                 max_i = i-1;
     660             :                         } else {
     661          46 :                                 min_i = i+1;
     662             :                         }
     663             :                 }
     664             : 
     665         309 :                 if (min_i != max_i) {
     666             :                         /* none match */
     667           0 :                         continue;
     668             :                 }
     669             : 
     670             :                 /* we now know that the entries start at min_i */
     671         826 :                 for (i=min_i;i<d->num_entries;i++) {
     672         583 :                         struct notify_entry *e = &d->entries[i];
     673         583 :                         if (p_len != e->path_len ||
     674         521 :                             strncmp(path, e->path, p_len) != 0) break;
     675         517 :                         if (next_p != NULL) {
     676         200 :                                 if (0 == (filter & e->subdir_filter)) {
     677          68 :                                         continue;
     678             :                                 }
     679             :                         } else {
     680         317 :                                 if (0 == (filter & e->filter)) {
     681          20 :                                         continue;
     682             :                                 }
     683             :                         }
     684         429 :                         notify_send(notify, e, path + e->path_len + 1, action);
     685             :                 }
     686             :         }
     687             : }

Generated by: LCOV version 1.14