2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 2000
5 Copyright (C) Jeremy Allison 1994-1998
6 Copyright (C) Volker Lendecke 2007
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.
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.
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/>.
23 #include "smbd/smbd.h"
24 #include "smbd/globals.h"
25 #include "../librpc/gen_ndr/ndr_notify.h"
26 #include "librpc/gen_ndr/ndr_file_id.h"
27 #include "libcli/security/privileges.h"
28 #include "libcli/security/security.h"
30 struct notify_change_event
{
36 struct notify_change_buf
{
38 * Filters for reinitializing after notifyd has been restarted
41 uint32_t subdir_filter
;
44 * If no requests are pending, changes are queued here. Simple array,
48 uint32_t max_buffer_size
;
51 * num_changes == -1 means that we have got a catch-all change, when
52 * asked we just return NT_STATUS_OK without specific changes.
55 struct notify_change_event
*changes
;
58 * If no changes are around requests are queued here. Using a linked
59 * list, because we have to append at the end and delete from the top.
61 struct notify_change_request
*requests
;
64 struct notify_change_request
{
65 struct notify_change_request
*prev
, *next
;
66 struct files_struct
*fsp
; /* backpointer for cancel by mid */
67 struct smb_request
*req
;
70 void (*reply_fn
)(struct smb_request
*req
,
72 uint8_t *buf
, size_t len
);
73 struct notify_mid_map
*mid_map
;
77 static void notify_fsp(files_struct
*fsp
, struct timespec when
,
78 uint32_t action
, const char *name
);
80 bool change_notify_fsp_has_changes(struct files_struct
*fsp
)
86 if (fsp
->notify
== NULL
) {
90 if (fsp
->notify
->num_changes
== 0) {
98 * For NTCancel, we need to find the notify_change_request indexed by
99 * mid. Separate list here.
102 struct notify_mid_map
{
103 struct notify_mid_map
*prev
, *next
;
104 struct notify_change_request
*req
;
108 static bool notify_change_record_identical(struct notify_change_event
*c1
,
109 struct notify_change_event
*c2
)
111 /* Note this is deliberately case sensitive. */
112 if (c1
->action
== c2
->action
&&
113 strcmp(c1
->name
, c2
->name
) == 0) {
119 static int compare_notify_change_events(const void *p1
, const void *p2
)
121 const struct notify_change_event
*e1
= p1
;
122 const struct notify_change_event
*e2
= p2
;
124 return timespec_compare(&e1
->when
, &e2
->when
);
127 static bool notify_marshall_changes(int num_changes
,
129 struct notify_change_event
*changes
,
130 DATA_BLOB
*final_blob
)
134 if (num_changes
== -1) {
139 * Sort the notifies by timestamp when the event happened to avoid
140 * coalescing and thus dropping events.
143 qsort(changes
, num_changes
,
144 sizeof(*changes
), compare_notify_change_events
);
146 for (i
=0; i
<num_changes
; i
++) {
147 enum ndr_err_code ndr_err
;
148 struct notify_change_event
*c
;
149 struct FILE_NOTIFY_INFORMATION m
;
153 /* Coalesce any identical records. */
154 while (i
+1 < num_changes
&&
155 notify_change_record_identical(&changes
[i
],
162 m
.FileName1
= c
->name
;
163 m
.FileNameLength
= strlen_m(c
->name
)*2;
164 m
.Action
= c
->action
;
166 m
._pad
= data_blob_null
;
169 * Offset to next entry, only if there is one
172 if (i
== (num_changes
-1)) {
173 m
.NextEntryOffset
= 0;
175 if ((m
.FileNameLength
% 4) == 2) {
176 m
._pad
= data_blob_const(&pad
, 2);
179 ndr_size_FILE_NOTIFY_INFORMATION(&m
, 0);
182 ndr_err
= ndr_push_struct_blob(&blob
, talloc_tos(), &m
,
183 (ndr_push_flags_fn_t
)ndr_push_FILE_NOTIFY_INFORMATION
);
184 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
188 if (DEBUGLEVEL
>= 10) {
189 NDR_PRINT_DEBUG(FILE_NOTIFY_INFORMATION
, &m
);
192 if (!data_blob_append(talloc_tos(), final_blob
,
193 blob
.data
, blob
.length
)) {
194 data_blob_free(&blob
);
198 data_blob_free(&blob
);
200 if (final_blob
->length
> max_offset
) {
201 /* Too much data for client. */
202 DEBUG(10, ("Client only wanted %d bytes, trying to "
203 "marshall %d bytes\n", (int)max_offset
,
204 (int)final_blob
->length
));
212 /****************************************************************************
213 Setup the common parts of the return packet and send it.
214 *****************************************************************************/
216 void change_notify_reply(struct smb_request
*req
,
219 struct notify_change_buf
*notify_buf
,
220 void (*reply_fn
)(struct smb_request
*req
,
222 uint8_t *buf
, size_t len
))
224 DATA_BLOB blob
= data_blob_null
;
226 if (!NT_STATUS_IS_OK(error_code
)) {
227 reply_fn(req
, error_code
, NULL
, 0);
231 if (notify_buf
== NULL
) {
232 reply_fn(req
, NT_STATUS_OK
, NULL
, 0);
236 max_param
= MIN(max_param
, notify_buf
->max_buffer_size
);
238 if (!notify_marshall_changes(notify_buf
->num_changes
, max_param
,
239 notify_buf
->changes
, &blob
)) {
241 * We exceed what the client is willing to accept. Send
244 data_blob_free(&blob
);
247 reply_fn(req
, NT_STATUS_OK
, blob
.data
, blob
.length
);
249 data_blob_free(&blob
);
251 TALLOC_FREE(notify_buf
->changes
);
252 notify_buf
->num_changes
= 0;
255 struct notify_fsp_state
{
256 struct files_struct
*notified_fsp
;
257 struct timespec when
;
258 const struct notify_event
*e
;
261 static struct files_struct
*notify_fsp_cb(struct files_struct
*fsp
,
264 struct notify_fsp_state
*state
= private_data
;
266 if (fsp
== state
->notified_fsp
) {
267 DBG_DEBUG("notify_callback called for %s\n", fsp_str_dbg(fsp
));
268 notify_fsp(fsp
, state
->when
, state
->e
->action
, state
->e
->path
);
275 void notify_callback(struct smbd_server_connection
*sconn
,
276 void *private_data
, struct timespec when
,
277 const struct notify_event
*e
)
279 struct notify_fsp_state state
= {
280 .notified_fsp
= private_data
, .when
= when
, .e
= e
282 files_forall(sconn
, notify_fsp_cb
, &state
);
285 NTSTATUS
change_notify_create(struct files_struct
*fsp
,
286 uint32_t max_buffer_size
,
290 size_t len
= fsp_fullbasepath(fsp
, NULL
, 0);
291 char fullpath
[len
+1];
292 NTSTATUS status
= NT_STATUS_NOT_IMPLEMENTED
;
295 * Setting a changenotify needs READ/LIST access
296 * on the directory handle.
298 status
= check_any_access_fsp(fsp
, SEC_DIR_LIST
);
299 if (!NT_STATUS_IS_OK(status
)) {
303 if (fsp
->notify
!= NULL
) {
304 DEBUG(1, ("change_notify_create: fsp->notify != NULL, "
305 "fname = %s\n", fsp
->fsp_name
->base_name
));
306 return NT_STATUS_INVALID_PARAMETER
;
309 if (!(fsp
->notify
= talloc_zero(NULL
, struct notify_change_buf
))) {
310 DEBUG(0, ("talloc failed\n"));
311 return NT_STATUS_NO_MEMORY
;
313 fsp
->notify
->filter
= filter
;
314 fsp
->notify
->subdir_filter
= recursive
? filter
: 0;
315 fsp
->notify
->max_buffer_size
= max_buffer_size
;
317 fsp_fullbasepath(fsp
, fullpath
, sizeof(fullpath
));
320 * Avoid /. at the end of the path name. notify can't deal with it.
322 if (len
> 1 && fullpath
[len
-1] == '.' && fullpath
[len
-2] == '/') {
323 fullpath
[len
-2] = '\0';
326 if ((fsp
->notify
->filter
!= 0) ||
327 (fsp
->notify
->subdir_filter
!= 0)) {
328 status
= notify_add(fsp
->conn
->sconn
->notify_ctx
,
329 fullpath
, fsp
->notify
->filter
,
330 fsp
->notify
->subdir_filter
, fsp
);
336 NTSTATUS
change_notify_add_request(struct smb_request
*req
,
338 uint32_t filter
, bool recursive
,
339 struct files_struct
*fsp
,
340 void (*reply_fn
)(struct smb_request
*req
,
342 uint8_t *buf
, size_t len
))
344 struct notify_change_request
*request
= NULL
;
345 struct notify_mid_map
*map
= NULL
;
346 struct smbd_server_connection
*sconn
= req
->sconn
;
348 DEBUG(10, ("change_notify_add_request: Adding request for %s: "
349 "max_param = %d\n", fsp_str_dbg(fsp
), (int)max_param
));
351 if (!(request
= talloc(NULL
, struct notify_change_request
))
352 || !(map
= talloc(request
, struct notify_mid_map
))) {
353 TALLOC_FREE(request
);
354 return NT_STATUS_NO_MEMORY
;
357 request
->mid_map
= map
;
360 request
->req
= talloc_move(request
, &req
);
361 request
->max_param
= max_param
;
362 request
->filter
= filter
;
364 request
->reply_fn
= reply_fn
;
365 request
->backend_data
= NULL
;
367 DLIST_ADD_END(fsp
->notify
->requests
, request
);
369 map
->mid
= request
->req
->mid
;
370 DLIST_ADD(sconn
->notify_mid_maps
, map
);
375 static void change_notify_remove_request(struct smbd_server_connection
*sconn
,
376 struct notify_change_request
*remove_req
)
379 struct notify_change_request
*req
;
382 * Paranoia checks, the fsp referenced must must have the request in
383 * its list of pending requests
386 fsp
= remove_req
->fsp
;
387 SMB_ASSERT(fsp
->notify
!= NULL
);
389 for (req
= fsp
->notify
->requests
; req
; req
= req
->next
) {
390 if (req
== remove_req
) {
396 smb_panic("notify_req not found in fsp's requests");
399 DLIST_REMOVE(fsp
->notify
->requests
, req
);
400 DLIST_REMOVE(sconn
->notify_mid_maps
, req
->mid_map
);
404 static void smbd_notify_cancel_by_map(struct notify_mid_map
*map
)
406 struct smb_request
*smbreq
= map
->req
->req
;
407 struct smbd_server_connection
*sconn
= smbreq
->sconn
;
408 struct smbd_smb2_request
*smb2req
= smbreq
->smb2req
;
409 NTSTATUS notify_status
= NT_STATUS_CANCELLED
;
411 if (smb2req
!= NULL
) {
414 if (smb2req
->session
== NULL
) {
415 sstatus
= NT_STATUS_USER_SESSION_DELETED
;
417 sstatus
= smb2req
->session
->status
;
420 if (NT_STATUS_EQUAL(sstatus
, NT_STATUS_NETWORK_SESSION_EXPIRED
)) {
421 sstatus
= NT_STATUS_OK
;
424 if (!NT_STATUS_IS_OK(sstatus
)) {
425 notify_status
= NT_STATUS_NOTIFY_CLEANUP
;
426 } else if (smb2req
->tcon
== NULL
) {
427 notify_status
= NT_STATUS_NOTIFY_CLEANUP
;
428 } else if (!NT_STATUS_IS_OK(smb2req
->tcon
->status
)) {
429 notify_status
= NT_STATUS_NOTIFY_CLEANUP
;
433 change_notify_reply(smbreq
, notify_status
,
434 0, NULL
, map
->req
->reply_fn
);
435 change_notify_remove_request(sconn
, map
->req
);
438 /****************************************************************************
439 Delete entries by mid from the change notify pending queue. Always send reply.
440 *****************************************************************************/
442 bool remove_pending_change_notify_requests_by_mid(
443 struct smbd_server_connection
*sconn
, uint64_t mid
)
445 struct notify_mid_map
*map
;
447 for (map
= sconn
->notify_mid_maps
; map
; map
= map
->next
) {
448 if (map
->mid
== mid
) {
457 smbd_notify_cancel_by_map(map
);
461 void smbd_notify_cancel_by_smbreq(const struct smb_request
*smbreq
)
463 struct smbd_server_connection
*sconn
= smbreq
->sconn
;
464 struct notify_mid_map
*map
;
466 for (map
= sconn
->notify_mid_maps
; map
; map
= map
->next
) {
467 if (map
->req
->req
== smbreq
) {
476 smbd_notify_cancel_by_map(map
);
479 static struct files_struct
*smbd_notify_cancel_deleted_fn(
480 struct files_struct
*fsp
, void *private_data
)
482 struct file_id
*fid
= talloc_get_type_abort(
483 private_data
, struct file_id
);
485 if (file_id_equal(&fsp
->file_id
, fid
)) {
486 remove_pending_change_notify_requests_by_fid(
487 fsp
, NT_STATUS_DELETE_PENDING
);
492 void smbd_notify_cancel_deleted(struct messaging_context
*msg
,
493 void *private_data
, uint32_t msg_type
,
494 struct server_id server_id
, DATA_BLOB
*data
)
496 struct smbd_server_connection
*sconn
= talloc_get_type_abort(
497 private_data
, struct smbd_server_connection
);
499 enum ndr_err_code ndr_err
;
501 fid
= talloc(talloc_tos(), struct file_id
);
503 DEBUG(1, ("talloc failed\n"));
507 ndr_err
= ndr_pull_struct_blob_all(
508 data
, fid
, fid
, (ndr_pull_flags_fn_t
)ndr_pull_file_id
);
509 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
510 DEBUG(10, ("%s: ndr_pull_file_id failed: %s\n", __func__
,
511 ndr_errstr(ndr_err
)));
515 files_forall(sconn
, smbd_notify_cancel_deleted_fn
, fid
);
521 static struct files_struct
*smbd_notifyd_reregister(struct files_struct
*fsp
,
524 DBG_DEBUG("reregister %s\n", fsp
->fsp_name
->base_name
);
526 if ((fsp
->conn
->sconn
->notify_ctx
!= NULL
) &&
527 (fsp
->notify
!= NULL
) &&
528 ((fsp
->notify
->filter
!= 0) ||
529 (fsp
->notify
->subdir_filter
!= 0))) {
530 size_t len
= fsp_fullbasepath(fsp
, NULL
, 0);
531 char fullpath
[len
+1];
535 fsp_fullbasepath(fsp
, fullpath
, sizeof(fullpath
));
536 if (len
> 1 && fullpath
[len
-1] == '.' &&
537 fullpath
[len
-2] == '/') {
538 fullpath
[len
-2] = '\0';
541 status
= notify_add(fsp
->conn
->sconn
->notify_ctx
,
542 fullpath
, fsp
->notify
->filter
,
543 fsp
->notify
->subdir_filter
, fsp
);
544 if (!NT_STATUS_IS_OK(status
)) {
545 DBG_DEBUG("notify_add failed: %s\n",
552 void smbd_notifyd_restarted(struct messaging_context
*msg
,
553 void *private_data
, uint32_t msg_type
,
554 struct server_id server_id
, DATA_BLOB
*data
)
556 struct smbd_server_connection
*sconn
= talloc_get_type_abort(
557 private_data
, struct smbd_server_connection
);
559 TALLOC_FREE(sconn
->notify_ctx
);
561 sconn
->notify_ctx
= notify_init(sconn
, sconn
->msg_ctx
,
562 sconn
, notify_callback
);
563 if (sconn
->notify_ctx
== NULL
) {
564 DBG_DEBUG("notify_init failed\n");
568 files_forall(sconn
, smbd_notifyd_reregister
, sconn
->notify_ctx
);
571 /****************************************************************************
572 Delete entries by fnum from the change notify pending queue.
573 *****************************************************************************/
575 void remove_pending_change_notify_requests_by_fid(files_struct
*fsp
,
578 if (fsp
->notify
== NULL
) {
582 while (fsp
->notify
->requests
!= NULL
) {
583 change_notify_reply(fsp
->notify
->requests
->req
,
585 fsp
->notify
->requests
->reply_fn
);
586 change_notify_remove_request(fsp
->conn
->sconn
,
587 fsp
->notify
->requests
);
591 void notify_fname(connection_struct
*conn
, uint32_t action
, uint32_t filter
,
594 struct notify_context
*notify_ctx
= conn
->sconn
->notify_ctx
;
596 if (path
[0] == '.' && path
[1] == '/') {
600 notify_trigger(notify_ctx
, action
, filter
, conn
->connectpath
, path
);
603 static bool user_can_stat_name_under_fsp(files_struct
*fsp
, const char *name
)
606 struct smb_filename
*fname
= NULL
;
607 char *filepath
= NULL
;
612 * Assume we get filepath (relative to the share)
615 * 'dir1/dir2/dir3/file'
617 * We start with LIST and TRAVERSE on the
618 * direct parent ('dir1/dir2/dir3')
620 * Then we switch to just TRAVERSE for
621 * the rest: 'dir1/dir2', 'dir1', '.'
623 * For a file in the share root, we'll have
625 * and would just check '.' with LIST and TRAVERSE.
627 * It's important to always check '.' as the last step,
628 * which means we check the permissions of the share root
632 if (ISDOT(fsp
->fsp_name
->base_name
)) {
633 filepath
= talloc_strdup(talloc_tos(), name
);
635 filepath
= talloc_asprintf(talloc_tos(),
637 fsp
->fsp_name
->base_name
,
640 if (filepath
== NULL
) {
641 DBG_ERR("Memory allocation failed\n");
645 rights
= SEC_DIR_LIST
|SEC_DIR_TRAVERSE
;
646 p
= strrchr_m(filepath
, '/');
648 * Check each path component, excluding the share root.
650 * We could check all components including root using
651 * a do { .. } while() loop, but IMHO the logic is clearer
652 * having the share root check separately afterwards.
656 status
= synthetic_pathref(talloc_tos(),
664 if (!NT_STATUS_IS_OK(status
)) {
665 DBG_ERR("synthetic_pathref failed for %s, error %s\n",
669 TALLOC_FREE(filepath
);
673 status
= smbd_check_access_rights_fsp(fsp
->conn
->cwd_fsp
,
677 if (!NT_STATUS_IS_OK(status
)) {
678 DBG_DEBUG("Access rights for %s/%s: %s\n",
679 fsp
->conn
->connectpath
,
683 TALLOC_FREE(filepath
);
688 rights
= SEC_DIR_TRAVERSE
;
689 p
= strrchr_m(filepath
, '/');
692 TALLOC_FREE(filepath
);
694 /* Finally check share root. */
695 filepath
= talloc_strdup(talloc_tos(), ".");
696 if (filepath
== NULL
) {
697 DBG_ERR("Memory allocation failed\n");
700 status
= synthetic_pathref(talloc_tos(),
708 if (!NT_STATUS_IS_OK(status
)) {
709 DBG_ERR("synthetic_pathref failed for %s, error %s\n",
713 TALLOC_FREE(filepath
);
716 status
= smbd_check_access_rights_fsp(fsp
->conn
->cwd_fsp
,
720 if (!NT_STATUS_IS_OK(status
)) {
721 DBG_DEBUG("TRAVERSE access rights for %s failed with %s\n",
722 fsp
->conn
->connectpath
,
725 TALLOC_FREE(filepath
);
729 TALLOC_FREE(filepath
);
733 static void notify_fsp(files_struct
*fsp
, struct timespec when
,
734 uint32_t action
, const char *name
)
736 struct notify_change_event
*change
, *changes
;
739 if (fsp
->notify
== NULL
) {
741 * Nobody is waiting, don't queue
746 if (lp_honor_change_notify_privilege(SNUM(fsp
->conn
))) {
747 bool has_sec_change_notify_privilege
;
750 has_sec_change_notify_privilege
= security_token_has_privilege(
751 fsp
->conn
->session_info
->security_token
,
752 SEC_PRIV_CHANGE_NOTIFY
);
754 if (has_sec_change_notify_privilege
) {
759 ok
= become_user_without_service_by_fsp(fsp
);
761 expose
= user_can_stat_name_under_fsp(fsp
, name
);
762 unbecome_user_without_service();
765 DBG_DEBUG("has_sec_change_notify_privilege=%s "
766 "expose=%s for %s notify %s\n",
767 has_sec_change_notify_privilege
? "true" : "false",
768 expose
? "true" : "false",
769 fsp
->fsp_name
->base_name
, name
);
776 * Someone has triggered a notify previously, queue the change for
780 if ((fsp
->notify
->num_changes
> 1000) || (name
== NULL
)) {
782 * The real number depends on the client buf, just provide a
783 * guard against a DoS here. If name == NULL the CN backend is
784 * alerting us to a problem. Possibly dropped events. Clear
785 * queued changes and send the catch-all response to the client
786 * if a request is pending.
788 TALLOC_FREE(fsp
->notify
->changes
);
789 fsp
->notify
->num_changes
= -1;
790 if (fsp
->notify
->requests
!= NULL
) {
791 change_notify_reply(fsp
->notify
->requests
->req
,
793 fsp
->notify
->requests
->max_param
,
795 fsp
->notify
->requests
->reply_fn
);
796 change_notify_remove_request(fsp
->conn
->sconn
,
797 fsp
->notify
->requests
);
802 /* If we've exceeded the server side queue or received a NULL name
803 * from the underlying CN implementation, don't queue up any more
804 * requests until we can send a catch-all response to the client */
805 if (fsp
->notify
->num_changes
== -1) {
809 if (!(changes
= talloc_realloc(
810 fsp
->notify
, fsp
->notify
->changes
,
811 struct notify_change_event
,
812 fsp
->notify
->num_changes
+1))) {
813 DEBUG(0, ("talloc_realloc failed\n"));
817 fsp
->notify
->changes
= changes
;
819 change
= &(fsp
->notify
->changes
[fsp
->notify
->num_changes
]);
821 if (!(tmp
= talloc_strdup(changes
, name
))) {
822 DEBUG(0, ("talloc_strdup failed\n"));
826 string_replace(tmp
, '/', '\\');
830 change
->action
= action
;
831 fsp
->notify
->num_changes
+= 1;
833 if (fsp
->notify
->requests
== NULL
) {
835 * Nobody is waiting, so don't send anything. The ot
840 if (action
== NOTIFY_ACTION_OLD_NAME
) {
842 * We have to send the two rename events in one reply. So hold
843 * the first part back.
849 * Someone is waiting for the change, trigger the reply immediately.
851 * TODO: do we have to walk the lists of requests pending?
854 change_notify_reply(fsp
->notify
->requests
->req
,
856 fsp
->notify
->requests
->max_param
,
858 fsp
->notify
->requests
->reply_fn
);
860 change_notify_remove_request(fsp
->conn
->sconn
, fsp
->notify
->requests
);
863 char *notify_filter_string(TALLOC_CTX
*mem_ctx
, uint32_t filter
)
867 result
= talloc_strdup(mem_ctx
, "");
868 if (result
== NULL
) {
872 if (filter
& FILE_NOTIFY_CHANGE_FILE_NAME
) {
873 result
= talloc_asprintf_append(result
, "FILE_NAME|");
874 if (result
== NULL
) {
878 if (filter
& FILE_NOTIFY_CHANGE_DIR_NAME
) {
879 result
= talloc_asprintf_append(result
, "DIR_NAME|");
880 if (result
== NULL
) {
884 if (filter
& FILE_NOTIFY_CHANGE_ATTRIBUTES
) {
885 result
= talloc_asprintf_append(result
, "ATTRIBUTES|");
886 if (result
== NULL
) {
890 if (filter
& FILE_NOTIFY_CHANGE_SIZE
) {
891 result
= talloc_asprintf_append(result
, "SIZE|");
892 if (result
== NULL
) {
896 if (filter
& FILE_NOTIFY_CHANGE_LAST_WRITE
) {
897 result
= talloc_asprintf_append(result
, "LAST_WRITE|");
898 if (result
== NULL
) {
902 if (filter
& FILE_NOTIFY_CHANGE_LAST_ACCESS
) {
903 result
= talloc_asprintf_append(result
, "LAST_ACCESS|");
904 if (result
== NULL
) {
908 if (filter
& FILE_NOTIFY_CHANGE_CREATION
) {
909 result
= talloc_asprintf_append(result
, "CREATION|");
910 if (result
== NULL
) {
914 if (filter
& FILE_NOTIFY_CHANGE_EA
) {
915 result
= talloc_asprintf_append(result
, "EA|");
916 if (result
== NULL
) {
920 if (filter
& FILE_NOTIFY_CHANGE_SECURITY
) {
921 result
= talloc_asprintf_append(result
, "SECURITY|");
922 if (result
== NULL
) {
926 if (filter
& FILE_NOTIFY_CHANGE_STREAM_NAME
) {
927 result
= talloc_asprintf_append(result
, "STREAM_NAME|");
928 if (result
== NULL
) {
932 if (filter
& FILE_NOTIFY_CHANGE_STREAM_SIZE
) {
933 result
= talloc_asprintf_append(result
, "STREAM_SIZE|");
934 if (result
== NULL
) {
938 if (filter
& FILE_NOTIFY_CHANGE_STREAM_WRITE
) {
939 result
= talloc_asprintf_append(result
, "STREAM_WRITE|");
940 if (result
== NULL
) {
945 if (*result
== '\0') return result
;
947 result
[strlen(result
)-1] = '\0';
951 struct sys_notify_context
*sys_notify_context_create(TALLOC_CTX
*mem_ctx
,
952 struct tevent_context
*ev
)
954 struct sys_notify_context
*ctx
;
956 if (!(ctx
= talloc(mem_ctx
, struct sys_notify_context
))) {
957 DEBUG(0, ("talloc failed\n"));
962 ctx
->private_data
= NULL
;