/**
* Copyright (C) 2008 Happy Fish / YuQing
*
* FastDFS may be copied only under the terms of the GNU General
* Public License V3, which may be found in the FastDFS source kit.
* Please visit the FastDFS Home Page http://www.fastken.com/ for more detail.
**/

//trunk_shared.c

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "fastcommon/logger.h"
#include "fastcommon/shared_func.h"
#include "trunk_shared.h"
#include "tracker_proto.h"

FDFSStorePaths g_fdfs_store_paths = {0, NULL};
struct base64_context g_fdfs_base64_context;
BufferInfo g_zero_buffer = {NULL, 0, 0};

int trunk_shared_init()
{
	base64_init_ex(&g_fdfs_base64_context, 0, '-', '_', '.');
    g_zero_buffer.alloc_size = g_zero_buffer.length = 256 * 1024;
    g_zero_buffer.buff = (char *)malloc(g_zero_buffer.alloc_size);
    if (g_zero_buffer.buff == NULL)
    {
        logError("file: "__FILE__", line: %d, "
                "malloc %d bytes fail", __LINE__,
                g_zero_buffer.alloc_size);
        return ENOMEM;
    }

    memset(g_zero_buffer.buff, 0, g_zero_buffer.length);
    return 0;
}

FDFSStorePathInfo *storage_load_paths_from_conf_file_ex(
        IniContext *pItemContext, const char *szSectionName,
        const bool bUseBasePath, int *path_count, int *err_no)
{
	char item_name[64];
	FDFSStorePathInfo *store_paths;
	char *pPath;
    int bytes;
	int i;

	*path_count = iniGetIntValue(szSectionName, "store_path_count", 
					pItemContext, 1);
	if (*path_count <= 0)
	{
		logError("file: "__FILE__", line: %d, "
			"store_path_count: %d is invalid!",
			__LINE__, *path_count);
		*err_no = EINVAL;
		return NULL;
	}

    bytes = sizeof(FDFSStorePathInfo) * (*path_count);
	store_paths = (FDFSStorePathInfo *)malloc(bytes);
	if (store_paths == NULL)
	{
		logError("file: "__FILE__", line: %d, "
			"malloc %d bytes fail, "
			"errno: %d, error info: %s", __LINE__,
            bytes, errno, STRERROR(errno));
		*err_no = errno != 0 ? errno : ENOMEM;
		return NULL;
	}
	memset(store_paths, 0, bytes);

	pPath = iniGetStrValue(szSectionName, "store_path0", pItemContext);
	if (pPath == NULL)
	{
		if (!bUseBasePath)
		{
			logError("file: "__FILE__", line: %d, "
				"conf file must have item "
				"\"store_path0\"!", __LINE__);
			*err_no = ENOENT;
			free(store_paths);
			return NULL;
		}

		pPath = SF_G_BASE_PATH_STR;
	}

    store_paths[0].path_len = strlen(pPath);
	store_paths[0].path = strdup(pPath);
	if (store_paths[0].path == NULL)
	{
		logError("file: "__FILE__", line: %d, "
			"malloc %d bytes fail, "
			"errno: %d, error info: %s",
			__LINE__, (int)strlen(pPath),
			errno, STRERROR(errno));
		*err_no = errno != 0 ? errno : ENOMEM;
		free(store_paths);
		return NULL;
	}

	*err_no = 0;
	for (i=1; i<*path_count; i++)
	{
		sprintf(item_name, "store_path%d", i);
		pPath = iniGetStrValue(szSectionName, item_name,
				pItemContext);
		if (pPath == NULL)
		{
			logError("file: "__FILE__", line: %d, "
				"conf file must have item \"%s\"!",
				__LINE__, item_name);
			*err_no = ENOENT;
			break;
		}

		chopPath(pPath);
		if (!fileExists(pPath))
		{
			logError("file: "__FILE__", line: %d, "
				"\"%s\" can't be accessed, "
				"errno: %d, error info: %s", __LINE__,
				pPath, errno, STRERROR(errno));
			*err_no = errno != 0 ? errno : ENOENT;
			break;
		}
		if (!isDir(pPath))
		{
			logError("file: "__FILE__", line: %d, "
				"\"%s\" is not a directory!",
				__LINE__, pPath);
			*err_no = ENOTDIR;
			break;
		}

        store_paths[i].path_len = strlen(pPath);
		store_paths[i].path = strdup(pPath);
		if (store_paths[i].path == NULL)
		{
			logError("file: "__FILE__", line: %d, " \
				"malloc %d bytes fail, " \
				"errno: %d, error info: %s", __LINE__, \
				(int)strlen(pPath), errno, STRERROR(errno));
			*err_no = errno != 0 ? errno : ENOMEM;
			break;
		}
	}

	if (*err_no != 0)
	{
		for (i=0; i<*path_count; i++)
		{
			if (store_paths[i].path != NULL)
			{
				free(store_paths[i].path);
			}
		}
		free(store_paths);
		return NULL;
	}

	return store_paths;
}

