/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* * Dispatch function for SMB2_LOCK */ #include struct SMB2_LOCK_ELEMENT { uint64_t Offset; uint64_t Length; uint32_t Flags; uint32_t reserved; }; static smb_sdrc_t smb2_lock_async(smb_request_t *); static uint32_t smb2_lock_exec(smb_request_t *, uint16_t); static uint32_t smb2_lock_elem(smb_request_t *, struct SMB2_LOCK_ELEMENT *); /* * This is a somewhat arbitrary sanity limit on the length of the * SMB2_LOCK_ELEMENT array. It usually has length one or two. */ int smb2_lock_max_elem = 1024; smb_sdrc_t smb2_lock(smb_request_t *sr) { struct SMB2_LOCK_ELEMENT elem; smb2fid_t smb2fid; uint32_t save_offset; uint32_t LockSequence; uint32_t status; uint16_t StructSize; uint16_t LockCount; uint16_t i; boolean_t MayBlock = B_FALSE; int rc = 0; /* * SMB2 Lock request */ rc = smb_mbc_decodef( &sr->smb_data, "wwlqq", &StructSize, /* w */ &LockCount, /* w */ &LockSequence, /* l */ &smb2fid.persistent, /* q */ &smb2fid.temporal); /* q */ if (rc || StructSize != 48) return (SDRC_ERROR); status = smb2sr_lookup_fid(sr, &smb2fid); if (status) goto errout; if (sr->fid_ofile->f_node == NULL || LockCount == 0) { status = NT_STATUS_INVALID_PARAMETER; goto errout; } if (LockCount > smb2_lock_max_elem) { status = NT_STATUS_INSUFFICIENT_RESOURCES; goto errout; } /* * Process the array of SMB2_LOCK_ELEMENT structs * We do this twice. (it's always a short list) * The first time, just validate the flags, and check * if any of the locking request might need to block. * The second time (either here, or in the async * handler function) process the locks for real. */ save_offset = sr->smb_data.chain_offset; for (i = 0; i < LockCount; i++) { rc = smb_mbc_decodef( &sr->smb_data, "qqll", &elem.Offset, /* q */ &elem.Length, /* q */ &elem.Flags, /* l */ &elem.reserved); /* l */ if (rc) { status = NT_STATUS_INVALID_PARAMETER; goto errout; } /* * Make sure the flags are valid; * Find out if we might block. */ switch (elem.Flags) { case SMB2_LOCKFLAG_SHARED_LOCK: case SMB2_LOCKFLAG_EXCLUSIVE_LOCK: MayBlock = B_TRUE; break; /* BEGIN CSTYLED */ case SMB2_LOCKFLAG_SHARED_LOCK | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: case SMB2_LOCKFLAG_EXCLUSIVE_LOCK | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: case SMB2_LOCKFLAG_UNLOCK: /* END CSTYLED */ break; default: status = NT_STATUS_INVALID_PARAMETER; goto errout; } } if (MayBlock) { /* * May need to block. "Go async". */ status = smb2sr_go_async(sr, smb2_lock_async); goto errout; } sr->smb_data.chain_offset = save_offset; status = smb2_lock_exec(sr, LockCount); if (status) goto errout; /* * SMB2 Lock reply (sync) */ StructSize = 4; (void) smb_mbc_encodef( &sr->reply, "w..", StructSize); /* w */ /* reserved .. */ return (SDRC_SUCCESS); errout: smb2sr_put_error(sr, status); return (SDRC_SUCCESS); } static smb_sdrc_t smb2_lock_async(smb_request_t *sr) { smb2fid_t smb2fid; uint32_t LockSequence; uint32_t status; uint16_t StructSize; uint16_t LockCount; int rc = 0; /* * Decode the lock request again. It should all decode * exactly the same as the first time we saw it. If not, * report an "internal error". */ rc = smb_mbc_decodef( &sr->smb_data, "wwlqq", &StructSize, /* w */ &LockCount, /* w */ &LockSequence, /* l */ &smb2fid.persistent, /* q */ &smb2fid.temporal); /* q */ if (rc || StructSize != 48) return (SDRC_ERROR); status = smb2sr_lookup_fid(sr, &smb2fid); if (status) goto errout; if (sr->fid_ofile->f_node == NULL || LockCount == 0) { status = NT_STATUS_INTERNAL_ERROR; goto errout; } status = smb2_lock_exec(sr, LockCount); if (status) goto errout; /* * SMB2 Lock reply (async) */ StructSize = 4; (void) smb_mbc_encodef( &sr->reply, "w..", StructSize); /* w */ /* reserved .. */ return (SDRC_SUCCESS); errout: smb2sr_put_error(sr, status); return (SDRC_SUCCESS); } /* * Execute the vector of locks. This is the common function called by * either the sync or async code paths. We've already decoded this * request once when we get here, so if there are any decode errors * then it's some kind of internal error. */ static uint32_t smb2_lock_exec(smb_request_t *sr, uint16_t LockCount) { struct SMB2_LOCK_ELEMENT elem; uint32_t status = 0; uint16_t i; int rc; /* * On entry, out position in the input data should be * after both the SMB2 header and the fixed part of * the SMB Lock request header (24). */ ASSERT(sr->smb_data.chain_offset == (sr->smb2_cmd_hdr + SMB2_HDR_SIZE + 24)); /* * This is checked by our callers, but let's make sure. */ ASSERT(sr->fid_ofile->f_node != NULL); for (i = 0; i < LockCount; i++) { rc = smb_mbc_decodef( &sr->smb_data, "qqll", &elem.Offset, /* q */ &elem.Length, /* q */ &elem.Flags, /* l */ &elem.reserved); /* l */ if (rc) { status = NT_STATUS_INTERNAL_ERROR; break; } status = smb2_lock_elem(sr, &elem); if (status) break; } return (status); } static uint32_t smb2_lock_elem(smb_request_t *sr, struct SMB2_LOCK_ELEMENT *elem) { smb_node_t *node = sr->fid_ofile->f_node; uint32_t status; uint32_t ltype; uint32_t timeout = 0; switch (elem->Flags) { case SMB2_LOCKFLAG_SHARED_LOCK: timeout = UINT_MAX; /* FALLTHROUGH */ case SMB2_LOCKFLAG_SHARED_LOCK | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: ltype = SMB_LOCK_TYPE_READONLY; status = smb_lock_range(sr, elem->Offset, elem->Length, timeout, ltype); break; case SMB2_LOCKFLAG_EXCLUSIVE_LOCK: timeout = UINT_MAX; /* FALLTHROUGH */ case SMB2_LOCKFLAG_EXCLUSIVE_LOCK | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: ltype = SMB_LOCK_TYPE_READWRITE; status = smb_lock_range(sr, elem->Offset, elem->Length, timeout, ltype); break; case SMB2_LOCKFLAG_UNLOCK: status = smb_unlock_range(sr, node, elem->Offset, elem->Length); break; /* * We've already checked the flags previously, so any * surprises here are some kind of internal error. */ default: status = NT_STATUS_INTERNAL_ERROR; break; } return (status); }