X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Fmodules%2Fvfs_default.c;h=7ce3775e54f0aa7b841a4f6e9a7cd89e398f2b6f;hb=e18610a197aab80a32cae8c1e09b96496679bbad;hp=81c707d15ecb65eb1ca5b1105ab9fe87e33dd2cc;hpb=47d77432e4e0c61ba93390a5e33641a401154b54;p=samba.git diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index 81c707d15ec..7ce3775e54f 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -686,7 +686,7 @@ static struct tevent_req *vfswrap_pread_send(struct vfs_handle_struct *handle, SMBPROFILE_BYTES_ASYNC_SET_IDLE(state->profile_bytes); subreq = pthreadpool_tevent_job_send( - state, ev, handle->conn->sconn->raw_thread_pool, + state, ev, handle->conn->sconn->pool, vfs_pread_do, state); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); @@ -742,8 +742,18 @@ static void vfs_pread_done(struct tevent_req *subreq) TALLOC_FREE(subreq); SMBPROFILE_BYTES_ASYNC_END(state->profile_bytes); talloc_set_destructor(state, NULL); - if (tevent_req_error(req, ret)) { - return; + if (ret != 0) { + if (ret != EAGAIN) { + tevent_req_error(req, ret); + return; + } + /* + * If we get EAGAIN from pthreadpool_tevent_job_recv() this + * means the lower level pthreadpool failed to create a new + * thread. Fallback to sync processing in that case to allow + * some progress for the client. + */ + vfs_pread_do(state); } tevent_req_done(req); @@ -804,7 +814,7 @@ static struct tevent_req *vfswrap_pwrite_send(struct vfs_handle_struct *handle, SMBPROFILE_BYTES_ASYNC_SET_IDLE(state->profile_bytes); subreq = pthreadpool_tevent_job_send( - state, ev, handle->conn->sconn->raw_thread_pool, + state, ev, handle->conn->sconn->pool, vfs_pwrite_do, state); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); @@ -860,8 +870,18 @@ static void vfs_pwrite_done(struct tevent_req *subreq) TALLOC_FREE(subreq); SMBPROFILE_BYTES_ASYNC_END(state->profile_bytes); talloc_set_destructor(state, NULL); - if (tevent_req_error(req, ret)) { - return; + if (ret != 0) { + if (ret != EAGAIN) { + tevent_req_error(req, ret); + return; + } + /* + * If we get EAGAIN from pthreadpool_tevent_job_recv() this + * means the lower level pthreadpool failed to create a new + * thread. Fallback to sync processing in that case to allow + * some progress for the client. + */ + vfs_pwrite_do(state); } tevent_req_done(req); @@ -914,8 +934,7 @@ static struct tevent_req *vfswrap_fsync_send(struct vfs_handle_struct *handle, SMBPROFILE_BYTES_ASYNC_SET_IDLE(state->profile_bytes); subreq = pthreadpool_tevent_job_send( - state, ev, handle->conn->sconn->raw_thread_pool, - vfs_fsync_do, state); + state, ev, handle->conn->sconn->pool, vfs_fsync_do, state); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } @@ -969,8 +988,18 @@ static void vfs_fsync_done(struct tevent_req *subreq) TALLOC_FREE(subreq); SMBPROFILE_BYTES_ASYNC_END(state->profile_bytes); talloc_set_destructor(state, NULL); - if (tevent_req_error(req, ret)) { - return; + if (ret != 0) { + if (ret != EAGAIN) { + tevent_req_error(req, ret); + return; + } + /* + * If we get EAGAIN from pthreadpool_tevent_job_recv() this + * means the lower level pthreadpool failed to create a new + * thread. Fallback to sync processing in that case to allow + * some progress for the client. + */ + vfs_fsync_do(state); } tevent_req_done(req); @@ -996,10 +1025,7 @@ static off_t vfswrap_lseek(vfs_handle_struct *handle, files_struct *fsp, off_t o START_PROFILE(syscall_lseek); - /* Cope with 'stat' file opens. */ - if (fsp->fh->fd != -1) - result = lseek(fsp->fh->fd, offset, whence); - + result = lseek(fsp->fh->fd, offset, whence); /* * We want to maintain the fiction that we can seek * on a fifo for file system purposes. This allows @@ -1329,7 +1355,9 @@ static NTSTATUS vfswrap_fsctl(struct vfs_handle_struct *handle, * * but I have to check that --metze */ + struct sid_parse_ret ret; struct dom_sid sid; + struct dom_sid_buf buf; uid_t uid; size_t sid_len; @@ -1346,14 +1374,16 @@ static NTSTATUS vfswrap_fsctl(struct vfs_handle_struct *handle, /* unknown 4 bytes: this is not the length of the sid :-( */ /*unknown = IVAL(pdata,0);*/ - if (!sid_parse(_in_data + 4, sid_len, &sid)) { + ret = sid_parse(_in_data + 4, sid_len, &sid); + if (ret.len == -1) { return NT_STATUS_INVALID_PARAMETER; } - DEBUGADD(10, ("for SID: %s\n", sid_string_dbg(&sid))); + DEBUGADD(10, ("for SID: %s\n", + dom_sid_str_buf(&sid, &buf))); if (!sid_to_uid(&sid, &uid)) { DEBUG(0,("sid_to_uid: failed, sid[%s] sid_len[%lu]\n", - sid_string_dbg(&sid), + dom_sid_str_buf(&sid, &buf), (unsigned long)sid_len)); uid = (-1); } @@ -1487,6 +1517,142 @@ static NTSTATUS vfswrap_get_dos_attributes(struct vfs_handle_struct *handle, return get_ea_dos_attribute(handle->conn, smb_fname, dosmode); } +struct vfswrap_get_dos_attributes_state { + struct vfs_aio_state aio_state; + connection_struct *conn; + TALLOC_CTX *mem_ctx; + struct tevent_context *ev; + files_struct *dir_fsp; + struct smb_filename *smb_fname; + uint32_t dosmode; + bool as_root; +}; + +static void vfswrap_get_dos_attributes_getxattr_done(struct tevent_req *subreq); + +static struct tevent_req *vfswrap_get_dos_attributes_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct vfs_handle_struct *handle, + files_struct *dir_fsp, + struct smb_filename *smb_fname) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct vfswrap_get_dos_attributes_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, + struct vfswrap_get_dos_attributes_state); + if (req == NULL) { + return NULL; + } + + *state = (struct vfswrap_get_dos_attributes_state) { + .conn = dir_fsp->conn, + .mem_ctx = mem_ctx, + .ev = ev, + .dir_fsp = dir_fsp, + .smb_fname = smb_fname, + }; + + subreq = SMB_VFS_GETXATTRAT_SEND(state, + ev, + dir_fsp, + smb_fname, + SAMBA_XATTR_DOS_ATTRIB, + sizeof(fstring)); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, + vfswrap_get_dos_attributes_getxattr_done, + req); + + return req; +} + +static void vfswrap_get_dos_attributes_getxattr_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct vfswrap_get_dos_attributes_state *state = + tevent_req_data(req, + struct vfswrap_get_dos_attributes_state); + ssize_t xattr_size; + DATA_BLOB blob = {0}; + NTSTATUS status; + + xattr_size = SMB_VFS_GETXATTRAT_RECV(subreq, + &state->aio_state, + state, + &blob.data); + TALLOC_FREE(subreq); + if (xattr_size == -1) { + status = map_nt_error_from_unix(state->aio_state.error); + + if (state->as_root) { + tevent_req_nterror(req, status); + return; + } + if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + tevent_req_nterror(req, status); + return; + } + + state->as_root = true; + + become_root(); + subreq = SMB_VFS_GETXATTRAT_SEND(state, + state->ev, + state->dir_fsp, + state->smb_fname, + SAMBA_XATTR_DOS_ATTRIB, + sizeof(fstring)); + unbecome_root(); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + vfswrap_get_dos_attributes_getxattr_done, + req); + return; + } + + blob.length = xattr_size; + + status = parse_dos_attribute_blob(state->smb_fname, + blob, + &state->dosmode); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + + tevent_req_done(req); + return; +} + +static NTSTATUS vfswrap_get_dos_attributes_recv(struct tevent_req *req, + struct vfs_aio_state *aio_state, + uint32_t *dosmode) +{ + struct vfswrap_get_dos_attributes_state *state = + tevent_req_data(req, + struct vfswrap_get_dos_attributes_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + + *aio_state = state->aio_state; + *dosmode = state->dosmode; + tevent_req_received(req); + return NT_STATUS_OK; +} + static NTSTATUS vfswrap_fget_dos_attributes(struct vfs_handle_struct *handle, struct files_struct *fsp, uint32_t *dosmode) @@ -1707,7 +1873,7 @@ static struct tevent_req *vfswrap_offload_write_send( return tevent_req_post(req, ev); } - state->src_ev = src_fsp->conn->user_ev_ctx; + state->src_ev = src_fsp->conn->sconn->ev_ctx; state->src_fsp = src_fsp; state->buf = talloc_array(state, uint8_t, num); @@ -2386,11 +2552,8 @@ static bool vfswrap_lock(vfs_handle_struct *handle, files_struct *fsp, int op, o START_PROFILE(syscall_fcntl_lock); - if (fsp->use_ofd_locks || !lp_parm_bool(SNUM(fsp->conn), - "smbd", - "force process locks", - false)) { - op = map_process_lock_to_ofd_lock(op, &fsp->use_ofd_locks); + if (fsp->use_ofd_locks) { + op = map_process_lock_to_ofd_lock(op); } result = fcntl_lock(fsp->fh->fd, op, offset, count, type); @@ -2414,11 +2577,8 @@ static bool vfswrap_getlock(vfs_handle_struct *handle, files_struct *fsp, off_t START_PROFILE(syscall_fcntl_getlock); - if (fsp->use_ofd_locks || !lp_parm_bool(SNUM(fsp->conn), - "smbd", - "force process locks", - false)) { - op = map_process_lock_to_ofd_lock(op, &fsp->use_ofd_locks); + if (fsp->use_ofd_locks) { + op = map_process_lock_to_ofd_lock(op); } result = fcntl_getlock(fsp->fh->fd, op, poffset, pcount, ptype, ppid); @@ -2763,13 +2923,22 @@ static ssize_t vfswrap_getxattr(struct vfs_handle_struct *handle, } struct vfswrap_getxattrat_state { - int dirfd; + struct tevent_context *ev; + files_struct *dir_fsp; + const struct smb_filename *smb_fname; + struct tevent_req *req; + + /* + * The following variables are talloced off "state" which is protected + * by a destructor and thus are guaranteed to be safe to be used in the + * job function in the worker thread. + */ char *name; - size_t xattr_bufsize; const char *xattr_name; - ssize_t xattr_size; uint8_t *xattr_value; + struct security_unix_token *token; + ssize_t xattr_size; struct vfs_aio_state vfs_aio_state; SMBPROFILE_BYTES_ASYNC_STATE(profile_bytes); }; @@ -2780,23 +2949,26 @@ static int vfswrap_getxattrat_state_destructor( return -1; } -static void vfswrap_getxattrat_do(void *private_data); +static void vfswrap_getxattrat_do_sync(struct tevent_req *req); +static void vfswrap_getxattrat_do_async(void *private_data); static void vfswrap_getxattrat_done(struct tevent_req *subreq); static struct tevent_req *vfswrap_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, const char *xattr_name, size_t alloc_hint) { - struct tevent_context *ev = smb_vfs_ev_glue_ev_ctx(evg); - struct pthreadpool_tevent *tp = smb_vfs_ev_glue_tp_chdir_safe(evg); struct tevent_req *req = NULL; struct tevent_req *subreq = NULL; struct vfswrap_getxattrat_state *state = NULL; + size_t max_threads = 0; + bool have_per_thread_cwd = false; + bool have_per_thread_creds = false; + bool do_async = false; req = tevent_req_create(mem_ctx, &state, struct vfswrap_getxattrat_state); @@ -2804,19 +2976,49 @@ static struct tevent_req *vfswrap_getxattrat_send( return NULL; } *state = (struct vfswrap_getxattrat_state) { - .dirfd = dir_fsp->fh->fd, - .xattr_bufsize = alloc_hint, + .ev = ev, + .dir_fsp = dir_fsp, + .smb_fname = smb_fname, + .req = req, }; + max_threads = pthreadpool_tevent_max_threads(dir_fsp->conn->sconn->pool); + if (max_threads >= 1) { + /* + * We need a non sync threadpool! + */ + have_per_thread_cwd = per_thread_cwd_supported(); + } +#ifdef HAVE_LINUX_THREAD_CREDENTIALS + have_per_thread_creds = true; +#endif + if (have_per_thread_cwd && have_per_thread_creds) { + do_async = true; + } + SMBPROFILE_BYTES_ASYNC_START(syscall_asys_getxattrat, profile_p, state->profile_bytes, 0); - if (state->dirfd == -1) { + if (dir_fsp->fh->fd == -1) { DBG_ERR("Need a valid directory fd\n"); tevent_req_error(req, EINVAL); return tevent_req_post(req, ev); } + if (alloc_hint > 0) { + state->xattr_value = talloc_zero_array(state, + uint8_t, + alloc_hint); + if (tevent_req_nomem(state->xattr_value, req)) { + return tevent_req_post(req, ev); + } + } + + if (!do_async) { + vfswrap_getxattrat_do_sync(req); + return tevent_req_post(req, ev); + } + /* * Now allocate all parameters from a memory context that won't go away * no matter what. These paremeters will get used in threads and we @@ -2834,22 +3036,32 @@ static struct tevent_req *vfswrap_getxattrat_send( return tevent_req_post(req, ev); } - if (state->xattr_bufsize > 0) { - state->xattr_value = talloc_zero_array(state, - uint8_t, - state->xattr_bufsize); - if (tevent_req_nomem(state->xattr_value, req)) { - return tevent_req_post(req, ev); - } + /* + * This is a hot codepath so at first glance one might think we should + * somehow optimize away the token allocation and do a + * talloc_reference() or similar black magic instead. But due to the + * talloc_stackframe pool per SMB2 request this should be a simple copy + * without a malloc in most cases. + */ + if (geteuid() == sec_initial_uid()) { + state->token = root_unix_token(state); + } else { + state->token = copy_unix_token( + state, + dir_fsp->conn->session_info->unix_token); + } + if (tevent_req_nomem(state->token, req)) { + return tevent_req_post(req, ev); } SMBPROFILE_BYTES_ASYNC_SET_IDLE(state->profile_bytes); - subreq = pthreadpool_tevent_job_send(state, - ev, - tp, - vfswrap_getxattrat_do, - state); + subreq = pthreadpool_tevent_job_send( + state, + ev, + dir_fsp->conn->sconn->pool, + vfswrap_getxattrat_do_async, + state); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } @@ -2860,7 +3072,43 @@ static struct tevent_req *vfswrap_getxattrat_send( return req; } -static void vfswrap_getxattrat_do(void *private_data) +static void vfswrap_getxattrat_do_sync(struct tevent_req *req) +{ + struct vfswrap_getxattrat_state *state = talloc_get_type_abort( + req, struct vfswrap_getxattrat_state); + char *path = NULL; + char *tofree = NULL; + char pathbuf[PATH_MAX+1]; + size_t pathlen; + int err; + + pathlen = full_path_tos(state->dir_fsp->fsp_name->base_name, + state->smb_fname->base_name, + pathbuf, + sizeof(pathbuf), + &path, + &tofree); + if (pathlen == -1) { + tevent_req_error(req, ENOMEM); + return; + } + + state->xattr_size = getxattr(path, + state->xattr_name, + state->xattr_value, + talloc_array_length(state->xattr_value)); + err = errno; + TALLOC_FREE(tofree); + if (state->xattr_size == -1) { + tevent_req_error(req, err); + return; + } + + tevent_req_done(req); + return; +} + +static void vfswrap_getxattrat_do_async(void *private_data) { struct vfswrap_getxattrat_state *state = talloc_get_type_abort( private_data, struct vfswrap_getxattrat_state); @@ -2874,14 +3122,22 @@ static void vfswrap_getxattrat_do(void *private_data) /* * Here we simulate a getxattrat() * call using fchdir();getxattr() - * - * We don't need to revert the directory - * change as pthreadpool_tevent wrapper - * handlers that. */ - SMB_ASSERT(pthreadpool_tevent_current_job_per_thread_cwd()); - ret = fchdir(state->dirfd); + per_thread_cwd_activate(); + + /* Become the correct credential on this thread. */ + ret = set_thread_credentials(state->token->uid, + state->token->gid, + (size_t)state->token->ngroups, + state->token->groups); + if (ret != 0) { + state->xattr_size = -1; + state->vfs_aio_state.error = errno; + goto end_profile; + } + + ret = fchdir(state->dir_fsp->fh->fd); if (ret == -1) { state->xattr_size = -1; state->vfs_aio_state.error = errno; @@ -2891,7 +3147,7 @@ static void vfswrap_getxattrat_do(void *private_data) state->xattr_size = getxattr(state->name, state->xattr_name, state->xattr_value, - state->xattr_bufsize); + talloc_array_length(state->xattr_value)); if (state->xattr_size == -1) { state->vfs_aio_state.error = errno; } @@ -2909,12 +3165,30 @@ static void vfswrap_getxattrat_done(struct tevent_req *subreq) struct vfswrap_getxattrat_state *state = tevent_req_data( req, struct vfswrap_getxattrat_state); int ret; + bool ok; + + /* + * Make sure we run as the user again + */ + ok = change_to_user_by_fsp(state->dir_fsp); + SMB_ASSERT(ok); ret = pthreadpool_tevent_job_recv(subreq); TALLOC_FREE(subreq); SMBPROFILE_BYTES_ASYNC_END(state->profile_bytes); talloc_set_destructor(state, NULL); - if (tevent_req_error(req, ret)) { + if (ret != 0) { + if (ret != EAGAIN) { + tevent_req_error(req, ret); + return; + } + /* + * If we get EAGAIN from pthreadpool_tevent_job_recv() this + * means the lower level pthreadpool failed to create a new + * thread. Fallback to sync processing in that case to allow + * some progress for the client. + */ + vfswrap_getxattrat_do_sync(req); return; } @@ -3164,8 +3438,8 @@ static struct vfs_fn_pointers vfs_default_fns = { .set_dos_attributes_fn = vfswrap_set_dos_attributes, .fset_dos_attributes_fn = vfswrap_fset_dos_attributes, .get_dos_attributes_fn = vfswrap_get_dos_attributes, - .get_dos_attributes_send_fn = vfs_not_implemented_get_dos_attributes_send, - .get_dos_attributes_recv_fn = vfs_not_implemented_get_dos_attributes_recv, + .get_dos_attributes_send_fn = vfswrap_get_dos_attributes_send, + .get_dos_attributes_recv_fn = vfswrap_get_dos_attributes_recv, .fget_dos_attributes_fn = vfswrap_fget_dos_attributes, .offload_read_send_fn = vfswrap_offload_read_send, .offload_read_recv_fn = vfswrap_offload_read_recv,