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
));
319 if ((fsp
->notify
->filter
!= 0) ||
320 (fsp
->notify
->subdir_filter
!= 0)) {
321 status
= notify_add(fsp
->conn
->sconn
->notify_ctx
,
322 fullpath
, fsp
->notify
->filter
,
323 fsp
->notify
->subdir_filter
, fsp
);
329 NTSTATUS
change_notify_add_request(struct smb_request
*req
,
331 uint32_t filter
, bool recursive
,
332 struct files_struct
*fsp
,
333 void (*reply_fn
)(struct smb_request
*req
,
335 uint8_t *buf
, size_t len
))
337 struct notify_change_request
*request
= NULL
;
338 struct notify_mid_map
*map
= NULL
;
339 struct smbd_server_connection
*sconn
= req
->sconn
;
341 DEBUG(10, ("change_notify_add_request: Adding request for %s: "
342 "max_param = %d\n", fsp_str_dbg(fsp
), (int)max_param
));
344 if (!(request
= talloc(NULL
, struct notify_change_request
))
345 || !(map
= talloc(request
, struct notify_mid_map
))) {
346 TALLOC_FREE(request
);
347 return NT_STATUS_NO_MEMORY
;
350 request
->mid_map
= map
;
353 request
->req
= talloc_move(request
, &req
);
354 request
->max_param
= max_param
;
355 request
->filter
= filter
;
357 request
->reply_fn
= reply_fn
;
358 request
->backend_data
= NULL
;
360 DLIST_ADD_END(fsp
->notify
->requests
, request
);
362 map
->mid
= request
->req
->mid
;
363 DLIST_ADD(sconn
->notify_mid_maps
, map
);
368 static void change_notify_remove_request(struct smbd_server_connection
*sconn
,
369 struct notify_change_request
*remove_req
)
372 struct notify_change_request
*req
;
375 * Paranoia checks, the fsp referenced must must have the request in
376 * its list of pending requests
379 fsp
= remove_req
->fsp
;
380 SMB_ASSERT(fsp
->notify
!= NULL
);
382 for (req
= fsp
->notify
->requests
; req
; req
= req
->next
) {
383 if (req
== remove_req
) {
389 smb_panic("notify_req not found in fsp's requests");
392 DLIST_REMOVE(fsp
->notify
->requests
, req
);
393 DLIST_REMOVE(sconn
->notify_mid_maps
, req
->mid_map
);
397 static void smbd_notify_cancel_by_map(struct notify_mid_map
*map
)
399 struct smb_request
*smbreq
= map
->req
->req
;
400 struct smbd_server_connection
*sconn
= smbreq
->sconn
;
401 struct smbd_smb2_request
*smb2req
= smbreq
->smb2req
;
402 NTSTATUS notify_status
= NT_STATUS_CANCELLED
;
404 if (smb2req
!= NULL
) {
407 if (smb2req
->session
== NULL
) {
408 sstatus
= NT_STATUS_USER_SESSION_DELETED
;
410 sstatus
= smb2req
->session
->status
;
413 if (NT_STATUS_EQUAL(sstatus
, NT_STATUS_NETWORK_SESSION_EXPIRED
)) {
414 sstatus
= NT_STATUS_OK
;
417 if (!NT_STATUS_IS_OK(sstatus
)) {
418 notify_status
= NT_STATUS_NOTIFY_CLEANUP
;
419 } else if (smb2req
->tcon
== NULL
) {
420 notify_status
= NT_STATUS_NOTIFY_CLEANUP
;
421 } else if (!NT_STATUS_IS_OK(smb2req
->tcon
->status
)) {
422 notify_status
= NT_STATUS_NOTIFY_CLEANUP
;
426 change_notify_reply(smbreq
, notify_status
,
427 0, NULL
, map
->req
->reply_fn
);
428 change_notify_remove_request(sconn
, map
->req
);
431 /****************************************************************************
432 Delete entries by mid from the change notify pending queue. Always send reply.
433 *****************************************************************************/
435 bool remove_pending_change_notify_requests_by_mid(
436 struct smbd_server_connection
*sconn
, uint64_t mid
)
438 struct notify_mid_map
*map
;
440 for (map
= sconn
->notify_mid_maps
; map
; map
= map
->next
) {
441 if (map
->mid
== mid
) {
450 smbd_notify_cancel_by_map(map
);
454 void smbd_notify_cancel_by_smbreq(const struct smb_request
*smbreq
)
456 struct smbd_server_connection
*sconn
= smbreq
->sconn
;
457 struct notify_mid_map
*map
;
459 for (map
= sconn
->notify_mid_maps
; map
; map
= map
->next
) {
460 if (map
->req
->req
== smbreq
) {
469 smbd_notify_cancel_by_map(map
);
472 static struct files_struct
*smbd_notify_cancel_deleted_fn(
473 struct files_struct
*fsp
, void *private_data
)
475 struct file_id
*fid
= talloc_get_type_abort(
476 private_data
, struct file_id
);
478 if (file_id_equal(&fsp
->file_id
, fid
)) {
479 remove_pending_change_notify_requests_by_fid(
480 fsp
, NT_STATUS_DELETE_PENDING
);
485 void smbd_notify_cancel_deleted(struct messaging_context
*msg
,
486 void *private_data
, uint32_t msg_type
,
487 struct server_id server_id
, DATA_BLOB
*data
)
489 struct smbd_server_connection
*sconn
= talloc_get_type_abort(
490 private_data
, struct smbd_server_connection
);
492 enum ndr_err_code ndr_err
;
494 fid
= talloc(talloc_tos(), struct file_id
);
496 DEBUG(1, ("talloc failed\n"));
500 ndr_err
= ndr_pull_struct_blob_all(
501 data
, fid
, fid
, (ndr_pull_flags_fn_t
)ndr_pull_file_id
);
502 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
503 DEBUG(10, ("%s: ndr_pull_file_id failed: %s\n", __func__
,
504 ndr_errstr(ndr_err
)));
508 files_forall(sconn
, smbd_notify_cancel_deleted_fn
, fid
);
514 static struct files_struct
*smbd_notifyd_reregister(struct files_struct
*fsp
,
517 DBG_DEBUG("reregister %s\n", fsp
->fsp_name
->base_name
);
519 if ((fsp
->conn
->sconn
->notify_ctx
!= NULL
) &&
520 (fsp
->notify
!= NULL
) &&
521 ((fsp
->notify
->filter
!= 0) ||
522 (fsp
->notify
->subdir_filter
!= 0))) {
523 size_t len
= fsp_fullbasepath(fsp
, NULL
, 0);
524 char fullpath
[len
+1];
528 fsp_fullbasepath(fsp
, fullpath
, sizeof(fullpath
));
530 status
= notify_add(fsp
->conn
->sconn
->notify_ctx
,
531 fullpath
, fsp
->notify
->filter
,
532 fsp
->notify
->subdir_filter
, fsp
);
533 if (!NT_STATUS_IS_OK(status
)) {
534 DBG_DEBUG("notify_add failed: %s\n",
541 void smbd_notifyd_restarted(struct messaging_context
*msg
,
542 void *private_data
, uint32_t msg_type
,
543 struct server_id server_id
, DATA_BLOB
*data
)
545 struct smbd_server_connection
*sconn
= talloc_get_type_abort(
546 private_data
, struct smbd_server_connection
);
548 TALLOC_FREE(sconn
->notify_ctx
);
550 sconn
->notify_ctx
= notify_init(sconn
, sconn
->msg_ctx
,
551 sconn
, notify_callback
);
552 if (sconn
->notify_ctx
== NULL
) {
553 DBG_DEBUG("notify_init failed\n");
557 files_forall(sconn
, smbd_notifyd_reregister
, sconn
->notify_ctx
);
560 /****************************************************************************
561 Delete entries by fnum from the change notify pending queue.
562 *****************************************************************************/
564 void remove_pending_change_notify_requests_by_fid(files_struct
*fsp
,
567 if (fsp
->notify
== NULL
) {
571 while (fsp
->notify
->requests
!= NULL
) {
572 change_notify_reply(fsp
->notify
->requests
->req
,
574 fsp
->notify
->requests
->reply_fn
);
575 change_notify_remove_request(fsp
->conn
->sconn
,
576 fsp
->notify
->requests
);
580 void notify_fname(struct connection_struct
*conn
,
583 const struct smb_filename
*smb_fname
,
584 const struct smb2_lease
*lease
)
586 struct notify_context
*notify_ctx
= conn
->sconn
->notify_ctx
;
587 const char *path
= smb_fname
->base_name
;
589 if (action
& NOTIFY_ACTION_DIRLEASE_BREAK
) {
590 contend_dirleases(conn
, smb_fname
, lease
);
592 action
&= ~NOTIFY_ACTION_DIRLEASE_BREAK
;
597 if (path
[0] == '.' && path
[1] == '/') {
601 notify_trigger(notify_ctx
, action
, filter
, conn
->connectpath
, path
);
604 static bool user_can_stat_name_under_fsp(files_struct
*fsp
, const char *name
)
607 struct smb_filename
*fname
= NULL
;
608 char *filepath
= NULL
;
613 * Assume we get filepath (relative to the share)
616 * 'dir1/dir2/dir3/file'
618 * We start with LIST and TRAVERSE on the
619 * direct parent ('dir1/dir2/dir3')
621 * Then we switch to just TRAVERSE for
622 * the rest: 'dir1/dir2', 'dir1', '.'
624 * For a file in the share root, we'll have
626 * and would just check '.' with LIST and TRAVERSE.
628 * It's important to always check '.' as the last step,
629 * which means we check the permissions of the share root
633 if (ISDOT(fsp
->fsp_name
->base_name
)) {
634 filepath
= talloc_strdup(talloc_tos(), name
);
636 filepath
= talloc_asprintf(talloc_tos(),
638 fsp
->fsp_name
->base_name
,
641 if (filepath
== NULL
) {
642 DBG_ERR("Memory allocation failed\n");
646 rights
= SEC_DIR_LIST
|SEC_DIR_TRAVERSE
;
647 p
= strrchr_m(filepath
, '/');
649 * Check each path component, excluding the share root.
651 * We could check all components including root using
652 * a do { .. } while() loop, but IMHO the logic is clearer
653 * having the share root check separately afterwards.
657 status
= synthetic_pathref(talloc_tos(),
665 if (!NT_STATUS_IS_OK(status
)) {
666 int dbg_lvl
= DBGLVL_ERR
;
667 if (NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
)) {
668 dbg_lvl
= DBGLVL_DEBUG
;
670 DBG_PREFIX(dbg_lvl
, (
671 "synthetic_pathref failed for %s, error %s\n",
675 TALLOC_FREE(filepath
);
679 status
= smbd_check_access_rights_fsp(fsp
->conn
->cwd_fsp
,
683 if (!NT_STATUS_IS_OK(status
)) {
684 DBG_DEBUG("Access rights for %s/%s: %s\n",
685 fsp
->conn
->connectpath
,
689 TALLOC_FREE(filepath
);
694 rights
= SEC_DIR_TRAVERSE
;
695 p
= strrchr_m(filepath
, '/');
698 TALLOC_FREE(filepath
);
700 /* Finally check share root. */
701 filepath
= talloc_strdup(talloc_tos(), ".");
702 if (filepath
== NULL
) {
703 DBG_ERR("Memory allocation failed\n");
706 status
= synthetic_pathref(talloc_tos(),
714 if (!NT_STATUS_IS_OK(status
)) {
715 DBG_ERR("synthetic_pathref failed for %s, error %s\n",
719 TALLOC_FREE(filepath
);
722 status
= smbd_check_access_rights_fsp(fsp
->conn
->cwd_fsp
,
726 if (!NT_STATUS_IS_OK(status
)) {
727 DBG_DEBUG("TRAVERSE access rights for %s failed with %s\n",
728 fsp
->conn
->connectpath
,
731 TALLOC_FREE(filepath
);
735 TALLOC_FREE(filepath
);
739 static void notify_fsp(files_struct
*fsp
, struct timespec when
,
740 uint32_t action
, const char *name
)
742 struct notify_change_event
*change
, *changes
;
745 if (fsp
->notify
== NULL
) {
747 * Nobody is waiting, don't queue
752 if (lp_honor_change_notify_privilege(SNUM(fsp
->conn
))) {
753 bool has_sec_change_notify_privilege
;
756 has_sec_change_notify_privilege
= security_token_has_privilege(
757 fsp
->conn
->session_info
->security_token
,
758 SEC_PRIV_CHANGE_NOTIFY
);
760 if (has_sec_change_notify_privilege
) {
765 ok
= become_user_without_service_by_fsp(fsp
);
767 expose
= user_can_stat_name_under_fsp(fsp
, name
);
768 unbecome_user_without_service();
771 DBG_DEBUG("has_sec_change_notify_privilege=%s "
772 "expose=%s for %s notify %s\n",
773 has_sec_change_notify_privilege
? "true" : "false",
774 expose
? "true" : "false",
775 fsp
->fsp_name
->base_name
, name
);
782 * Someone has triggered a notify previously, queue the change for
786 if ((fsp
->notify
->num_changes
> 1000) || (name
== NULL
)) {
788 * The real number depends on the client buf, just provide a
789 * guard against a DoS here. If name == NULL the CN backend is
790 * alerting us to a problem. Possibly dropped events. Clear
791 * queued changes and send the catch-all response to the client
792 * if a request is pending.
794 TALLOC_FREE(fsp
->notify
->changes
);
795 fsp
->notify
->num_changes
= -1;
796 if (fsp
->notify
->requests
!= NULL
) {
797 change_notify_reply(fsp
->notify
->requests
->req
,
799 fsp
->notify
->requests
->max_param
,
801 fsp
->notify
->requests
->reply_fn
);
802 change_notify_remove_request(fsp
->conn
->sconn
,
803 fsp
->notify
->requests
);
808 /* If we've exceeded the server side queue or received a NULL name
809 * from the underlying CN implementation, don't queue up any more
810 * requests until we can send a catch-all response to the client */
811 if (fsp
->notify
->num_changes
== -1) {
815 if (!(changes
= talloc_realloc(
816 fsp
->notify
, fsp
->notify
->changes
,
817 struct notify_change_event
,
818 fsp
->notify
->num_changes
+1))) {
819 DEBUG(0, ("talloc_realloc failed\n"));
823 fsp
->notify
->changes
= changes
;
825 change
= &(fsp
->notify
->changes
[fsp
->notify
->num_changes
]);
827 if (!(tmp
= talloc_strdup(changes
, name
))) {
828 DEBUG(0, ("talloc_strdup failed\n"));
832 string_replace(tmp
, '/', '\\');
836 change
->action
= action
;
837 fsp
->notify
->num_changes
+= 1;
839 if (fsp
->notify
->requests
== NULL
) {
841 * Nobody is waiting, so don't send anything. The ot
846 if (action
== NOTIFY_ACTION_OLD_NAME
) {
848 * We have to send the two rename events in one reply. So hold
849 * the first part back.
855 * Someone is waiting for the change, trigger the reply immediately.
857 * TODO: do we have to walk the lists of requests pending?
860 change_notify_reply(fsp
->notify
->requests
->req
,
862 fsp
->notify
->requests
->max_param
,
864 fsp
->notify
->requests
->reply_fn
);
866 change_notify_remove_request(fsp
->conn
->sconn
, fsp
->notify
->requests
);
869 char *notify_filter_string(TALLOC_CTX
*mem_ctx
, uint32_t filter
)
873 result
= talloc_strdup(mem_ctx
, "");
875 if (filter
& FILE_NOTIFY_CHANGE_FILE_NAME
) {
876 talloc_asprintf_addbuf(&result
, "FILE_NAME|");
878 if (filter
& FILE_NOTIFY_CHANGE_DIR_NAME
) {
879 talloc_asprintf_addbuf(&result
, "DIR_NAME|");
881 if (filter
& FILE_NOTIFY_CHANGE_ATTRIBUTES
) {
882 talloc_asprintf_addbuf(&result
, "ATTRIBUTES|");
884 if (filter
& FILE_NOTIFY_CHANGE_SIZE
) {
885 talloc_asprintf_addbuf(&result
, "SIZE|");
887 if (filter
& FILE_NOTIFY_CHANGE_LAST_WRITE
) {
888 talloc_asprintf_addbuf(&result
, "LAST_WRITE|");
890 if (filter
& FILE_NOTIFY_CHANGE_LAST_ACCESS
) {
891 talloc_asprintf_addbuf(&result
, "LAST_ACCESS|");
893 if (filter
& FILE_NOTIFY_CHANGE_CREATION
) {
894 talloc_asprintf_addbuf(&result
, "CREATION|");
896 if (filter
& FILE_NOTIFY_CHANGE_EA
) {
897 talloc_asprintf_addbuf(&result
, "EA|");
899 if (filter
& FILE_NOTIFY_CHANGE_SECURITY
) {
900 talloc_asprintf_addbuf(&result
, "SECURITY|");
902 if (filter
& FILE_NOTIFY_CHANGE_STREAM_NAME
) {
903 talloc_asprintf_addbuf(&result
, "STREAM_NAME|");
905 if (filter
& FILE_NOTIFY_CHANGE_STREAM_SIZE
) {
906 talloc_asprintf_addbuf(&result
, "STREAM_SIZE|");
908 if (filter
& FILE_NOTIFY_CHANGE_STREAM_WRITE
) {
909 talloc_asprintf_addbuf(&result
, "STREAM_WRITE|");
912 if (result
== NULL
) {
916 if (*result
== '\0') return result
;
918 result
[strlen(result
)-1] = '\0';
922 struct sys_notify_context
*sys_notify_context_create(TALLOC_CTX
*mem_ctx
,
923 struct tevent_context
*ev
)
925 struct sys_notify_context
*ctx
;
927 if (!(ctx
= talloc(mem_ctx
, struct sys_notify_context
))) {
928 DEBUG(0, ("talloc failed\n"));
933 ctx
->private_data
= NULL
;