/* 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
 *
 * ---------------------------------------
 */

/**
 * @file nfs_file_handle.h
 * @brief Prototypes for the file handle in v3 and v4
 */

#ifndef NFS_FILE_HANDLE_H
#define NFS_FILE_HANDLE_H

#include <sys/types.h>
#include <sys/param.h>

#include "log.h"
#include "display.h"
#include "sal_data.h"
#include "export_mgr.h"
#include "nfs_fh.h"
#include "nfs_proto_functions.h"

/**
 * @brief Get the actual size of a v3 handle based on the sized fsopaque
 *
 * @return The filehandle size
 */

static inline size_t nfs3_sizeof_handle(struct file_handle_v3 *hdl)
{
	int hsize;
	int aligned_hsize;

	hsize = offsetof(struct file_handle_v3, fsopaque) + hdl->fs_len;

	/* correct packet's fh length so it's divisible by 4 to trick dNFS into
	   working. This is essentially sending the padding. */
	aligned_hsize = roundup(hsize, 4);
	if (aligned_hsize <= NFS3_FHSIZE)
		hsize = aligned_hsize;

	return hsize;
}

/**
 *
 * @brief Allocates a buffer to be used for storing a NFSv4 filehandle.
 *
 * Allocates a buffer to be used for storing a NFSv3 filehandle.
 *
 * @param fh [INOUT] the filehandle to manage.
 *
 */
static inline void nfs3_AllocateFH(nfs_fh3 *fh)
{
	/* Allocating the filehandle in memory */
	fh->data.data_len = NFS3_FHSIZE;
	fh->data.data_val = (char *)gsh_calloc(1, NFS3_FHSIZE);
}

static inline void nfs3_freeFH(nfs_fh3 *fh)
{
	fh->data.data_len = 0;
	gsh_free(fh->data.data_val);
	fh->data.data_val = NULL;
}

/**
 *
 * @brief Allocates a buffer to be used for storing a NFSv4 filehandle.
 *
 * Allocates a buffer to be used for storing a NFSv4 filehandle.
 *
 * @param fh [INOUT] the filehandle to manage.
 *
 */
static inline void nfs4_AllocateFH(nfs_fh4 *fh)
{
	/* Allocating the filehandle in memory */
	fh->nfs_fh4_len = NFS4_FHSIZE;
	fh->nfs_fh4_val = (char *)gsh_calloc(1, NFS4_FHSIZE);
}

static inline void nfs4_freeFH(nfs_fh4 *fh)
{
	fh->nfs_fh4_len = 0;
	gsh_free(fh->nfs_fh4_val);
	fh->nfs_fh4_val = NULL;
}

/**
 * @brief Get the actual size of a v4 handle based on the sized fsopaque
 *
 * @return The filehandle size
 */

static inline size_t nfs4_sizeof_handle(struct file_handle_v4 *hdl)
{
	return offsetof(struct file_handle_v4, fsopaque) + hdl->fs_len;
}

static inline size_t nfs4_sizeof_handle_padding(struct file_handle_v4 *hdl)
{
	int hsize;
	int aligned_hsize;

	hsize = offsetof(struct file_handle_v4, fsopaque) + hdl->fs_len;
	/* align on 4 byte block boundary */
	aligned_hsize = roundup(hsize, 4);
	if (aligned_hsize <= NFS4_FHSIZE)
		hsize = aligned_hsize;
	return hsize;
}

static inline bool valid_Fh4_Len(nfs_fh4 *fh, file_handle_v4_t *pfile_handle)
{
	if (!nfs_param.core_param.enable_v3_fh_for_v4) {
		return fh->nfs_fh4_len == nfs4_sizeof_handle(pfile_handle);
	} else {
		return ((fh->nfs_fh4_len == nfs4_sizeof_handle(pfile_handle)) ||
			(fh->nfs_fh4_len ==
			 nfs4_sizeof_handle_padding(pfile_handle)));
	}
}

#define LEN_FH_STR OPAQUE_BYTES_SIZE(NFS4_FHSIZE)

