/* SPDX-License-Identifier: LGPL-3.0-or-later */
/*
 *
 *
 * Copyright CEA/DAM/DIF  (2008)
 * contributeur : Philippe DENIEL   philippe.deniel@cea.fr
 *                Thomas LEIBOVICI  thomas.leibovici@cea.fr
 *
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 * ---------------------------------------
 */

/**
 * @defgroup SAL State abstraction layer
 * @{
 */

/**
 * @file sal_data.h
 * @brief Data structures for state management.
 */

#ifndef SAL_DATA_H
#define SAL_DATA_H

#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <time.h>
#include <pthread.h>
#include <dirent.h> /* For having MAXNAMLEN */

#include "abstract_atomic.h"
#include "abstract_mem.h"
#include "hashtable.h"
#include "fsal_pnfs.h"
#include "config_parsing.h"

#include "gsh_lttng/gsh_lttng.h"

#ifdef _USE_9P
/* define u32 and related types independent of SAL and 9P */
#include "9p_types.h"
#endif /* _USE_9P */

/**
** Forward declarations to avoid circular dependency conflicts
*/

#include "gsh_status.h"

typedef struct nfs_client_id_t nfs_client_id_t;
typedef struct nfs41_session nfs41_session_t;

/**
** Consolidated circular dependencies
*/

#include "nfs_proto_data.h"

/**
 ** Forward references to types
 */
typedef struct nfs_argop4_state nfs_argop4_state;
typedef struct nfs_client_record_t nfs_client_record_t;
typedef struct state_async_queue_t state_async_queue_t;
typedef struct state_block_data_t state_block_data_t;
typedef struct state_cookie_entry_t state_cookie_entry_t;
typedef struct state_lock_entry_t state_lock_entry_t;
typedef struct state_nfs4_owner_t state_nfs4_owner_t;
typedef struct state_nlm_client_t state_nlm_client_t;
typedef struct state_owner_t state_owner_t;
typedef struct state_t state_t;

/**
 * @brief Number of errors before giving up on recovery
 *
 * We set a maximum because the recovery routines need to terminate at
 * some point.
 */
#define STATE_ERR_MAX 100

/**
 * @brief Maximum number of operation in a compound request
 *
 * We cap the number of operation to this value for all V4 versions
 */
#define NFS4_MAX_OPERATIONS 100

/**
 * @brief Indicate that lock extends to the entire range of the file
 *
 * This is true no matter what the beginning of the lock range is.
 */
#define STATE_LOCK_OFFSET_EOF 0xFFFFFFFFFFFFFFFFLL

extern struct fridgethr *state_async_fridge;

/*****************************************************************************
 *
 * NFSv4.1 Session data
 *
 *****************************************************************************/

extern pool_t *nfs41_session_pool;

/**
 * @param Session ID hash
 */

extern hash_table_t *ht_session_id;

/**
 * @brief Default number of forechannel slots in a session
 *
 * This is also the maximum number of backchannel slots we'll use,
 * even if the client offers more.
 */
#define NFS41_NB_SLOTS_DEF 64

/**
 * @brief Default string length of all operations in one compound
 */
#define NFS4_COMPOUND_OPERATIONS_STR_LEN 256

/**
 * @brief the simple info of last request to save in slot
 */

struct nfs41_cached_req {
	nfs_opnum4 opcodes[NFS4_MAX_OPERATIONS]; /*< opcodes
		of compound */
	uint32_t opcode_num; /*< number of opcode in one compound */
	uint32_t xid; /*< Xid of request */
	sequenceid4 seq_id; /*< Sequence id of request */
	uint64_t finish_time_ms; /*< Finish time of request in ms */
};

/**
 * @brief Members in the slot table
 */

typedef struct nfs41_session_slot__ {
	sequenceid4 sequence; /*< Sequence number of this operation */
	pthread_mutex_t slot_lock; /*< Lock on the slot */
	struct COMPOUND4res_extended *cached_result; /*< NFv41: pointer to
							   cached RPC result in
							   a session's slot */
	struct nfs41_cached_req last_req; /*< Last request of the slot */
} nfs41_session_slot_t;

/**
 * @brief Bookkeeping for callback slots on the client
 */

typedef struct nfs41_cb_session_slot {
	sequenceid4 sequence; /*< Sequence number of last call */
	bool in_use; /*< Set if a call if in progress on this slot */
} nfs41_cb_session_slot_t;

/**
 * Flag indicating that the backchannel is up
 */
enum {
	session_bc_up = 0x01,
	session_bc_fault = 0x02, /* not actually used anywhere */
};

/**
 * @brief minimum values for channel attributes
 */
#define NFS41_MIN_REQUEST_SIZE 256
#define NFS41_MIN_RESPONSE_SIZE 256
#define NFS41_MIN_OPERATIONS 2
#define NFS41_MAX_CONNECTIONS 16

/**
 * @brief buffer size for displaying session-id
 */
#define NFS4_SESSIONID_BUFFER_SIZE 128

/**
 * @brief Structure representing a connection-xprt entry in nfs41_session's
 * connection_xprts
 */

typedef struct connection_xprt {
	SVCXPRT *xprt;
	struct glist_head node;
} connection_xprt_t;

/**
 * @brief Structure representing an NFSv4.1 session
 */

