smb_locking_andx.c revision 8c10a8659ac31335ed870a1711c0182623f72fd6
2549N/A/*
2549N/A * CDDL HEADER START
2549N/A *
2549N/A * The contents of this file are subject to the terms of the
2549N/A * Common Development and Distribution License (the "License").
2549N/A * You may not use this file except in compliance with the License.
2549N/A *
2549N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2549N/A * or http://www.opensolaris.org/os/licensing.
2549N/A * See the License for the specific language governing permissions
2549N/A * and limitations under the License.
2549N/A *
2549N/A * When distributing Covered Code, include this CDDL HEADER in each
2549N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2549N/A * If applicable, add the following below this CDDL HEADER, with the
2549N/A * fields enclosed by brackets "[]" replaced with your own identifying
2549N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2549N/A *
2549N/A * CDDL HEADER END
2549N/A */
2549N/A/*
2549N/A * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
2549N/A * Use is subject to license terms.
2549N/A */
3215N/A
2549N/A#pragma ident "%Z%%M% %I% %E% SMI"
2549N/A
2549N/A/*
2549N/A * SMB: locking_andx
2549N/A *
2549N/A * SMB_COM_LOCKING_ANDX allows both locking and/or unlocking of file range(s).
2549N/A *
2549N/A * Client Request Description
2549N/A * ================================== =================================
2549N/A *
2549N/A * UCHAR WordCount; Count of parameter words = 8
2549N/A * UCHAR AndXCommand; Secondary (X) command; 0xFF = none
2549N/A * UCHAR AndXReserved; Reserved (must be 0)
2549N/A * USHORT AndXOffset; Offset to next command WordCount
2549N/A * USHORT Fid; File handle
2549N/A * UCHAR LockType; See LockType table below
2549N/A * UCHAR OplockLevel; The new oplock level
2549N/A * ULONG Timeout; Milliseconds to wait for unlock
2549N/A * USHORT NumberOfUnlocks; Num. unlock range structs following
2549N/A * USHORT NumberOfLocks; Num. lock range structs following
2549N/A * USHORT ByteCount; Count of data bytes
2549N/A * LOCKING_ANDX_RANGE Unlocks[]; Unlock ranges
2549N/A * LOCKING_ANDX_RANGE Locks[]; Lock ranges
2549N/A *
2549N/A * LockType Flag Name Value Description
2549N/A * ============================ ===== ================================
2549N/A *
2549N/A * LOCKING_ANDX_SHARED_LOCK 0x01 Read-only lock
2549N/A * LOCKING_ANDX_OPLOCK_RELEASE 0x02 Oplock break notification
2549N/A * LOCKING_ANDX_CHANGE_LOCKTYPE 0x04 Change lock type
2549N/A * LOCKING_ANDX_CANCEL_LOCK 0x08 Cancel outstanding request
2549N/A * LOCKING_ANDX_LARGE_FILES 0x10 Large file locking format
2549N/A *
2549N/A * LOCKING_ANDX_RANGE Format
2549N/A * =====================================================================
2549N/A *
2549N/A * USHORT Pid; PID of process "owning" lock
2549N/A * ULONG Offset; Offset to bytes to [un]lock
2549N/A * ULONG Length; Number of bytes to [un]lock
2549N/A *
2549N/A * Large File LOCKING_ANDX_RANGE Format
2549N/A * =====================================================================
2549N/A *
2549N/A * USHORT Pid; PID of process "owning" lock
2549N/A * USHORT Pad; Pad to DWORD align (mbz)
2549N/A * ULONG OffsetHigh; Offset to bytes to [un]lock
2549N/A * (high)
2549N/A * ULONG OffsetLow; Offset to bytes to [un]lock (low)
2549N/A * ULONG LengthHigh; Number of bytes to [un]lock
2549N/A * (high)
2549N/A * ULONG LengthLow; Number of bytes to [un]lock (low)
2549N/A *
2549N/A * Server Response Description
2859N/A * ================================== =================================
2549N/A *
2549N/A * UCHAR WordCount; Count of parameter words = 2
2549N/A * UCHAR AndXCommand; Secondary (X) command; 0xFF =
2549N/A * none
2549N/A * UCHAR AndXReserved; Reserved (must be 0)
2549N/A * USHORT AndXOffset; Offset to next command WordCount
2549N/A * USHORT ByteCount; Count of data bytes = 0
2549N/A *
2549N/A * Locking is a simple mechanism for excluding other processes read/write
2549N/A * access to regions of a file. The locked regions can be anywhere in the
2549N/A * logical file. Locking beyond end-of-file is permitted. Any process
2549N/A * using the Fid specified in this request's Fid has access to the locked
2549N/A * bytes, other processes will be denied the locking of the same bytes.
2549N/A *
2549N/A * The proper method for using locks is not to rely on being denied read or
2549N/A * write access on any of the read/write protocols but rather to attempt
2549N/A * the locking protocol and proceed with the read/write only if the locks
2549N/A * succeeded.
2549N/A *
2549N/A * Locking a range of bytes will fail if any subranges or overlapping
2549N/A * ranges are locked. In other words, if any of the specified bytes are
2549N/A * already locked, the lock will fail.
2549N/A *
2549N/A * If NumberOfUnlocks is non-zero, the Unlocks vector contains
2549N/A * NumberOfUnlocks elements. Each element requests that a lock at Offset
2549N/A * of Length be released. If NumberOfLocks is nonzero, the Locks vector
2549N/A * contains NumberOfLocks elements. Each element requests the acquisition
2549N/A * of a lock at Offset of Length.
2549N/A *
2549N/A * Timeout is the maximum amount of time to wait for the byte range(s)
2549N/A * specified to become unlocked. A timeout value of 0 indicates that the
2549N/A * server should fail immediately if any lock range specified is locked. A
2549N/A *
2549N/A * timeout value of -1 indicates that the server should wait as long as it
2549N/A * takes for each byte range specified to become unlocked so that it may be
2549N/A * again locked by this protocol. Any other value of smb_timeout specifies
2549N/A * the maximum number of milliseconds to wait for all lock range(s)
2549N/A * specified to become available.
2549N/A *
2549N/A * If any of the lock ranges timeout because of the area to be locked is
2549N/A * already locked (or the lock fails), the other ranges in the protocol
2549N/A * request which were successfully locked as a result of this protocol will
2549N/A * be unlocked (either all requested ranges will be locked when this
2549N/A * protocol returns to the client or none).
2549N/A *
2549N/A * If LockType has the LOCKING_ANDX_SHARED_LOCK flag set, the lock is
2549N/A * specified as a shared lock. Locks for both read and write (where
2549N/A * LOCKING_ANDX_SHARED_LOCK is clear) should be prohibited, but other
2859N/A * shared locks should be permitted. If shared locks can not be supported
2859N/A * by a server, the server should map the lock to a lock for both read and
2549N/A * write. Closing a file with locks still in force causes the locks to be
2859N/A * released in no defined order.
2859N/A *
2859N/A * If LockType has the LOCKING_ANDX_LARGE_FILES flag set and if the
2549N/A * negotiated protocol is NT LM 0.12 or later, then the Locks and Unlocks
2859N/A * vectors are in the Large File LOCKING_ANDX_RANGE format. This allows
2549N/A * specification of 64 bit offsets for very large files.
2914N/A *
2914N/A * If the one and only member of the Locks vector has the
2914N/A * LOCKING_ANDX_CANCEL_LOCK flag set in the LockType field, the client is
2859N/A * requesting the server to cancel a previously requested, but not yet
2859N/A * responded to, lock.
2859N/A *
2859N/A * If LockType has the LOCKING_ANDX_CHANGE_LOCKTYPE flag set, the client is
2859N/A * requesting that the server atomically change the lock type from a shared
2549N/A * lock to an exclusive lock or vice versa. If the server can not do this
2549N/A * in an atomic fashion, the server must reject this request. NT and W95
2859N/A * servers do not support this capability.
2859N/A *
2859N/A * Oplocks are described in the "Opportunistic Locks" section elsewhere in
2859N/A * this document. A client requests an oplock by setting the appropriate
2859N/A * bit in the SMB_COM_OPEN_ANDX request when the file is being opened in a
2859N/A * mode which is not exclusive. The server responds by setting the
2859N/A * appropriate bit in the response SMB indicating whether or not the oplock
2859N/A * was granted. By granting the oplock, the server tells the client the
2859N/A * file is currently only being used by this one client process at the
2859N/A * current time. The client can therefore safely do read ahead and write
2859N/A * behind as well as local caching of file locks knowing that the file will
2859N/A * not be accessed/changed in any way by another process while the oplock
2859N/A * is in effect. The client will be notified when any other process
2859N/A * attempts to open or modify the oplocked file.
2859N/A *
2859N/A * When another user attempts to open or otherwise modify the file which a
2859N/A * client has oplocked, the server delays the second attempt and notifies
2859N/A * the client via an SMB_LOCKING_ANDX SMB asynchronously sent from the
2859N/A * server to the client. This message has the LOCKING_ANDX_OPLOCK_RELEASE
2859N/A * flag set indicating to the client that the oplock is being broken.
2859N/A *
2859N/A * OplockLevel indicates the type of oplock the client now owns. If
2859N/A * OplockLevel is 0, the client possesses no oplocks on the file at all, if
2859N/A * OplockLevel is 1 the client possesses a Level II oplock. The client is
2859N/A * expected to flush any dirty buffers to the server, submit any file locks
2859N/A * and respond to the server with either an SMB_LOCKING_ANDX SMB having the
2549N/A * LOCKING_ANDX_OPLOCK_RELEASE flag set, or with a file close if the file
2549N/A * is no longer in use by the client. If the client sends an
2549N/A * SMB_LOCKING_ANDX SMB with the LOCKING_ANDX_OPLOCK_RELEASE flag set and
2549N/A * NumberOfLocks is zero, the server does not send a response. Since a
2549N/A * close being sent to the server and break oplock notification from the
2549N/A * server could cross on the wire, if the client gets an oplock
2549N/A * notification on a file which it does not have open, that notification
2549N/A * should be ignored.
2549N/A *
2549N/A * Due to timing, the client could get an "oplock broken" notification in a
2549N/A * user's data buffer as a result of this notification crossing on the wire
2549N/A * with a SMB_COM_READ_RAW request. The client must detect this (use
2549N/A * length of msg, "FFSMB", MID of -1 and Command of SMB_COM_LOCKING_ANDX)
2549N/A * and honor the "oplock broken" notification as usual. The server must
2549N/A * also note on receipt of an SMB_COM_READ_RAW request that there is an
2549N/A * outstanding (unanswered) "oplock broken" notification to the client and
2549N/A * return a zero length response denoting failure of the read raw request.
2549N/A * The client should (after responding to the "oplock broken"
2549N/A * notification), use a standard read protocol to redo the read request.
2549N/A * This allows a file to actually contain data matching an "oplock broken"
2549N/A * notification and still be read correctly.
2549N/A *
2549N/A * The entire message sent and received including the optional second
2549N/A * protocol must fit in the negotiated maximum transfer size. The
2549N/A * following are the only valid SMB commands for AndXCommand for
2549N/A * SMB_COM_LOCKING_ANDX:
2549N/A *
2549N/A * SMB_COM_READ SMB_COM_READ_ANDX
2549N/A * SMB_COM_WRITE SMB_COM_WRITE_ANDX
2549N/A * SMB_COM_FLUSH
2549N/A *
2549N/A * 4.2.6.1 Errors
2549N/A *
2549N/A * ERRDOS/ERRbadfile
2549N/A * ERRDOS/ERRbadfid
2549N/A * ERRDOS/ERRlock
2549N/A * ERRDOS/ERRinvdevice
2549N/A * ERRSRV/ERRinvid
2549N/A * ERRSRV/ERRbaduid
2549N/A */
2549N/A
2549N/A#include <smbsrv/smb_incl.h>
2549N/A
2549N/Asmb_sdrc_t
2549N/Asmb_pre_locking_andx(smb_request_t *sr)
2549N/A{
2549N/A DTRACE_SMB_1(op__LockingX__start, smb_request_t *, sr);
2859N/A return (SDRC_SUCCESS);
2859N/A}
2859N/A
2859N/Avoid
2859N/Asmb_post_locking_andx(smb_request_t *sr)
2859N/A{
2859N/A DTRACE_SMB_1(op__LockingX__done, smb_request_t *, sr);
2859N/A}
smb_sdrc_t
smb_com_locking_andx(smb_request_t *sr)
{
unsigned short i;
unsigned char lock_type; /* See lock_type table above */
unsigned char oplock_level; /* The new oplock level */
uint32_t timeout; /* Milliseconds to wait for lock */
unsigned short unlock_num; /* # unlock range structs */
unsigned short lock_num; /* # lock range structs */
unsigned short pid; /* Process Id of owner */
uint32_t offset32, length32;
uint64_t offset64;
uint64_t length64;
DWORD result;
int rc;
uint32_t ltype;
rc = smbsr_decode_vwv(sr, "4.wbblww", &sr->smb_fid, &lock_type,
&oplock_level, &timeout, &unlock_num, &lock_num);
if (rc != 0)
return (SDRC_ERROR);
sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid);
if (sr->fid_ofile == NULL) {
smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
return (SDRC_ERROR);
}
if (lock_type & LOCKING_ANDX_SHARED_LOCK)
ltype = SMB_LOCK_TYPE_READONLY;
else
ltype = SMB_LOCK_TYPE_READWRITE;
pid = sr->smb_pid; /* Save the original pid */
if (lock_type & LOCKING_ANDX_OPLOCK_RELEASE) {
smb_oplock_release(sr->fid_ofile->f_node, B_FALSE);
/*
* According to the protocol:
*
* If the client sends an SMB_LOCKING_ANDX request with the
* LOCKING_ANDX_OPLOCK_RELEASE flag set
* and NumberOfLocks is zero,
* the server does not send a response.
*
* I'm not sure if it's going to break anything if I change
* it according to the protocol. So, I leave it unchanged
* for now.
*/
if (unlock_num == 0 && lock_num == 0)
return (SDRC_NO_REPLY);
}
/*
* No support for changing locktype (although we could probably
* implement this)
*/
if (lock_type & LOCKING_ANDX_CHANGE_LOCK_TYPE) {
smbsr_error(sr, 0, ERRDOS, ERRnoatomiclocks);
return (SDRC_ERROR);
}
/*
* No support for cancel lock (smbtorture expects this)
*/
if (lock_type & LOCKING_ANDX_CANCEL_LOCK) {
smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
ERRDOS, ERROR_INVALID_PARAMETER);
return (SDRC_ERROR);
}
if (lock_type & LOCKING_ANDX_LARGE_FILES) {
/*
* negotiated protocol should be NT LM 0.12 or later
*/
if (sr->session->dialect < NT_LM_0_12) {
smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
ERRDOS, ERROR_INVALID_PARAMETER);
return (SDRC_ERROR);
}
for (i = 0; i < unlock_num; i++) {
rc = smb_decode_mbc(&sr->smb_data, "w2.QQ",
&sr->smb_pid, &offset64, &length64);
if (rc) {
/*
* This is the error returned by Windows 2000
* even when STATUS32 has been negotiated.
*/
smbsr_error(sr, 0, ERRSRV, ERRerror);
return (SDRC_ERROR);
}
result = smb_unlock_range(sr, sr->fid_ofile->f_node,
offset64, length64);
if (result != NT_STATUS_SUCCESS) {
smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED,
ERRDOS, ERRnotlocked);
return (SDRC_ERROR);
}
}
for (i = 0; i < lock_num; i++) {
rc = smb_decode_mbc(&sr->smb_data, "w2.QQ",
&sr->smb_pid, &offset64, &length64);
if (rc) {
smbsr_error(sr, 0, ERRSRV, ERRerror);
return (SDRC_ERROR);
}
result = smb_lock_range(sr, offset64, length64, timeout,
ltype);
if (result != NT_STATUS_SUCCESS) {
smb_lock_range_error(sr, result);
return (SDRC_ERROR);
}
}
} else {
for (i = 0; i < unlock_num; i++) {
rc = smb_decode_mbc(&sr->smb_data, "wll", &sr->smb_pid,
&offset32, &length32);
if (rc) {
smbsr_error(sr, 0, ERRSRV, ERRerror);
return (SDRC_ERROR);
}
result = smb_unlock_range(sr, sr->fid_ofile->f_node,
(uint64_t)offset32, (uint64_t)length32);
if (result != NT_STATUS_SUCCESS) {
smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED,
ERRDOS, ERRnotlocked);
return (SDRC_ERROR);
}
}
for (i = 0; i < lock_num; i++) {
rc = smb_decode_mbc(&sr->smb_data, "wll", &sr->smb_pid,
&offset32, &length32);
if (rc) {
smbsr_error(sr, 0, ERRSRV, ERRerror);
return (SDRC_ERROR);
}
result = smb_lock_range(sr, (uint64_t)offset32,
(uint64_t)length32, timeout, ltype);
if (result != NT_STATUS_SUCCESS) {
smb_lock_range_error(sr, result);
return (SDRC_ERROR);
}
}
}
sr->smb_pid = pid;
if (smbsr_encode_result(sr, 2, 0, "bb.ww", 2, sr->andx_com, 7, 0))
return (SDRC_ERROR);
return (SDRC_SUCCESS);
}