int storage_load_paths_from_conf_file(IniContext *pItemContext)
{
	char *pPath;
	int result;

	pPath = iniGetStrValue(NULL, "base_path", pItemContext);
	if (pPath == NULL)
	{
		logError("file: "__FILE__", line: %d, "
			"conf file must have item \"base_path\"!", __LINE__);
		return ENOENT;
	}

	snprintf(SF_G_BASE_PATH_STR, sizeof(SF_G_BASE_PATH_STR), "%s", pPath);
	chopPath(SF_G_BASE_PATH_STR);
	if (!fileExists(SF_G_BASE_PATH_STR))
	{
		logError("file: "__FILE__", line: %d, "
			"\"%s\" can't be accessed, error info: %s",
			__LINE__, STRERROR(errno), SF_G_BASE_PATH_STR);
		return errno != 0 ? errno : ENOENT;
	}
	if (!isDir(SF_G_BASE_PATH_STR))
	{
		logError("file: "__FILE__", line: %d, "
			"\"%s\" is not a directory!",
			__LINE__, SF_G_BASE_PATH_STR);
		return ENOTDIR;
	}

	g_fdfs_store_paths.paths = storage_load_paths_from_conf_file_ex(
		pItemContext, NULL, true, &g_fdfs_store_paths.count, &result);

	return result;
}

#define SPLIT_FILENAME_BODY(logic_filename, filename_len, true_filename, \
	store_path_index, check_path_index) \
	do \
	{ \
	char buff[3]; \
	char *pEnd; \
 \
	if (*filename_len <= FDFS_LOGIC_FILE_PATH_LEN) \
	{ \
		logError("file: "__FILE__", line: %d, " \
			"filename_len: %d is invalid, <= %d", \
			__LINE__, *filename_len, FDFS_LOGIC_FILE_PATH_LEN); \
		return EINVAL; \
	} \
 \
	if (*logic_filename != FDFS_STORAGE_STORE_PATH_PREFIX_CHAR) \
	{ /* version < V1.12 */ \
		store_path_index = 0; \
		memcpy(true_filename, logic_filename, (*filename_len)+1); \
		break; \
	} \
 \
	if (*(logic_filename + 3) != '/') \
	{ \
		logError("file: "__FILE__", line: %d, " \
			"filename: %s is invalid", \
			__LINE__, logic_filename); \
		return EINVAL; \
	} \
 \
	*buff = *(logic_filename+1); \
	*(buff+1) = *(logic_filename+2); \
	*(buff+2) = '\0'; \
 \
	pEnd = NULL; \
	store_path_index = strtol(buff, &pEnd, 16); \
	if (pEnd != NULL && *pEnd != '\0') \
	{ \
		logError("file: "__FILE__", line: %d, " \
			"filename: %s is invalid", \
			__LINE__, logic_filename); \
		return EINVAL; \
	} \
 \
	if (check_path_index && (store_path_index < 0 || \
		store_path_index >= g_fdfs_store_paths.count)) \
	{ \
		logError("file: "__FILE__", line: %d, " \
			"filename: %s is invalid, " \
			"invalid store path index: %d", \
			__LINE__, logic_filename, store_path_index); \
		return EINVAL; \
	} \
 \
	*filename_len -= 4; \
	memcpy(true_filename, logic_filename + 4, (*filename_len) + 1); \
 \
	} while (0)


int storage_split_filename(const char *logic_filename, \
		int *filename_len, char *true_filename, char **ppStorePath)
{
	int store_path_index;

	SPLIT_FILENAME_BODY(logic_filename, filename_len, true_filename, \
		store_path_index, true);

	*ppStorePath = g_fdfs_store_paths.paths[store_path_index].path;

	return 0;
}

int storage_split_filename_ex(const char *logic_filename, \
		int *filename_len, char *true_filename, int *store_path_index)
{
	SPLIT_FILENAME_BODY(logic_filename, \
		filename_len, true_filename, *store_path_index, true);

	return 0;
}

