/* SPDX-License-Identifier: LGPL-3.0-or-later */
/*
 * vim:noexpandtab:shiftwidth=8:tabstop=8:
 *
 * Copyright (C) Red Hat  Inc., 2011
 * Author: Anand Subramanian anands@redhat.com
 *
 * 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
 *
 * -------------
 */

#ifndef GLUSTER_INTERNAL
#define GLUSTER_INTERNAL

#include "fsal.h"
#include "fsal_types.h"
#include "fsal_api.h"
#include "posix_acls.h"
#include "FSAL/fsal_commonlib.h"
#include <glusterfs/api/glfs.h>
#include <glusterfs/api/glfs-handles.h>
#include "nfs_exports.h"

#define GLUSTER_VOLNAME_KEY "volume"
#define GLUSTER_HOSTNAME_KEY "hostname"
#define GLUSTER_VOLPATH_KEY "volpath"

/* defined the set of attributes supported with POSIX */
#define GLUSTERFS_SUPPORTED_ATTRIBUTES \
	(ATTRS_POSIX | ATTR_ACL | ATTR4_SEC_LABEL)

/**
 * The attributes this FSAL can set.
 */

#define GLUSTERFS_SETTABLE_ATTRIBUTES                                     \
	(ATTR_MODE | ATTR_OWNER | ATTR_GROUP | ATTR_ATIME | ATTR_CTIME |  \
	 ATTR_MTIME | ATTR_SIZE | ATTR_MTIME_SERVER | ATTR_ATIME_SERVER | \
	 ATTR_ACL | ATTR4_SEC_LABEL | ATTR4_XATTR)

/**
 * Override internal Gluster defines for the time being.
 */
/* Values for valid flags to be used when using XXXsetattr, to set multiple
 * attribute values passed via the related stat structure.
 */
#define GLAPI_SET_ATTR_MODE GFAPI_SET_ATTR_MODE
#define GLAPI_SET_ATTR_UID GFAPI_SET_ATTR_UID
#define GLAPI_SET_ATTR_GID GFAPI_SET_ATTR_GID
#define GLAPI_SET_ATTR_SIZE GFAPI_SET_ATTR_SIZE
#define GLAPI_SET_ATTR_ATIME GFAPI_SET_ATTR_ATIME
#define GLAPI_SET_ATTR_MTIME GFAPI_SET_ATTR_MTIME

/* UUID length for the object returned from glfs_get_volume_id */
#define GLAPI_UUID_LENGTH 16

/*
 * GFAPI_HANDLE_LENGTH is the handle length for object handles
 * returned from glfs_h_extract_handle or glfs_h_create_from_handle
 *
 * GLAPI_HANDLE_LENGTH is the total length of the handle descriptor
 * to be used in the wire handle.
 */
#define GLAPI_HANDLE_LENGTH (GFAPI_HANDLE_LENGTH + GLAPI_UUID_LENGTH)

/* Flags to determine if ACLs are supported */
#define NFSv4_ACL_SUPPORT (!op_ctx_export_has_option(EXPORT_OPTION_DISABLE_ACL))

/* define flags for attr_valid */
#define XATTR_STAT (1 << 0) /* 01 */
#define XATTR_ACL (1 << 1) /* 02 */

/* END Override */

#ifdef GLTIMING
typedef enum {
	lat_handle_release = 0,
	lat_lookup,
	lat_create,
	lat_getattrs,
	lat_handle_to_wire,
	lat_handle_to_key,
	lat_extract_handle,
	lat_create_handle,
	lat_read_dirents,
	lat_makedir,
	lat_makenode,
	lat_setattrs,
	lat_file_unlink,
	lat_file_open,
	lat_file_read,
	lat_file_seek,
	lat_file_write,
	lat_commit,
	lat_file_close,
	lat_makesymlink,
	lat_readsymlink,
	lat_linkfile,
	lat_renamefile,
	lat_lock_op,
	lat_end_slots
} latency_slots_t;
#define LATENCY_SLOTS 24

struct latency_data {
	uint64_t count;
	nsecs_elapsed_t overall_time;
};
#endif

