/*
 * 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 2017 Nexenta Systems, Inc.  All rights reserved.
 */

#ifndef _SMBSRV_SMB_OPLOCK_H
#define	_SMBSRV_SMB_OPLOCK_H

#include <smbsrv/ntifs.h>

#ifdef __cplusplus
extern "C" {
#endif

/*
 * 2.1.1.10 Per Oplock
 *
 *
 * ExclusiveOpen: The Open used to request the opportunistic lock.
 *
 * IIOplocks: A list of zero or more Opens used to request a LEVEL_TWO
 *  opportunistic lock, as specified in section 2.1.5.17.1.
 *
 * ROplocks: A list of zero or more Opens used to request a LEVEL_GRANULAR
 *  (RequestedOplockLevel: READ_CACHING) opportunistic lock, as specified in
 *   section 2.1.5.17.1.
 *
 * RHOplocks: A list of zero or more Opens used to request a LEVEL_GRANULAR
 *  (RequestedOplockLevel: (READ_CACHING|HANDLE_CACHING)) opportunistic lock,
 *  as specified in section 2.1.5.17.1.
 *
 * RHBreakQueue: A list of zero or more RHOpContext objects. This queue is
 *  used to track (READ_CACHING|HANDLE_CACHING) oplocks as they are breaking.
 *
 * WaitList: A list of zero or more Opens belonging to operations that are
 *  waiting for an oplock to break, as specified in section 2.1.4.12.
 *
 * State: The current state of the oplock, expressed as a combination of
 *  one or more flags. Valid flags are:
 *	[ As follows;  Re-ordered a bit from the spec. ]
 */

/*
 * READ_CACHING - Indicates that this Oplock represents an oplock
 * that provides caching of reads; this provides the SMB 2.1 read
 * caching lease, as described in [MS-SMB2] section 2.2.13.2.8.
 */
#define	READ_CACHING	OPLOCK_LEVEL_CACHE_READ		/* 1 */

/*
 * HANDLE_CACHING - Indicates that this Oplock represents an oplock
 * that provides caching of handles; this provides the SMB 2.1 handle
 * caching lease, as described in [MS-SMB2] section 2.2.13.2.8.
 */
#define	HANDLE_CACHING	OPLOCK_LEVEL_CACHE_HANDLE	/* 2 */

/*
 * WRITE_CACHING - Indicates that this Oplock represents an oplock
 * that provides caching of writes; this provides the SMB 2.1 write
 * caching lease, as described in [MS-SMB2] section 2.2.13.2.8.
 */
#define	WRITE_CACHING	OPLOCK_LEVEL_CACHE_WRITE	/* 4 */

/*
 * EXCLUSIVE - Indicates that this Oplock represents an oplock that
 * can be held by exactly one client at a time. This flag always appears
 * in combination with other flags that indicate the actual oplock level.
 * For example, (READ_CACHING|WRITE_CACHING|EXCLUSIVE) represents a
 * read caching and write caching oplock, which can be held by only
 * one client at a time.
 */
#define	EXCLUSIVE			0x00000010

/*
 * MIXED_R_AND_RH - Always appears together with READ_CACHING and
 * HANDLE_CACHING.  Indicates that this Oplock represents an oplock
 * on which at least one client has been granted a read caching oplock,
 * and at least one other client has been granted a read caching and
 * handle caching oplock.
 */
#define	MIXED_R_AND_RH			0x00000020

/*
 * LEVEL_TWO_OPLOCK - Indicates that this Oplock represents a
 * Level 2 (also called Shared) oplock.
 * Corresponds to SMB2_OPLOCK_LEVEL_II
 */
#define	LEVEL_TWO_OPLOCK	OPLOCK_LEVEL_TWO	/* 0x100 */

/*
 * LEVEL_ONE_OPLOCK - Indicates that this Oplock represents a
 * Level 1 (also called Exclusive) oplock.
 * Corresponds to SMB2_OPLOCK_LEVEL_EXCLUSIVE
 */
#define	LEVEL_ONE_OPLOCK	OPLOCK_LEVEL_ONE	/* 0x200 */

/*
 * BATCH_OPLOCK - Indicates that this Oplock represents a Batch oplock.
 * Corresponds to SMB2_OPLOCK_LEVEL_BATCH
 */
#define	BATCH_OPLOCK		OPLOCK_LEVEL_BATCH	/* 0x400 */

/* Note: ntifs.h		OPLOCK_LEVEL_GRANULAR	   0x800 */

/*
 * Note that the oplock leasing implementation uses this shift
 * to convert (i.e.) CACHE_READ to BREAK_TO_READ_CACHING etc.
 * This relationship is checked in smb_srv_oplock.c
 */
#define	BREAK_SHIFT 16

/*
 * BREAK_TO_READ_CACHING - Indicates that this Oplock represents an
 * oplock that is currently breaking to an oplock that provides
 * caching of reads; the oplock has broken but the break has not yet
 * been acknowledged.
 */
#define	BREAK_TO_READ_CACHING		0x00010000

/*
 * BREAK_TO_HANDLE_CACHING - Indicates that this Oplock represents an
 * oplock that is currently breaking to an oplock that provides
 * caching of handles; the oplock has broken but the break has not yet
 * been acknowledged.  Note: == (CACHE_HANDLE << BREAK_SHIFT)
 */
#define	BREAK_TO_HANDLE_CACHING		0x00020000

/*
 * BREAK_TO_WRITE_CACHING - Indicates that this Oplock represents an
 * oplock that is currently breaking to an oplock that provides
 * caching of writes; the oplock has broken but the break has
 * not yet been acknowledged.
 */
#define	BREAK_TO_WRITE_CACHING		0x00040000

/*
 * BREAK_TO_NO_CACHING - Indicates that this Oplock represents an
 * oplock that is currently breaking to None (that is, no oplock);
 * the oplock has broken but the break has not yet been acknowledged.
 */
#define	BREAK_TO_NO_CACHING		0x00080000

/*
 * BREAK_TO_TWO - Indicates that this Oplock represents an oplock
 * that is currently breaking from either Level 1 or Batch to Level 2;
 * the oplock has broken but the break has not yet been acknowledged.
 */
#define	BREAK_TO_TWO			0x00100000

/*
 * BREAK_TO_NONE - Indicates that this Oplock represents an oplock
 * that is currently breaking from either Level 1 or Batch to None
 * (that is, no oplock); the oplock has broken but the break has
 * not yet been acknowledged.
 */
#define	BREAK_TO_NONE			0x00200000

/*
 * BREAK_TO_TWO_TO_NONE - Indicates that this Oplock represents an
 * oplock that is currently breaking from either Level 1 or Batch to
 * None (that is, no oplock), and was previously breaking from Level 1
 *  or Batch to Level 2; the oplock has broken but the break has
 * not yet been acknowledged.
 */
#define	BREAK_TO_TWO_TO_NONE		0x00400000

/*
 * NO_OPLOCK - Indicates that this Oplock does not represent a
 * currently granted or breaking oplock. This is semantically
 * equivalent to the Oplock object being entirely absent from a
 * Stream. This flag always appears alone.
 * Note we also have OPLOCK_LEVEL_NONE == 0 from ntifs.h
 */
#define	NO_OPLOCK			0x10000000

/*
 * An internal flag, non-overlapping wth other oplock flags,
 * used only in smb_cmn_oplock.c (and here only to make clear
 * that it does not overlap with an other flags above).
 */
#define	PARENT_OBJECT			0x40000000

/*
 * Also not in the spec, but convenient
 */
#define	BREAK_LEVEL_MASK (\
	BREAK_TO_READ_CACHING |\
	BREAK_TO_WRITE_CACHING |\
	BREAK_TO_HANDLE_CACHING |\
	BREAK_TO_NO_CACHING)