int storage_split_filename_no_check(const char *logic_filename, \
		int *filename_len, char *true_filename, int *store_path_index)
{
	SPLIT_FILENAME_BODY(logic_filename, \
		filename_len, true_filename, *store_path_index, false);

	return 0;
}

char *trunk_info_dump(const FDFSTrunkFullInfo *pTrunkInfo, char *buff, \
				const int buff_size)
{
	snprintf(buff, buff_size, \
		"store_path_index=%d, " \
		"sub_path_high=%d, " \
		"sub_path_low=%d, " \
		"id=%u, offset=%d, size=%d, status=%d", \
		pTrunkInfo->path.store_path_index, \
		pTrunkInfo->path.sub_path_high, \
		pTrunkInfo->path.sub_path_low,  \
		pTrunkInfo->file.id, pTrunkInfo->file.offset, pTrunkInfo->file.size, \
		pTrunkInfo->status);

	return buff;
}

char *trunk_header_dump(const FDFSTrunkHeader *pTrunkHeader, char *buff, \
				const int buff_size)
{
	snprintf(buff, buff_size, \
		"file_type=%d, " \
		"alloc_size=%d, " \
		"file_size=%d, " \
		"crc32=%d, " \
		"mtime=%d, " \
		"ext_name(%d)=%s", \
		pTrunkHeader->file_type, pTrunkHeader->alloc_size, \
		pTrunkHeader->file_size, pTrunkHeader->crc32, \
		pTrunkHeader->mtime, \
		(int)strlen(pTrunkHeader->formatted_ext_name), \
		pTrunkHeader->formatted_ext_name);

	return buff;
}

char *trunk_get_full_filename_ex(const FDFSStorePaths *pStorePaths, \
		const FDFSTrunkFullInfo *pTrunkInfo, \
		char *full_filename, const int buff_size)
{
	char short_filename[64];
	char *pStorePath;

	pStorePath = pStorePaths->paths[pTrunkInfo->path.store_path_index].path;
	TRUNK_GET_FILENAME(pTrunkInfo->file.id, short_filename);

	snprintf(full_filename, buff_size, \
			"%s/data/"FDFS_STORAGE_DATA_DIR_FORMAT"/" \
			FDFS_STORAGE_DATA_DIR_FORMAT"/%s", \
			pStorePath, pTrunkInfo->path.sub_path_high, \
			pTrunkInfo->path.sub_path_low, short_filename);

	return full_filename;
}

void trunk_pack_header(const FDFSTrunkHeader *pTrunkHeader, char *buff)
{
	*(buff + FDFS_TRUNK_FILE_FILE_TYPE_OFFSET) = pTrunkHeader->file_type;
	int2buff(pTrunkHeader->alloc_size, \
		buff + FDFS_TRUNK_FILE_ALLOC_SIZE_OFFSET);
	int2buff(pTrunkHeader->file_size, \
		buff + FDFS_TRUNK_FILE_FILE_SIZE_OFFSET);
	int2buff(pTrunkHeader->crc32, \
		buff + FDFS_TRUNK_FILE_FILE_CRC32_OFFSET);
	int2buff(pTrunkHeader->mtime, \
		buff + FDFS_TRUNK_FILE_FILE_MTIME_OFFSET);
	memcpy(buff + FDFS_TRUNK_FILE_FILE_EXT_NAME_OFFSET, \
		pTrunkHeader->formatted_ext_name, \
		FDFS_FILE_EXT_NAME_MAX_LEN + 1);
}

void trunk_unpack_header(const char *buff, FDFSTrunkHeader *pTrunkHeader)
{
	pTrunkHeader->file_type = *(buff + FDFS_TRUNK_FILE_FILE_TYPE_OFFSET);
	pTrunkHeader->alloc_size = buff2int(
			buff + FDFS_TRUNK_FILE_ALLOC_SIZE_OFFSET);
	pTrunkHeader->file_size = buff2int(
			buff + FDFS_TRUNK_FILE_FILE_SIZE_OFFSET);
	pTrunkHeader->crc32 = buff2int(
			buff + FDFS_TRUNK_FILE_FILE_CRC32_OFFSET);
	pTrunkHeader->mtime = buff2int(
			buff + FDFS_TRUNK_FILE_FILE_MTIME_OFFSET);
	memcpy(pTrunkHeader->formatted_ext_name, buff + \
		FDFS_TRUNK_FILE_FILE_EXT_NAME_OFFSET, \
		FDFS_FILE_EXT_NAME_MAX_LEN + 1);
	*(pTrunkHeader->formatted_ext_name+FDFS_FILE_EXT_NAME_MAX_LEN+1)='\0';
}