struct glusterfs_fsal_module {
	struct fsal_module fsal;
	struct fsal_obj_ops handle_ops;
	struct glist_head fs_obj; /* list of glusterfs_fs filesystem objects */
	pthread_mutex_t glfs_lock; /* lock to protect above list */
};
extern struct glusterfs_fsal_module GlusterFS;

struct glusterfs_fs {
	struct glist_head fs_obj; /* link to glusterfs_fs filesystem objects */
	char *volname;
	glfs_t *fs;
	const struct fsal_up_vector *up_ops; /*< Upcall operations */
	int64_t refcnt;
	pthread_t up_thread; /* upcall thread */
	int8_t destroy_mode;
	uint64_t up_poll_usec;
	bool enable_upcall;
};

struct glusterfs_export {
	struct glusterfs_fs *gl_fs;
	char *mount_path;
	char *export_path;
	uid_t saveduid;
	gid_t savedgid;
	struct fsal_export export;
	bool pnfs_ds_enabled;
	bool pnfs_mds_enabled;
	char *sec_label_xattr;
};

#ifdef USE_GLUSTER_DELEGATION
#define GLAPI_LEASE_ID_SIZE GLFS_LEASE_ID_SIZE
#endif

struct glusterfs_fd {
	/** open and share mode plus fd management */
	struct fsal_fd fsal_fd;

	/** Gluster file descriptor. */
	struct glfs_fd *glfd;
	struct user_cred creds; /* user creds opening fd*/
#ifdef USE_GLUSTER_DELEGATION
	char lease_id[GLAPI_LEASE_ID_SIZE];
#endif
};

#define GLUSTERFS_FD_INIT             \
	{                             \
		FSAL_FD_INIT, NULL,   \
		{                     \
			0, 0, 0, NULL \
		}                     \
	}

struct glusterfs_handle {
	struct glfs_object *glhandle;
	unsigned char globjhdl[GLAPI_HANDLE_LENGTH]; /* handle descriptor,
							   for wire handle */
	struct glusterfs_fd globalfd;
	struct fsal_obj_handle handle; /* public FSAL handle */
	struct fsal_share share; /* share_reservations */

	/* following added for pNFS support */
	uint64_t rd_issued;
	uint64_t rd_serial;
	uint64_t rw_issued;
	uint64_t rw_serial;
	uint64_t rw_max_len;
#ifdef USE_GLUSTER_DELEGATION
	glfs_lease_types_t lease_type; /* Store lease_type granted to
					 this file: NONE,RD or RW */
#endif
};

/* Structures defined for PNFS */
struct glfs_ds_handle {
	struct fsal_ds_handle ds;
	struct glfs_object *glhandle;
	stable_how4 stability_got;
	bool connected;
};

struct glfs_file_layout {
	uint32_t stripe_length;
	uint64_t stripe_type;
	uint32_t devid;
};

struct glfs_ds_wire {
	unsigned char gfid[16];
	struct glfs_file_layout layout; /*< Layout information */
};

/* Define the buffer size for GLUSTERFS NFS4 ACL. */
#define GLFS_ACL_BUF_SIZE 0x1000

/* A set of buffers to retrieve multiple attributes at the same time. */
typedef struct fsal_xstat__ {
	int attr_valid;
	struct stat buffstat;
	acl_t e_acl; /* stores effective acl */
	acl_t i_acl; /* stores inherited acl */
	bool is_dir;
} glusterfs_fsal_xstat_t;

static inline void glusterfs_fsal_clean_xstat(glusterfs_fsal_xstat_t *buffxstat)
{
	if (buffxstat->e_acl) {
		acl_free(buffxstat->e_acl);
		buffxstat->e_acl = NULL;
	}

	if (buffxstat->i_acl) {
		acl_free(buffxstat->i_acl);
		buffxstat->i_acl = NULL;
	}
}

struct glusterfs_state_fd {
	/** state MUST be first to use default free_state */
	struct state_t state;
	struct glusterfs_fd glusterfs_fd;
};