struct nfs41_session {
	char session_id[NFS4_SESSIONID_SIZE]; /*< Session ID */
	struct glist_head session_link; /*< Link in the list of
					   sessions for this
					   clientid */

	channel_attrs4 fore_channel_attrs; /*< Fore-channel attributes */

	channel_attrs4 back_channel_attrs; /*< Back-channel attributes */
	struct rpc_call_channel cb_chan; /*< Back channel */
	pthread_mutex_t cb_mutex; /*< Protects the cb slot table,
					   when searching for a free slot */
	pthread_cond_t cb_cond; /*< Condition variable on which we
				   wait if the slot table is full
				   and on which we signal when we
				   free an entry. */

	pthread_rwlock_t conn_lock;
	int num_conn;
	struct glist_head connection_xprts;

	nfs_client_id_t *clientid_record; /*< Client record
						   corresponding to ID */
	clientid4 clientid; /*< Clientid owning this session */
	uint32_t cb_program; /*< Callback program ID */
	struct {
		u_int sec_parms_len;
		callback_sec_parms4 *sec_parms_val;
	} cb_sec_parms; /*< Callback security params */
	uint32_t flags; /*< Flags pertaining to this session */
	int32_t refcount;
	uint32_t nb_slots; /**< Number of slots in this session */
	nfs41_session_slot_t *fc_slots; /**< Forechannel slot table*/
	nfs41_cb_session_slot_t *bc_slots; /**< Backchannel slot table */
	bool has_revoked_delegations; /**< True if delegations were revoked */
};

/**
 * @brief Track the call that creates a state, for NFSv4.1.
 */

struct state_refer {
	sessionid4 session; /*< The session of the creating call. */
	sequenceid4 sequence; /*< The sequence ID of the creating
				   call. */
	slotid4 slot; /*< The slot ID of the creating call. */
};

/******************************************************************************
 *
 * NFSv4 Recovery data
 *
 *****************************************************************************/

/**
 * @brief Revoked filehandle list
 */
typedef struct rdel_fh {
	struct glist_head rdfh_list;
	char *rdfh_handle_str;
} rdel_fh_t;

/**
 * @brief A client entry
 */
typedef struct clid_entry {
	struct glist_head cl_list; /*< Link in the list */
	struct glist_head cl_rfh_list;
	char cl_name[PATH_MAX]; /*< Client name */
	bool cl_reclaim_complete; /*< Whether the NFS v4.1+ client
						reclaims completely */
} clid_entry_t;

extern char v4_old_dir[PATH_MAX];
extern char v4_recov_dir[PATH_MAX];

/******************************************************************************
 *
 * NFSv4 State data
 *
 *****************************************************************************/

extern hash_table_t *ht_state_id;

#include "sal_shared.h"

/**
 * @brief Data for an NFS v4 share reservation/open
 */

struct state_share {
	struct glist_head share_lockstates; /*< Lock states for this
						   open state
						   This field MUST be first */
	unsigned int share_access; /*< The NFSv4 Share Access state */
	unsigned int share_deny; /*< The NFSv4 Share Deny state */
	unsigned int share_access_prev; /*< Previous share access state */
	unsigned int share_deny_prev; /*< Previous share deny state   */
};

/**
 * @brief Data for an NLM share reservation/open
 */

struct state_nlm_share {
	/** Put this share state on a list per client. This field MUST be first
	 */
	struct glist_head share_perclient;
	/** The NLM Share Access state */
	unsigned int share_access;
	/** The NLM Share Deny state */
	unsigned int share_deny;
	/** Counts of each share access state */
	unsigned int share_access_counts[fsa_RW + 1];
	/** Counts of each share deny state   */
	unsigned int share_deny_counts[fsm_DRW + 1];
};

/**
 * @brief The number of bytes in the stateid.other
 *
 * The value 12 is fixed by RFC 3530/RFC 5661.
 */
#define OTHERSIZE 12

extern char all_zero[OTHERSIZE];
extern char all_ones[OTHERSIZE];

/**
 * @brief Data for a set of locks
 */

struct state_lock {
	struct glist_head state_locklist; /*< List of locks owned by
						   this stateid
						   This field MUST be first */
	struct glist_head state_sharelist; /*< List of states related
						   to a share */
	char openstate_key[OTHERSIZE]; /*< The hash key of related open-state */
};

/**
 * @brief Data for a 9p fid
 */

struct state_9p_fid {
	struct glist_head state_locklist; /*< List of locks owned by
						   this fid
						   This field MUST be first */
	unsigned int share_access; /*< The 9p Access state */
	unsigned int share_deny; /*< Will always be 0 */
};

/**
 * @brief Stats for client-file delegation heuristics
 */

/* @brief Per client, per file stats */
struct cf_deleg_stats {
	time_t cfd_rs_time; /* time when the client responded
						 NFS4_OK for a recall. */
	time_t cfd_r_time; /* time of the recall attempt */
};

/**
 * @brief States of a delegation
 *
 * Different states a delegation can be in during its lifetime
 */
enum deleg_state {
	DELEG_GRANTED = 1, /* Granted               */
	DELEG_RECALL_WIP, /* Recall in progress    */
};

/**
 * @brief Data for a delegation
 */