void trunk_file_info_encode(const FDFSTrunkFileInfo *pTrunkFile, char *str)
{
	char buff[sizeof(int) * 3];
	int len;

	int2buff(pTrunkFile->id, buff);
	int2buff(pTrunkFile->offset, buff + sizeof(int));
	int2buff(pTrunkFile->size, buff + sizeof(int) * 2);
	base64_encode_ex(&g_fdfs_base64_context, buff, sizeof(buff),
			str, &len, false);
}

void trunk_file_info_decode(const char *str, FDFSTrunkFileInfo *pTrunkFile)
{
	char buff[FDFS_TRUNK_FILE_INFO_LEN];
	int len;

	base64_decode_auto(&g_fdfs_base64_context, str, FDFS_TRUNK_FILE_INFO_LEN,
		buff, &len);

	pTrunkFile->id = buff2int(buff);
	pTrunkFile->offset = buff2int(buff + sizeof(int));
	pTrunkFile->size = buff2int(buff + sizeof(int) * 2);
}

int trunk_file_get_content_ex(const FDFSStorePaths *pStorePaths, \
		const FDFSTrunkFullInfo *pTrunkInfo, const int file_size, \
		int *pfd, char *buff, const int buff_size)
{
	char full_filename[MAX_PATH_SIZE];
	int fd;
	int result;
	int read_bytes;

	if (file_size > buff_size)
	{
		return ENOSPC;
	}

	if (pfd != NULL)
	{
		fd = *pfd;
	}
	else
	{
		trunk_get_full_filename_ex(pStorePaths, pTrunkInfo, \
			full_filename, sizeof(full_filename));
		fd = open(full_filename, O_RDONLY);
		if (fd < 0)
		{
			return errno != 0 ? errno : EIO;
		}

		if (lseek(fd, pTrunkInfo->file.offset + \
			FDFS_TRUNK_FILE_HEADER_SIZE, SEEK_SET) < 0)
		{
			result = errno != 0 ? errno : EIO;
			close(fd);
			return result;
		}
	}

	read_bytes = fc_safe_read(fd, buff, file_size);
	if (read_bytes == file_size)
	{
		result = 0;
	}
	else
	{
		result = errno != 0 ? errno : EINVAL;
	}

	if (pfd == NULL)
	{
		close(fd);
	}

	return result;
}

int trunk_file_stat_func_ex(const FDFSStorePaths *pStorePaths, \
	const int store_path_index, const char *true_filename, \
	const int filename_len, const int stat_func, \
	struct stat *pStat, FDFSTrunkFullInfo *pTrunkInfo, \
	FDFSTrunkHeader *pTrunkHeader, int *pfd)
{
	int result;
	int src_store_path_index;
	int src_filename_len;
	char src_filename[128];
	char src_true_filename[128];

	result = trunk_file_do_lstat_func_ex(pStorePaths, store_path_index, \
		true_filename, filename_len, stat_func, \
		pStat, pTrunkInfo, pTrunkHeader, pfd);
	if (result != 0)
	{
		return result;
	}

	if (!(stat_func == FDFS_STAT_FUNC_STAT && IS_TRUNK_FILE_BY_ID( \
		(*pTrunkInfo)) && S_ISLNK(pStat->st_mode)))
	{
		return 0;
	}

	do
	{
		result = trunk_file_get_content_ex(pStorePaths, pTrunkInfo, \
				pStat->st_size, pfd, src_filename, \
				sizeof(src_filename) - 1);
		if (result != 0)
		{
			break;
		}

		src_filename_len = pStat->st_size;
		*(src_filename + src_filename_len) = '\0';
		if ((result=storage_split_filename_no_check(src_filename, \
			&src_filename_len, src_true_filename, \
			&src_store_path_index)) != 0)
		{
			break;
		}
		if (src_store_path_index < 0 || \
			src_store_path_index >= pStorePaths->count)
		{
			logError("file: "__FILE__", line: %d, " \
				"filename: %s is invalid, " \
				"invalid store path index: %d, " \
				"which < 0 or >= %d", __LINE__, \
				src_filename, src_store_path_index, \
				pStorePaths->count);
			result = EINVAL;
			break;
		}

		if (pfd != NULL)
		{
			close(*pfd);
			*pfd = -1;
		}

		result = trunk_file_do_lstat_func_ex(pStorePaths, \
				src_store_path_index, src_true_filename, \
				src_filename_len, stat_func, pStat, \
				pTrunkInfo, pTrunkHeader, pfd);
	} while (0);

	if (result != 0 && pfd != NULL && *pfd >= 0)
	{
		close(*pfd);
		*pfd = -1;
	}

	return result;
}