void setglustercreds(struct glusterfs_export *glfs_export, uid_t *uid,
		     gid_t *gid, unsigned int ngrps, gid_t *groups,
		     char *client_addr, unsigned int client_addr_len,
		     char *file, int line, char *function);

/* clang-format off */
#ifdef USE_GLUSTER_DELEGATION
#define SET_GLUSTER_CREDS_OP_CTX(glfs_export)                                  \
	do {                                                                   \
		int old_errno = errno;                                         \
		void *sa = NULL;                                               \
		size_t sa_len = 0;                                             \
									       \
		if (op_ctx->caller_addr != NULL) {                             \
			sa = socket_addr(op_ctx->caller_addr);                 \
			sa_len = socket_addr_len(op_ctx->caller_addr);         \
		} else if (op_ctx->client != NULL) {                           \
			sa = socket_addr(&op_ctx->client->cl_addrbuf);         \
			sa_len = socket_addr_len(&op_ctx->client->cl_addrbuf); \
		}                                                              \
									       \
		((void)setglustercreds(glfs_export, &op_ctx->creds.caller_uid, \
				       &op_ctx->creds.caller_gid,              \
				       op_ctx->creds.caller_glen,              \
				       op_ctx->creds.caller_garray, sa,        \
				       sa_len, (char *)__FILE__, __LINE__,     \
				       (char *)__func__));                     \
		errno = old_errno;                                             \
	} while (0)

#define SET_GLUSTER_CREDS_MY_FD(glfs_export, my_fd)                            \
	do {                                                                   \
		int old_errno = errno;                                         \
									       \
		((void)setglustercreds(glfs_export,                            \
				       &(my_fd)->creds.caller_uid,             \
				       &(my_fd)->creds.caller_gid,             \
				       (my_fd)->creds.caller_glen,             \
				       (my_fd)->creds.caller_garray,           \
				       (my_fd)->lease_id, GLAPI_LEASE_ID_SIZE, \
				       (char *)__FILE__, __LINE__,             \
				       (char *)__func__));                     \
		errno = old_errno;                                             \
	} while (0)

/** @todo: This doesn't quite work right if op_ctx has no client, hopefully
 *         in that case there won't actually ever be any leases...
 */
#define SET_GLUSTER_LEASE_ID(my_fd)                                            \
	do {                                                                   \
		void *sa = NULL;                                               \
		size_t sa_len = 0;                                             \
									       \
		if (op_ctx->caller_addr != NULL) {                             \
			sa = socket_addr(op_ctx->caller_addr);                 \
			sa_len = socket_addr_len(op_ctx->caller_addr);         \
		} else if (op_ctx->client != NULL) {                           \
			sa = socket_addr(&op_ctx->client->cl_addrbuf);         \
			sa_len = socket_addr_len(&op_ctx->client->cl_addrbuf); \
		}                                                              \
									       \
		memset((my_fd)->lease_id, 0, GLAPI_LEASE_ID_SIZE);             \
									       \
		if (sa_len != 0 && sa_len <= GLAPI_LEASE_ID_SIZE) {            \
			memcpy((my_fd)->lease_id, sa, sa_len);                 \
		}                                                              \
	} while (0)

#else
#define SET_GLUSTER_CREDS_OP_CTX(glfs_export)                                  \
	do {                                                                   \
		int old_errno = errno;                                         \
									       \
		((void)setglustercreds(glfs_export, &op_ctx->creds.caller_uid, \
				       &op_ctx->creds.caller_gid,              \
				       op_ctx->creds.caller_glen,              \
				       op_ctx->creds.caller_garray, NULL, 0,   \
				       (char *)__FILE__, __LINE__,             \
				       (char *)__func__));                     \
		errno = old_errno;                                             \
	} while (0)

#define SET_GLUSTER_CREDS_MY_FD(glfs_export, my_fd)                           \
	do {                                                                  \
		int old_errno = errno;                                        \
									       \
		((void)setglustercreds(glfs_export,                           \
				       &(my_fd)->creds.caller_uid,            \
				       &(my_fd)->creds.caller_gid,            \
				       (my_fd)->creds.caller_glen,            \
				       (my_fd)->creds.caller_garray, NULL, 0, \
				       (char *)__FILE__, __LINE__,            \
				       (char *)__func__));                    \
		errno = old_errno;                                            \
	} while (0)