struct state_deleg {
	open_delegation_type4 sd_type;
	enum deleg_state sd_state;
	struct cf_deleg_stats sd_clfile_stats; /* client specific */
	uint32_t share_access; /*< The NFSv4 Share Access state */
	uint32_t share_deny; /*< The NFSv4 Share Deny state */
	char openstate_key[OTHERSIZE]; /*< The hash key of related open-state */
};

struct revoked_delegation {
	struct glist_head list; /* glist linkage */
	stateid4 stateid; /* delegation stateid key */
};

/**
 * @brief Data for a set of layouts of a given type
 */

struct state_layout {
	struct glist_head state_segments; /*< List of segments */
	layouttype4 state_layout_type; /*< The type of layout this state
					   represents */
	uint32_t granting; /*< Number of LAYOUTGETs in progress */
	bool state_return_on_close; /*< Whether this layout should be
					   returned on last close. */
};

/**
 * @brief Type specific state data
 */

union state_data {
	struct state_share share;
	struct state_nlm_share nlm_share;
	struct state_lock lock;
	struct state_deleg deleg;
	struct state_layout layout;
	struct state_9p_fid fid;
	uint32_t io_advise;
};

/**
 * @brief Structure representing a single NFSv4 state
 *
 * Each state is identified by stateid and represents some resource or
 * set of resources.
 *
 * The lists are protected by the st_lock
 */
typedef void(state_free_t)(struct state_t *state);

struct state_t {
	struct glist_head state_list; /**< List of states on a file */
	struct glist_head state_owner_list; /**< List of states for an owner */
	struct glist_head state_export_list; /**< List of states on the same
						 export */
#ifdef DEBUG_SAL
	struct glist_head state_list_all; /**< Global list of all stateids */
#endif
	pthread_mutex_t state_mutex; /**< Mutex protecting following pointers */
	state_free_t *state_free; /**< function to free if default gsh_free
				      doesn't work. */
	struct gsh_export *state_export; /**< Export this entry belongs to */
	/* Don't re-order or move these next two.  They are used for hashing */
	state_owner_t *state_owner; /**< State Owner related to state */
	struct fsal_obj_handle *state_obj; /**< owning object */
	union state_data state_data;
	enum state_type state_type;
	u_int32_t state_seqid; /**< The NFSv4 Sequence id */
	int32_t state_refcount; /**< Refcount for state_t objects */
	char stateid_other[OTHERSIZE]; /**< "Other" part of state id,
					   used as hash key */
	struct state_refer state_refer; /**< For NFSv4.1, track the
					   call that created a
					   state. */
};

static inline void free_state(struct state_t *state)
{
	if (state->state_free != NULL)
		state->state_free(state);
	else
		gsh_free(state);
}

/* Macros to compare and copy state_t to a struct stateid4 */
#define SAME_STATEID(id4, state)                 \
	((id4)->seqid == (state)->state_seqid && \
	 memcmp((id4)->other, (state)->stateid_other, OTHERSIZE) == 0)

#define COPY_STATEID(id4, state)                                               \
	do {                                                                   \
		(id4)->seqid = (state)->state_seqid;                           \
		(void)memcpy((id4)->other, (state)->stateid_other, OTHERSIZE); \
	} while (0)

/*****************************************************************************
 *
 * NFS Owner data
 *
 *****************************************************************************/

typedef void(state_owner_init_t)(state_owner_t *powner);

extern hash_table_t *ht_nlm_owner;
#ifdef _USE_9P
extern hash_table_t *ht_9p_owner;
#endif
extern hash_table_t *ht_nfs4_owner;

extern struct glist_head cached_open_owners;
extern pthread_mutex_t cached_open_owners_lock;

/**
 * @brief A structure identifying the owner of an NFSv4 open or lock state
 *
 * This serves as the key to the NFSv4 owner table, and is generated
 * from either an open or lock owner.
 */

typedef struct state_nfs4_owner_name_t {
	unsigned int son_owner_len;
	char *son_owner_val;
} state_nfs4_owner_name_t;

/**
 * @brief The type of entity responsible for a state
 *
 * For NLM and 9P, one kind of owner owns every kind of state.  For
 * NFSv4.1 open owners and lock owners are in disjoint spaces, and all
 * non-open and non-lock states are associated with a given client.
 */

typedef enum state_owner_type_t {
	STATE_LOCK_OWNER_UNKNOWN, /*< Unknown */
#ifdef _USE_NLM
	STATE_LOCK_OWNER_NLM, /*< An NLM client */
#endif /* _USE_NLM */
#ifdef _USE_9P
	STATE_LOCK_OWNER_9P, /*< A 9P client */
#endif
	STATE_OPEN_OWNER_NFSV4, /*< An NFSv4 owner of an open */
	STATE_LOCK_OWNER_NFSV4, /*< An NFSv4 owner of a set of locks */
	STATE_CLIENTID_OWNER_NFSV4 /*< An NFSv4 client, owns all
					   states but opens and locks. */
} state_owner_type_t;

/**
 * @brief Specifies interest in a client for NLN/NSM.
 */

typedef enum care_t {
	CARE_NOT, /*< Do not care about client's status */
	CARE_OWNER, /*< Don't care about nsm state but do need an
				    owner. */
	CARE_ALWAYS, /*< Always care about client's status */
	CARE_NO_MONITOR, /*< Care, but will not actively monitor */
	CARE_MONITOR /*< Will actively monitor client status */
} care_t;

