vfs: perform impersonation in smb_vfs_call_get_dos_attributes_done()
[samba.git] / source3 / smbd / vfs.c
index 2687e3540b89e916b05d942fa606fe88db68ba4b..2910128ccf6dfe2666993509260b3b7bffaa333a 100644 (file)
@@ -787,7 +787,6 @@ const char *vfs_readdirname(connection_struct *conn, void *p,
 int vfs_ChDir(connection_struct *conn, const struct smb_filename *smb_fname)
 {
        int ret;
-       int saved_errno = 0;
        struct smb_filename *old_cwd = conn->cwd_fname;
 
        if (!LastDir) {
@@ -825,7 +824,7 @@ int vfs_ChDir(connection_struct *conn, const struct smb_filename *smb_fname)
                 * Return to original directory
                 * and return -1.
                 */
-               saved_errno = errno;
+               int saved_errno = errno;
 
                if (old_cwd == NULL) {
                        /*
@@ -860,9 +859,6 @@ int vfs_ChDir(connection_struct *conn, const struct smb_filename *smb_fname)
        DEBUG(4,("vfs_ChDir got %s\n", conn->cwd_fname->base_name));
 
        TALLOC_FREE(old_cwd);
-       if (saved_errno != 0) {
-               errno = saved_errno;
-       }
        return ret;
 }
 
@@ -983,7 +979,7 @@ struct smb_filename *vfs_GetWd(TALLOC_CTX *ctx, connection_struct *conn)
 
 /*******************************************************************
  Reduce a file name, removing .. elements and checking that
- it is below dir in the heirachy. This uses realpath.
+ it is below dir in the hierarchy. This uses realpath.
  This function must run as root, and will return names
  and valid stat structs that can be checked on open.
 ********************************************************************/
@@ -1172,7 +1168,7 @@ NTSTATUS check_reduced_name_with_privilege(connection_struct *conn,
 
 /*******************************************************************
  Reduce a file name, removing .. elements and checking that
- it is below dir in the heirachy. This uses realpath.
+ it is below dir in the hierarchy. This uses realpath.
 
  If cwd_name == NULL then fname is a client given path relative
  to the root path of the share.
@@ -1456,878 +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 hierachie.
-        *
-        * 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;
-}
-
-static bool smb_vfs_ev_glue_push_use(const struct smb_vfs_ev_glue *evg,
-                                    struct tevent_req *req)
-{
-       if (evg->run_ev == evg->return_ev) {
-               /*
-                * We're already in the correct
-                * impersonation environment.
-                */
-               return true;
-       }
-
-       /*
-        * Make sure that our callers callback function
-        * will be called in the return_ev environment.
-        */
-       tevent_req_defer_callback(req, evg->return_ev);
-
-       /*
-        * let the event context wrapper do
-        * the required impersonation.
-        */
-       return tevent_context_push_use(evg->run_ev);
-}
-
-static void smb_vfs_ev_glue_pop_use(const struct smb_vfs_ev_glue *evg)
-{
-       if (evg->run_ev == evg->return_ev) {
-               /*
-                * smb_vfs_ev_glue_push_use() didn't
-                * change the impersonation environment.
-                */
-               return;
-       }
-
-       /*
-        * undo the impersonation
-        */
-       tevent_context_pop_use(evg->run_ev);
-}
-
 int smb_vfs_call_connect(struct vfs_handle_struct *handle,
                         const char *service, const char *user)
 {
@@ -2579,14 +1703,16 @@ ssize_t SMB_VFS_PREAD_RECV(struct tevent_req *req,
 {
        struct smb_vfs_call_pread_state *state = tevent_req_data(
                req, struct smb_vfs_call_pread_state);
+       ssize_t retval;
 
        if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
                tevent_req_received(req);
                return -1;
        }
        *vfs_aio_state = state->vfs_aio_state;
+       retval = state->retval;
        tevent_req_received(req);
-       return state->retval;
+       return retval;
 }
 
 ssize_t smb_vfs_call_pwrite(struct vfs_handle_struct *handle,
@@ -2653,14 +1779,16 @@ ssize_t SMB_VFS_PWRITE_RECV(struct tevent_req *req,
 {
        struct smb_vfs_call_pwrite_state *state = tevent_req_data(
                req, struct smb_vfs_call_pwrite_state);
+       ssize_t retval;
 
        if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
                tevent_req_received(req);
                return -1;
        }
        *vfs_aio_state = state->vfs_aio_state;
+       retval = state->retval;
        tevent_req_received(req);
-       return state->retval;
+       return retval;
 }
 
 off_t smb_vfs_call_lseek(struct vfs_handle_struct *handle,
@@ -2748,14 +1876,16 @@ int SMB_VFS_FSYNC_RECV(struct tevent_req *req, struct vfs_aio_state *vfs_aio_sta
 {
        struct smb_vfs_call_fsync_state *state = tevent_req_data(
                req, struct smb_vfs_call_fsync_state);
+       ssize_t retval;
 
        if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
                tevent_req_received(req);
                return -1;
        }
        *vfs_aio_state = state->vfs_aio_state;
+       retval = state->retval;
        tevent_req_received(req);
-       return state->retval;
+       return retval;
 }
 
 /*
@@ -3252,6 +2382,7 @@ NTSTATUS smb_vfs_call_offload_write_recv(struct vfs_handle_struct *handle,
 }
 
 struct smb_vfs_call_get_dos_attributes_state {
+       files_struct *dir_fsp;
        NTSTATUS (*recv_fn)(struct tevent_req *req,
                            struct vfs_aio_state *aio_state,
                            uint32_t *dosmode);
@@ -3263,7 +2394,7 @@ static void smb_vfs_call_get_dos_attributes_done(struct tevent_req *subreq);
 
 struct tevent_req *smb_vfs_call_get_dos_attributes_send(
                        TALLOC_CTX *mem_ctx,
-                       const struct smb_vfs_ev_glue *evg,
+                       struct tevent_context *ev,
                        struct vfs_handle_struct *handle,
                        files_struct *dir_fsp,
                        struct smb_filename *smb_fname)
@@ -3271,7 +2402,6 @@ struct tevent_req *smb_vfs_call_get_dos_attributes_send(
        struct tevent_req *req = NULL;
        struct smb_vfs_call_get_dos_attributes_state *state = NULL;
        struct tevent_req *subreq = NULL;
-       bool ok;
 
        req = tevent_req_create(mem_ctx, &state,
                                struct smb_vfs_call_get_dos_attributes_state);
@@ -3280,24 +2410,22 @@ struct tevent_req *smb_vfs_call_get_dos_attributes_send(
        }
 
        VFS_FIND(get_dos_attributes_send);
-       state->recv_fn = handle->fns->get_dos_attributes_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);
-       }
+       *state = (struct smb_vfs_call_get_dos_attributes_state) {
+               .dir_fsp = dir_fsp,
+               .recv_fn = handle->fns->get_dos_attributes_recv_fn,
+       };
 
        subreq = handle->fns->get_dos_attributes_send_fn(mem_ctx,
-                                                        evg->next_glue,
+                                                        ev,
                                                         handle,
                                                         dir_fsp,
                                                         smb_fname);
-       smb_vfs_ev_glue_pop_use(evg);
-
        if (tevent_req_nomem(subreq, req)) {
-               return tevent_req_post(req, evg->return_ev);
+               return tevent_req_post(req, ev);
        }
+       tevent_req_defer_callback(req, ev);
+
        tevent_req_set_callback(subreq,
                                smb_vfs_call_get_dos_attributes_done,
                                req);
@@ -3314,6 +2442,13 @@ static void smb_vfs_call_get_dos_attributes_done(struct tevent_req *subreq)
                tevent_req_data(req,
                struct smb_vfs_call_get_dos_attributes_state);
        NTSTATUS status;
+       bool ok;
+
+       /*
+        * Make sure we run as the user again
+        */
+       ok = change_to_user_by_fsp(state->dir_fsp);
+       SMB_ASSERT(ok);
 
        status = state->recv_fn(subreq,
                                &state->aio_state,
@@ -3537,7 +2672,7 @@ static void smb_vfs_call_getxattrat_done(struct tevent_req *subreq);
 
 struct tevent_req *smb_vfs_call_getxattrat_send(
                        TALLOC_CTX *mem_ctx,
-                       const struct smb_vfs_ev_glue *evg,
+                       struct tevent_context *ev,
                        struct vfs_handle_struct *handle,
                        files_struct *dir_fsp,
                        const struct smb_filename *smb_fname,
@@ -3547,7 +2682,6 @@ struct tevent_req *smb_vfs_call_getxattrat_send(
        struct tevent_req *req = NULL;
        struct smb_vfs_call_getxattrat_state *state = NULL;
        struct tevent_req *subreq = NULL;
-       bool ok;
 
        req = tevent_req_create(mem_ctx, &state,
                                struct smb_vfs_call_getxattrat_state);
@@ -3558,24 +2692,18 @@ struct tevent_req *smb_vfs_call_getxattrat_send(
        VFS_FIND(getxattrat_send);
        state->recv_fn = handle->fns->getxattrat_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->getxattrat_send_fn(mem_ctx,
-                                                evg->next_glue,
+                                                ev,
                                                 handle,
                                                 dir_fsp,
                                                 smb_fname,
                                                 xattr_name,
                                                 alloc_hint);
-       smb_vfs_ev_glue_pop_use(evg);
-
        if (tevent_req_nomem(subreq, req)) {
-               return tevent_req_post(req, evg->return_ev);
+               return tevent_req_post(req, ev);
        }
+       tevent_req_defer_callback(req, ev);
+
        tevent_req_set_callback(subreq, smb_vfs_call_getxattrat_done, req);
        return req;
 }