#define SET_GLUSTER_LEASE_ID(my_fd) \
	do {                        \
		/* NOTHING TO DO */ \
	} while (0)
#endif

#define RESET_GLUSTER_CREDS(glfs_export)                                       \
	do {                                                                   \
		int old_errno = errno;                                         \
		((void)setglustercreds(glfs_export, NULL, NULL, 0, NULL, NULL, \
				       0, (char *)__FILE__, __LINE__,          \
				       (char *)__func__));                     \
		errno = old_errno;                                             \
	} while (0)
/* clang-format on */

#ifdef GLTIMING
struct latency_data glfsal_latencies[LATENCY_SLOTS];

void latency_update(struct timespec *s_time, struct timespec *e_time,
		    int opnum);

void latency_dump(void);
#endif

void handle_ops_init(struct fsal_obj_ops *ops);

fsal_status_t gluster2fsal_error(const int gluster_errorcode);

void stat2fsal_attributes(const struct stat *buffstat,
			  struct fsal_attrlist *fsalattr);

void construct_handle(struct glusterfs_export *glexport, const struct stat *st,
		      struct glfs_object *glhandle, unsigned char *globjhdl,
		      struct glusterfs_handle **obj, const char *vol_uuid);

fsal_status_t glfs2fsal_handle(struct glusterfs_export *glfs_export,
			       struct glfs_object *glhandle,
			       struct fsal_obj_handle **pub_handle,
			       struct stat *sb,
			       struct fsal_attrlist *attrs_out);

fsal_status_t glusterfs_create_export(struct fsal_module *fsal_hdl,
				      void *parse_node,
				      struct config_error_type *err_type,
				      const struct fsal_up_vector *up_ops);

void gluster_cleanup_vars(struct glfs_object *glhandle);

fsal_status_t glusterfs_get_acl(struct glusterfs_export *glfs_export,
				struct glfs_object *objhandle,
				glusterfs_fsal_xstat_t *buffxstat,
				struct fsal_attrlist *fsalattr);

fsal_status_t glusterfs_set_acl(struct glusterfs_export *glfs_export,
				struct glusterfs_handle *objhandle,
				glusterfs_fsal_xstat_t *buffxstat);

fsal_status_t glusterfs_process_acl(struct glfs *fs, struct glfs_object *object,
				    struct fsal_attrlist *attrs,
				    glusterfs_fsal_xstat_t *buffxstat);

void glusterfs_free_fs(struct glusterfs_fs *gl_fs);

/*
 * Following have been introduced for pNFS support
 */

/* Need to call this to initialize export_ops for pnfs */
void export_ops_pnfs(struct export_ops *ops);

/* Need to call this to initialize obj_ops for pnfs */
void handle_ops_pnfs(struct fsal_obj_ops *ops);

/* Need to call this to initialize ops for pnfs */
void fsal_ops_pnfs(struct fsal_ops *ops);

void dsh_ops_init(struct fsal_dsh_ops *ops);

void pnfs_ds_ops_init(struct fsal_pnfs_ds_ops *ops);

nfsstat4 getdeviceinfo(struct fsal_module *fsal_hdl, XDR *da_addr_body,
		       const layouttype4 type,
		       const struct pnfs_deviceid *deviceid);

/* UP thread routines */
void *GLUSTERFSAL_UP_Thread(void *Arg);
int initiate_up_thread(struct glusterfs_fs *gl_fs);
int up_process_event_object(struct glusterfs_fs *gl_fs,
			    struct glfs_object *object,
			    enum glfs_upcall_reason reason);
void gluster_process_upcall(struct glfs_upcall *cbk, void *data);

fsal_status_t glusterfs_close_my_fd(struct glusterfs_fd *my_fd);
#endif /* GLUSTER_INTERNAL */