#ifdef _USE_NLM
extern hash_table_t *ht_nsm_client;
extern hash_table_t *ht_nlm_client;
extern pthread_mutex_t granted_mutex;
extern pthread_mutex_t nlm_async_resp_mutex;
extern pthread_cond_t nlm_async_resp_cond;
extern pthread_mutex_t nsm_mutex;
#endif

extern pthread_mutex_t cached_open_owners_lock;

#ifdef DEBUG_SAL
extern pthread_mutex_t all_state_owners_mutex;
extern pthread_mutex_t all_state_v4_mutex;
#endif

/**
 * @brief NSM (rpc.statd) state for a given client.
 *
 * All nsm clients have a caller name.
 *
 * When nsm_use_caller_name is true, the caller name comes from the NLM
 * requests.
 *
 * When nsm_use_caller_name is false, the caller name is the string form of
 * the IP address. If the IP address is IPv4 encapsulated in IPv6, the name
 * will be the simple IPv4 version of the address.
 */

typedef struct state_nsm_client_t {
	pthread_mutex_t ssc_mutex; /*< Mutex protecting this
					   structure */
	struct glist_head ssc_lock_list; /*< All locks held by client */
	struct glist_head ssc_share_list; /*< All share reservations */
	struct gsh_client *ssc_client; /*< The client involved */
	int32_t ssc_refcount; /*< Reference count to protect
				   structure */
	int32_t ssc_monitored; /*< If this client is actively
				   monitored */
	int32_t ssc_nlm_caller_name_len; /*< Length of identifier */
	char *ssc_nlm_caller_name; /*< Client identifier */
} state_nsm_client_t;

/**
 * @brief Represent a single client accessing us through NLM
 */

struct state_nlm_client_t {
	state_nsm_client_t *slc_nsm_client; /*< Related NSM client */
	xprt_type_t slc_client_type; /*< The transport type to this
					   client */
	int32_t slc_refcount; /*< Reference count for disposal */
	sockaddr_t slc_server_addr; /*< local addr when request is
						     made */
	int32_t slc_nlm_caller_name_len; /*< Length of client name */
	char *slc_nlm_caller_name; /*< Client name */
	CLIENT *slc_callback_clnt; /*< Callback for blocking locks */
	AUTH *slc_callback_auth; /*< Authentication for callback */
};

/**
 * @brief Owner of an NLM lock or share.
 */

typedef struct state_nlm_owner_t {
	state_nlm_client_t *so_client; /*< Structure for this client */
	int32_t so_nlm_svid; /*< Owner within client */
	struct glist_head so_nlm_shares; /*< Share reservations */
} state_nlm_owner_t;

/**
 * @brief 9P lock owner
 */

#ifdef _USE_9P
typedef struct state_9p_owner_t {
	u32 proc_id; /*< PID on the client */
	sockaddr_t client_addr; /*< Network address of client */
} state_9p_owner_t;
#endif /* _USE_9P */

/**
 * @brief Share and lock operations for NFSv4.0
 *
 * This structure saves the arguments to the most recent operation on
 * an open or lock owner associated with an NFSv4.0 client.  This is
 * part of the At-Most Once semantics and not used under NFSv4.1.
 */

struct nfs_argop4_state {
	nfs_opnum4 argop; /*< Operation being saved */
	union {
		CLOSE4args opclose; /*< CLOSE */
		LOCK4args oplock; /*< LOCK */
		LOCKU4args oplocku; /*< LOCKU */
		OPEN4args opopen; /*< OPEN */
		OPEN_CONFIRM4args opopen_confirm; /*< OPEN_CONFIRM */
		OPEN_DOWNGRADE4args opopen_downgrade; /*< OPEN_DOWNGRADE */
	} nfs_argop4_u;
};

/**
 * @brief A structure supporting all NFSv4 owners.
 */

struct state_nfs4_owner_t {
	clientid4 so_clientid; /*< Owning clientid */
	nfs_client_id_t *so_clientrec; /*< Owning client id record */
	bool so_confirmed; /*< Confirmation (NFSv4.0 only) */
	seqid4 so_seqid; /*< Seqid for serialization of operations on
				   owner (NFSv4.0 only) */
	nfs_argop4_state so_args; /*< Saved args */
	void *so_last_entry; /*< Last file operated on by this state owner
				 *  we don't keep a reference so this is a
				 *  void * to prevent it's dereferencing because
				 *  the pointer might become invalid if cache
				 *  inode flushes the entry out. But it sufices
				 *  for the purposes of detecting replayed
				 *  operations.
				 */
	nfs_resop4 so_resp; /*< Saved response */
	state_owner_t *so_related_owner; /*< For lock-owners, the
						   open-owner under which
						   the lock owner was
						   created */
	struct glist_head so_state_list; /*< This is the head of a list of
					   states owned by this owner.
					   so_mutex MUST be held when
					   accessing this field. */
	struct glist_head so_perclient; /*< open owner entry to be
					   linked to client */
	struct glist_head so_cache_entry; /*< Entry on cached_open_owners */
	time_t so_cache_expire; /* time cached OPEN owner will expire.  If
				   non-zero, so_cache_entry is in
				   cached_open_owners list.
				   cached_open_owners_lock MUST be held
				   when accessing this field.*/
};

