mirror of
https://github.com/samba-team/samba.git
synced 2025-02-26 21:57:41 +03:00
Revert "s3: vfs: add smb_vfs_ev_glue"
This reverts commit 1251a536df4b1df58d9ddacab03d3ebe6f4e5b60. See the discussion in https://lists.samba.org/archive/samba-technical/2018-December/131731.html for the reasoning behind this revert. Signed-off-by: Ralph Boehme <slow@samba.org> Reviewed-by: Volker Lendecke <vl@samba.org> Reviewed-by: Stefan Metzmacher <metze@samba.org>
This commit is contained in:
parent
29dd6f3e59
commit
cedbfbd9b8
@ -257,8 +257,6 @@
|
||||
/* Version 39 - Remove struct dfree_cached_info pointer from
|
||||
connection struct */
|
||||
/* Bump to version 40, Samba 4.10 will ship with that */
|
||||
/* Version 40 - Introduce smb_vfs_ev_glue infrastructure. */
|
||||
/* Version 40 - Add vfs_not_implemented_* helper functions. */
|
||||
/* Version 40 - Add SMB_VFS_GETXATTRAT_SEND/RECV */
|
||||
/* Version 40 - Add SMB_VFS_GET_DOS_ATTRIBUTES_SEND/RECV */
|
||||
|
||||
@ -283,9 +281,6 @@ struct smb_file_time;
|
||||
struct blocking_lock_record;
|
||||
struct smb_filename;
|
||||
struct dfs_GetDFSReferral;
|
||||
struct tevent_context;
|
||||
struct pthreadpool_tevent;
|
||||
struct smb_vfs_ev_glue;
|
||||
|
||||
typedef union unid_t {
|
||||
uid_t uid;
|
||||
@ -1553,31 +1548,6 @@ void *vfs_fetch_fsp_extension(vfs_handle_struct *handle, files_struct *fsp);
|
||||
void smb_vfs_assert_all_fns(const struct vfs_fn_pointers* fns,
|
||||
const char *module);
|
||||
|
||||
/*
|
||||
* Notice the "Design of the smb_vfs_ev_glue infrastructure"
|
||||
* comment in source3/smbd/vfs.c!
|
||||
*
|
||||
* This explains smb_vfs_ev_glue infrastructure in detail.
|
||||
*/
|
||||
struct tevent_context *smb_vfs_ev_glue_ev_ctx(const struct smb_vfs_ev_glue *evg);
|
||||
struct pthreadpool_tevent *smb_vfs_ev_glue_tp_fd_safe(const struct smb_vfs_ev_glue *evg);
|
||||
struct pthreadpool_tevent *smb_vfs_ev_glue_tp_path_safe(const struct smb_vfs_ev_glue *evg);
|
||||
struct pthreadpool_tevent *smb_vfs_ev_glue_tp_chdir_safe(const struct smb_vfs_ev_glue *evg);
|
||||
const struct smb_vfs_ev_glue *smb_vfs_ev_glue_get_root_glue(const struct smb_vfs_ev_glue *evg);
|
||||
struct smb_vfs_ev_glue *smb_vfs_ev_glue_create(TALLOC_CTX *mem_ctx,
|
||||
struct tevent_context *user_ev,
|
||||
struct pthreadpool_tevent *user_tp_fd_safe,
|
||||
struct pthreadpool_tevent *user_tp_path_safe,
|
||||
struct pthreadpool_tevent *user_tp_chdir_safe,
|
||||
struct tevent_context *root_ev,
|
||||
struct pthreadpool_tevent *root_tp_fd_safe,
|
||||
struct pthreadpool_tevent *root_tp_path_safe,
|
||||
struct pthreadpool_tevent *root_tp_chdir_safe);
|
||||
struct smb_vfs_ev_glue *smb_vfs_ev_glue_create_switch(
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct smb_vfs_ev_glue *return_evg,
|
||||
const struct smb_vfs_ev_glue *run_evg);
|
||||
|
||||
/*
|
||||
* Helper functions from source3/modules/vfs_not_implemented.c
|
||||
*/
|
||||
|
@ -1452,838 +1452,6 @@ struct file_id vfs_file_id_from_sbuf(connection_struct *conn, const SMB_STRUCT_S
|
||||
return SMB_VFS_FILE_ID_CREATE(conn, sbuf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Design of the smb_vfs_ev_glue infrastructure:
|
||||
*
|
||||
* smb_vfs_ev_glue makes it possible to pass
|
||||
* down an tevent_context and pthreadpool_tevent
|
||||
* used for impersonation through the SMB_VFS stack.
|
||||
*
|
||||
* tevent_req based function take an tevent_context as
|
||||
* there 2nd argument, e.g.:
|
||||
*
|
||||
* struct tevent_req *something_send(TALLOC_CTX *mem_ctx,
|
||||
* struct tevent_context *ev,
|
||||
* ...);
|
||||
*
|
||||
* For the SMB_VFS stack we'll use the following:
|
||||
*
|
||||
* struct tevent_req *SMB_VFS_SOMETHING_SEND(TALLOC_CTX *mem_ctx,
|
||||
* const struct smb_vfs_ev_glue *evg,
|
||||
* ...);
|
||||
*
|
||||
* Typically the 'evg' is just passed through the stack down
|
||||
* to vfs_default.c. In order to do real work an
|
||||
* tevent_context and pthreadpool_tevent are required
|
||||
* to do call a 'somthing()' syscall in an async fashion.
|
||||
* Therefore it will the following to get the pointer
|
||||
* back out of evg:
|
||||
*
|
||||
* ev = smb_vfs_ev_glue_ev_ctx(evg);
|
||||
* tp = smb_vfs_ev_glue_tp_chdir_safe(evg);
|
||||
*
|
||||
* If some function in the stack is sure it needs to run as root
|
||||
* to get some information (after careful checks!), it used
|
||||
* to frame that work into become_root()/unbecome_root().
|
||||
* This can't work when using async functions!
|
||||
* Now it's possible to use something like this (simplified!):
|
||||
*
|
||||
* ev = smb_vfs_ev_glue_ev_ctx(evg);
|
||||
* root_evg = smb_vfs_ev_glue_get_root_glue(evg);
|
||||
* subreq = SMB_VFS_SOMETHING_NEXT_SEND(state, root_evg, ...);
|
||||
* if (tevent_req_nomem(subreq, req)) {
|
||||
* return tevent_req_post(req, ev);
|
||||
* }
|
||||
* tevent_req_set_callback(subreq, module_something_done, req);
|
||||
*
|
||||
* return req;
|
||||
*
|
||||
* static void module_something_done(struct tevent_req *subreq)
|
||||
* {
|
||||
* ...
|
||||
*
|
||||
* status = SMB_VFS_SOMETHING_NEXT_RECV(subreq, &state->aio_state);
|
||||
* TALLOC_FREE(subreq);
|
||||
*
|
||||
* tevent_req_done(req);
|
||||
* }
|
||||
*
|
||||
* In the code above the something_send_fn() function of the next
|
||||
* module in the stack will be called as root.
|
||||
* The smb_vfs_call_something_*() glue code, which is the magic
|
||||
* behind the SMB_VFS_SOMETHING[_NEXT]_{SEND,RECV}() macros,
|
||||
* will look like this:
|
||||
*
|
||||
* struct smb_vfs_call_something_state {
|
||||
* ssize_t (*recv_fn)(struct tevent_req *req,
|
||||
* struct vfs_aio_state *aio_state,
|
||||
* ...);
|
||||
* ssize_t retval;
|
||||
* struct vfs_aio_state vfs_aio_state;
|
||||
* ...
|
||||
* };
|
||||
*
|
||||
* static void smb_vfs_call_something_done(struct tevent_req *subreq);
|
||||
*
|
||||
* struct tevent_req *smb_vfs_call_something_send(
|
||||
* TALLOC_CTX *mem_ctx,
|
||||
* const struct smb_vfs_ev_glue *evg,
|
||||
* struct vfs_handle_struct *handle,
|
||||
* ...)
|
||||
* {
|
||||
* struct tevent_req *req = NULL;
|
||||
* struct smb_vfs_call_something_state *state = NULL;
|
||||
* struct tevent_req *subreq = NULL;
|
||||
* bool ok;
|
||||
*
|
||||
* req = tevent_req_create(mem_ctx, &state,
|
||||
* struct smb_vfs_call_something_state);
|
||||
* if (req == NULL) {
|
||||
* return NULL;
|
||||
* }
|
||||
*
|
||||
* VFS_FIND(something_send);
|
||||
* state->recv_fn = handle->fns->something_recv_fn;
|
||||
*
|
||||
* ok = smb_vfs_ev_glue_push_use(evg, req);
|
||||
* if (!ok) {
|
||||
* tevent_req_error(req, EIO);
|
||||
* return tevent_req_post(req, evg->return_ev);
|
||||
* }
|
||||
*
|
||||
* subreq = handle->fns->something_send_fn(mem_ctx,
|
||||
* evg->next_glue,
|
||||
* handle,
|
||||
* ...);
|
||||
* smb_vfs_ev_glue_pop_use(evg);
|
||||
*
|
||||
* if (tevent_req_nomem(subreq, req)) {
|
||||
* return tevent_req_post(req, evg->return_ev);
|
||||
* }
|
||||
* tevent_req_set_callback(subreq, smb_vfs_call_something_done, req);
|
||||
*
|
||||
* return req;
|
||||
* }
|
||||
*
|
||||
* static void smb_vfs_call_something_done(struct tevent_req *subreq)
|
||||
* {
|
||||
* struct tevent_req *req =
|
||||
* tevent_req_callback_data(subreq,
|
||||
* struct tevent_req);
|
||||
* struct smb_vfs_call_something_state *state =
|
||||
* tevent_req_data(req,
|
||||
* struct smb_vfs_call_something_state);
|
||||
*
|
||||
* state->retval = state->recv_fn(subreq,
|
||||
* &state->vfs_aio_state,
|
||||
* ....);
|
||||
* TALLOC_FREE(subreq);
|
||||
*
|
||||
* if (state->retval == -1) {
|
||||
* tevent_req_error(req, state->vfs_aio_state.error);
|
||||
* return;
|
||||
* }
|
||||
* tevent_req_done(req);
|
||||
* }
|
||||
*
|
||||
* ssize_t smb_vfs_call_something_recv(struct tevent_req *req,
|
||||
* struct vfs_aio_state *aio_state,
|
||||
* ....)
|
||||
* {
|
||||
* struct smb_vfs_call_something_state *state =
|
||||
* tevent_req_data(req,
|
||||
* struct smb_vfs_call_something_state);
|
||||
* ssize_t retval = state->retval;
|
||||
*
|
||||
* if (tevent_req_is_unix_error(req, &aio_state->error)) {
|
||||
* tevent_req_received(req);
|
||||
* return -1;
|
||||
* }
|
||||
*
|
||||
* *aio_state = state->vfs_aio_state;
|
||||
* ...
|
||||
*
|
||||
* tevent_req_received(req);
|
||||
* return retval;
|
||||
* }
|
||||
*
|
||||
* The most important details are these:
|
||||
*
|
||||
* 1. smb_vfs_ev_glue_push_use(evg, req):
|
||||
* - is a no-op if evg->run_ev and evg->return_ev are the same,
|
||||
* it means that we're already at the correct impersonation
|
||||
* and don't need any additional work to be done.
|
||||
* - Otherwise it will call tevent_req_defer_callback(req, evg->return_ev)
|
||||
* This means that tevent_req_error() and tevent_req_done()
|
||||
* will just trigger an immediate event on evg->return_ev.
|
||||
* Therefore the callers callback function will be called
|
||||
* in the impersonation of evg->return_ev! This is important
|
||||
* in order to get the impersonation correct on the way back
|
||||
* through the stack.
|
||||
* - It will call tevent_context_push_use(evg->run_ev),
|
||||
* which will start the impersonation to run_ev.
|
||||
* So the following code run in the correct context.
|
||||
* 2. handle->fns->something_send_fn(..., evg->next_glue, ...):
|
||||
* - We're passing evg->next_glue to the next module.
|
||||
* - Typically evg->next_glue points to evg again.
|
||||
* - In case evg->run_ev and evg->return_ev are not the same,
|
||||
* next_glue will have run_ev and return_ev pointing to evg->run_ev.
|
||||
* So that the switch from evg->run_ev to evg->return_ev
|
||||
* happens on the correct boundary.
|
||||
* 3. smb_vfs_ev_glue_pop_use(evg):
|
||||
* - is a no-op if evg->run_ev and evg->return_ev are the same,
|
||||
* it means that we're already at the correct impersonation
|
||||
* and don't need any additional work to be done.
|
||||
* - It will call tevent_context_pop_use(evg->run_ev),
|
||||
* which will revert the impersonation done in
|
||||
* smb_vfs_ev_glue_push_use().
|
||||
* 4. smb_vfs_call_something_send():
|
||||
* - The is called in the environment of evg->return_ev.
|
||||
* - So it needs to use tevent_req_post(req, evg->return_ev)
|
||||
* 5. smb_vfs_call_something_done():
|
||||
* - The is called in the environment of evg->run_ev
|
||||
* 6. smb_vfs_call_something_recv():
|
||||
* - The is called in the environment of evg->return_ev again.
|
||||
*
|
||||
*
|
||||
* Here are some more complex examples:
|
||||
*
|
||||
* Example 1: only user_evg without switch to root
|
||||
*
|
||||
* SMBD: already impersonated user_evg
|
||||
* evg'1 = smb2_req->user_evg
|
||||
* r'1 = SMB_VFS_*_SEND(evg'1); # smb_vfs_call_*_send()
|
||||
* |
|
||||
* | smb_vfs_ev_glue_push_use(evg'1, r'1);
|
||||
* | |
|
||||
* | | # no-op run_ev == return_ev
|
||||
* | |
|
||||
* | evg'2 = evg'1->next_glue;
|
||||
* | r'2 = module1_*_send(evg'2);
|
||||
* | |
|
||||
* | | evg'3 = evg'2
|
||||
* | | r'3 = SMB_VFS_*_NEXT_SEND(evg'3); # smb_vfs_call_*_send()
|
||||
* | | |
|
||||
* | | | smb_vfs_ev_glue_push_use(evg'3, r'3);
|
||||
* | | | |
|
||||
* | | | | # no-op run_ev == return_ev
|
||||
* | | | |
|
||||
* | | | evg'4 = evg'3->next_glue;
|
||||
* | | | r'4 = module2_*_send(evg'4);
|
||||
* | | | |
|
||||
* | | | | evg'5 = evg'4
|
||||
* | | | | r'5 = SMB_VFS_*_NEXT_SEND(evg'5); # smb_vfs_call_*_send()
|
||||
* | | | | |
|
||||
* | | | | | smb_vfs_ev_glue_push_use(evg'5, r'5);
|
||||
* | | | | | |
|
||||
* | | | | | | # no-op run_ev == return_ev
|
||||
* | | | | | |
|
||||
* | | | | | evg'6 = evg'5->next_glue;
|
||||
* | | | | | r'6 = default_*_send(evg'6);
|
||||
* | | | | | |
|
||||
* | | | | | | ev'6 = smb_vfs_ev_glue_ev_ctx(evg'6)
|
||||
* | | | | | | tp'6 = smb_vfs_ev_glue_tp_chdir_safe(evg'6)
|
||||
* | | | | | | r'7 = pthreadpool_tevent_send(ev'6, tp'6);
|
||||
* | | | | | | |
|
||||
* | | | | | | | pthread_create...
|
||||
* | | | | | | |
|
||||
* | | | | | | tevent_req_set_callback(r'7, default_*_done, r'6);
|
||||
* | | | | | |
|
||||
* | | | | | smb_vfs_ev_glue_pop_use(evg'5);
|
||||
* | | | | | |
|
||||
* | | | | | | # no-op run_ev == return_ev
|
||||
* | | | | | |
|
||||
* | | | | | tevent_req_set_callback(r'6, smb_vfs_call_*_done, r'5);
|
||||
* | | | | |
|
||||
* | | | | tevent_req_set_callback(r'5, module2_*_done, r'4);
|
||||
* | | | |
|
||||
* | | | smb_vfs_ev_glue_pop_use(evg'3);
|
||||
* | | | |
|
||||
* | | | | # no-op run_ev == return_ev
|
||||
* | | | |
|
||||
* | | | tevent_req_set_callback(r'4, smb_vfs_call_*_done, r'3);
|
||||
* | | |
|
||||
* | | tevent_req_set_callback(r'3, module1_*_done, r'2);
|
||||
* | |
|
||||
* | smb_vfs_ev_glue_pop_use(evg'1);
|
||||
* | |
|
||||
* | | # no-op run_ev == return_ev
|
||||
* | |
|
||||
* | tevent_req_set_callback(r'2, smb_vfs_call_*_done, r'1);
|
||||
* |
|
||||
* tevent_req_set_callback(r'1, smbd_*_done, smb2_req);
|
||||
*
|
||||
* Worker thread finished, just one event handler processes
|
||||
* everything as there's no impersonation change.
|
||||
*
|
||||
* tevent_common_invoke_immediate_handler:
|
||||
* |
|
||||
* | before_immediate_handler(ev'6);
|
||||
* | |
|
||||
* | | change_to_user()
|
||||
* | |
|
||||
* | pthreadpool_tevent_job_done(r'7);
|
||||
* | |
|
||||
* | | default_*_done(r'7);
|
||||
* | | |
|
||||
* | | | pthreadpool_tevent_recv(r'7);
|
||||
* | | | TALLOC_FREE(r'7);
|
||||
* | | | tevent_req_done('r6);
|
||||
* | | | |
|
||||
* | | | | smb_vfs_call_*_done(r'6);
|
||||
* | | | | |
|
||||
* | | | | | default_*_recv(r'6);
|
||||
* | | | | | TALLOC_FREE(r'6)
|
||||
* | | | | | tevent_req_done(r'5);
|
||||
* | | | | | |
|
||||
* | | | | | | module2_*_done(r'5):
|
||||
* | | | | | | |
|
||||
* | | | | | | | SMB_VFS_*_recv(r'5); # smb_vfs_call_*_recv()
|
||||
* | | | | | | | TALLOC_FREE(r'5)
|
||||
* | | | | | | | tevent_req_done(r'4);
|
||||
* | | | | | | | |
|
||||
* | | | | | | | | smb_vfs_call_*_done(r'4);
|
||||
* | | | | | | | | |
|
||||
* | | | | | | | | | module2_*_recv(r'4);
|
||||
* | | | | | | | | | TALLOC_FREE(r'4)
|
||||
* | | | | | | | | | tevent_req_done(r'3);
|
||||
* | | | | | | | | | |
|
||||
* | | | | | | | | | | module1_*_done(r'3):
|
||||
* | | | | | | | | | | |
|
||||
* | | | | | | | | | | | SMB_VFS_*_recv(r'3); # smb_vfs_call_*_recv()
|
||||
* | | | | | | | | | | | TALLOC_FREE(r'3)
|
||||
* | | | | | | | | | | | tevent_req_done(r'2);
|
||||
* | | | | | | | | | | | |
|
||||
* | | | | | | | | | | | | smb_vfs_*_done(r'2);
|
||||
* | | | | | | | | | | | | |
|
||||
* | | | | | | | | | | | | | module1_*_recv(r'2);
|
||||
* | | | | | | | | | | | | | TALLOC_FREE(r'2)
|
||||
* | | | | | | | | | | | | | tevent_req_done(r'1);
|
||||
* | | | | | | | | | | | | | |
|
||||
* | | | | | | | | | | | | | | smbd_*_done(r'1);
|
||||
* | | | | | | | | | | | | | | |
|
||||
* | | | | | | | | | | | | | | | SMB_VFS_*_recv(r'1); # smb_vfs_call_*_recv()
|
||||
* | | | | | | | | | | | | | | | TALLOC_FREE(r'1)
|
||||
* | | | | | | | | | | | | | | | smbd_response_to_client()
|
||||
* | | | | | | | | | | | | | | | return
|
||||
* | | | | | | | | | | | | | | |
|
||||
* | | | | | | | | | | | | | | return
|
||||
* | | | | | | | | | | | | | |
|
||||
* | | | | | | | | | | | | | return
|
||||
* | | | | | | | | | | | | |
|
||||
* | | | | | | | | | | | | return
|
||||
* | | | | | | | | | | | |
|
||||
* | | | | | | | | | | | return
|
||||
* | | | | | | | | | | |
|
||||
* | | | | | | | | | | return
|
||||
* | | | | | | | | | |
|
||||
* | | | | | | | | | return
|
||||
* | | | | | | | | |
|
||||
* | | | | | | | | return
|
||||
* | | | | | | | |
|
||||
* | | | | | | | return
|
||||
* | | | | | | |
|
||||
* | | | | | | return
|
||||
* | | | | | |
|
||||
* | | | | | return
|
||||
* | | | | |
|
||||
* | | | | return
|
||||
* | | | |
|
||||
* | | | return
|
||||
* | | |
|
||||
* | | return
|
||||
* | |
|
||||
* | after_immediate_handler(ev'6);
|
||||
* | |
|
||||
* | | # lazy no change_to_user()
|
||||
* | |
|
||||
* | return
|
||||
*
|
||||
*
|
||||
* Example 2: start with user_evg and let module1 switch to root
|
||||
*
|
||||
* SMBD: already impersonated user_evg
|
||||
* evg'1 = smb2_req->user_evg
|
||||
* r'1 = SMB_VFS_*_SEND(evg'1); # smb_vfs_call_*_send()
|
||||
* |
|
||||
* | smb_vfs_ev_glue_push_use(evg'1, r'1);
|
||||
* | |
|
||||
* | | # no-op run_ev == return_ev
|
||||
* | |
|
||||
* | evg'2 = evg'1->next_glue;
|
||||
* | r'2 = module1_*_send(evg'2);
|
||||
* | |
|
||||
* | | evg'3 = smb_vfs_ev_glue_get_root_glue(evg'2)
|
||||
* | | r'3 = SMB_VFS_*_NEXT_SEND(evg'3); # smb_vfs_call_*_send()
|
||||
* | | |
|
||||
* | | | smb_vfs_ev_glue_push_use(evg'3, r'3);
|
||||
* | | | |
|
||||
* | | | | tevent_req_defer_callback(r'3, evg'3->return_ev);
|
||||
* | | | | tevent_context_push_use(evg'3->run_ev)
|
||||
* | | | | |
|
||||
* | | | | | become_root()
|
||||
* | | | | |
|
||||
* | | | |
|
||||
* | | | evg'4 = evg'3->next_glue;
|
||||
* | | | r'4 = module2_*_send(evg'4);
|
||||
* | | | |
|
||||
* | | | | evg'5 = smb_vfs_ev_glue_get_root_glue(evg'4)
|
||||
* | | | | r'5 = SMB_VFS_*_NEXT_SEND(evg'5); # smb_vfs_call_*_send()
|
||||
* | | | | |
|
||||
* | | | | | smb_vfs_ev_glue_push_use(evg'5, r'5);
|
||||
* | | | | | |
|
||||
* | | | | | | # no-op run_ev == return_ev, already root
|
||||
* | | | | | |
|
||||
* | | | | | evg'6 = evg'5->next_glue;
|
||||
* | | | | | r'6 = default_*_send(evg'6);
|
||||
* | | | | | |
|
||||
* | | | | | | ev'6 = smb_vfs_ev_glue_ev_ctx(evg'6)
|
||||
* | | | | | | tp'6 = smb_vfs_ev_glue_tp_chdir_safe(evg'6)
|
||||
* | | | | | | r'7 = pthreadpool_tevent_send(ev'6, tp'6);
|
||||
* | | | | | | |
|
||||
* | | | | | | | pthread_create...
|
||||
* | | | | | | |
|
||||
* | | | | | | tevent_req_set_callback(r'7, default_*_done, r'6);
|
||||
* | | | | | |
|
||||
* | | | | | smb_vfs_ev_glue_pop_use(evg'5);
|
||||
* | | | | | |
|
||||
* | | | | | | # no-op run_ev == return_ev, still stay as root
|
||||
* | | | | | |
|
||||
* | | | | | tevent_req_set_callback(r'6, smb_vfs_*_done, r'5);
|
||||
* | | | | |
|
||||
* | | | | tevent_req_set_callback(r'5, module2_*_done, r'4);
|
||||
* | | | |
|
||||
* | | | smb_vfs_ev_glue_pop_use(evg'3);
|
||||
* | | | |
|
||||
* | | | | tevent_context_pop_use(evg'3->run_ev)
|
||||
* | | | | |
|
||||
* | | | | | unbecome_root()
|
||||
* | | | |
|
||||
* | | | tevent_req_set_callback(r'4, smb_vfs_*_done, r'3);
|
||||
* | | |
|
||||
* | | tevent_req_set_callback(r'3, module1_*_done, r'2);
|
||||
* | |
|
||||
* | smb_vfs_ev_glue_pop_use(evg'1);
|
||||
* | |
|
||||
* | | # no-op run_ev == return_ev
|
||||
* | |
|
||||
* | tevent_req_set_callback(r'2, smb_vfs_*_done, r'1);
|
||||
* |
|
||||
* tevent_req_set_callback(r'1, smbd_*_done, smb2_req);
|
||||
*
|
||||
* Worker thread finished, just one event handler processes
|
||||
* everything as there's no impersonation change.
|
||||
*
|
||||
* tevent_common_invoke_immediate_handler:
|
||||
* |
|
||||
* | before_immediate_handler(ev'6);
|
||||
* | |
|
||||
* | | become_root()
|
||||
* | |
|
||||
* | pthreadpool_tevent_job_done(r'7);
|
||||
* | |
|
||||
* | | default_*_done(r'7);
|
||||
* | | |
|
||||
* | | | pthreadpool_tevent_recv(r'7);
|
||||
* | | | TALLOC_FREE(r'7);
|
||||
* | | | tevent_req_done('r6);
|
||||
* | | | |
|
||||
* | | | | smb_vfs_*_done(r'6);
|
||||
* | | | | |
|
||||
* | | | | | default_*_recv(r'6);
|
||||
* | | | | | TALLOC_FREE(r'6)
|
||||
* | | | | | tevent_req_done(r'5);
|
||||
* | | | | | |
|
||||
* | | | | | | module2_*_done(r'5):
|
||||
* | | | | | | |
|
||||
* | | | | | | | SMB_VFS_*_recv(r'5);
|
||||
* | | | | | | | TALLOC_FREE(r'5)
|
||||
* | | | | | | | tevent_req_done(r'4);
|
||||
* | | | | | | | |
|
||||
* | | | | | | | | smb_vfs_*_done(r'4);
|
||||
* | | | | | | | | |
|
||||
* | | | | | | | | | module2_*_recv(r'4);
|
||||
* | | | | | | | | | TALLOC_FREE(r'4)
|
||||
* | | | | | | | | | tevent_req_done(r'3);
|
||||
* | | | | | | | | | | return
|
||||
* | | | | | | | | | |
|
||||
* | | | | | | | | | return
|
||||
* | | | | | | | | |
|
||||
* | | | | | | | | return
|
||||
* | | | | | | | |
|
||||
* | | | | | | | return
|
||||
* | | | | | | |
|
||||
* | | | | | | return
|
||||
* | | | | | |
|
||||
* | | | | | return
|
||||
* | | | | |
|
||||
* | | | | return
|
||||
* | | | |
|
||||
* | | | return
|
||||
* | | |
|
||||
* | | return
|
||||
* | |
|
||||
* | |
|
||||
* | after_immediate_handler(ev'6);
|
||||
* | |
|
||||
* | | unbecome_root()
|
||||
* | |
|
||||
* | return
|
||||
* |
|
||||
* tevent_common_invoke_immediate_handler:
|
||||
* |
|
||||
* | before_immediate_handler(ev'6);
|
||||
* | |
|
||||
* | | change_to_user()
|
||||
* | |
|
||||
* | tevent_req_trigger();
|
||||
* | ...
|
||||
* | _tevent_req_notify_callback(r'3)
|
||||
* | |
|
||||
* | | module1_*_done(r'3):
|
||||
* | | |
|
||||
* | | | SMB_VFS_*_recv(r'3);
|
||||
* | | | TALLOC_FREE(r'3)
|
||||
* | | | tevent_req_done(r'2);
|
||||
* | | | |
|
||||
* | | | | smb_vfs_*_done(r'2);
|
||||
* | | | | |
|
||||
* | | | | | module1_*_recv(r'2);
|
||||
* | | | | | TALLOC_FREE(r'2)
|
||||
* | | | | | tevent_req_done(r'1);
|
||||
* | | | | | |
|
||||
* | | | | | | smbd_*_done(r'1);
|
||||
* | | | | | | |
|
||||
* | | | | | | | SMB_VFS_*_recv(r'1);
|
||||
* | | | | | | | TALLOC_FREE(r'1)
|
||||
* | | | | | | | smbd_response_to_client()
|
||||
* | | | | | | | return
|
||||
* | | | | | | |
|
||||
* | | | | | | return
|
||||
* | | | | | |
|
||||
* | | | | | return
|
||||
* | | | | |
|
||||
* | | | | return
|
||||
* | | | |
|
||||
* | | | return
|
||||
* | | |
|
||||
* | | return
|
||||
* | |
|
||||
* | after_immediate_handler(ev'6);
|
||||
* | |
|
||||
* | | # lazy no change_to_user()
|
||||
* | |
|
||||
* | return
|
||||
*
|
||||
*/
|
||||
struct smb_vfs_ev_glue {
|
||||
/*
|
||||
* The event context that should be used
|
||||
* to report the result back.
|
||||
*
|
||||
* The is basically the callers context.
|
||||
*/
|
||||
struct tevent_context *return_ev;
|
||||
|
||||
/*
|
||||
* The event context and threadpool wrappers
|
||||
* the current context should use.
|
||||
*
|
||||
* tp_fd_safe only allows fd based functions
|
||||
* which don't require impersonation, this
|
||||
* is basically the raw threadpool.
|
||||
*
|
||||
* tp_path_safe allows path based functions
|
||||
* to be called under the correct impersonation.
|
||||
* But chdir/fchdir is not allowed!
|
||||
* Typically calls like openat() or other *at()
|
||||
* syscalls.
|
||||
*
|
||||
* tp_chdir_safe is like path_safe, but also
|
||||
* allows chdir/fchdir to be called, the job
|
||||
* can safely return with a changed directory,
|
||||
* the threadpool wrapper takes care of
|
||||
* a cleanup if required.
|
||||
* This is needed if *at() syscalls need
|
||||
* to be simulated by fchdir();$syscall(),
|
||||
* e.g. getxattr().
|
||||
*
|
||||
* The distinction between these threadpool
|
||||
* is required because of OS limitations
|
||||
* (as of 2018):
|
||||
* - only Linux supports per thread
|
||||
* credentials (seteuid....)
|
||||
* - only Linux supports a per thread
|
||||
* current working directory,
|
||||
* using unshare(CLONE_FS). But
|
||||
* in some constrained container
|
||||
* environments even that is not available
|
||||
* on Linux.
|
||||
*
|
||||
* tp_fd_safe is typically the raw threadpool
|
||||
* without a wrapper.
|
||||
*
|
||||
* On Linux tp_path_safe and tp_chdir_safe
|
||||
* are typically the same (if unshare(CLONE_FS) is available)
|
||||
* they're implemented as wrappers of the raw threadpool.
|
||||
*
|
||||
* On other OSes tp_path_safe is a wrapper
|
||||
* arround a sync threadpool (without real threads, just blocking
|
||||
* the main thread), but hidden behind the pthreadpool_tevent
|
||||
* api in order to make the restriction transparent.
|
||||
*
|
||||
* On other OSes tp_chdir_safe is a wrapper
|
||||
* arround a sync threadpool (without real threads, just blocking
|
||||
* the main thread), but hidden behind the pthreadpool_tevent
|
||||
* api in order to make the restriction transparent.
|
||||
* It just remembers/restores the current working directory,
|
||||
* typically using open(".", O_RDONLY | O_DIRECTORY) and fchdir().
|
||||
*/
|
||||
struct tevent_context *run_ev;
|
||||
struct pthreadpool_tevent *run_tp_fd_safe;
|
||||
struct pthreadpool_tevent *run_tp_path_safe;
|
||||
struct pthreadpool_tevent *run_tp_chdir_safe;
|
||||
|
||||
/*
|
||||
* The glue that should be passed down
|
||||
* to sub request in the stack.
|
||||
*
|
||||
* Typically this points to itself.
|
||||
*
|
||||
* But smb_vfs_ev_glue_create_switch() allows
|
||||
* to create context that can switch
|
||||
* between two user glues.
|
||||
*/
|
||||
const struct smb_vfs_ev_glue *next_glue;
|
||||
|
||||
/*
|
||||
* If some code path wants to run
|
||||
* some constraint code as root,
|
||||
* basically an async version of become_root()
|
||||
* and unbecome_root().
|
||||
*
|
||||
* The caller can call smb_vfs_ev_glue_get_root_glue()
|
||||
* to get a root glue that can be passed
|
||||
* to the SMB_VFS_*_SEND() function that
|
||||
* should run as root.
|
||||
*
|
||||
* Note that the callback (registered with
|
||||
* tevent_req_set_callback()) won't run as
|
||||
* root anymore!
|
||||
*/
|
||||
const struct smb_vfs_ev_glue *root_glue;
|
||||
};
|
||||
|
||||
static struct smb_vfs_ev_glue *smb_vfs_ev_glue_create_internal(
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct tevent_context *return_ev,
|
||||
struct tevent_context *run_ev,
|
||||
struct pthreadpool_tevent *run_tp_fd_safe,
|
||||
struct pthreadpool_tevent *run_tp_path_safe,
|
||||
struct pthreadpool_tevent *run_tp_chdir_safe)
|
||||
{
|
||||
struct smb_vfs_ev_glue *evg = NULL;
|
||||
|
||||
evg = talloc_zero(mem_ctx, struct smb_vfs_ev_glue);
|
||||
if (evg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
*evg = (struct smb_vfs_ev_glue) {
|
||||
.return_ev = return_ev,
|
||||
.run_ev = run_ev,
|
||||
.run_tp_fd_safe = run_tp_fd_safe,
|
||||
.run_tp_path_safe = run_tp_path_safe,
|
||||
.run_tp_chdir_safe = run_tp_chdir_safe,
|
||||
.next_glue = evg,
|
||||
};
|
||||
|
||||
return evg;
|
||||
}
|
||||
|
||||
struct tevent_context *smb_vfs_ev_glue_ev_ctx(const struct smb_vfs_ev_glue *evg)
|
||||
{
|
||||
return evg->run_ev;
|
||||
}
|
||||
|
||||
struct pthreadpool_tevent *smb_vfs_ev_glue_tp_fd_safe(const struct smb_vfs_ev_glue *evg)
|
||||
{
|
||||
return evg->run_tp_fd_safe;
|
||||
}
|
||||
|
||||
struct pthreadpool_tevent *smb_vfs_ev_glue_tp_path_safe(const struct smb_vfs_ev_glue *evg)
|
||||
{
|
||||
return evg->run_tp_path_safe;
|
||||
}
|
||||
|
||||
struct pthreadpool_tevent *smb_vfs_ev_glue_tp_chdir_safe(const struct smb_vfs_ev_glue *evg)
|
||||
{
|
||||
return evg->run_tp_chdir_safe;
|
||||
}
|
||||
|
||||
const struct smb_vfs_ev_glue *smb_vfs_ev_glue_get_root_glue(const struct smb_vfs_ev_glue *evg)
|
||||
{
|
||||
return evg->root_glue;
|
||||
}
|
||||
|
||||
struct smb_vfs_ev_glue *smb_vfs_ev_glue_create(TALLOC_CTX *mem_ctx,
|
||||
struct tevent_context *user_ev,
|
||||
struct pthreadpool_tevent *user_tp_fd_safe,
|
||||
struct pthreadpool_tevent *user_tp_path_safe,
|
||||
struct pthreadpool_tevent *user_tp_chdir_safe,
|
||||
struct tevent_context *root_ev,
|
||||
struct pthreadpool_tevent *root_tp_fd_safe,
|
||||
struct pthreadpool_tevent *root_tp_path_safe,
|
||||
struct pthreadpool_tevent *root_tp_chdir_safe)
|
||||
{
|
||||
struct smb_vfs_ev_glue *evg_uu = NULL;
|
||||
struct smb_vfs_ev_glue *evg_ru = NULL;
|
||||
struct smb_vfs_ev_glue *evg_rr = NULL;
|
||||
|
||||
/*
|
||||
* The top level glue (directly returned from this function).
|
||||
*
|
||||
* It uses user_ev and user_tp_* only.
|
||||
*/
|
||||
evg_uu = smb_vfs_ev_glue_create_internal(mem_ctx,
|
||||
user_ev, /* return_ev */
|
||||
user_ev, /* run_ev */
|
||||
user_tp_fd_safe,
|
||||
user_tp_path_safe,
|
||||
user_tp_chdir_safe);
|
||||
if (evg_uu == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The first root glue (returned by smb_vfs_ev_glue_get_root_glue()).
|
||||
*
|
||||
* It uses root_ev and root_tp, but user_ev as return ev,
|
||||
* which means that the caller's callback (registered with
|
||||
* tevent_req_set_callback()) will run as user_ev.
|
||||
*/
|
||||
evg_ru = smb_vfs_ev_glue_create_internal(evg_uu,
|
||||
user_ev, /* return_ev */
|
||||
root_ev, /* run_ev */
|
||||
root_tp_fd_safe,
|
||||
root_tp_path_safe,
|
||||
root_tp_chdir_safe);
|
||||
if (evg_ru == NULL) {
|
||||
TALLOC_FREE(evg_uu);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The second root glue (returned by smb_vfs_ev_glue_get_root_glue() on
|
||||
* root glue itself. This means code can always call
|
||||
* smb_vfs_ev_glue_get_root_glue() and don't have to care if the
|
||||
* passed glue is already a root glue.
|
||||
*
|
||||
* This will then recursively point to its own root_glue pointer.
|
||||
*
|
||||
* It only uses root_ev and root_tp.
|
||||
*/
|
||||
evg_rr = smb_vfs_ev_glue_create_internal(evg_ru,
|
||||
root_ev, /* return_ev */
|
||||
root_ev, /* run_ev */
|
||||
root_tp_fd_safe,
|
||||
root_tp_path_safe,
|
||||
root_tp_chdir_safe);
|
||||
if (evg_rr == NULL) {
|
||||
TALLOC_FREE(evg_uu);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* We now setup the glue hierarchy.
|
||||
*
|
||||
* Search for "Design of the smb_vfs_ev_glue infrastructure" above
|
||||
* for a detailed description how the chain works.
|
||||
*
|
||||
* "Example 2: start with user_evg and let module1 switch to root"
|
||||
* explains it for the root_glue chaining.
|
||||
*/
|
||||
evg_rr->root_glue = evg_rr;
|
||||
evg_ru->root_glue = evg_rr;
|
||||
evg_uu->root_glue = evg_ru;
|
||||
|
||||
/*
|
||||
* As evg_ru is a boundary with
|
||||
* run_ev != return_ev, we need to
|
||||
* alter its next_glue.
|
||||
*/
|
||||
evg_ru->next_glue = evg_rr;
|
||||
|
||||
return evg_uu;
|
||||
}
|
||||
|
||||
/*
|
||||
* This can be used to create a temporary glue
|
||||
* if you need to switch between two user contexts
|
||||
*
|
||||
* It's the caller's duty to make sure both
|
||||
* glues stay alive for the lifetime of the
|
||||
* created switch.
|
||||
*/
|
||||
struct smb_vfs_ev_glue *smb_vfs_ev_glue_create_switch(
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct smb_vfs_ev_glue *return_evg,
|
||||
const struct smb_vfs_ev_glue *run_evg)
|
||||
{
|
||||
const struct smb_vfs_ev_glue *run_root = run_evg->root_glue;
|
||||
struct smb_vfs_ev_glue *evg_u = NULL;
|
||||
struct smb_vfs_ev_glue *evg_r = NULL;
|
||||
|
||||
/*
|
||||
* Here we basically need to dup run_evg (and run_evg->root_glue)
|
||||
* and replace their return_ev with return_evg->return_ev.
|
||||
*
|
||||
* We need to put the new evgs in front of the chain...
|
||||
*/
|
||||
evg_u = smb_vfs_ev_glue_create_internal(mem_ctx,
|
||||
return_evg->return_ev,
|
||||
run_evg->run_ev,
|
||||
run_evg->run_tp_fd_safe,
|
||||
run_evg->run_tp_path_safe,
|
||||
run_evg->run_tp_chdir_safe);
|
||||
if (evg_u == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
evg_r = smb_vfs_ev_glue_create_internal(evg_u,
|
||||
return_evg->return_ev,
|
||||
run_root->run_ev,
|
||||
run_root->run_tp_fd_safe,
|
||||
run_root->run_tp_path_safe,
|
||||
run_root->run_tp_chdir_safe);
|
||||
if (evg_r == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* evg_r is a boundary with run_ev != return_ev.
|
||||
* As run_root is also a boundary, we need to
|
||||
* use run_root->next_glue in order to get
|
||||
* a glue that stays as root.
|
||||
*
|
||||
* The same applies to the chaining of root
|
||||
* glues.
|
||||
*/
|
||||
evg_r->next_glue = run_root->next_glue;
|
||||
evg_r->root_glue = run_root->root_glue;
|
||||
|
||||
/*
|
||||
* evg_r is a boundary with run_ev != return_ev.
|
||||
* But run_evg is typically not a boundary,
|
||||
* we use it directly as next_glue.
|
||||
*
|
||||
* And the root_glue is the one we constructed above.
|
||||
*/
|
||||
evg_u->next_glue = run_evg;
|
||||
evg_u->root_glue = evg_r;
|
||||
|
||||
return evg_u;
|
||||
}
|
||||
|
||||
int smb_vfs_call_connect(struct vfs_handle_struct *handle,
|
||||
const char *service, const char *user)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user