2 Unix SMB/CIFS implementation.
5 Copyright (C) Stefan Metzmacher 2009
6 Copyright (C) Jeremy Allison 2010
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "smbd/smbd.h"
24 #include "smbd/globals.h"
25 #include "../libcli/smb/smb_common.h"
27 #include "../lib/util/tevent_ntstatus.h"
28 #include "../librpc/gen_ndr/open_files.h"
29 #include "source3/lib/dbwrap/dbwrap_watch.h"
31 #include "librpc/gen_ndr/ndr_quota.h"
34 #define DBGC_CLASS DBGC_SMB2
36 static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx,
37 struct tevent_context *ev,
38 struct smbd_smb2_request *smb2req,
39 struct files_struct *in_fsp,
41 uint8_t in_file_info_class,
42 DATA_BLOB in_input_buffer,
43 uint32_t in_additional_information);
44 static NTSTATUS smbd_smb2_setinfo_recv(struct tevent_req *req);
46 static void smbd_smb2_request_setinfo_done(struct tevent_req *subreq);
47 NTSTATUS smbd_smb2_request_process_setinfo(struct smbd_smb2_request *req)
49 struct smbXsrv_connection *xconn = req->xconn;
51 const uint8_t *inbody;
53 uint8_t in_file_info_class;
54 uint16_t in_input_buffer_offset;
55 uint32_t in_input_buffer_length;
56 DATA_BLOB in_input_buffer;
57 uint32_t in_additional_information;
58 uint64_t in_file_id_persistent;
59 uint64_t in_file_id_volatile;
60 struct files_struct *in_fsp;
61 struct tevent_req *subreq;
63 status = smbd_smb2_request_verify_sizes(req, 0x21);
64 if (!NT_STATUS_IS_OK(status)) {
65 return smbd_smb2_request_error(req, status);
67 inbody = SMBD_SMB2_IN_BODY_PTR(req);
69 in_info_type = CVAL(inbody, 0x02);
70 in_file_info_class = CVAL(inbody, 0x03);
71 in_input_buffer_length = IVAL(inbody, 0x04);
72 in_input_buffer_offset = SVAL(inbody, 0x08);
73 /* 0x0A 2 bytes reserved */
74 in_additional_information = IVAL(inbody, 0x0C);
75 in_file_id_persistent = BVAL(inbody, 0x10);
76 in_file_id_volatile = BVAL(inbody, 0x18);
78 if (in_input_buffer_offset == 0 && in_input_buffer_length == 0) {
80 } else if (in_input_buffer_offset !=
81 (SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req))) {
82 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
85 if (in_input_buffer_length > SMBD_SMB2_IN_DYN_LEN(req)) {
86 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
89 in_input_buffer.data = SMBD_SMB2_IN_DYN_PTR(req);
90 in_input_buffer.length = in_input_buffer_length;
92 if (in_input_buffer.length > xconn->smb2.server.max_trans) {
93 DEBUG(2,("smbd_smb2_request_process_setinfo: "
94 "client ignored max trans: %s: 0x%08X: 0x%08X\n",
95 __location__, (unsigned)in_input_buffer.length,
96 (unsigned)xconn->smb2.server.max_trans));
97 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
100 status = smbd_smb2_request_verify_creditcharge(req,
101 in_input_buffer.length);
102 if (!NT_STATUS_IS_OK(status)) {
103 return smbd_smb2_request_error(req, status);
106 in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
107 if (in_fsp == NULL) {
108 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
111 subreq = smbd_smb2_setinfo_send(req, req->sconn->ev_ctx,
116 in_additional_information);
117 if (subreq == NULL) {
118 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
120 tevent_req_set_callback(subreq, smbd_smb2_request_setinfo_done, req);
122 return smbd_smb2_request_pending_queue(req, subreq, 500);
125 static void smbd_smb2_request_setinfo_done(struct tevent_req *subreq)
127 struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
128 struct smbd_smb2_request);
131 NTSTATUS error; /* transport error */
133 status = smbd_smb2_setinfo_recv(subreq);
135 if (!NT_STATUS_IS_OK(status)) {
136 error = smbd_smb2_request_error(req, status);
137 if (!NT_STATUS_IS_OK(error)) {
138 smbd_server_connection_terminate(req->xconn,
145 outbody = smbd_smb2_generate_outbody(req, 0x02);
146 if (outbody.data == NULL) {
147 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
148 if (!NT_STATUS_IS_OK(error)) {
149 smbd_server_connection_terminate(req->xconn,
156 SSVAL(outbody.data, 0x00, 0x02); /* struct size */
158 error = smbd_smb2_request_done(req, outbody, NULL);
159 if (!NT_STATUS_IS_OK(error)) {
160 smbd_server_connection_terminate(req->xconn,
166 struct defer_rename_state {
167 struct tevent_req *req;
168 struct smbd_smb2_request *smb2req;
169 struct tevent_context *ev;
170 struct files_struct *fsp;
175 static int defer_rename_state_destructor(struct defer_rename_state *rename_state)
177 SAFE_FREE(rename_state->data);
181 static void defer_rename_done(struct tevent_req *subreq);
183 struct delay_rename_lease_break_state {
184 struct files_struct *fsp;
188 static bool delay_rename_lease_break_fn(
189 struct share_mode_entry *e,
192 struct delay_rename_lease_break_state *state = private_data;
193 struct files_struct *fsp = state->fsp;
194 uint32_t e_lease_type, break_to;
197 ours = smb2_lease_equal(fsp_client_guid(fsp),
198 &fsp->lease->lease.lease_key,
205 e_lease_type = get_lease_type(e, fsp->file_id);
207 if ((e_lease_type & SMB2_LEASE_HANDLE) == 0) {
211 stale = share_entry_stale_pid(e);
216 break_to = (e_lease_type & ~SMB2_LEASE_HANDLE);
219 fsp->conn->sconn->msg_ctx, &fsp->file_id, e, break_to);
224 static struct tevent_req *delay_rename_for_lease_break(struct tevent_req *req,
225 struct smbd_smb2_request *smb2req,
226 struct tevent_context *ev,
227 struct files_struct *fsp,
228 struct share_mode_lock *lck,
233 struct tevent_req *subreq;
234 struct defer_rename_state *rename_state;
235 struct delay_rename_lease_break_state state = { .fsp = fsp };
236 struct timeval timeout;
239 if (fsp->oplock_type != LEASE_OPLOCK) {
243 ok = share_mode_forall_leases(
244 lck, delay_rename_lease_break_fn, &state);
253 /* Setup a watch on this record. */
254 rename_state = talloc_zero(req, struct defer_rename_state);
255 if (rename_state == NULL) {
259 rename_state->req = req;
260 rename_state->smb2req = smb2req;
261 rename_state->ev = ev;
262 rename_state->fsp = fsp;
263 rename_state->data = data;
264 rename_state->data_size = data_size;
266 talloc_set_destructor(rename_state, defer_rename_state_destructor);
268 subreq = share_mode_watch_send(
272 (struct server_id){0});
274 if (subreq == NULL) {
275 exit_server("Could not watch share mode record for rename\n");
278 tevent_req_set_callback(subreq, defer_rename_done, rename_state);
280 timeout = timeval_set(OPLOCK_BREAK_TIMEOUT*2, 0);
281 if (!tevent_req_set_endtime(subreq,
283 timeval_sum(&smb2req->request_time, &timeout))) {
284 exit_server("Could not set rename timeout\n");
290 static void defer_rename_done(struct tevent_req *subreq)
292 struct defer_rename_state *state = tevent_req_callback_data(
293 subreq, struct defer_rename_state);
295 struct share_mode_lock *lck;
299 status = share_mode_watch_recv(subreq, NULL, NULL);
301 if (!NT_STATUS_IS_OK(status)) {
302 DEBUG(5, ("dbwrap_record_watch_recv returned %s\n",
304 tevent_req_nterror(state->req, status);
309 * Make sure we run as the user again
311 ok = change_to_user_and_service(
312 state->smb2req->tcon->compat,
313 state->smb2req->session->global->session_wire_id);
315 tevent_req_nterror(state->req, NT_STATUS_ACCESS_DENIED);
319 /* Do we still need to wait ? */
320 lck = get_existing_share_mode_lock(state->req, state->fsp->file_id);
322 tevent_req_nterror(state->req, NT_STATUS_UNSUCCESSFUL);
325 subreq = delay_rename_for_lease_break(state->req,
333 /* Yep - keep waiting. */
340 /* Do the rename under the lock. */
341 status = smbd_do_setfilepathinfo(state->fsp->conn,
342 state->smb2req->smb1req,
344 SMB2_FILE_RENAME_INFORMATION_INTERNAL,
346 state->fsp->fsp_name,
352 SAFE_FREE(state->data);
354 if (!NT_STATUS_IS_OK(status)) {
355 tevent_req_nterror(state->req, status);
359 tevent_req_done(state->req);
362 struct smbd_smb2_setinfo_state {
363 struct smbd_smb2_request *smb2req;
366 static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx,
367 struct tevent_context *ev,
368 struct smbd_smb2_request *smb2req,
369 struct files_struct *fsp,
370 uint8_t in_info_type,
371 uint8_t in_file_info_class,
372 DATA_BLOB in_input_buffer,
373 uint32_t in_additional_information)
375 struct tevent_req *req = NULL;
376 struct smbd_smb2_setinfo_state *state = NULL;
377 struct smb_request *smbreq = NULL;
378 connection_struct *conn = smb2req->tcon->compat;
379 struct share_mode_lock *lck = NULL;
382 req = tevent_req_create(mem_ctx, &state,
383 struct smbd_smb2_setinfo_state);
387 state->smb2req = smb2req;
389 DEBUG(10,("smbd_smb2_setinfo_send: %s - %s\n",
390 fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
392 smbreq = smbd_smb2_fake_smb_request(smb2req);
393 if (tevent_req_nomem(smbreq, req)) {
394 return tevent_req_post(req, ev);
398 tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
399 return tevent_req_post(req, ev);
402 switch (in_info_type) {
403 case SMB2_0_INFO_FILE:
405 uint16_t file_info_level;
411 file_info_level = in_file_info_class + 1000;
412 if (file_info_level == SMB_FILE_RENAME_INFORMATION) {
413 /* SMB2_FILE_RENAME_INFORMATION_INTERNAL == 0xFF00 + in_file_info_class */
414 file_info_level = SMB2_FILE_RENAME_INFORMATION_INTERNAL;
417 if (fsp->fh->fd == -1) {
419 * This is actually a SETFILEINFO on a directory
420 * handle (returned from an NT SMB). NT5.0 seems
421 * to do this call. JRA.
423 if (fsp->fsp_name->flags & SMB_FILENAME_POSIX_PATH) {
424 /* Always do lstat for UNIX calls. */
425 if (SMB_VFS_LSTAT(conn, fsp->fsp_name)) {
426 DEBUG(3,("smbd_smb2_setinfo_send: "
427 "SMB_VFS_LSTAT of %s failed "
428 "(%s)\n", fsp_str_dbg(fsp),
430 status = map_nt_error_from_unix(errno);
431 tevent_req_nterror(req, status);
432 return tevent_req_post(req, ev);
435 if (SMB_VFS_STAT(conn, fsp->fsp_name) != 0) {
436 DEBUG(3,("smbd_smb2_setinfo_send: "
437 "fileinfo of %s failed (%s)\n",
440 status = map_nt_error_from_unix(errno);
441 tevent_req_nterror(req, status);
442 return tevent_req_post(req, ev);
445 } else if (fsp->print_file) {
447 * Doing a DELETE_ON_CLOSE should cancel a print job.
449 if ((file_info_level == SMB_SET_FILE_DISPOSITION_INFO)
450 && in_input_buffer.length >= 1
451 && CVAL(in_input_buffer.data,0)) {
452 fsp->fh->private_options |= NTCREATEX_OPTIONS_PRIVATE_DELETE_ON_CLOSE;
454 DEBUG(3,("smbd_smb2_setinfo_send: "
455 "Cancelling print job (%s)\n",
458 tevent_req_done(req);
459 return tevent_req_post(req, ev);
461 tevent_req_nterror(req, NT_STATUS_OBJECT_PATH_INVALID);
462 return tevent_req_post(req, ev);
465 * Original code - this is an open file.
468 if (SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st) != 0) {
469 DEBUG(3,("smbd_smb2_setinfo_send: fstat "
470 "of %s failed (%s)\n",
473 status = map_nt_error_from_unix(errno);
474 tevent_req_nterror(req, status);
475 return tevent_req_post(req, ev);
480 data_size = in_input_buffer.length;
482 data = (char *)SMB_MALLOC_ARRAY(char, data_size);
483 if (tevent_req_nomem(data, req)) {
484 return tevent_req_post(req, ev);
486 memcpy(data, in_input_buffer.data, data_size);
489 if (file_info_level == SMB2_FILE_RENAME_INFORMATION_INTERNAL) {
490 struct tevent_req *subreq;
492 lck = get_existing_share_mode_lock(mem_ctx,
496 tevent_req_nterror(req,
497 NT_STATUS_UNSUCCESSFUL);
498 return tevent_req_post(req, ev);
501 subreq = delay_rename_for_lease_break(req,
509 /* Wait for lease break response. */
511 /* Ensure we can't be closed in flight. */
512 if (!aio_add_req_to_fsp(fsp, req)) {
514 tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
515 return tevent_req_post(req, ev);
523 status = smbd_do_setfilepathinfo(conn, smbreq, state,
532 if (!NT_STATUS_IS_OK(status)) {
533 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL)) {
534 status = NT_STATUS_INVALID_INFO_CLASS;
536 tevent_req_nterror(req, status);
537 return tevent_req_post(req, ev);
542 case SMB2_0_INFO_FILESYSTEM:
544 uint16_t file_info_level = in_file_info_class + 1000;
546 status = smbd_do_setfsinfo(conn, smbreq, state,
550 if (!NT_STATUS_IS_OK(status)) {
551 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL)) {
552 status = NT_STATUS_INVALID_INFO_CLASS;
554 tevent_req_nterror(req, status);
555 return tevent_req_post(req, ev);
560 case SMB2_0_INFO_SECURITY:
562 if (!CAN_WRITE(conn)) {
563 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
564 return tevent_req_post(req, ev);
567 status = set_sd_blob(fsp,
568 in_input_buffer.data,
569 in_input_buffer.length,
570 in_additional_information &
571 SMB_SUPPORTED_SECINFO_FLAGS);
572 if (!NT_STATUS_IS_OK(status)) {
573 tevent_req_nterror(req, status);
574 return tevent_req_post(req, ev);
579 case SMB2_0_INFO_QUOTA:
581 #ifdef HAVE_SYS_QUOTAS
582 struct file_quota_information info = {0};
583 SMB_NTQUOTA_STRUCT qt = {0};
584 enum ndr_err_code err;
587 if (!fsp->fake_file_handle) {
588 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
589 return tevent_req_post(req, ev);
591 err = ndr_pull_struct_blob(
592 &in_input_buffer, state, &info,
593 (ndr_pull_flags_fn_t)ndr_pull_file_quota_information);
594 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
595 tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
596 return tevent_req_post(req, ev);
599 qt.usedspace = info.quota_used;
601 qt.softlim = info.quota_threshold;
603 qt.hardlim = info.quota_limit;
606 ret = vfs_set_ntquota(fsp, SMB_USER_QUOTA_TYPE, &qt.sid, &qt);
608 status = map_nt_error_from_unix(errno);
609 tevent_req_nterror(req, status);
610 return tevent_req_post(req, ev);
612 status = NT_STATUS_OK;
615 tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
616 return tevent_req_post(req, ev);
620 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
621 return tevent_req_post(req, ev);
624 tevent_req_done(req);
625 return tevent_req_post(req, ev);
628 static NTSTATUS smbd_smb2_setinfo_recv(struct tevent_req *req)
632 if (tevent_req_is_nterror(req, &status)) {
633 tevent_req_received(req);
637 tevent_req_received(req);