/**
 * @brief General state owner
 *
 * This structure encodes the owner of any state, protocol specific
 * information is contained within the union.
 */

struct state_owner_t {
	state_owner_type_t so_type; /*< Owner type */
	struct glist_head so_lock_list; /*< Locks for this owner */
#ifdef DEBUG_SAL
	struct glist_head so_all_owners; /**< Global list of all state owners */
#endif /* _DEBUG_MEMLEAKS */
	pthread_mutex_t so_mutex; /*< Mutex on this owner */
	int32_t so_refcount; /*< Reference count for lifecyce management */
	int so_owner_len; /*< Length of owner name */
	char *so_owner_val; /*< Owner name */
	union {
		state_nfs4_owner_t so_nfs4_owner; /*< All NFSv4 state owners */
		state_nlm_owner_t so_nlm_owner; /*< NLM lock and share
						   owners */
#ifdef _USE_9P
		state_9p_owner_t so_9p_owner; /*< 9P lock owners */
#endif
	} so_owner;
};

/* Test if the lock owner type is 9P */
#ifdef _USE_9P
#define LOCK_OWNER_9P(owner) ((owner)->so_type == STATE_LOCK_OWNER_9P)
#else
#define LOCK_OWNER_9P(owner) (0)
#endif

extern state_owner_t unknown_owner;

/******************************************************************************
 *
 * NFSv4 Clientid data
 *
 *****************************************************************************/

/**
 * @brief State of a clientid record.
 */

typedef enum nfs_clientid_confirm_state__ {
	UNCONFIRMED_CLIENT_ID,
	CONFIRMED_CLIENT_ID,
	EXPIRED_CLIENT_ID,
	STALE_CLIENT_ID
} nfs_clientid_confirm_state_t;

/**
 * @brief Errors from the clientid functions
 */

typedef enum clientid_status {
	CLIENT_ID_SUCCESS = 0, /*< Success */
	CLIENT_ID_INSERT_MALLOC_ERROR, /*< Unable to allocate memory */
	CLIENT_ID_INVALID_ARGUMENT, /*< Invalid argument */
	CLIENT_ID_EXPIRED, /*< requested client id expired */
	CLIENT_ID_STALE /*< requested client id stale */
} clientid_status_t;

/**
 * @brief Record associated with a clientid
 *
 * This record holds Ganesha's state on an NFSv4.0 client.
 */

struct nfs_client_id_t {
	clientid4 cid_clientid; /*< The clientid */
	verifier4 cid_verifier; /*< Known verifier */
	verifier4 cid_incoming_verifier; /*< Most recently supplied verifier */
	time_t cid_last_renew; /*< Time of last renewal */
	nfs_clientid_confirm_state_t cid_confirmed; /*< Confirm/expire state */
	nfs_clientid_confirm_state_t
		cid_confirmed_saved; /* hang on to cid_confirmed for
					v4.0 unmount cid cleanup */
	bool cid_allow_reclaim; /*< Can still reclaim state? */
	nfs_client_cred_t cid_credential; /*< Client credential */
	char *cid_recov_tag; /*< Recovery tag */
	nfs_client_record_t *cid_client_record; /*< Record for managing
						   confirmation and
						   replacement */
	struct glist_head cid_openowners; /*< All open owners */
	struct glist_head cid_lockowners; /*< All lock owners */
	pthread_mutex_t cid_mutex; /*< Mutex for this client */
	union {
		struct {
			/** Callback channel */
			struct rpc_call_channel cb_chan;
			/** Decoded address */
			gsh_addr_t cb_addr;
			/** Callback identifier */
			uint32_t cb_callback_ident;
			/** Universal address */
			char cb_client_r_addr[SOCK_NAME_MAX];
			/** Callback program */
			uint32_t cb_program;
			bool cb_chan_down; /* Callback channel state */
		} v40; /*< v4.0 callback information */
		struct {
			bool cid_reclaim_complete; /*< reclaim complete
						       indication */
			/** All sessions */
			struct glist_head cb_session_list;
		} v41; /*< v4.1 callback information */
	} cid_cb; /*< Version specific callback information */
	time_t first_path_down_resp_time; /* Time when the server first sent
					       NFS4ERR_CB_PATH_DOWN */
	/* Since Ganesha only supports SP4_NONE, we do not store state
	 * protection data required for other state protection mechanisms
	 */
	state_protect_how4 cid_state_protect_how; /* State protection type */
	unsigned int cid_nb_session; /*< Number of sessions stored */
	uint32_t cid_create_session_sequence; /*< Sequence number for session
						   creation. */
	CREATE_SESSION4res cid_create_session_slot; /*< Cached response to
							  last CREATE_SESSION */
	state_owner_t cid_owner; /*< Owner for per-client state */
	int32_t cid_refcount; /*< Reference count for lifecycle */
	int cid_lease_reservations; /*< Counted lease reservations, to spare
					   this clientid from the reaper */
	uint32_t cid_minorversion;
	uint32_t cid_stateid_counter;

	uint32_t curr_deleg_grants; /* current num of delegations owned by
				       this client */
	uint32_t num_revokes; /* Num revokes for the client */
	struct gsh_client *gsh_client; /* for client specific statistics. */
	uint32_t cid_open_state_counter; /* Num of files opened by client */
	struct glist_head expired_client; /* List entry for expired clients */
	bool marked_for_delayed_cleanup; /* Flag marked to state that entry is
					    intended to be cleaned up from the
					    delayed cleanup list */
};