int trunk_file_do_lstat_func_ex(const FDFSStorePaths *pStorePaths, \
	const int store_path_index, const char *true_filename, \
	const int filename_len, const int stat_func, \
	struct stat *pStat, FDFSTrunkFullInfo *pTrunkInfo, \
	FDFSTrunkHeader *pTrunkHeader, int *pfd)
{
	char full_filename[MAX_PATH_SIZE];
	char buff[128];
	char pack_buff[FDFS_TRUNK_FILE_HEADER_SIZE];
	int64_t file_size;
	int buff_len;
	int fd;
	int read_bytes;
	int result;

	pTrunkInfo->file.id = 0;
	if (filename_len != FDFS_TRUNK_FILENAME_LENGTH) //not trunk file
	{
		snprintf(full_filename, sizeof(full_filename), "%s/data/%s", \
			pStorePaths->paths[store_path_index].path, true_filename);

		if (stat_func == FDFS_STAT_FUNC_STAT)
		{
			result = stat(full_filename, pStat);
		}
		else
		{
			result = lstat(full_filename, pStat);
		}
		if (result == 0)
		{
			return 0;
		}
		else
		{
			return errno != 0 ? errno : ENOENT;
		}
	}

	memset(buff, 0, sizeof(buff));
	base64_decode_auto(&g_fdfs_base64_context, (char *)true_filename + \
		FDFS_TRUE_FILE_PATH_LEN, FDFS_FILENAME_BASE64_LENGTH, \
		buff, &buff_len);

	file_size = buff2long(buff + sizeof(int) * 2);
	if (!IS_TRUNK_FILE(file_size))  //slave file
	{
		snprintf(full_filename, sizeof(full_filename), "%s/data/%s", \
			pStorePaths->paths[store_path_index].path, true_filename);

		if (stat_func == FDFS_STAT_FUNC_STAT)
		{
			result = stat(full_filename, pStat);
		}
		else
		{
			result = lstat(full_filename, pStat);
		}
		if (result == 0)
		{
			return 0;
		}
		else
		{
			return errno != 0 ? errno : ENOENT;
		}
	}

	trunk_file_info_decode(true_filename + FDFS_TRUE_FILE_PATH_LEN + \
		 FDFS_FILENAME_BASE64_LENGTH, &pTrunkInfo->file);

	pTrunkHeader->file_size = FDFS_TRUNK_FILE_TRUE_SIZE(file_size);
	pTrunkHeader->mtime = buff2int(buff + sizeof(int));
	pTrunkHeader->crc32 = buff2int(buff + sizeof(int) * 4);
	memcpy(pTrunkHeader->formatted_ext_name, true_filename + \
		(filename_len - (FDFS_FILE_EXT_NAME_MAX_LEN + 1)), \
		FDFS_FILE_EXT_NAME_MAX_LEN + 2); //include tailing '\0'
	pTrunkHeader->alloc_size = pTrunkInfo->file.size;

	pTrunkInfo->path.store_path_index = store_path_index;
	pTrunkInfo->path.sub_path_high = strtol(true_filename, NULL, 16);
	pTrunkInfo->path.sub_path_low = strtol(true_filename + 3, NULL, 16);

	trunk_get_full_filename_ex(pStorePaths, pTrunkInfo, full_filename, \
				sizeof(full_filename));
	fd = open(full_filename, O_RDONLY);
	if (fd < 0)
	{
		return errno != 0 ? errno : EIO;
	}

	if (lseek(fd, pTrunkInfo->file.offset, SEEK_SET) < 0)
	{
		result = errno != 0 ? errno : EIO;
		close(fd);
		return result;
	}

	read_bytes = fc_safe_read(fd, buff, FDFS_TRUNK_FILE_HEADER_SIZE);
	if (read_bytes == FDFS_TRUNK_FILE_HEADER_SIZE)
	{
		result = 0;
	}
	else
	{
		result = errno;
		close(fd);
		return result != 0 ? result : EINVAL;
	}

	memset(pStat, 0, sizeof(struct stat));
	pTrunkHeader->file_type = *(buff + FDFS_TRUNK_FILE_FILE_TYPE_OFFSET);
	if (pTrunkHeader->file_type == FDFS_TRUNK_FILE_TYPE_REGULAR)
	{
		pStat->st_mode = S_IFREG;
	}
	else if (pTrunkHeader->file_type == FDFS_TRUNK_FILE_TYPE_LINK)
	{
		pStat->st_mode = S_IFLNK;
	}
	else if (pTrunkHeader->file_type == FDFS_TRUNK_FILE_TYPE_NONE)
	{
		close(fd);
		return ENOENT;
	}
	else
	{
        /*
		logError("file: "__FILE__", line: %d, "
			"Invalid file type: %d", __LINE__,
			pTrunkHeader->file_type);
         */

		close(fd);
		return ENOENT;
	}

	trunk_pack_header(pTrunkHeader, pack_buff);

	/*
	{
	char temp[265];
	char szHexBuff[2 * FDFS_TRUNK_FILE_HEADER_SIZE + 1];
	FDFSTrunkHeader trueTrunkHeader;

	fprintf(stderr, "file: "__FILE__", line: %d, true buff=%s\n", __LINE__, \
		bin2hex(buff+1, FDFS_TRUNK_FILE_HEADER_SIZE - 1, szHexBuff));
	trunk_unpack_header(buff, &trueTrunkHeader);
	fprintf(stderr, "file: "__FILE__", line: %d, true fields=%s\n", __LINE__, \
		trunk_header_dump(&trueTrunkHeader, full_filename, sizeof(full_filename)));

	fprintf(stderr, "file: "__FILE__", line: %d, my buff=%s\n", __LINE__, \
		bin2hex(pack_buff+1, FDFS_TRUNK_FILE_HEADER_SIZE - 1, szHexBuff));
	fprintf(stderr, "file: "__FILE__", line: %d, my trunk=%s, my fields=%s\n", __LINE__, \
		trunk_info_dump(pTrunkInfo, temp, sizeof(temp)), \
		trunk_header_dump(pTrunkHeader, full_filename, sizeof(full_filename)));
	}
	*/

	if (memcmp(pack_buff, buff, FDFS_TRUNK_FILE_HEADER_SIZE) != 0)
	{
		close(fd);
		return ENOENT;
	}

	pStat->st_size = pTrunkHeader->file_size;
	pStat->st_mtime = pTrunkHeader->mtime;

	if (pfd != NULL)
	{
		*pfd = fd;
	}
	else
	{
		close(fd);
	}

	return 0;
}