/* File handle translation utility */
#ifdef _USE_NFS3
struct fsal_obj_handle *nfs3_FhandleToCache(nfs_fh3 *, nfsstat3 *, int *);
#endif

bool nfs4_FSALToFhandle(bool allocate, nfs_fh4 *fh4,
			const struct fsal_obj_handle *fsalhandle,
			struct gsh_export *exp);

bool nfs3_FSALToFhandle(bool allocate, nfs_fh3 *fh3,
			const struct fsal_obj_handle *fsalhandle,
			struct gsh_export *exp);

/* nfs3 validation */
int nfs3_Is_Fh_Invalid(nfs_fh3 *);

/**
 *
 * nfs3_FhandleToExportId
 *
 * This routine extracts the export id from the file handle NFSv3
 *
 * @param pfh3 [IN] file handle to manage.
 *
 * @return the export id.
 *
 */
static inline int nfs3_FhandleToExportId(nfs_fh3 *pfh3)
{
	file_handle_v3_t *pfile_handle;

	if (nfs3_Is_Fh_Invalid(pfh3) != NFS4_OK)
		return -1; /* Badly formed argument */

	pfile_handle = (file_handle_v3_t *)(pfh3->data.data_val);

	/*exportid is in network byte order in nfs_fh3*/
	return ntohs(pfile_handle->exportid);
} /* nfs3_FhandleToExportId */

static inline int nlm4_FhandleToExportId(netobj *pfh3)
{
	nfs_fh3 fh3;

	if (pfh3 == NULL)
		return nfs3_FhandleToExportId(NULL);
	fh3.data.data_val = pfh3->n_bytes;
	fh3.data.data_len = pfh3->n_len;
	return nfs3_FhandleToExportId(&fh3);
}

/**
 *
 * @brief Test if an NFS v4 file handle is empty.
 *
 * This routine is used to test if a fh is empty (contains no data).
 *
 * @param pfh [IN] file handle to test.
 *
 * @return NFS4_OK if successful, NFS4ERR_NOFILEHANDLE is fh is empty.
 *
 */
static inline int nfs4_Is_Fh_Empty(nfs_fh4 *pfh)
{
	if (pfh == NULL) {
		LogMajor(COMPONENT_FILEHANDLE, "INVALID HANDLE: pfh=NULL");
		return NFS4ERR_NOFILEHANDLE;
	}

	if (pfh->nfs_fh4_len == 0) {
		LogInfo(COMPONENT_FILEHANDLE, "INVALID HANDLE: empty");
		return NFS4ERR_NOFILEHANDLE;
	}

	return NFS4_OK;
} /* nfs4_Is_Fh_Empty */

/* NFSv4 specific FH related functions */
int nfs4_Is_Fh_Invalid(nfs_fh4 *);
int nfs4_Is_Fh_DSHandle(nfs_fh4 *);

nfsstat4 nfs4_sanity_check_FH(compound_data_t *data,
			      object_file_type_t required_type,
			      bool ds_allowed);

nfsstat4 nfs4_sanity_check_saved_FH(compound_data_t *data, int required_type,
				    bool ds_allowed);

/* File handle print function (mostly used for debugging) */

/* clang-format off */