#define GSH_CLIENT_ID_AUTO_TRACEPOINT(prov, event, log_level, _client_id,    \
				      format, ...)                           \
	do {                                                                 \
		const int32_t __ref_count =                                  \
			atomic_fetch_int32_t(&_client_id->cid_refcount);     \
		GSH_AUTO_TRACEPOINT(                                         \
			prov, event, log_level,                              \
			"client_id={},confirmed={},ref_count={} | " format,  \
			_client_id->cid_clientid, _client_id->cid_confirmed, \
			__ref_count, ##__VA_ARGS__);                         \
	} while (0)

/**
 * @brief Client owner record for verification and replacement
 *
 * @note The cr_mutex should never be acquired while holding a
 * cid_mutex.
 */

struct nfs_client_record_t {
	int32_t cr_refcount; /*< Reference count for lifecycle */
	pthread_mutex_t cr_mutex; /*< Mutex protecting this structure */
	nfs_client_id_t *cr_confirmed_rec; /*< The confirmed record associated
					       with this owner (if there is
					       one.) */
	nfs_client_id_t *cr_unconfirmed_rec; /*< The unconfirmed record
						   associated with this
						   client name, if there is
						   one. */
	sockaddr_t cr_server_addr; /*< Server IP address the client connected to
				    */
	sockaddr_t cr_client_addr; /*< The client address */
	uint32_t cr_pnfs_flags; /*< pNFS flags.  RFC 5661 allows us
				     to treat identical co_owners with
				     different pNFS flags as
				     disjoint. Linux client stupidity
				     forces us to do so. */
	int cr_client_val_len; /*< Length of owner */
	char cr_client_val[]; /*< Supplied co_owner */
};

extern pool_t *client_id_pool;

extern hash_table_t *ht_client_record;
extern hash_table_t *ht_confirmed_client_id;
extern hash_table_t *ht_unconfirmed_client_id;

/******************************************************************************
 *
 * Lock Data
 *
 *****************************************************************************/

/**
 * @brief Blocking lock type and state
 */

typedef enum state_blocking_t {
	STATE_NON_BLOCKING,
	STATE_BLOCKING,
	STATE_AVAILABLE,
	STATE_CANCELED
} state_blocking_t;

/**
 * @brief Lock protocol type
 */

typedef enum lock_protocol_t { LOCK_NLM, LOCK_NFSv4, LOCK_9P } lock_protocol_t;

/**
 * @brief Grant callback
 *
 * The granted call back is responsible for acquiring a reference to
 * the lock entry if needed.
 */
typedef state_status_t (*granted_callback_t)(struct fsal_obj_handle *obj,
					     state_lock_entry_t *lock_entry);

/**
 * @brief NLM specific Blocking lock data
 */

typedef struct state_nlm_block_data_t {
	netobj sbd_nlm_fh; /*< Filehandle */
	char sbd_nlm_fh_buf[NFS3_FHSIZE]; /*< Statically allocated
						   FH buffer */
} state_nlm_block_data_t;

/**
 * @brief NFSv4 specific Blocking lock data
 */

typedef struct state_nfsv4_block_data_t {
	time_t snbd_notified_eligible_time; /* Only valid for available locks */
	time_t snbd_last_poll_time; /* Only valid for blocked locks */
} state_nfsv4_block_data_t;

extern struct glist_head state_blocked_locks;

/**
 * @brief Grant types
 */
typedef enum state_grant_type_t {
	STATE_GRANT_NONE, /*< No grant */
	STATE_GRANT_INTERNAL, /*< Grant generated by SAL */
	STATE_GRANT_FSAL, /*< FSAL granting lock */
	STATE_GRANT_FSAL_AVAILABLE, /*< FSAL signalling lock availability */
	STATE_GRANT_POLL, /*< Poll this lock */
} state_grant_type_t;

typedef enum state_block_type_t {
	/** Not blocking */
	STATE_BLOCK_NONE,
	/** Lock is blocked by an internal lock. */
	STATE_BLOCK_INTERNAL,
	/** Lock is blocked and FSAL supports async blocking locks. */
	STATE_BLOCK_ASYNC,
	/** Lock is blocked and SAL will poll the lock. */
	STATE_BLOCK_POLL,
} state_block_type_t;

/**
 * @brief Blocking lock data
 */
struct state_block_data_t {
	struct glist_head sbd_list; /*< Lost of blocking locks */
	state_grant_type_t sbd_grant_type; /*< Type of grant */
	state_block_type_t sbd_block_type; /*< Type of block */
	granted_callback_t sbd_granted_callback; /*< Callback for grant */
	state_cookie_entry_t *sbd_blocked_cookie; /*< Blocking lock cookie */
	state_lock_entry_t *sbd_lock_entry; /*< Details of lock */
	union {
		state_nlm_block_data_t sbd_nlm; /*< NLM block data */
		state_nfsv4_block_data_t sbd_v4; /*< NFSv4 block data */
	} sbd_prot;
};

struct state_lock_entry_t {
	struct glist_head sle_list; /*< Locks on this file */
	struct glist_head sle_owner_locks; /*< Link on the owner lock list */
	struct glist_head sle_client_locks; /*< Locks on this client */
	struct glist_head sle_state_locks; /*< Locks on this state */
#ifdef DEBUG_SAL
	struct glist_head sle_all_locks; /*< Link on the global lock list */
#endif /* DEBUG_SAL */
	struct glist_head sle_export_locks; /*< Link on the export
						   lock list */
	struct gsh_export *sle_export;
	struct fsal_obj_handle *sle_obj; /*< File being locked */
	state_block_data_t *sle_block_data; /*< Blocking lock data */
	state_owner_t *sle_owner; /* Lock owner */
	state_t *sle_state; /*< Associated lock state */
	state_blocking_t sle_blocked; /*< Blocking status */
	lock_protocol_t sle_protocol; /*< Protocol type */
	int32_t sle_ref_count; /*< Reference count */
	fsal_lock_param_t sle_lock; /*< Lock description */
	pthread_mutex_t sle_mutex; /*< Mutex to protect the structure */
};

/**
 * @brief Stats for file-specific and client-file delegation heuristics
 */

struct file_deleg_stats {
	uint32_t fds_curr_delegations; /* number of delegations on file */
	open_delegation_type4 fds_deleg_type; /* delegation type */
	uint32_t fds_delegation_count; /* times file has been delegated */
	uint32_t fds_recall_count; /* times file has been recalled */
	time_t fds_avg_hold; /* avg amount of time deleg held */
	time_t fds_last_delegation;
	time_t fds_last_recall;
	uint32_t fds_num_opens; /* total num of opens so far. */
	time_t fds_first_open; /* time that we started recording
					   num_opens */
	uint32_t fds_num_write_opens; /* total num of opens in write mode
					   incremented irrespective of
					   whether or not delegation
					   was offered for the file */
};

enum cbgetattr_state {
	CB_GETATTR_NONE = 0, /* initial state or reset to as
				and when finished processing
				response */
	CB_GETATTR_WIP, /* when req sent */
	CB_GETATTR_RSP_OK, /* successful response */
	CB_GETATTR_FAILED /* for any failure */
};

struct cbgetattr_rsp {
	enum cbgetattr_state state;
	uint64_t change;
	uint64_t filesize;
	bool modified;
};
typedef struct cbgetattr_rsp cbgetattr_t;

/**
 * @brief Per-file state lists
 *
 * To be used by FSALs
 */
struct state_file {
	/** File owning state */
	struct fsal_obj_handle *obj;
	/** NFSv4 states on this file. Protected by st_lock */
	struct glist_head list_of_states;
	/** Layout recalls on this file. Protected by st_lock */
	struct glist_head layoutrecall_list;
	/** Pointers for lock list. Protected by st_lock */
	struct glist_head lock_list;
	/** Pointers for NLM share list. Protected by st_lock */
	struct glist_head nlm_share_list;
	/** true iff write delegated. Protected by st_lock */
	bool write_delegated;
	/** client holding write_deleg. Protected by st_lock */
	nfs_client_id_t *write_deleg_client;
	/** Delegation statistics. Protected by st_lock */
	struct file_deleg_stats fdeleg_stats;
	/* cbgetattr stats. Protected by st_lock */
	cbgetattr_t cbgetattr;
	uint32_t anon_ops; /* number of anonymous operations
			      * happening at the moment which
			      * prevents delegations from being
			      * granted */
};

/**
 * @brief Per-directory state lists
 *
 * To be used by FSALs/SAL
 */
struct state_dir {
	/** If this is a junction, the export this node points to.
	 * Protected by jct_lock. */
	struct gsh_export *junction_export;
	/** gsh_refstr to stash copy of the export's pseudopath */
	struct gsh_refstr *jct_pseudopath;
	/** List of exports that have this object as their root.
	 * Protected by jct_lock */
	struct glist_head export_roots;
	/** There is one export root reference counted for each export
	    for which this entry is a root for. This field is used
	    with the atomic inc/dec/fetch routines. */
	int32_t exp_root_refcount;
};

/**
 *  @brief Associate lock state or export junction information with an object.
 *
 * LOCK ORDERING
 *
 * The state handle has two overlapping locks for different purposes.
 *
 * The st_lock is used to protect byte range locks, opens, and such for regular
 * files. It is a mutex since there is no parallelism benefit to it being a
 * rwlock.
 *
 * The jct_lock is used to protect export junction information for directories.
 * It is a rwlock since most of the time junctions are being looked at not
 * modified.
 *
 * Both of these locks are often used in conjunction with the export->exp_lock,
 * but the rules of lock order are different.
 *
 * For st_lock, the export->exp_lock MUST NOT be held, the export->exp_lock is
 * often taken while already holding st_lock, so the order is:
 *
 * st_lock THEN export->exp_lock
 *
 * For jct_lock, there is one place where the export->exp_lock is already held,
 * so it makes sense for the order to be:
 *
 * export->exp_lock THEN jct_lock.
 *
 */
struct state_hdl {
	union {
		/** Lock protecting state */
		pthread_mutex_t st_lock;
		/** Lock protecting export junctions */
		pthread_rwlock_t jct_lock;
	};
	bool no_cleanup;
	union {
		/** Details about the state held for a regular file. */
		struct state_file file;
		/** Details about an export junction. */
		struct state_dir dir;
	};
};

#define JCT_PSEUDOPATH(shdl) \
	(shdl->dir.jct_pseudopath ? shdl->dir.jct_pseudopath->gr_val : NULL)

/**
 * @brief Description of a layout segment
 */

typedef struct state_layout_segment {
	struct glist_head sls_state_segments; /*< Link on the per-layout-state
						   segment list */
	state_t *sls_state; /*< Associated layout state */
	struct pnfs_segment sls_segment; /*< Segment descriptor */
	void *sls_fsal_data; /*< FSAL data */
} state_layout_segment_t;

/**
 * @brief An entry in a list of states affected by a recall
 *
 * We make a non-intrusive list so that a state can be affected by any
 * number of recalls.
 */

struct recall_state_list {
	struct glist_head link; /*< Link to the next layout state in the
				   queue */
	state_t *state; /*< State on which to recall */
};

/**
 * @brief Processing for a LAYOUTRECALL from the FSAL.
 */

struct state_layout_recall_file {
	struct glist_head entry_link; /*< List of recalls on a file */
	struct fsal_obj_handle *obj; /*< Related file */
	layouttype4 type; /*< Type of layout being recalled */
	struct pnfs_segment segment; /*< Segment to recall */
	struct glist_head state_list; /*< List of states affected by this
					   layout */
	void *recall_cookie; /*< Cookie returned to FSAL on return of last
				   segment satisfying the layout */
};

/**
 * @brief Blocking lock cookie entry
 *
 * Management of lce_refcount
 * ==========================
 *
 * * state_add_grant_cookie
 *
 *   creates a reference.
 *
 * * state_find_grant
 *
 *   gets a reference
 *
 * * state_complete_grant
 *
 *   always releases 1 reference it releases a 2nd reference when the
 *   call instance is the first to actually try and complete the grant
 *
 * * state_release_grant
 *
 *   always releases 1 reference it releases a 2nd reference when the
 *   call instance is the first to actually try and release the grant
 *
 * * state_cancel_grant
 *
 *   calls cancel_blocked_lock, which will release the initial
 *   reference
 *
 * * cancel_blocked_lock
 *
 *   releases 1 reference if cookie exists called by
 *   state_cancel_grant also called by unlock, cancel, sm_notify
 */

struct state_cookie_entry_t {
	struct fsal_obj_handle *sce_obj; /*< Associated file */
	state_lock_entry_t *sce_lock_entry; /*< Associated lock */
	void *sce_cookie; /*< Cookie data */
	int sce_cookie_size; /*< Length of cookie */
};

/*
 * Structures for state async processing
 *
 */

/**
 * @brief Asynchronous state function
 */
typedef void(state_async_func_t)(state_async_queue_t *arg);

/**
 * @brief Data for asynchronous NLM calls
 */
typedef struct state_nlm_async_data_t {
	state_nlm_client_t *nlm_async_host; /*< The client */
	void *nlm_async_key; /*< Identifying key */
	union {
		nfs_res_t nlm_async_res; /*< Asynchronous response */
		nlm4_testargs nlm_async_grant; /*< Arguments for grant */
	} nlm_async_args;
} state_nlm_async_data_t;

/**
 * @brief Asynchronous blocking lock data
 */

typedef struct state_async_block_data_t {
	state_lock_entry_t *state_async_lock_entry; /*< Associated lock */
} state_async_block_data_t;

/**
 * @brief Queue of asynchronous events
 */

struct state_async_queue_t {
	struct glist_head state_async_glist; /*< List of events */
	state_async_func_t *state_async_func; /*< Processing function */
	union {
		state_nlm_async_data_t state_nlm_async_data; /*< Data for
								   operation */
		void *state_no_data; /*< Dummy pointer */
	} state_async_data;
};

/**
 * @brief Start of grace period
 *
 * @note This looks specific to SONAS and ought not to be in the top
 *       level SAL files.  (The SAL needs more A to support this kind
 *       of thing.)
 */

#define EVENT_JUST_GRACE 0 /* Just start grace period */
#define EVENT_CLEAR_BLOCKED \
	1 /* Start grace period, and clear blocked locks
				 */
#define EVENT_RELEASE_IP \
	2 /* Start grace period, clear blocked locks,
				   and release all locks */
#define EVENT_UPDATE_CLIENTS \
	3 /* Start grace period, clear blocked locks,
				   release all locks, and update clients list.
				 */
#define EVENT_TAKE_NODEID \
	4 /* Start grace period, clear blocked locks,
				   release all locks, and update clients using
				   node id. */
#define EVENT_TAKE_IP \
	5 /* Start grace period, clear blocked locks,
				   release all locks, and update clients using
				   IP address. */

typedef struct nfs_grace_start {
	int event; /*< Reason for grace period, see EVENT_nnn */
	int nodeid; /*< Node from which we are taking over */
	char *ipaddr; /*< IP of releasing/takeover node (string) */
	sockaddr_t sa; /*< IP of releasing/takeover node (sockaddr_t) */
} nfs_grace_start_t;

/* Memory pools */

extern pool_t *state_owner_pool; /*< Pool for NFSv4 files's open owner */

extern int g_total_num_files_delegated;
extern int g_max_files_delegatable;

#ifdef DEBUG_SAL
extern struct glist_head state_v4_all;
extern struct glist_head state_owners_all;
#endif

#endif /* SAL_DATA_H */
/** @} */
