diff --git a/source/include/smb.h b/source/include/smb.h index 5ad05cd2f98..4b4cebf5c18 100644 --- a/source/include/smb.h +++ b/source/include/smb.h @@ -669,6 +669,7 @@ typedef struct connection_struct { name_compare_entry *aio_write_behind_list; /* Per-share list of files to use aio write behind on. */ struct dfree_cached_info *dfree_info; struct trans_state *pending_trans; + struct notify_context *notify_ctx; } connection_struct; struct current_user { diff --git a/source/smbd/files.c b/source/smbd/files.c index 982de4c55fd..fc1700de118 100644 --- a/source/smbd/files.c +++ b/source/smbd/files.c @@ -483,7 +483,10 @@ void file_free(files_struct *fsp) fsp->fh->ref_count--; } - TALLOC_FREE(fsp->notify); + if (fsp->notify) { + notify_remove(fsp->conn->notify_ctx, fsp); + TALLOC_FREE(fsp->notify); + } bitmap_clear(file_bmap, fsp->fnum - FILE_HANDLE_OFFSET); files_used--; diff --git a/source/smbd/notify.c b/source/smbd/notify.c index 49219bcca52..93a6357d0c8 100644 --- a/source/smbd/notify.c +++ b/source/smbd/notify.c @@ -159,7 +159,8 @@ void change_notify_reply(const char *request_buf, uint32 max_param_count, } NTSTATUS change_notify_add_request(const char *inbuf, uint32 max_param_count, - uint32 filter, struct files_struct *fsp) + uint32 filter, BOOL recursive, + struct files_struct *fsp) { struct notify_change_request *request = NULL; struct notify_mid_map *map = NULL; @@ -177,9 +178,7 @@ NTSTATUS change_notify_add_request(const char *inbuf, uint32 max_param_count, request->max_param_count = max_param_count; request->filter = filter; request->fsp = fsp; - - request->backend_data = cnotify->notify_add(NULL, smbd_event_context(), - fsp, &request->filter); + request->backend_data = NULL; DLIST_ADD_END(fsp->notify->requests, request, struct notify_change_request *); @@ -343,6 +342,18 @@ void notify_action(connection_struct *conn, const char *parent, return; } + { + char *fullpath; + + if (asprintf(&fullpath, "%s/%s/%s", conn->connectpath, + parent, name) != -1) { + notify_trigger(conn->notify_ctx, action, filter, + fullpath); + SAFE_FREE(fullpath); + } + return; + } + if (!(lck = get_share_mode_lock(NULL, sbuf.st_dev, sbuf.st_ino, NULL, NULL))) { return; @@ -405,20 +416,21 @@ void notify_action(connection_struct *conn, const char *parent, void notify_fname(connection_struct *conn, uint32 action, uint32 filter, const char *path) { - char *parent; - const char *name; + char *fullpath; - if (!parent_dirname_talloc(tmp_talloc_ctx(), path, &parent, &name)) { + if (asprintf(&fullpath, "%s/%s", conn->connectpath, path) == -1) { + DEBUG(0, ("asprintf failed\n")); return; } - notify_action(conn, parent, name, filter, action); - TALLOC_FREE(parent); + notify_trigger(conn->notify_ctx, action, filter, fullpath); + SAFE_FREE(fullpath); } -void notify_fsp(files_struct *fsp, uint32 action, char *name) +void notify_fsp(files_struct *fsp, uint32 action, const char *name) { struct notify_change *change, *changes; + char *name2; if (fsp->notify == NULL) { /* @@ -427,35 +439,12 @@ void notify_fsp(files_struct *fsp, uint32 action, char *name) return; } - if (fsp->notify->requests != NULL) { - /* - * Someone is waiting for the change, trigger the reply - * immediately. - * - * TODO: do we have to walk the lists of requests pending? - */ - - struct notify_change_request *req = fsp->notify->requests; - struct notify_change onechange; - - if (name == NULL) { - /* - * Catch-all change, possibly from notify_hash.c - */ - change_notify_reply(req->request_buf, - req->max_param_count, - -1, NULL); + if (!(name2 = talloc_strdup(fsp->notify, name))) { + DEBUG(0, ("talloc_strdup failed\n")); return; } - onechange.action = action; - onechange.name = name; - - change_notify_reply(req->request_buf, req->max_param_count, - 1, &onechange); - change_notify_remove_request(req); - return; - } + string_replace(name2, '/', '\\'); /* * Someone has triggered a notify previously, queue the change for @@ -468,6 +457,7 @@ void notify_fsp(files_struct *fsp, uint32 action, char *name) * W2k3 seems to store at most 30 changes. */ TALLOC_FREE(fsp->notify->changes); + TALLOC_FREE(name2); fsp->notify->num_changes = -1; return; } @@ -480,6 +470,7 @@ void notify_fsp(files_struct *fsp, uint32 action, char *name) fsp->notify, fsp->notify->changes, struct notify_change, fsp->notify->num_changes+1))) { DEBUG(0, ("talloc_realloc failed\n")); + TALLOC_FREE(name2); return; } @@ -487,14 +478,40 @@ void notify_fsp(files_struct *fsp, uint32 action, char *name) change = &(fsp->notify->changes[fsp->notify->num_changes]); - if (!(change->name = talloc_strdup(changes, name))) { - DEBUG(0, ("talloc_strdup failed\n")); - return; - } + change->name = talloc_move(changes, &name2); change->action = action; fsp->notify->num_changes += 1; + if (fsp->notify->requests == NULL) { + /* + * Nobody is waiting, so don't send anything. The ot + */ + return; + } + + if (action == NOTIFY_ACTION_OLD_NAME) { + /* + * We have to send the two rename events in one reply. So hold + * the first part back. + */ return; + } + + /* + * Someone is waiting for the change, trigger the reply immediately. + * + * TODO: do we have to walk the lists of requests pending? + */ + + change_notify_reply(fsp->notify->requests->request_buf, + fsp->notify->requests->max_param_count, + fsp->notify->num_changes, + fsp->notify->changes); + + change_notify_remove_request(fsp->notify->requests); + + TALLOC_FREE(fsp->notify->changes); + fsp->notify->num_changes = 0; } static void notify_message_callback(int msgtype, struct process_id pid, diff --git a/source/smbd/nttrans.c b/source/smbd/nttrans.c index 07d345aacd4..6db6311a29b 100644 --- a/source/smbd/nttrans.c +++ b/source/smbd/nttrans.c @@ -1787,6 +1787,13 @@ int reply_ntrename(connection_struct *conn, don't allow a directory to be opened. ****************************************************************************/ +static void notify_callback(void *private_data, const struct notify_event *e) +{ + files_struct *fsp = (files_struct *)private_data; + DEBUG(10, ("notify_callback called for %s\n", fsp->fsp_name)); + notify_fsp(fsp, e->action, e->path); +} + static int call_nt_transact_notify_change(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize, @@ -1801,6 +1808,7 @@ static int call_nt_transact_notify_change(connection_struct *conn, char *inbuf, files_struct *fsp; uint32 filter; NTSTATUS status; + BOOL recursive; if(setup_count < 6) { return ERROR_DOS(ERRDOS,ERRbadfunc); @@ -1808,6 +1816,7 @@ static int call_nt_transact_notify_change(connection_struct *conn, char *inbuf, fsp = file_fsp((char *)setup,4); filter = IVAL(setup, 0); + recursive = (SVAL(setup, 6) != 0) ? True : False; DEBUG(3,("call_nt_transact_notify_change\n")); @@ -1815,18 +1824,55 @@ static int call_nt_transact_notify_change(connection_struct *conn, char *inbuf, return ERROR_DOS(ERRDOS,ERRbadfid); } - DEBUG(3,("call_nt_transact_notify_change: notify change called on " - "directory name = %s\n", fsp->fsp_name )); + { + char *filter_string; + + if (!(filter_string = notify_filter_string(NULL, filter))) { + return ERROR_NT(NT_STATUS_NO_MEMORY); + } + + DEBUG(3,("call_nt_transact_notify_change: notify change " + "called on %s, filter = %s, recursive = %d\n", + fsp->fsp_name, filter_string, recursive)); + + TALLOC_FREE(filter_string); + } if((!fsp->is_directory) || (conn != fsp->conn)) { return ERROR_DOS(ERRDOS,ERRbadfid); } if (fsp->notify == NULL) { + char *fullpath; + struct notify_entry e; + if (!(fsp->notify = TALLOC_ZERO_P( NULL, struct notify_change_buf))) { return ERROR_NT(NT_STATUS_NO_MEMORY); } + + if (asprintf(&fullpath, "%s/%s", fsp->conn->connectpath, + fsp->fsp_name) == -1) { + DEBUG(0, ("asprintf failed\n")); + return ERROR_NT(NT_STATUS_NO_MEMORY); + } + + e.path = fullpath; + e.filter = filter; + e.subdir_filter = 0; + if (recursive) { + e.subdir_filter = filter; + } + + status = notify_add(fsp->conn->notify_ctx, &e, notify_callback, + fsp); + SAFE_FREE(fullpath); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("notify_add returned %s\n", + nt_errstr(status))); + return ERROR_NT(status); + } } if (fsp->notify->num_changes != 0) { @@ -1840,8 +1886,6 @@ static int call_nt_transact_notify_change(connection_struct *conn, char *inbuf, * here. */ - SMB_ASSERT(fsp->notify->requests == NULL); - change_notify_reply(inbuf, max_param_count, fsp->notify->num_changes, fsp->notify->changes); @@ -1861,7 +1905,7 @@ static int call_nt_transact_notify_change(connection_struct *conn, char *inbuf, */ status = change_notify_add_request(inbuf, max_param_count, filter, - fsp); + recursive, fsp); if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); } diff --git a/source/smbd/service.c b/source/smbd/service.c index b5569c680de..8dbcc2b5abe 100644 --- a/source/smbd/service.c +++ b/source/smbd/service.c @@ -946,6 +946,13 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, set_conn_connectpath(conn,s); } + if ((!conn->printer) && (!conn->ipc)) { + conn->notify_ctx = notify_init(conn->mem_ctx, server_id_self(), + smbd_messaging_context(), + smbd_event_context(), + conn->params); + } + /* ROOT Activities: */ /* check number of connections */ if (!claim_connection(conn,