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 if (!(fsp
->access_mask
& SEC_DIR_LIST
)) {
299 return NT_STATUS_ACCESS_DENIED
;
302 if (fsp
->notify
!= NULL
) {
303 DEBUG(1, ("change_notify_create: fsp->notify != NULL, "
304 "fname = %s\n", fsp
->fsp_name
->base_name
));
305 return NT_STATUS_INVALID_PARAMETER
;
308 if (!(fsp
->notify
= talloc_zero(NULL
, struct notify_change_buf
))) {
309 DEBUG(0, ("talloc failed\n"));
310 return NT_STATUS_NO_MEMORY
;
312 fsp
->notify
->filter
= filter
;
313 fsp
->notify
->subdir_filter
= recursive
? filter
: 0;
314 fsp
->notify
->max_buffer_size
= max_buffer_size
;
316 fsp_fullbasepath(fsp
, fullpath
, sizeof(fullpath
));
319 * Avoid /. at the end of the path name. notify can't deal with it.
321 if (len
> 1 && fullpath
[len
-1] == '.' && fullpath
[len
-2] == '/') {
322 fullpath
[len
-2] = '\0';
325 if ((fsp
->notify
->filter
!= 0) ||
326 (fsp
->notify
->subdir_filter
!= 0)) {
327 status
= notify_add(fsp
->conn
->sconn
->notify_ctx
,
328 fullpath
, fsp
->notify
->filter
,
329 fsp
->notify
->subdir_filter
, fsp
);
335 NTSTATUS
change_notify_add_request(struct smb_request
*req
,
337 uint32_t filter
, bool recursive
,
338 struct files_struct
*fsp
,
339 void (*reply_fn
)(struct smb_request
*req
,
341 uint8_t *buf
, size_t len
))
343 struct notify_change_request
*request
= NULL
;
344 struct notify_mid_map
*map
= NULL
;
345 struct smbd_server_connection
*sconn
= req
->sconn
;
347 DEBUG(10, ("change_notify_add_request: Adding request for %s: "
348 "max_param = %d\n", fsp_str_dbg(fsp
), (int)max_param
));
350 if (!(request
= talloc(NULL
, struct notify_change_request
))
351 || !(map
= talloc(request
, struct notify_mid_map
))) {
352 TALLOC_FREE(request
);
353 return NT_STATUS_NO_MEMORY
;
356 request
->mid_map
= map
;
359 request
->req
= talloc_move(request
, &req
);
360 request
->max_param
= max_param
;
361 request
->filter
= filter
;
363 request
->reply_fn
= reply_fn
;
364 request
->backend_data
= NULL
;
366 DLIST_ADD_END(fsp
->notify
->requests
, request
);
368 map
->mid
= request
->req
->mid
;
369 DLIST_ADD(sconn
->notify_mid_maps
, map
);
374 static void change_notify_remove_request(struct smbd_server_connection
*sconn
,
375 struct notify_change_request
*remove_req
)
378 struct notify_change_request
*req
;
381 * Paranoia checks, the fsp referenced must must have the request in
382 * its list of pending requests
385 fsp
= remove_req
->fsp
;
386 SMB_ASSERT(fsp
->notify
!= NULL
);
388 for (req
= fsp
->notify
->requests
; req
; req
= req
->next
) {
389 if (req
== remove_req
) {
395 smb_panic("notify_req not found in fsp's requests");
398 DLIST_REMOVE(fsp
->notify
->requests
, req
);
399 DLIST_REMOVE(sconn
->notify_mid_maps
, req
->mid_map
);
403 static void smbd_notify_cancel_by_map(struct notify_mid_map
*map
)
405 struct smb_request
*smbreq
= map
->req
->req
;
406 struct smbd_server_connection
*sconn
= smbreq
->sconn
;
407 struct smbd_smb2_request
*smb2req
= smbreq
->smb2req
;
408 NTSTATUS notify_status
= NT_STATUS_CANCELLED
;
410 if (smb2req
!= NULL
) {
413 if (smb2req
->session
== NULL
) {
414 sstatus
= NT_STATUS_USER_SESSION_DELETED
;
416 sstatus
= smb2req
->session
->status
;
419 if (NT_STATUS_EQUAL(sstatus
, NT_STATUS_NETWORK_SESSION_EXPIRED
)) {
420 sstatus
= NT_STATUS_OK
;
423 if (!NT_STATUS_IS_OK(sstatus
)) {
424 notify_status
= NT_STATUS_NOTIFY_CLEANUP
;
425 } else if (smb2req
->tcon
== NULL
) {
426 notify_status
= NT_STATUS_NOTIFY_CLEANUP
;
427 } else if (!NT_STATUS_IS_OK(smb2req
->tcon
->status
)) {
428 notify_status
= NT_STATUS_NOTIFY_CLEANUP
;
432 change_notify_reply(smbreq
, notify_status
,
433 0, NULL
, map
->req
->reply_fn
);
434 change_notify_remove_request(sconn
, map
->req
);
437 /****************************************************************************
438 Delete entries by mid from the change notify pending queue. Always send reply.
439 *****************************************************************************/
441 bool remove_pending_change_notify_requests_by_mid(
442 struct smbd_server_connection
*sconn
, uint64_t mid
)
444 struct notify_mid_map
*map
;
446 for (map
= sconn
->notify_mid_maps
; map
; map
= map
->next
) {
447 if (map
->mid
== mid
) {
456 smbd_notify_cancel_by_map(map
);
460 void smbd_notify_cancel_by_smbreq(const struct smb_request
*smbreq
)
462 struct smbd_server_connection
*sconn
= smbreq
->sconn
;
463 struct notify_mid_map
*map
;
465 for (map
= sconn
->notify_mid_maps
; map
; map
= map
->next
) {
466 if (map
->req
->req
== smbreq
) {
475 smbd_notify_cancel_by_map(map
);
478 static struct files_struct
*smbd_notify_cancel_deleted_fn(
479 struct files_struct
*fsp
, void *private_data
)
481 struct file_id
*fid
= talloc_get_type_abort(
482 private_data
, struct file_id
);
484 if (file_id_equal(&fsp
->file_id
, fid
)) {
485 remove_pending_change_notify_requests_by_fid(
486 fsp
, NT_STATUS_DELETE_PENDING
);
491 void smbd_notify_cancel_deleted(struct messaging_context
*msg
,
492 void *private_data
, uint32_t msg_type
,
493 struct server_id server_id
, DATA_BLOB
*data
)
495 struct smbd_server_connection
*sconn
= talloc_get_type_abort(
496 private_data
, struct smbd_server_connection
);
498 enum ndr_err_code ndr_err
;
500 fid
= talloc(talloc_tos(), struct file_id
);
502 DEBUG(1, ("talloc failed\n"));
506 ndr_err
= ndr_pull_struct_blob_all(
507 data
, fid
, fid
, (ndr_pull_flags_fn_t
)ndr_pull_file_id
);
508 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
509 DEBUG(10, ("%s: ndr_pull_file_id failed: %s\n", __func__
,
510 ndr_errstr(ndr_err
)));
514 files_forall(sconn
, smbd_notify_cancel_deleted_fn
, fid
);
520 static struct files_struct
*smbd_notifyd_reregister(struct files_struct
*fsp
,
523 DBG_DEBUG("reregister %s\n", fsp
->fsp_name
->base_name
);
525 if ((fsp
->conn
->sconn
->notify_ctx
!= NULL
) &&
526 (fsp
->notify
!= NULL
) &&
527 ((fsp
->notify
->filter
!= 0) ||
528 (fsp
->notify
->subdir_filter
!= 0))) {
529 size_t len
= fsp_fullbasepath(fsp
, NULL
, 0);
530 char fullpath
[len
+1];
534 fsp_fullbasepath(fsp
, fullpath
, sizeof(fullpath
));
535 if (len
> 1 && fullpath
[len
-1] == '.' &&
536 fullpath
[len
-2] == '/') {
537 fullpath
[len
-2] = '\0';
540 status
= notify_add(fsp
->conn
->sconn
->notify_ctx
,
541 fullpath
, fsp
->notify
->filter
,
542 fsp
->notify
->subdir_filter
, fsp
);
543 if (!NT_STATUS_IS_OK(status
)) {
544 DBG_DEBUG("notify_add failed: %s\n",
551 void smbd_notifyd_restarted(struct messaging_context
*msg
,
552 void *private_data
, uint32_t msg_type
,
553 struct server_id server_id
, DATA_BLOB
*data
)
555 struct smbd_server_connection
*sconn
= talloc_get_type_abort(
556 private_data
, struct smbd_server_connection
);
558 TALLOC_FREE(sconn
->notify_ctx
);
560 sconn
->notify_ctx
= notify_init(sconn
, sconn
->msg_ctx
,
561 sconn
, notify_callback
);
562 if (sconn
->notify_ctx
== NULL
) {
563 DBG_DEBUG("notify_init failed\n");
567 files_forall(sconn
, smbd_notifyd_reregister
, sconn
->notify_ctx
);
570 /****************************************************************************
571 Delete entries by fnum from the change notify pending queue.
572 *****************************************************************************/
574 void remove_pending_change_notify_requests_by_fid(files_struct
*fsp
,
577 if (fsp
->notify
== NULL
) {
581 while (fsp
->notify
->requests
!= NULL
) {
582 change_notify_reply(fsp
->notify
->requests
->req
,
584 fsp
->notify
->requests
->reply_fn
);
585 change_notify_remove_request(fsp
->conn
->sconn
,
586 fsp
->notify
->requests
);
590 void notify_fname(connection_struct
*conn
, uint32_t action
, uint32_t filter
,
593 struct notify_context
*notify_ctx
= conn
->sconn
->notify_ctx
;
595 if (path
[0] == '.' && path
[1] == '/') {
599 notify_trigger(notify_ctx
, action
, filter
, conn
->connectpath
, path
);
602 static bool user_can_stat_name_under_fsp(files_struct
*fsp
, const char *name
)
605 struct smb_filename
*fname
= NULL
;
606 char *filepath
= NULL
;
611 * Assume we get filepath (relative to the share)
614 * 'dir1/dir2/dir3/file'
616 * We start with LIST and TRAVERSE on the
617 * direct parent ('dir1/dir2/dir3')
619 * Then we switch to just TRAVERSE for
620 * the rest: 'dir1/dir2', 'dir1', '.'
622 * For a file in the share root, we'll have
624 * and would just check '.' with LIST and TRAVERSE.
626 * It's important to always check '.' as the last step,
627 * which means we check the permissions of the share root
631 if (ISDOT(fsp
->fsp_name
->base_name
)) {
632 filepath
= talloc_strdup(talloc_tos(), name
);
634 filepath
= talloc_asprintf(talloc_tos(),
636 fsp
->fsp_name
->base_name
,
639 if (filepath
== NULL
) {
640 DBG_ERR("Memory allocation failed\n");
644 rights
= SEC_DIR_LIST
|SEC_DIR_TRAVERSE
;
645 p
= strrchr_m(filepath
, '/');
647 * Check each path component, exluding the share root.
649 * We could check all components including root using
650 * a do { .. } while() loop, but IMHO the logic is clearer
651 * having the share root check separately afterwards.
655 status
= synthetic_pathref(talloc_tos(),
663 if (!NT_STATUS_IS_OK(status
)) {
664 DBG_ERR("synthetic_pathref failed for %s, error %s\n",
668 TALLOC_FREE(filepath
);
672 status
= smbd_check_access_rights_fsp(fsp
->conn
->cwd_fsp
,
676 if (!NT_STATUS_IS_OK(status
)) {
677 DBG_DEBUG("Access rights for %s/%s: %s\n",
678 fsp
->conn
->connectpath
,
682 TALLOC_FREE(filepath
);
687 rights
= SEC_DIR_TRAVERSE
;
688 p
= strrchr_m(filepath
, '/');
691 TALLOC_FREE(filepath
);
693 /* Finally check share root. */
694 filepath
= talloc_strdup(talloc_tos(), ".");
695 if (filepath
== NULL
) {
696 DBG_ERR("Memory allocation failed\n");
699 status
= synthetic_pathref(talloc_tos(),
707 if (!NT_STATUS_IS_OK(status
)) {
708 DBG_ERR("synthetic_pathref failed for %s, error %s\n",
712 TALLOC_FREE(filepath
);
715 status
= smbd_check_access_rights_fsp(fsp
->conn
->cwd_fsp
,
719 if (!NT_STATUS_IS_OK(status
)) {
720 DBG_DEBUG("TRAVERSE access rights for %s failed with %s\n",
721 fsp
->conn
->connectpath
,
724 TALLOC_FREE(filepath
);
728 TALLOC_FREE(filepath
);
732 static void notify_fsp(files_struct
*fsp
, struct timespec when
,
733 uint32_t action
, const char *name
)
735 struct notify_change_event
*change
, *changes
;
738 if (fsp
->notify
== NULL
) {
740 * Nobody is waiting, don't queue
745 if (lp_honor_change_notify_privilege(SNUM(fsp
->conn
))) {
746 bool has_sec_change_notify_privilege
;
749 has_sec_change_notify_privilege
= security_token_has_privilege(
750 fsp
->conn
->session_info
->security_token
,
751 SEC_PRIV_CHANGE_NOTIFY
);
753 if (has_sec_change_notify_privilege
) {
758 ok
= become_user_without_service_by_fsp(fsp
);
760 expose
= user_can_stat_name_under_fsp(fsp
, name
);
761 unbecome_user_without_service();
764 DBG_DEBUG("has_sec_change_notify_privilege=%s "
765 "expose=%s for %s notify %s\n",
766 has_sec_change_notify_privilege
? "true" : "false",
767 expose
? "true" : "false",
768 fsp
->fsp_name
->base_name
, name
);
775 * Someone has triggered a notify previously, queue the change for
779 if ((fsp
->notify
->num_changes
> 1000) || (name
== NULL
)) {
781 * The real number depends on the client buf, just provide a
782 * guard against a DoS here. If name == NULL the CN backend is
783 * alerting us to a problem. Possibly dropped events. Clear
784 * queued changes and send the catch-all response to the client
785 * if a request is pending.
787 TALLOC_FREE(fsp
->notify
->changes
);
788 fsp
->notify
->num_changes
= -1;
789 if (fsp
->notify
->requests
!= NULL
) {
790 change_notify_reply(fsp
->notify
->requests
->req
,
792 fsp
->notify
->requests
->max_param
,
794 fsp
->notify
->requests
->reply_fn
);
795 change_notify_remove_request(fsp
->conn
->sconn
,
796 fsp
->notify
->requests
);
801 /* If we've exceeded the server side queue or received a NULL name
802 * from the underlying CN implementation, don't queue up any more
803 * requests until we can send a catch-all response to the client */
804 if (fsp
->notify
->num_changes
== -1) {
808 if (!(changes
= talloc_realloc(
809 fsp
->notify
, fsp
->notify
->changes
,
810 struct notify_change_event
,
811 fsp
->notify
->num_changes
+1))) {
812 DEBUG(0, ("talloc_realloc failed\n"));
816 fsp
->notify
->changes
= changes
;
818 change
= &(fsp
->notify
->changes
[fsp
->notify
->num_changes
]);
820 if (!(tmp
= talloc_strdup(changes
, name
))) {
821 DEBUG(0, ("talloc_strdup failed\n"));
825 string_replace(tmp
, '/', '\\');
829 change
->action
= action
;
830 fsp
->notify
->num_changes
+= 1;
832 if (fsp
->notify
->requests
== NULL
) {
834 * Nobody is waiting, so don't send anything. The ot
839 if (action
== NOTIFY_ACTION_OLD_NAME
) {
841 * We have to send the two rename events in one reply. So hold
842 * the first part back.
848 * Someone is waiting for the change, trigger the reply immediately.
850 * TODO: do we have to walk the lists of requests pending?
853 change_notify_reply(fsp
->notify
->requests
->req
,
855 fsp
->notify
->requests
->max_param
,
857 fsp
->notify
->requests
->reply_fn
);
859 change_notify_remove_request(fsp
->conn
->sconn
, fsp
->notify
->requests
);
862 char *notify_filter_string(TALLOC_CTX
*mem_ctx
, uint32_t filter
)
866 result
= talloc_strdup(mem_ctx
, "");
868 if (filter
& FILE_NOTIFY_CHANGE_FILE_NAME
)
869 result
= talloc_asprintf_append(result
, "FILE_NAME|");
870 if (filter
& FILE_NOTIFY_CHANGE_DIR_NAME
)
871 result
= talloc_asprintf_append(result
, "DIR_NAME|");
872 if (filter
& FILE_NOTIFY_CHANGE_ATTRIBUTES
)
873 result
= talloc_asprintf_append(result
, "ATTRIBUTES|");
874 if (filter
& FILE_NOTIFY_CHANGE_SIZE
)
875 result
= talloc_asprintf_append(result
, "SIZE|");
876 if (filter
& FILE_NOTIFY_CHANGE_LAST_WRITE
)
877 result
= talloc_asprintf_append(result
, "LAST_WRITE|");
878 if (filter
& FILE_NOTIFY_CHANGE_LAST_ACCESS
)
879 result
= talloc_asprintf_append(result
, "LAST_ACCESS|");
880 if (filter
& FILE_NOTIFY_CHANGE_CREATION
)
881 result
= talloc_asprintf_append(result
, "CREATION|");
882 if (filter
& FILE_NOTIFY_CHANGE_EA
)
883 result
= talloc_asprintf_append(result
, "EA|");
884 if (filter
& FILE_NOTIFY_CHANGE_SECURITY
)
885 result
= talloc_asprintf_append(result
, "SECURITY|");
886 if (filter
& FILE_NOTIFY_CHANGE_STREAM_NAME
)
887 result
= talloc_asprintf_append(result
, "STREAM_NAME|");
888 if (filter
& FILE_NOTIFY_CHANGE_STREAM_SIZE
)
889 result
= talloc_asprintf_append(result
, "STREAM_SIZE|");
890 if (filter
& FILE_NOTIFY_CHANGE_STREAM_WRITE
)
891 result
= talloc_asprintf_append(result
, "STREAM_WRITE|");
893 if (result
== NULL
) return NULL
;
894 if (*result
== '\0') return result
;
896 result
[strlen(result
)-1] = '\0';
900 struct sys_notify_context
*sys_notify_context_create(TALLOC_CTX
*mem_ctx
,
901 struct tevent_context
*ev
)
903 struct sys_notify_context
*ctx
;
905 if (!(ctx
= talloc(mem_ctx
, struct sys_notify_context
))) {
906 DEBUG(0, ("talloc failed\n"));
911 ctx
->private_data
= NULL
;