bool fdfs_is_trunk_file(const char *remote_filename, const int filename_len)
{
	int buff_len;
	char buff[64];
	int64_t file_size;

	if (filename_len != FDFS_TRUNK_LOGIC_FILENAME_LENGTH) //not trunk file
	{
		return false;
	}

	memset(buff, 0, sizeof(buff));
	base64_decode_auto(&g_fdfs_base64_context, (char *)remote_filename + \
		FDFS_LOGIC_FILE_PATH_LEN, FDFS_FILENAME_BASE64_LENGTH, \
		buff, &buff_len);

	file_size = buff2long(buff + sizeof(int) * 2);
	return IS_TRUNK_FILE(file_size);
}

int fdfs_decode_trunk_info(const int store_path_index, \
		const char *true_filename, const int filename_len, \
		FDFSTrunkFullInfo *pTrunkInfo)
{
	if (filename_len != FDFS_TRUNK_FILENAME_LENGTH) //not trunk file
	{
		logWarning("file: "__FILE__", line: %d, " \
			"trunk filename length: %d != %d, filename: %s", \
			__LINE__, filename_len, FDFS_TRUNK_FILENAME_LENGTH, \
			true_filename);
		return EINVAL;
	}

	pTrunkInfo->path.store_path_index = store_path_index;
	pTrunkInfo->path.sub_path_high = strtol(true_filename, NULL, 16);
	pTrunkInfo->path.sub_path_low = strtol(true_filename + 3, NULL, 16);
	trunk_file_info_decode(true_filename + FDFS_TRUE_FILE_PATH_LEN + \
		FDFS_FILENAME_BASE64_LENGTH, &pTrunkInfo->file);
	return 0;
}