#define	BREAK_ANY (\
	BREAK_LEVEL_MASK |\
	BREAK_TO_TWO |\
	BREAK_TO_NONE |\
	BREAK_TO_TWO_TO_NONE)


/*
 * Convenience macro to walk ofiles on a give node.
 * Used as follows:
 *	FOREACH_NODE_OFILE(node, o) { muck_with(o); }
 */
#define	FOREACH_NODE_OFILE(node, o)	for \
	(o = smb_llist_head(&node->n_ofile_list); \
	o != NULL; \
	o = smb_llist_next(&node->n_ofile_list, o))

/*
 * Some short-hand names used in the oplock code.
 */

#define	STATUS_NEW_HANDLE	NT_STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE
#define	STATUS_CANT_GRANT	NT_STATUS_CANNOT_GRANT_REQUESTED_OPLOCK

typedef enum oplock_type {
	LEVEL_NONE = OPLOCK_LEVEL_NONE,
	LEVEL_TWO = OPLOCK_LEVEL_TWO,
	LEVEL_ONE = OPLOCK_LEVEL_ONE,
	LEVEL_BATCH = OPLOCK_LEVEL_BATCH,
	LEVEL_GRANULAR = OPLOCK_LEVEL_GRANULAR
} oplock_type_t;

typedef enum oplock_cache_level {
	CACHE_R =	READ_CACHING,

	CACHE_RH =	READ_CACHING |
			HANDLE_CACHING,

	CACHE_RW =	READ_CACHING |
			WRITE_CACHING,

	CACHE_RWH =	READ_CACHING |
			WRITE_CACHING |
			HANDLE_CACHING,
} oplock_cache_t;

#ifdef __cplusplus
}
#endif

#endif /* _SMBSRV_SMB_OPLOCK_H */