#define LogNFS3_Operation(component, req, fh, format, args...)                \
	do {                                                                  \
		if (unlikely(component_log_level[component] >= NIV_DEBUG)) {  \
			char str[LEN_FH_STR];                                 \
			struct display_buffer dspbuf = { sizeof(str), str,    \
							 str };               \
									      \
			display_opaque_bytes(&dspbuf, (fh)->data.data_val,    \
					     (fh)->data.data_len);            \
									      \
			DisplayLogComponentLevel(                             \
				component, __FILE__, __LINE__, __func__,      \
				NIV_DEBUG,                                    \
				"REQUEST PROCESSING: Calling %s "             \
				"File Handle V3: Len=%u %s" format,           \
				nfs3_func_desc[req->rq_msg.cb_proc].funcname, \
				(fh)->data.data_len, str, ##args);            \
		}                                                             \
	} while (0)

#define LogNFS3_Operation2(component, req, fh1, name1, fh2, name2)            \
	do {                                                                  \
		if (unlikely(component_log_level[component] >= NIV_DEBUG)) {  \
			char str1[LEN_FH_STR];                                \
			struct display_buffer dspbuf1 = { sizeof(str1), str1, \
							  str1 };             \
			char str2[LEN_FH_STR];                                \
			struct display_buffer dspbuf2 = { sizeof(str2), str2, \
							  str2 };             \
									      \
			display_opaque_bytes(&dspbuf1, (fh1)->data.data_val,  \
					     (fh1)->data.data_len);           \
			display_opaque_bytes(&dspbuf2, (fh2)->data.data_val,  \
					     (fh2)->data.data_len);           \
									      \
			DisplayLogComponentLevel(                             \
				component, __FILE__, __LINE__, __func__,      \
				NIV_DEBUG,                                    \
				"REQUEST PROCESSING: Calling %s "             \
				"File Handle V3: Len=%u %s%s%s to "           \
				"File Handle V3: Len=%u %s name %s",          \
				nfs3_func_desc[req->rq_msg.cb_proc].funcname, \
				(fh1)->data.data_len, str1,                   \
				name1 ? " name " : "", name1 ? name1 : "",    \
				(fh2)->data.data_len, str2, name2);           \
		}                                                             \
	} while (0)

#define LogHandleNFS4(label, fh4)                                          \
	do {                                                               \
		if (isFullDebug(COMPONENT_NFS_V4)) {                       \
			char str[LEN_FH_STR];                              \
			struct display_buffer dspbuf = { sizeof(str), str, \
							 str };            \
									   \
			display_opaque_bytes(&dspbuf, (fh4)->nfs_fh4_val,  \
					     (fh4)->nfs_fh4_len);          \
									   \
			LogFullDebug(COMPONENT_NFS_V4,                     \
				     "%sFile Handle V4: Len=%u %s", label, \
				     (fh4)->nfs_fh4_len, str);             \
		}                                                          \
	} while (0)

#define LogCompoundFH(data)                                                \
	do {                                                               \
		if (isFullDebug(COMPONENT_NFS_V4)) {                       \
			char str[LEN_FH_STR];                              \
			struct display_buffer dspbuf = { sizeof(str), str, \
							 str };            \
									   \
			display_opaque_bytes(&dspbuf,                      \
					     &data->currentFH.nfs_fh4_val, \
					     data->currentFH.nfs_fh4_len); \
									   \
			LogFullDebug(COMPONENT_NFS_V4,                     \
				     "Current FH  Len=%u %s",              \
				     data->currentFH.nfs_fh4_len, str);    \
									   \
			display_reset_buffer(&dspbuf);                     \
									   \
			display_opaque_bytes(&dspbuf,                      \
					     &data->savedFH.nfs_fh4_val,   \
					     data->savedFH.nfs_fh4_len);   \
									   \
			LogFullDebug(COMPONENT_NFS_V4,                     \
				     "Saved FH    Len=%u %s",              \
				     data->savedFH.nfs_fh4_len, str);      \
		}                                                          \
	} while (0)

#define LogNFSACL_Operation(component, req, fh, format, args...)               \
	do {                                                                   \
		if (unlikely(component_log_level[component] >= NIV_DEBUG)) {   \
			char str[LEN_FH_STR];                                  \
			struct display_buffer dspbuf = { sizeof(str), str,     \
							 str };                \
									       \
			display_opaque_bytes(&dspbuf, (fh)->data.data_val,     \
					     (fh)->data.data_len);             \
									       \
			DisplayLogComponentLevel(                              \
			       component, __FILE__, __LINE__, __func__,        \
			       NIV_DEBUG,                                      \
			       "REQUEST PROCESSING: Calling %s "               \
			       "File Handle V3: Len=%u %s" format,             \
			       nfsacl_func_desc[req->rq_msg.cb_proc].funcname, \
			       (fh)->data.data_len, str, ##args);              \
		}                                                              \
	} while (0)

/* clang-format on */

#endif /* NFS_FILE_HANDLE_H */
