/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2020 Tintri by DDN, Inc. All rights reserved. * Copyright 2022 RackTop Systems, Inc. */ /* * SMB: locking_andx * * SMB_COM_LOCKING_ANDX allows both locking and/or unlocking of file range(s). * * Client Request Description * ================================== ================================= * * UCHAR WordCount; Count of parameter words = 8 * UCHAR AndXCommand; Secondary (X) command; 0xFF = none * UCHAR AndXReserved; Reserved (must be 0) * USHORT AndXOffset; Offset to next command WordCount * USHORT Fid; File handle * UCHAR LockType; See LockType table below * UCHAR OplockLevel; The new oplock level * ULONG Timeout; Milliseconds to wait for unlock * USHORT NumberOfUnlocks; Num. unlock range structs following * USHORT NumberOfLocks; Num. lock range structs following * USHORT ByteCount; Count of data bytes * LOCKING_ANDX_RANGE Unlocks[]; Unlock ranges * LOCKING_ANDX_RANGE Locks[]; Lock ranges * * LockType Flag Name Value Description * ============================ ===== ================================ * * LOCKING_ANDX_SHARED_LOCK 0x01 Read-only lock * LOCKING_ANDX_OPLOCK_RELEASE 0x02 Oplock break notification * LOCKING_ANDX_CHANGE_LOCKTYPE 0x04 Change lock type * LOCKING_ANDX_CANCEL_LOCK 0x08 Cancel outstanding request * LOCKING_ANDX_LARGE_FILES 0x10 Large file locking format * * LOCKING_ANDX_RANGE Format * ===================================================================== * * USHORT Pid; PID of process "owning" lock * ULONG Offset; Offset to bytes to [un]lock * ULONG Length; Number of bytes to [un]lock * * Large File LOCKING_ANDX_RANGE Format * ===================================================================== * * USHORT Pid; PID of process "owning" lock * USHORT Pad; Pad to DWORD align (mbz) * ULONG OffsetHigh; Offset to bytes to [un]lock * (high) * ULONG OffsetLow; Offset to bytes to [un]lock (low) * ULONG LengthHigh; Number of bytes to [un]lock * (high) * ULONG LengthLow; Number of bytes to [un]lock (low) * * Server Response Description * ================================== ================================= * * UCHAR WordCount; Count of parameter words = 2 * UCHAR AndXCommand; Secondary (X) command; 0xFF = * none * UCHAR AndXReserved; Reserved (must be 0) * USHORT AndXOffset; Offset to next command WordCount * USHORT ByteCount; Count of data bytes = 0 * * Locking is a simple mechanism for excluding other processes read/write * access to regions of a file. The locked regions can be anywhere in the * logical file. Locking beyond end-of-file is permitted. Any process * using the Fid specified in this request's Fid has access to the locked * bytes, other processes will be denied the locking of the same bytes. * * The proper method for using locks is not to rely on being denied read or * write access on any of the read/write protocols but rather to attempt * the locking protocol and proceed with the read/write only if the locks * succeeded. * * Locking a range of bytes will fail if any subranges or overlapping * ranges are locked. In other words, if any of the specified bytes are * already locked, the lock will fail. * * If NumberOfUnlocks is non-zero, the Unlocks vector contains * NumberOfUnlocks elements. Each element requests that a lock at Offset * of Length be released. If NumberOfLocks is nonzero, the Locks vector * contains NumberOfLocks elements. Each element requests the acquisition * of a lock at Offset of Length. * * Timeout is the maximum amount of time to wait for the byte range(s) * specified to become unlocked. A timeout value of 0 indicates that the * server should fail immediately if any lock range specified is locked. A * * timeout value of -1 indicates that the server should wait as long as it * takes for each byte range specified to become unlocked so that it may be * again locked by this protocol. Any other value of smb_timeout specifies * the maximum number of milliseconds to wait for all lock range(s) * specified to become available. * * If any of the lock ranges timeout because of the area to be locked is * already locked (or the lock fails), the other ranges in the protocol * request which were successfully locked as a result of this protocol will * be unlocked (either all requested ranges will be locked when this * protocol returns to the client or none). * * If LockType has the LOCKING_ANDX_SHARED_LOCK flag set, the lock is * specified as a shared lock. Locks for both read and write (where * LOCKING_ANDX_SHARED_LOCK is clear) should be prohibited, but other * shared locks should be permitted. If shared locks can not be supported * by a server, the server should map the lock to a lock for both read and * write. Closing a file with locks still in force causes the locks to be * released in no defined order. * * If LockType has the LOCKING_ANDX_LARGE_FILES flag set and if the * negotiated protocol is NT LM 0.12 or later, then the Locks and Unlocks * vectors are in the Large File LOCKING_ANDX_RANGE format. This allows * specification of 64 bit offsets for very large files. * * If the one and only member of the Locks vector has the * LOCKING_ANDX_CANCEL_LOCK flag set in the LockType field, the client is * requesting the server to cancel a previously requested, but not yet * responded to, lock. * * If LockType has the LOCKING_ANDX_CHANGE_LOCKTYPE flag set, the client is * requesting that the server atomically change the lock type from a shared * lock to an exclusive lock or vice versa. If the server can not do this * in an atomic fashion, the server must reject this request. NT and W95 * servers do not support this capability. * * Oplocks are described in the "Opportunistic Locks" section elsewhere in * this document. A client requests an oplock by setting the appropriate * bit in the SMB_COM_OPEN_ANDX request when the file is being opened in a * mode which is not exclusive. The server responds by setting the * appropriate bit in the response SMB indicating whether or not the oplock * was granted. By granting the oplock, the server tells the client the * file is currently only being used by this one client process at the * current time. The client can therefore safely do read ahead and write * behind as well as local caching of file locks knowing that the file will * not be accessed/changed in any way by another process while the oplock * is in effect. The client will be notified when any other process * attempts to open or modify the oplocked file. * * When another user attempts to open or otherwise modify the file which a * client has oplocked, the server delays the second attempt and notifies * the client via an SMB_LOCKING_ANDX SMB asynchronously sent from the * server to the client. This message has the LOCKING_ANDX_OPLOCK_RELEASE * flag set indicating to the client that the oplock is being broken. * * OplockLevel indicates the type of oplock the client now owns. If * OplockLevel is 0, the client possesses no oplocks on the file at all, if * OplockLevel is 1 the client possesses a Level II oplock. The client is * expected to flush any dirty buffers to the server, submit any file locks * and respond to the server with either an SMB_LOCKING_ANDX SMB having the * LOCKING_ANDX_OPLOCK_RELEASE flag set, or with a file close if the file * is no longer in use by the client. If the client sends an * SMB_LOCKING_ANDX SMB with the LOCKING_ANDX_OPLOCK_RELEASE flag set and * NumberOfLocks is zero, the server does not send a response. Since a * close being sent to the server and break oplock notification from the * server could cross on the wire, if the client gets an oplock * notification on a file which it does not have open, that notification * should be ignored. * * Due to timing, the client could get an "oplock broken" notification in a * user's data buffer as a result of this notification crossing on the wire * with a SMB_COM_READ_RAW request. The client must detect this (use * length of msg, "FFSMB", MID of -1 and Command of SMB_COM_LOCKING_ANDX) * and honor the "oplock broken" notification as usual. The server must * also note on receipt of an SMB_COM_READ_RAW request that there is an * outstanding (unanswered) "oplock broken" notification to the client and * return a zero length response denoting failure of the read raw request. * The client should (after responding to the "oplock broken" * notification), use a standard read protocol to redo the read request. * This allows a file to actually contain data matching an "oplock broken" * notification and still be read correctly. * * The entire message sent and received including the optional second * protocol must fit in the negotiated maximum transfer size. The * following are the only valid SMB commands for AndXCommand for * SMB_COM_LOCKING_ANDX: * * SMB_COM_READ SMB_COM_READ_ANDX * SMB_COM_WRITE SMB_COM_WRITE_ANDX * SMB_COM_FLUSH * * 4.2.6.1 Errors * * ERRDOS/ERRbadfile * ERRDOS/ERRbadfid * ERRDOS/ERRlock * ERRDOS/ERRinvdevice * ERRSRV/ERRinvid * ERRSRV/ERRbaduid */ #include /* * This is a somewhat arbitrary sanity limit on the length of the * SMB2_LOCK_ELEMENT array. It usually has length one or two. */ int smb_lock_max_elem = 1024; smb_sdrc_t smb_pre_locking_andx(smb_request_t *sr) { DTRACE_SMB_START(op__LockingX, smb_request_t *, sr); return (SDRC_SUCCESS); } void smb_post_locking_andx(smb_request_t *sr) { DTRACE_SMB_DONE(op__LockingX, smb_request_t *, sr); } struct lreq { uint64_t off; uint64_t len; uint32_t pid; uint32_t reserved; }; 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 */ DWORD result; int rc; uint32_t ltype; smb_ofile_t *ofile; uint16_t tmp_pid; /* locking uses 16-bit pids */ uint32_t lrv_tot; struct lreq *lrv_ul; struct lreq *lrv_lk; struct lreq *lr; 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); smbsr_lookup_file(sr); if (sr->fid_ofile == NULL) { smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid); return (SDRC_ERROR); } ofile = sr->fid_ofile; if (ofile->f_node == NULL) { smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, ERRDOS, ERROR_INVALID_PARAMETER); return (SDRC_ERROR); } if (unlock_num > smb_lock_max_elem || lock_num > smb_lock_max_elem) { smbsr_error(sr, NT_STATUS_INSUFFICIENT_RESOURCES, ERRDOS, ERROR_NO_SYSTEM_RESOURCES); return (SDRC_ERROR); } if (lock_type & LOCKING_ANDX_SHARED_LOCK) ltype = SMB_LOCK_TYPE_READONLY; else ltype = SMB_LOCK_TYPE_READWRITE; if (lock_type & LOCKING_ANDX_OPLOCK_RELEASE) { smb1_oplock_ack_break(sr, oplock_level); if (unlock_num == 0 && lock_num == 0) return (SDRC_NO_REPLY); /* * Don't allow combining other lock/unlock actions * with an oplock ACK (normally don't get here). */ smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, ERRDOS, ERROR_INVALID_PARAMETER); return (SDRC_ERROR); } /* * No support for changing locktype (although we could probably * implement this) */ if (lock_type & LOCKING_ANDX_CHANGE_LOCK_TYPE) { smbsr_error(sr, 0, ERRDOS, ERROR_ATOMIC_LOCKS_NOT_SUPPORTED); 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); } } /* * Parse the unlock, lock vectors. Will parse all the * unlock + lock records into one array, and then use * pointers to the unlock and lock parts. */ lrv_tot = unlock_num + lock_num; lrv_ul = smb_srm_zalloc(sr, lrv_tot * sizeof (*lrv_ul)); lrv_lk = &lrv_ul[unlock_num]; for (i = 0; i < lrv_tot; i++) { lr = &lrv_ul[i]; if (lock_type & LOCKING_ANDX_LARGE_FILES) { rc = smb_mbc_decodef(&sr->smb_data, "w2.QQ", &tmp_pid, &lr->off, &lr->len); } else { uint32_t offset32, length32; rc = smb_mbc_decodef(&sr->smb_data, "wll", &tmp_pid, &offset32, &length32); lr->off = offset32; lr->len = length32; } lr->pid = tmp_pid; /* 16-bit PID */ 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); } } /* * Cancel waiting locks. MS-CIFS says one place that * this cancels all waiting locks for this FID+PID, * but smbtorture insists this cancels just one. * Tests with Windows 7 confirms that. */ if ((lock_type & LOCKING_ANDX_CANCEL_LOCK) != 0) { lr = lrv_lk; result = smb_lock_range_cancel(sr, lr->off, lr->len, lr->pid); if (result != NT_STATUS_SUCCESS) { smbsr_error(sr, 0, ERRDOS, ERROR_CANCEL_VIOLATION); return (SDRC_ERROR); } goto out; } /* * Normal unlock and lock list */ for (i = 0; i < unlock_num; i++) { lr = &lrv_ul[i]; result = smb_unlock_range(sr, lr->off, lr->len, lr->pid); if (result != NT_STATUS_SUCCESS) { smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED, ERRDOS, ERROR_NOT_LOCKED); return (SDRC_ERROR); } } for (i = 0; i < lock_num; i++) { lr = &lrv_lk[i]; result = smb_lock_range(sr, lr->off, lr->len, lr->pid, ltype, timeout); if (result != NT_STATUS_SUCCESS) { /* * Oh... we have to rollback. */ while (i > 0) { --i; lr = &lrv_lk[i]; (void) smb_unlock_range(sr, lr->off, lr->len, lr->pid); } smb_lock_range_error(sr, result); return (SDRC_ERROR); } } out: if (smbsr_encode_result(sr, 2, 0, "bb.ww", 2, sr->andx_com, 0x27, 0) != 0) return (SDRC_ERROR); return (SDRC_SUCCESS); }