一个C++实现的FUSE网络文件系统,客户端linux 服务端 Windows
服务端
#include "FuseServer.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#ifdef _WIN32
#include <WinSock2.h>
#include <Windows.h>
#include <io.h>
#else//!_WIN32
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#endif // _WIN32
#include <filesystem>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <mutex>
#include <thread>
#include <chrono>
#include <new>
#include "FS_Util.h"
#include "HandleMgr.h"
using namespace std;
#include "SocketApi.h"
#include "FuseCommon.h"
#include "SockMgr.h"
#include "win_lowio.h"
#include "Utils.h"
#ifdef _WIN32
namespace fs = std::experimental::filesystem::v1;
#else//!_WIN32
namespace fs = std::filesystem;
#endif // _WIN32
// 处理认证请求
bool handle_authenticate(HSOCKET client_socket, const CommunicationPacket &request)
{
ResponsePkt response;
response.init(request);
// 检查密码是否正确
if (std::string(request.auth.password) == string(AUTH_PASSWORD))
{
response.response.status = ResponseStatus::FU_SUCCESS;
print_with_time_and_thread(LOG_WARN, "Authentication successful\n");
}
else
{
response.response.status = ResponseStatus::FU_AUTH_FAILED;
print_with_time_and_thread(LOG_ERROR, "Authentication failed\n");
}
skapi_send(client_socket, (char*)&response, sizeof(response));
return response.response.status == ResponseStatus::FU_SUCCESS;
}
string make_this_file_path(const CommunicationPacket &request)
{
string x = SHARE_DIR + "/" + request.path;
Utils::_FormatWindowsPathA(x);
return x;
}
// 处理GET_ATTR请求
void handle_get_attr(HSOCKET client_socket, const CommunicationPacket &request)
{
ResponsePkt response;
response.init(request);
std::string full_path = make_this_file_path(request);
FsAttr attr;
int et = 0, er = 0;
if (FsUtil_GetAttr(full_path.c_str(), attr, et, er))
{
memset(&response.response.mustat, 0, sizeof(response.response.mustat));
fill_fa(response.response.mustat, attr);
response.response.status = ResponseStatus::FU_SUCCESS;
print_with_time_and_thread(LOG_DEBUG, "GET_ATTR successful for path: %s\n", full_path.c_str());
}
else
{
response.response.status = ResponseStatus::FU_NOT_FOUND;
print_with_time_and_thread(LOG_ERROR, "GET_ATTR failed for path: %s, et: %d, er: %d\n", full_path.c_str(), et, er);
}
skapi_send(client_socket, (char*)&response, sizeof(response));
}
// 处理READ_DIR请求
void handle_read_dir(HSOCKET client_socket, const CommunicationPacket &request)
{
std::string full_path = make_this_file_path(request);
std::stringstream ss;
try
{
// 读取目录内容
ss << "0|." << std::endl;
ss << "0|.." << std::endl;
for (const auto &entry : fs::directory_iterator(full_path))
{
//文件属性
std::string file_path = entry.path().u8string();
FsAttr fa = {};
int et = 0, er = 0;
if (!FsUtil_GetAttr(file_path.c_str(), fa, et, er))
{
print_with_time_and_thread(LOG_ERROR, "READ_DIR failed getattr path: %s %d-%d\n", full_path.c_str(), et, er);
}
ss << FA2str(fa) << "|";
ss << entry.path().filename().u8string() << std::endl;
}
std::string dir_content = ss.str();
size_t al = sizeof(ResponsePkt) + dir_content.length() + 1;
ResponsePkt *rList = (ResponsePkt*)malloc(al);
if (rList)
{
rList->init(request);
rList->set_variable_data(dir_content.c_str(), (uint32_t)(dir_content.length() + 1), (uint32_t)(al + 1));
rList->response.status = ResponseStatus::FU_SUCCESS;
skapi_send(client_socket, (char*)rList, (int)(rList->get_total_need()));
print_with_time_and_thread(LOG_DEBUG, "READ_DIR successful for path: %s\n", full_path.c_str());
free(rList);
}
else
{
ResponsePkt rErr;
rErr.init(request);
rErr.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "READ_DIR failed for path: %s \n", full_path.c_str());
skapi_send(client_socket, (char*)&rErr, sizeof(rErr));
}
}
catch (...)
{
ResponsePkt rErr;
rErr.init(request);
rErr.response.status = ResponseStatus::FU_NOT_FOUND;
print_with_time_and_thread(LOG_ERROR, "READ_DIR failed for path: %s, error: %d\n", full_path.c_str(), errno);
skapi_send(client_socket, (char*)&rErr, sizeof(rErr));
}
}
// 处理CREATE请求
void handle_create(HSOCKET client_socket, const CommunicationPacket &request)
{
ResponsePkt response;
response.init(request);
std::string full_path = make_this_file_path(request);
#ifdef _WIN32
unsigned int mode = linux_open_mode_to_win(request.create.create_mode);
#else//!_WIN32
unsigned int mode = request.create.create_mode;
#endif//_WIN32
try
{
// 创建文件
wlo_handle fd = wlo_create(full_path.c_str(), (mode));
if (fd < 0)
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "CREATE failed for path: %s, error: %d\n", full_path.c_str(), errno);
}
else
{
long long handle_id = FileHandleManager::getInstance().addFileHandle(fd);
response.response.status = ResponseStatus::FU_SUCCESS;
response.response.fd_id_out = handle_id;
print_with_time_and_thread(LOG_DEBUG, "CREATE successful for path: %s\n", full_path.c_str());
}
}
catch (...)
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "CREATE failed for path: %s, error: %d\n", full_path.c_str(), errno);
}
skapi_send(client_socket, (char*)&response, sizeof(response));
}
// 处理MKDIR请求
void handle_mkdir(HSOCKET client_socket, const CommunicationPacket &request)
{
ResponsePkt response;
response.init(request);
std::string full_path = make_this_file_path(request);
try
{
// 创建目录
fs::path fpat = full_path;
fs::create_directory(fpat);
response.response.status = ResponseStatus::FU_SUCCESS;
print_with_time_and_thread(LOG_DEBUG, "MKDIR successful for path: %s\n", full_path.c_str());
}
catch (const fs::filesystem_error &e)
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "MKDIR failed for path: %s, error: %s\n", full_path.c_str(), e.what());
}
skapi_send(client_socket, (char*)&response, sizeof(response));
}
// 处理RMDIR请求
void handle_rmdir(HSOCKET client_socket, const CommunicationPacket &request)
{
ResponsePkt response;
response.init(request);
std::string full_path = make_this_file_path(request);
try
{
// 删除目录
fs::remove(full_path);
response.response.status = ResponseStatus::FU_SUCCESS;
print_with_time_and_thread(LOG_DEBUG, "RMDIR successful for path: %s\n", full_path.c_str());
}
catch (const fs::filesystem_error &e)
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "RMDIR failed for path: %s, error: %s\n", full_path.c_str(), e.what());
}
skapi_send(client_socket, (char*)&response, sizeof(response));
}
// 处理UNLINK请求
void handle_unlink(HSOCKET client_socket, const CommunicationPacket &request)
{
ResponsePkt response;
response.init(request);
std::string full_path = make_this_file_path(request);
try
{
// 删除文件
int err = my_unlink(full_path.c_str());
if (err == 0)
{
response.response.status = ResponseStatus::FU_SUCCESS;
print_with_time_and_thread(LOG_DEBUG, "UNLINK successful for path: %s\n", full_path.c_str());
}
else
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "UNLINK err for path: %s, err=%d\n", full_path.c_str(), err);
}
}
catch (const fs::filesystem_error &e)
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "UNLINK failed for path: %s, error: %s\n", full_path.c_str(), e.what());
}
skapi_send(client_socket, (char*)&response, sizeof(response));
}
// 处理RENAME请求
void handle_rename(HSOCKET client_socket, const CommunicationPacket &request)
{
ResponsePkt response;
response.init(request);
std::string old_full_path = make_this_file_path(request);
std::string new_full_path = SHARE_DIR + "/" + request.rename.newpath;
unsigned int flags = request.rename.Flag_Rename;
/** Rename a file
*
* *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If
* RENAME_NOREPLACE is specified, the filesystem must not
* overwrite *newname* if it exists and return an error
* instead. If `RENAME_EXCHANGE` is specified, the filesystem
* must atomically exchange the two files, i.e. both must
* exist and neither may be deleted.
*/
try
{
int rerrr;
if (0 != (rerrr = my_renameat2(old_full_path.c_str(), new_full_path.c_str(), flags)))
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "RENAME failed from %s to %s, error: %d\n", old_full_path.c_str(), new_full_path.c_str(), rerrr);
skapi_send(client_socket, (char*)&response, sizeof(response));
return;
}
response.response.status = ResponseStatus::FU_SUCCESS;
print_with_time_and_thread(LOG_DEBUG, "RENAME successful from %s to %s\n", old_full_path.c_str(), new_full_path.c_str());
}
catch (const fs::filesystem_error &e)
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "RENAME failed from %s to %s, error: %s\n", old_full_path.c_str(), new_full_path.c_str(), e.what());
}
skapi_send(client_socket, (char*)&response, sizeof(response));
}
// 处理LINK请求
void handle_link(HSOCKET client_socket, const CommunicationPacket &request)
{
ResponsePkt response;
response.init(request);
std::string old_full_path = make_this_file_path(request);
std::string new_full_path = SHARE_DIR + "/" + request.link.newpath;
try
{
// 创建硬链接
fs::create_hard_link(old_full_path, new_full_path);
response.response.status = ResponseStatus::FU_SUCCESS;
print_with_time_and_thread(LOG_DEBUG, "LINK successful from %s to %s\n", old_full_path.c_str(), new_full_path.c_str());
}
catch (const fs::filesystem_error &e)
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "LINK failed from %s to %s, error: %s\n", old_full_path.c_str(), new_full_path.c_str(), e.what());
}
skapi_send(client_socket, (char*)&response, sizeof(response));
}
// 处理SYMLINK请求
void handle_symlink(HSOCKET client_socket, const CommunicationPacket &request)
{
ResponsePkt response;
response.init(request);
std::string target_path = request.symlink.target;
std::string link_full_path = make_this_file_path(request);
try
{
// 创建符号链接
fs::create_symlink(target_path, link_full_path);
response.response.status = ResponseStatus::FU_SUCCESS;
print_with_time_and_thread(LOG_DEBUG, "SYMLINK successful from %s to %s\n", target_path.c_str(), link_full_path.c_str());
}
catch (const fs::filesystem_error &e)
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "SYMLINK failed from %s to %s, error: %s\n", target_path.c_str(), link_full_path.c_str(), e.what());
}
skapi_send(client_socket, (char*)&response, sizeof(response));
}
// 处理READLINK请求
void handle_readlink(HSOCKET client_socket, const CommunicationPacket &request)
{
std::string full_path = make_this_file_path(request);
try
{
// 读取符号链接
#ifdef _WIN32
std::string target0 = GetSymbollnkTarget(full_path);
#else//!_WIN32
fs::path ftar = fs::read_symlink(full_path);
std::string target0 = ftar.string();
#endif // _WIN32
//全路径转为相对路径
std::string target = get_realitive_path(SHARE_DIR, target0);
size_t aln = sizeof(ResponsePkt) + target.length() + 1;
ResponsePkt *pTr = (ResponsePkt*)malloc(aln);
if (pTr)
{
pTr->init(request);
pTr->set_variable_data(target.c_str(), (uint32_t)(target.size() + 1), (uint32_t)(aln));
pTr->response.status = ResponseStatus::FU_SUCCESS;
print_with_time_and_thread(LOG_DEBUG, "READLINK successful for path: %s, target: %s\n", full_path.c_str(), target.c_str());
skapi_send(client_socket, (char*)pTr, (int)(pTr->get_total_need()));
free(pTr);
}
else
{
ResponsePkt rErr;
rErr.init(request);
rErr.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "READLINK failed for path: %s, error: %d\n", full_path.c_str(), errno);
skapi_send(client_socket, (char*)&rErr, sizeof(rErr));
}
}
catch (const fs::filesystem_error &e)
{
ResponsePkt rErr;
rErr.init(request);
rErr.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "READLINK failed for path: %s, error: %s\n", full_path.c_str(), e.what());
skapi_send(client_socket, (char*)&rErr, sizeof(rErr));
}
}
// 处理SYNC请求
void handle_sync(HSOCKET client_socket, const CommunicationPacket &request)
{
ResponsePkt response;
response.init(request);
FileHandleManager::FileHandleInfo *info = FileHandleManager::getInstance().getFileHandle(request.fd_id_in);
if (!info)
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "SYNC failed: invalid file handle\n");
skapi_send(client_socket, (char*)&response, sizeof(response));
return;
}
try
{
// 同步文件到磁盘
wlo_sync(info->fd);
response.response.status = ResponseStatus::FU_SUCCESS;
print_with_time_and_thread(LOG_DEBUG, "SYNC successful for file handle: %lld\n", (long long)request.fd_id_in);
}
catch (const std::exception &e)
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "SYNC failed: %s\n", e.what());
}
skapi_send(client_socket, (char*)&response, sizeof(response));
}
// 处理OPEN请求
void handle_open(HSOCKET client_socket, const CommunicationPacket &request)
{
ResponsePkt response;
response.init(request);
std::string full_path = make_this_file_path(request);
#ifdef _WIN32
unsigned int flags = linux_open_flag_to_win(request.open.open_flags);
unsigned int mode = linux_open_mode_to_win(request.open.open_mode);
#else//!_WIN32
unsigned int flags = (request.open.open_flags);
unsigned int mode = (request.open.open_mode);
#endif//_WIN32
try
{
// 打开文件
wlo_handle fd = wlo_open(full_path.c_str(), flags, mode);
if (fd < 0)
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "OPEN failed for path: %s, error: %d\n", full_path.c_str(), errno);
}
else
{
// 添加到文件句柄管理器
long long handle_id = FileHandleManager::getInstance().addFileHandle(fd);
response.response.status = ResponseStatus::FU_SUCCESS;
response.response.fd_id_out = handle_id;
print_with_time_and_thread(LOG_DEBUG, "OPEN successful for path: %s, handle: %lld\n", full_path.c_str(), handle_id);
}
}
catch (...)
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "OPEN failed for path: %s, error: %d\n", full_path.c_str(), errno);
}
skapi_send(client_socket, (char*)&response, sizeof(response));
}
// 处理CLOSE请求
void handle_close(HSOCKET client_socket, const CommunicationPacket &request)
{
ResponsePkt response;
response.init(request);
uint64_t handle_id = request.fd_id_in;
try
{
// 从文件句柄管理器中移除
FileHandleManager::getInstance().removeFileHandle(handle_id);
response.response.status = ResponseStatus::FU_SUCCESS;
print_with_time_and_thread(LOG_DEBUG, "CLOSE successful for handle: %lld\n", handle_id);
}
catch (const std::exception &e)
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "CLOSE failed for handle: %lld, error: %s\n", handle_id, e.what());
}
skapi_send(client_socket, (char*)&response, sizeof(response));
}
void handle_utimens(HSOCKET client_socket, const CommunicationPacket &request)
{
ResponsePkt response;
response.init(request);
//utimensat
try
{
std::string full_path = make_this_file_path(request);
FileHandleManager::FileHandleInfo *info = FileHandleManager::getInstance().getFileHandle(request.fd_id_in);
int ret = -1;
if (info)
{
// 如果文件已打开,使用futimens通过文件描述符修改时间
ret = wlo_futimens(info->fd, request.utimens.times);
}
else
{
// 文件未打开,使用utimensat通过文件路径修改时间
// AT_FDCWD表示相对于当前工作目录,AT_SYMLINK_NOFOLLOW表示不跟随符号链接
ret = utimensat(/*AT_FDCWD*/0, full_path.c_str(), request.utimens.times, /*AT_SYMLINK_NOFOLLOW*/ 0);
}
if (ret == 0)
{
response.response.status = ResponseStatus::FU_SUCCESS;
print_with_time_and_thread(LOG_DEBUG, "UTIME successful for handle: %lld\n", request.fd_id_in);
}
else
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "UTIME failed for handle: %lld, error: %d\n", request.fd_id_in, errno);
}
}
catch (const std::exception &e)
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "CLOSE failed for handle: %lld, error: %s\n", request.fd_id_in, e.what());
}
skapi_send(client_socket, (char*)&response, sizeof(response));
}
// 处理READ请求(优化大文件读取)
void handle_read(HSOCKET client_socket, const CommunicationPacket &request)
{
ResponsePkt errorResponse;
errorResponse.init(request);
FileHandleManager::FileHandleInfo *info = FileHandleManager::getInstance().getFileHandle(request.fd_id_in);
if (!info)
{
errorResponse.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "READ failed: invalid file handle\n");
skapi_send(client_socket, (char*)&errorResponse, sizeof(errorResponse));
return;
}
// 使用64位偏移量处理大文件
long long offset = request.read.offset;
size_t read_size = request.read.size;
// 读取文件数据
size_t aln = sizeof(ResponsePkt) + read_size;
ResponsePkt *pTT = (ResponsePkt*)malloc(aln);
if (pTT)
{
pTT->init(request);
long long oldpos = wlo_seek64(info->fd, 0, SEEK_CUR);
wlo_seek64(info->fd, offset, SEEK_SET);
ssize_t bytes_read = (ssize_t)wlo_read(info->fd, pTT->variable_data, (unsigned int)read_size);
wlo_seek64(info->fd, oldpos, SEEK_SET);
if (bytes_read < 0)
{
free(pTT);
pTT = 0;
errorResponse.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "READ failed: failed to read file\n");
skapi_send(client_socket, (char*)&errorResponse, sizeof(errorResponse));
return;
}
pTT->response.status = ResponseStatus::FU_SUCCESS;
pTT->response.bytes_transferred = bytes_read;
pTT->variable_data_size = (uint32_t)bytes_read;
unsigned long long hs = HashData(pTT->variable_data, bytes_read);
print_with_time_and_thread(LOG_DEBUG, "READ successful: read %zd bytes at offset %lld, hash=0x%016llX\n", bytes_read, (long long)offset, hs);
skapi_send_multi(client_socket, (char*)pTT, (int)(pTT->get_total_need()));
free(pTT);
pTT = 0;
}
else
{
errorResponse.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "READ failed: failed to read file\n");
skapi_send(client_socket, (char*)&errorResponse, sizeof(errorResponse));
}
}
// 处理WRITE请求(优化大文件写入)
void handle_write(HSOCKET client_socket, const CommunicationPacket &request, ssize_t bytes_read)
{
ResponsePkt response;
response.init(request);
//需要读取完整了
const CommunicationPacket* pTotalReq = &request;
bool bNeedFree = false;
if (request.get_total_need() > (size_t)bytes_read)
{
pTotalReq = (CommunicationPacket*)malloc(request.get_total_need());
if (!pTotalReq)
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "WRITE failed: alloc\n");
skapi_send(client_socket, (char*)&response, sizeof(response));
return;
}
bNeedFree = true;
memcpy((void*)pTotalReq, &request, bytes_read);
size_t remain = request.get_total_need() - bytes_read;
int readrm = skapi_recv_multi_bytes_known(client_socket, ((char*)pTotalReq) + bytes_read, (int)remain);
if (readrm != (int)remain)
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "WRITE failed: read remain\n");
skapi_send(client_socket, (char*)&response, sizeof(response));
if (bNeedFree && pTotalReq)
free((void*)pTotalReq);
return;
}
}
FileHandleManager::FileHandleInfo *info = FileHandleManager::getInstance().getFileHandle(request.fd_id_in);
if (!info)
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "WRITE failed: invalid file handle\n");
skapi_send(client_socket, (char*)&response, sizeof(response));
if (bNeedFree && pTotalReq)
free((void*)pTotalReq);
return;
}
// 使用64位偏移量处理大文件
long long offset = request.write.offset;
size_t write_size = request.write.size;
// 写入文件数据
long long oldpos = wlo_seek64(info->fd, 0, SEEK_CUR);
wlo_seek64(info->fd, offset, SEEK_SET);
ssize_t bytes_written = wlo_write(info->fd, pTotalReq->variable_data, (unsigned int)write_size);
unsigned long long hs = HashData(pTotalReq->variable_data, write_size);
wlo_seek64(info->fd, oldpos, SEEK_SET);//一般采用pwrite 不影响位置
if (bNeedFree && pTotalReq) {
free((void*)pTotalReq);
pTotalReq = 0;
}
if (bytes_written < 0)
{
response.response.status = ResponseStatus::FU_ERROR;
print_with_time_and_thread(LOG_ERROR, "WRITE failed: failed to write file\n");
skapi_send(client_socket, (char*)&response, sizeof(response));
return;
}
response.response.status = ResponseStatus::FU_SUCCESS;
response.response.bytes_transferred = bytes_written;
print_with_time_and_thread(LOG_DEBUG, "WRITE successful: wrote %zd bytes at offset %lld, hash=0x%016llX\n",
bytes_written, (long long)offset, hs);
skapi_send(client_socket, (char*)&response, sizeof(response));
}
struct ClientCtx
{
HSOCKET sk = BAD_SOCKET;
};
// 处理客户端请求
void* handle_client(void *arg)
{
ClientCtx *pCTX = (ClientCtx*)arg; // 使用C风格指针强制转换
ClientCtx CTX = *pCTX;
delete pCTX; // 使用C风格指针强制转换
pCTX = 0;
arg = 0;
try
{
// 首先进行认证
CommunicationPacket auth_request;
ssize_t bytes_read = skapi_recv(CTX.sk, (char*)&auth_request, sizeof(auth_request));
if (bytes_read <= 0)
{
print_with_time_and_thread(LOG_ERROR, "Failed to receive authentication request\n");
skapi_closesocket(CTX.sk);
return nullptr;
}
if (auth_request.command != CommandType::AUTHENTICATE)
{
print_with_time_and_thread(LOG_ERROR, "First command must be authentication\n");
skapi_closesocket(CTX.sk);
return nullptr;
}
if (!handle_authenticate(CTX.sk, auth_request))
{
print_with_time_and_thread(LOG_ERROR, "Authentication failed\n");
skapi_closesocket(CTX.sk);
return nullptr;
}
print_with_time_and_thread(LOG_DEBUG, "Client authenticated successfully\n");
// 认证成功后处理其他请求
while (true)
{
CommunicationPacket request;
bytes_read = skapi_recv(CTX.sk, (char*)&request, sizeof(request));
if (bytes_read <= 0)
{
break;
}
switch (request.command)
{
case CommandType::GET_ATTR:
handle_get_attr(CTX.sk, request);
break;
case CommandType::READ_DIR:
handle_read_dir(CTX.sk, request);
break;
case CommandType::CREATE:
handle_create(CTX.sk, request);
break;
case CommandType::MKDIR:
handle_mkdir(CTX.sk, request);
break;
case CommandType::RMDIR:
handle_rmdir(CTX.sk, request);
break;
case CommandType::UNLINK:
handle_unlink(CTX.sk, request);
break;
case CommandType::RENAME:
handle_rename(CTX.sk, request);
break;
case CommandType::LINK:
handle_link(CTX.sk, request);
break;
case CommandType::SYMLINK:
handle_symlink(CTX.sk, request);
break;
case CommandType::READLINK:
handle_readlink(CTX.sk, request);
break;
case CommandType::SYNC:
handle_sync(CTX.sk, request);
break;
case CommandType::READ:
handle_read(CTX.sk, request);
break;
case CommandType::WRITE:
handle_write(CTX.sk, request, bytes_read);
break;
case CommandType::OPEN:
handle_open(CTX.sk, request);
break;
case CommandType::CLOSE:
handle_close(CTX.sk, request);
break;
case CommandType::UTIMENS:
handle_utimens(CTX.sk, request);
break;
// 其他命令的处理保持不变...
default:
ResponsePkt response;
response.init(request);
response.response.status = ResponseStatus::FU_NOT_IMPLEMENTED;
print_with_time_and_thread(LOG_ERROR, "Command not implemented: %d\n", (int)request.command); // 使用C风格指针强制转换
skapi_send(CTX.sk, (char*)&response, sizeof(response));
break;
}
}
}
catch (const std::exception &e)
{
print_with_time_and_thread(LOG_ERROR, "Client error: %s\n", e.what());
}
skapi_closesocket(CTX.sk);
print_with_time_and_thread(LOG_ERROR, "Client connection closed\n");
return nullptr;
}
HSOCKET g_server_socket = BAD_SOCKET;
int server_main()
{
skapi_global_init();
// 创建共享目录(如果不存在)
fs::create_directories(SHARE_DIR);
HSOCKET client_socket;
struct sockaddr_in server_addr, client_addr;
int client_len = sizeof(client_addr);
// 创建套接字
if ((g_server_socket = skapi_socket()) == BAD_SOCKET)
{
print_with_time_and_thread(LOG_ERROR, "Failed to create socket\n");
return 1;
}
// 配置服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(SERVER_IP.c_str()); // inet_addr("127.0.0.1");
server_addr.sin_port = htons((unsigned short)SERVER_PORT);
#ifndef _WIN32
// 设置SO_REUSEADDR选项 允许端口快速重用
int opt = 1;
setsockopt((int)(long long)g_server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
#endif // !_WIN32
// 绑定套接字
if (skapi_bind(g_server_socket, (struct sockaddr*) &server_addr, sizeof(server_addr)) != 0)
{
print_with_time_and_thread(LOG_ERROR, "Failed to bind socket\n");
return 1;
}
// 监听连接
if (skapi_listen(g_server_socket, SOMAXCONN) != 0)
{
print_with_time_and_thread(LOG_ERROR, "Failed to listen on socket\n");
return 1;
}
printf("listen ok %s:%d\n", SERVER_IP.c_str(), SERVER_PORT);
#ifdef _WIN32
string sCmd = ::GetCommandLineA();
if (strstr(sCmd.c_str(), "--silient"))
{
HWND hConsole = ::GetConsoleWindow();
if (hConsole)
{
ShowWindow(hConsole, SW_HIDE);
}
}
#endif // _WIN32
while (1)
{
if (BAD_SOCKET == (client_socket = skapi_accept(g_server_socket, (struct sockaddr*) &client_addr, &client_len)))
{
continue;
}
print_with_time_and_thread(LOG_WARN, "New connection from %s\n", inet_ntoa(client_addr.sin_addr));
// 创建新线程处理客户端请求
ClientCtx *client_socket_ptr = new (nothrow) ClientCtx;
if (client_socket_ptr)
{
client_socket_ptr->sk = client_socket;
pthread_t thread_id;
if (create_thread(&thread_id, handle_client, (void*)client_socket_ptr) != 0)
{ // 使用C风格指针强制转换
print_with_time_and_thread(LOG_ERROR, "Failed to create thread\n");
delete client_socket_ptr;
skapi_closesocket(client_socket);
continue;
}
// 分离线程,避免内存泄漏
pthread_detach(thread_id);
}
else
{
skapi_closesocket(client_socket);
continue;
}
}
skapi_closesocket(g_server_socket);
return 0;
}
客户端
#include "FuseClient.h"
#define FUSE_USE_VERSION 30
#include <fuse3/fuse.h>
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#ifdef _WIN32
#include <WinSock2.h>
#include <Windows.h>
#include <io.h>
#else//!_WIN32
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif // _WIN32
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <vector>
#include <map>
#include <mutex>
#include <sstream>
#include "FuseCommon.h"
#include "SocketApi.h"
#include "SockMgr.h"
#include "FS_Util.h"
#include "AutoLock.h"
ACDCriticalSection g_lockSerial;
uint64_t g_lstReqSerial = 1;
uint64_t get_new_req_serial()
{
CAutoLock lock(&g_lockSerial);
g_lstReqSerial++;
return g_lstReqSerial;
}
// 发送请求并接收响应
int send_request(CommunicationPacket &request, CmdResponse &response)
{
request.serial = get_new_req_serial();
HSOCKET sock = SocketManager::getInstance().getSocket();
if (sock == BAD_SOCKET)
{
return -ENOENT;
}
try
{
if (response.prepare())
{
// 发送请求
int nSend = skapi_send_multi(sock, (char*)&request, (int)request.get_total_need());
if (nSend != (int)request.get_total_need())
{
SocketManager::getInstance().resetSocket();
print_with_time_and_thread(LOG_ERROR, "Failed to send req\n");
return -ENOENT;
}
// 接收响应
ssize_t bytes_read = skapi_recv(sock, (char*)response.pPkt, sizeof(ResponsePkt));
if (bytes_read <= 0)
{
SocketManager::getInstance().resetSocket();
print_with_time_and_thread(LOG_ERROR, "Failed to receive response1\n");
return -ENOENT;
}
//要处理剩余数据
if (response.pPkt->variable_data_size > 0)
{
size_t total_need = response.pPkt->get_total_need();
if (total_need > (size_t)bytes_read)
{
if (response.extend_to(total_need))
{
size_t remain = total_need - bytes_read;
ssize_t bytes_read2 = skapi_recv_multi_bytes_known(sock, ((char*)(response.pPkt)) + bytes_read, (int)remain);
if (bytes_read2 <= 0)
{
SocketManager::getInstance().resetSocket();
print_with_time_and_thread(LOG_ERROR, "Failed to receive response3\n");
return -ENOENT;
}
}
else
{
SocketManager::getInstance().resetSocket();
print_with_time_and_thread(LOG_ERROR, "Failed to receive response2\n");
return -ENOENT;
}
}
}
if (response.pPkt->commandSrc != request.command || response.pPkt->serialSrc != request.serial)
{
SocketManager::getInstance().resetSocket();
print_with_time_and_thread(LOG_ERROR, "Request mismatch cmd=%d\n", request.command);
return -ENOENT; // 返回文件不存在错误
}
if (response.pPkt->response.status != ResponseStatus::FU_SUCCESS)
{
print_with_time_and_thread(LOG_ERROR, "Request failed status=%d, cmd=%d\n", response.pPkt->response.status, request.command);
return -ENOENT; // 返回文件不存在错误
}
return 0;
}
else
{
SocketManager::getInstance().resetSocket();
print_with_time_and_thread(LOG_ERROR, "Failed to receive response4\n");
return -ENOENT;
}
}
catch (...)
{
SocketManager::getInstance().resetSocket();
print_with_time_and_thread(LOG_ERROR, "Request failed with exception\n");
return -ENOENT;
}
}
// GET_ATTR操作
static int nfs_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
{
CommunicationPacket request;
request.init(CommandType::GET_ATTR);
strncpy(request.path, path, sizeof(request.path) - 1);
CmdResponse response;
int res = send_request(request, response);
if (res == 0)
{
memset(stbuf, 0, sizeof(struct stat));
equal_stat((*stbuf), response.pPkt->response.mustat);
print_with_time_and_thread(LOG_DEBUG, "GET_ATTR successful for path: %s, dir=%d, reg=%d, lnk=%d\n", path, S_ISDIR(stbuf->st_mode), S_ISREG(stbuf->st_mode), S_ISLNK(stbuf->st_mode));
}
else
{
print_with_time_and_thread(LOG_ERROR, "GET_ATTR failed for path: %s\n", path);
}
return res;
}
int nfs_utimens(const char *path, const struct timespec tv[2], struct fuse_file_info *fi)
{
CommunicationPacket request;
request.init(CommandType::UTIMENS);
strncpy(request.path, path, sizeof(request.path) - 1);
memcpy(request.utimens.times, tv, sizeof(request.utimens.times));
request.fd_id_in = fi->fh;
CmdResponse response;
int res = send_request(request, response);
if (res == 0)
{
print_with_time_and_thread(LOG_DEBUG, "UTIMENS successful for path: %s\n", path);
}
else
{
print_with_time_and_thread(LOG_ERROR, "UTIMENS failed for path: %s\n", path);
}
return res;
}
// READ_DIR操作
struct DIRFA
{
FsAttr fa;
string path;
};
static int nfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, _Xoff_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags)
{
CommunicationPacket request;
request.init(CommandType::READ_DIR);
strncpy(request.path, path, sizeof(request.path) - 1);
CmdResponse response;
int res = send_request(request, response);
if (res == 0)
{
// 解析目录内容
std::string dir_content(response.pPkt->variable_data, response.pPkt->variable_data_size);
std::stringstream ss(dir_content);
std::string entry;
vector<DIRFA> vetAll;
while (std::getline(ss, entry))
{
if (entry.length() == 0)
break;
DIRFA dd;
size_t fShu = entry.find('|');
if (fShu != string::npos)
{
string fastr = entry.substr(0, fShu);
dd.path = entry.substr(fShu + 1);
FAfromStr(dd.fa, fastr.c_str());
if (dd.path.length())
{
vetAll.push_back(dd);
}
}
}
int itemCnt = (int)vetAll.size();
int readPos = 0;
int next_index = 0;
if (offset > 0)
{
//跳过offset个
for (int i = 0; i < (int)offset; i++)
{
readPos++;
if (readPos >= itemCnt)
{
return 0;
}
}
next_index = (int)offset;
}
// 根据flags处理目录项
bool readdir_plus = (flags & FUSE_READDIR_PLUS) != 0;
DIRFA *de = 0;
while (readPos < itemCnt && 0 != (de = &vetAll[readPos])) // 此处可能进入getattr 需要返回的 可能需要把符号链接加上
{
next_index++;
readPos++;
if (strcmp(de->path.c_str(), ".") == 0 || strcmp(de->path.c_str(), "..") == 0)
{
if (filler(buf, de->path.c_str(), 0, next_index, (fuse_fill_dir_flags)0) != 0)
{
break;
}
else
{
printf("dir[%d]: %s\n", next_index, de->path.c_str());
}
continue;
}
if (readdir_plus)
{
struct stat st;
memset(&st, 0, sizeof(st));
fill_fa(st, de->fa);
if (filler(buf, de->path.c_str(), &st, next_index, FUSE_FILL_DIR_PLUS) != 0)
{
break;
}
else
{
print_with_time_and_thread(LOG_DEBUG, "dir[%d]: %s, dir=%d, reg=%d, lnk=%d\n", next_index, de->path.c_str(), S_ISDIR(st.st_mode), S_ISREG(st.st_mode), S_ISLNK(st.st_mode));
}
}
else
{
if (filler(buf, de->path.c_str(), 0, next_index, (fuse_fill_dir_flags)0) != 0)
{
break;
}
else
{
print_with_time_and_thread(LOG_DEBUG, "dir[%d]: %s\n", next_index, de->path.c_str());
}
}
}
print_with_time_and_thread(LOG_DEBUG, "READ_DIR successful for path: %s\n", path);
}
else
{
print_with_time_and_thread(LOG_ERROR, "READ_DIR failed for path: %s\n", path);
}
return res;
}
// CREATE操作
static int nfs_create(const char *path, mode_t mode, struct fuse_file_info *fi)
{
CommunicationPacket request;
request.init(CommandType::CREATE);
strncpy(request.path, path, sizeof(request.path) - 1);
request.create.create_mode = mode;
CmdResponse response;
int res = send_request(request, response);
if (res == 0)
{
fi->fh = response.pPkt->response.fd_id_out; // 使用C风格指针强制转换
print_with_time_and_thread(LOG_DEBUG, "CREATE successful for path: %s\n", path);
}
else
{
print_with_time_and_thread(LOG_ERROR, "CREATE failed for path: %s\n", path);
}
return res;
}
// MKDIR操作
static int nfs_mkdir(const char *path, mode_t mode)
{
CommunicationPacket request;
request.init(CommandType::MKDIR);
strncpy(request.path, path, sizeof(request.path) - 1);
request.mkdir.mkdir_mode = mode;
CmdResponse response;
int res = send_request(request, response);
if (res == 0)
{
print_with_time_and_thread(LOG_DEBUG, "MKDIR successful for path: %s\n", path);
}
else
{
print_with_time_and_thread(LOG_ERROR, "MKDIR failed for path: %s\n", path);
}
return res;
}
// RMDIR操作
static int nfs_rmdir(const char *path)
{
CommunicationPacket request;
request.init(CommandType::RMDIR);
strncpy(request.path, path, sizeof(request.path) - 1);
CmdResponse response;
int res = send_request(request, response);
if (res == 0)
{
print_with_time_and_thread(LOG_DEBUG, "RMDIR successful for path: %s\n", path);
}
else
{
print_with_time_and_thread(LOG_ERROR, "RMDIR failed for path: %s\n", path);
}
return res;
}
// UNLINK操作
static int nfs_unlink(const char *path)
{
CommunicationPacket request;
request.init(CommandType::UNLINK);
strncpy(request.path, path, sizeof(request.path) - 1);
CmdResponse response;
int res = send_request(request, response);
if (res == 0)
{
print_with_time_and_thread(LOG_DEBUG, "UNLINK successful for path: %s\n", path);
}
else
{
print_with_time_and_thread(LOG_ERROR, "UNLINK failed for path: %s\n", path);
}
return res;
}
// RENAME操作
static int nfs_rename(const char *oldpath, const char *newpath, unsigned int flags)
{
CommunicationPacket request;
request.init(CommandType::RENAME);
strncpy(request.path, oldpath, sizeof(request.path) - 1);
strncpy(request.rename.newpath, newpath, sizeof(request.rename.newpath) - 1);
request.rename.Flag_Rename = flags;
CmdResponse response;
int res = send_request(request, response);
if (res == 0)
{
print_with_time_and_thread(LOG_DEBUG, "RENAME successful from %s to %s\n", oldpath, newpath);
}
else
{
print_with_time_and_thread(LOG_ERROR, "RENAME failed from %s to %s\n", oldpath, newpath);
}
return res;
}
// LINK操作
static int nfs_link(const char *oldpath, const char *newpath)
{
CommunicationPacket request;
request.init(CommandType::LINK);
strncpy(request.path, oldpath, sizeof(request.path) - 1);
strncpy(request.link.newpath, newpath, sizeof(request.link.newpath) - 1);
CmdResponse response;
int res = send_request(request, response);
if (res == 0)
{
print_with_time_and_thread(LOG_DEBUG, "LINK successful from %s to %s\n", oldpath, newpath);
}
else
{
print_with_time_and_thread(LOG_ERROR, "LINK failed from %s to %s\n", oldpath, newpath);
}
return res;
}
// SYMLINK操作
static int nfs_symlink(const char *target, const char *linkpath)
{
CommunicationPacket request;
request.init(CommandType::SYMLINK);
strncpy(request.symlink.target, target, sizeof(request.symlink.target) - 1);
strncpy(request.path, linkpath, sizeof(request.path) - 1);
CmdResponse response;
int res = send_request(request, response);
if (res == 0)
{
print_with_time_and_thread(LOG_DEBUG, "SYMLINK successful from %s to %s\n", target, linkpath);
}
else
{
print_with_time_and_thread(LOG_ERROR, "SYMLINK failed from %s to %s\n", target, linkpath);
}
return res;
}
// READLINK操作
static int nfs_readlink(const char *path, char *buf, size_t bufsize)
{
CommunicationPacket request;
request.init(CommandType::READLINK);
strncpy(request.path, path, sizeof(request.path) - 1);
CmdResponse response;
int res = send_request(request, response);
if (res == 0)
{
size_t copy_size = min(bufsize - 1, (size_t)response.pPkt->variable_data_size);
memcpy(buf, response.pPkt->variable_data, copy_size);
buf[copy_size] = '\0';
print_with_time_and_thread(LOG_DEBUG, "READLINK successful for path: %s, target: %s\n", path, buf);
return 0;
}
else
{
print_with_time_and_thread(LOG_ERROR, "READLINK failed for path: %s\n", path);
}
return res;
}
// SYNC操作
static int nfs_fsync(const char *path, int datasync, struct fuse_file_info *fi)
{
CommunicationPacket request;
request.init(CommandType::SYNC);
request.fd_id_in = fi->fh; // 使用C风格指针强制转换
CmdResponse response;
int res = send_request(request, response);
if (res == 0)
{
print_with_time_and_thread(LOG_DEBUG, "SYNC successful for file handle: %lld\n", (long long)fi->fh);
}
else
{
print_with_time_and_thread(LOG_ERROR, "SYNC failed for file handle: %lld\n", (long long)fi->fh);
}
return res;
}
// READ操作(优化大文件读取)
static int nfs_read(const char *path, char *buf, size_t size, _Xoff_t offset, struct fuse_file_info *fi)
{
CommunicationPacket request;
request.init(CommandType::READ);
request.fd_id_in = fi->fh; // 使用C风格指针强制转换
request.read.size = (uint32_t)size;
request.read.offset = offset; // 使用64位偏移量,支持大文件
CmdResponse response;
int res = send_request(request, response);
if (res == 0)
{
memcpy(buf, response.pPkt->variable_data, response.pPkt->response.bytes_transferred);
unsigned long long hs = HashData(response.pPkt->variable_data, response.pPkt->response.bytes_transferred);
print_with_time_and_thread(LOG_DEBUG, "READ successful: read %zd bytes from path: %s at offset %lld, hash=0x%016llX\n",
response.pPkt->response.bytes_transferred, path, (long long)offset, hs); // 使用C风格指针强制转换
return (int)response.pPkt->response.bytes_transferred;
}
print_with_time_and_thread(LOG_ERROR, "READ failed for path: %s\n", path);
return res;
}
// WRITE操作(优化大文件写入)
static int nfs_write(const char *path, const char *buf, size_t size, _Xoff_t offset, struct fuse_file_info *fi)
{
size_t aln = sizeof(CommunicationPacket) + size;
CommunicationPacket *request = (CommunicationPacket*)malloc(aln);
if (request)
{
request->init(CommandType::WRITE);
request->fd_id_in = fi->fh; // 使用C风格指针强制转换
request->write.size = (uint32_t)size;
request->write.offset = offset; // 使用64位偏移量,支持大文件
request->set_variable_data(buf, (uint32_t)size, (uint32_t)aln);
CmdResponse response;
int res = send_request(*request, response);
free(request);
request = 0;
if (res == 0)
{
unsigned long long hash_data = HashData(buf, size);
print_with_time_and_thread(LOG_DEBUG, "WRITE successful: wrote %zd bytes to path: %s at offset %lld, hash=0x%016llX\n",
response.pPkt->response.bytes_transferred, path, (long long)offset, hash_data); // 使用C风格指针强制转换
return (int)response.pPkt->response.bytes_transferred;
}
print_with_time_and_thread(LOG_ERROR, "WRITE failed for path: %s\n", path);
return res;
}
return -ENOENT;
}
// OPEN操作
static int nfs_open(const char *path, struct fuse_file_info *fi)
{
CommunicationPacket request;
request.init(CommandType::OPEN);
strncpy(request.path, path, sizeof(request.path) - 1);
request.open.open_flags = fi->flags;
request.open.open_mode = 0644;
CmdResponse response;
int res = send_request(request, response);
if (res == 0)
{
fi->fh = response.pPkt->response.fd_id_out; // 使用C风格指针强制转换
}
return res;
}
// CLOSE操作
static int nfs_release(const char *path, struct fuse_file_info *fi)
{
CommunicationPacket request;
request.init(CommandType::CLOSE);
request.fd_id_in = fi->fh; // 使用C风格指针强制转换
CmdResponse response;
return send_request(request, response);
}
// FUSE操作结构体
static struct fuse_operations nfs_oper =
{ 0 };
void Init_Nfs_Opper(struct fuse_operations &oper)
{
//oper.init = nfs_init;
//oper.destroy = nfs_destroy;
oper.getattr = nfs_getattr;
oper.utimens = nfs_utimens;
oper.readdir = nfs_readdir;
oper.create = nfs_create;
oper.mkdir = nfs_mkdir;
oper.rmdir = nfs_rmdir;
oper.unlink = nfs_unlink;
oper.rename = nfs_rename;
oper.link = nfs_link;
oper.symlink = nfs_symlink;
oper.readlink = nfs_readlink;
oper.fsync = nfs_fsync;
oper.open = nfs_open;
oper.read = nfs_read; // 优化大文件读取
oper.write = nfs_write; // 优化大文件写入
oper.release = nfs_release;
}
int client_main(int argc, char *argv[])
{
skapi_global_init();
Init_Nfs_Opper(nfs_oper);
print_with_time_and_thread(LOG_WARN, "Network file system client starting...\n");
print_with_time_and_thread(LOG_WARN, "Connecting to server: %s:%d\n", SERVER_IP.c_str(), SERVER_PORT);
print_with_time_and_thread(LOG_ERROR, "Authentication password: %s\n", AUTH_PASSWORD.c_str());
return fuse_main(argc, argv, &nfs_oper, NULL);
}
main
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include <WinSock2.h>
#include <Windows.h>
#include <Shlwapi.h>
#include "AWConv.h"
#else//!win
#include <sys/mount.h>
#endif//_WIN32
#include <string>
#define FUSE_USE_VERSION 30
#include <fuse3/fuse.h>
using namespace std;
#include "FuseClient.h"
#include "FuseServer.h"
#include "SockMgr.h"
#include "Utils.h"
#include "Config.h"
void LoadCfg()
{
ConfigMap cfmp = ConfigParser::load(Utils::GetPathUnderFolderOfCurrentModule("myfuse.conf"));
if (cfmp["SERVER_IP"].length())
SERVER_IP = cfmp["SERVER_IP"];
if (cfmp["AUTH_PASSWORD"].length())
AUTH_PASSWORD = cfmp["AUTH_PASSWORD"];
if (cfmp["SHARE_DIR"].length())
SHARE_DIR = cfmp["SHARE_DIR"];
if (cfmp["SERVER_PORT"].length())
SERVER_PORT = atoi(cfmp["SERVER_PORT"].c_str());
if (SHARE_DIR.compare(".") == 0)
{
wchar_t szExe[MAX_PATH] = {};
::GetModuleFileNameW(0, szExe, MAX_PATH);
PathAppendW(szExe, L"..");
SHARE_DIR = (LPCSTR)MyCW2A(szExe, CP_UTF8);
}
}
//#define TEST_SERV
#ifndef _WIN32
/**
* @brief 卸载FUSE文件系统
* @param mountpoint 挂载点路径
* @param lazy 是否延迟卸载(对应 -z 选项)
* @return 0成功,-1失败
*
* 等价于: fusermount3 -z -u <mountpoint>
*/
int fuse_unmount(const char* mountpoint, int lazy) {
if (mountpoint == NULL) {
errno = EINVAL;
return -1;
}
int flags = 0;
// -z 选项:延迟卸载(lazy unmount)
if (lazy) {
flags |= MNT_DETACH;
}
// -u 选项:卸载操作
// umount2() 是 Linux 特有的系统调用
if (umount2(mountpoint, flags) != 0) {
return -1;
}
return 0;
}
#endif // !_WIN32
int main(int argc, char *argv[])
{
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
#endif//_WIN32
LoadCfg();
//<app> --server
if (argc >= 2 && argv[1] && strcmp(argv[1], "--server") == 0)
return server_main();
//test create server
#ifdef TEST_SERV
char exe[MAX_PATH] = {}; GetModuleFileNameA(0, exe, MAX_PATH);
ShellExecuteA(0, "open", (exe), " --server", 0, SW_SHOW);
#endif//!TEST_SERV
#ifndef _WIN32
string mount_point = argv[1];
int ur = fuse_unmount(mount_point.c_str(), 1);
printf("ur=%d,%d\n", ur, errno);
// 设置FUSE选项
const char *newargvs[10] =
{ argv[0], argv[1], "-f", "-s", "-oallow_other" };
struct fuse_args args = FUSE_ARGS_INIT(5, (char**) newargvs);
// 挂载FUSE文件系统
return client_main(args.argc, args.argv);
#else//_WIN32
return client_main(argc, argv);
#endif // _WIN32
}
Utils
#include "FS_Util.h"
#include "AWConv.h"
#include <string.h>
#include <vector>
#include <filesystem>
#include "CStringUtil.h"
#ifdef _WIN32
#include <Windows.h>
#include <strsafe.h>
#include <Shlwapi.h>
#include <io.h>
#else//_WIN32
#include <unistd.h>
#endif//_WIN32
#include "md5.h"
//必须使用fuse3 接口 .
#define FUSE_USE_VERSION 30
#include <fuse3/fuse.h>
#include "Utils.h"
#define PATH_MAX 4096
#ifdef _WIN32
namespace fs = std::experimental::filesystem::v1;
#else//!_WIN32
namespace fs = std::filesystem;
#endif // _WIN32
using namespace std;
#ifdef _WIN32
uint64_t get_file_index(const wchar_t* path)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
if (PathIsDirectory(path))
{
// 先尝试直接打开文件或目录
hFile = CreateFileW(
path,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL, // 关键:FILE_FLAG_BACKUP_SEMANTICS
NULL
);
}
else
{
hFile = CreateFileW(
path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
if (hFile == INVALID_HANDLE_VALUE) {
return 0;
}
BY_HANDLE_FILE_INFORMATION fileInfo;
if (!GetFileInformationByHandle(hFile, &fileInfo)) {
CloseHandle(hFile);
return 0;
}
CloseHandle(hFile);
// 拼接64位File Index
return ((uint64_t)fileInfo.nFileIndexHigh << 32) | fileInfo.nFileIndexLow;
}
static uint64_t get_inode_from_file_index(const wchar_t* path) {
// 获取File Index
uint64_t file_index = get_file_index(path);
// 组合设备号和File Index
return ((uint64_t)3 << 40) | (file_index & 0xFFFFFFFFFFULL);
}
#endif//_WIN32
bool FsUtil_GetAttr(const char *fn, FsAttr &atr, int& et, int& er)
{
try
{
memset(&atr, 0, sizeof(FsAttr));
#ifdef _WIN32
std::wstring wsPath = MyCA2W(fn, CP_UTF8);
//Utils::_FormatWindowsPathW(wsPath);
WIN32_FILE_ATTRIBUTE_DATA fileData = { 0 };
if (!GetFileAttributesExW(wsPath.c_str(), GetFileExInfoStandard, &fileData)) {
// 处理错误:文件不存在、权限不足等
er = GetLastError();
et = FSUTIL_GATTR_gaex;
//if (er == ERROR_FILE_NOT_FOUND)
//{
// wstring t = wsPath;
// Utils::_FormatWindowsPathW(t);
// if (PathFileExistsW(t.c_str()))
// {
// printf("!!! File Exist %s\n", fn);
// }
//}
return false;
}
// 转换文件时间(Windows FILETIME转time_t)
FILETIME ft = {};
LARGE_INTEGER li = {};
// 最后访问时间
ft = fileData.ftLastAccessTime;
li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;
atr.mst_atime = (li.QuadPart - 116444736000000000LL) / 10000000LL;
// 最后修改时间
ft = fileData.ftLastWriteTime;
li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;
atr.mst_mtime = (li.QuadPart - 116444736000000000LL) / 10000000LL;
// 最后状态改变时间(创建时间)
ft = fileData.ftCreationTime;
li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;
atr.mst_ctime = (li.QuadPart - 116444736000000000LL) / 10000000LL;
// 文件大小
atr.mst_size = (static_cast<uint64_t>(fileData.nFileSizeHigh) << 32) | fileData.nFileSizeLow;
// 文件类型和权限
if (fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
atr.mst_isdir = true;
}
else {
atr.mst_isdir = false;
}
//link
if (fileData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
{
atr.mst_islink = true;
}
atr.mst_ino = get_inode_from_file_index(wsPath.c_str());
#else //! _WIN32
struct stat statbuf;
if (stat(fn, &statbuf) != 0)
{
// 处理错误:文件不存在、权限不足等
return false;
}
// 直接复制stat结构体中的字段
//atr.mst_mode = static_cast<unsigned short>(statbuf.st_mode);
atr.mst_isdir = S_ISDIR(statbuf.st_mode);
atr.mst_islink = S_ISLNK(statbuf.st_mode);
atr.mst_size = static_cast<uint64_t>(statbuf.st_size);
atr.mst_atime = statbuf.st_atime;
atr.mst_mtime = statbuf.st_mtime;
atr.mst_ctime = statbuf.st_ctime;
atr.mst_ino = statbuf.st_ino;
#endif//_WIN32
return true; // 获取属性成功
}
catch (...)
{
#ifdef _WIN32
er = GetLastError();
#endif//_WIN32
et = FSUTIL_GATTR_except;
return false;
}
return false;
}
int my_unlink(const char * fn)
{
#ifdef _WIN32
std::wstring wsPath = MyCA2W(fn, CP_UTF8);
BOOL bRet = ::DeleteFileW(wsPath.c_str());
if (!bRet)
{
DWORD err = GetLastError();
if (err == ERROR_ACCESS_DENIED)
return EACCES;
return EIO;
}
return 0;
#else//!_WIN32
return unlink(fn);
#endif // _WIN32
}
string FA2str(FsAttr & fa)
{
string sr;
CStrUtilA::Format(sr, "%llu,%llu,%llu,%llu,%llu,%d,%d",
(unsigned long long)fa.mst_ino,
(unsigned long long)fa.mst_size,
(unsigned long long)fa.mst_atime,
(unsigned long long)fa.mst_mtime,
(unsigned long long)fa.mst_ctime,
(int)fa.mst_isdir,
(int)fa.mst_islink
);
return sr;
}
void FAfromStr(FsAttr & fa, const char * fas)
{
unsigned long long fa_mst_ino = 0;
unsigned long long fa_mst_size = 0;
unsigned long long fa_mst_atim = 0;
unsigned long long fa_mst_mtim = 0;
unsigned long long fa_mst_ctim = 0;
int mst_isdir = 0;
int mst_islink = 0;
sscanf(fas, "%llu,%llu,%llu,%llu,%llu,%d,%d",
&fa_mst_ino,
&fa_mst_size,
&fa_mst_atim,
&fa_mst_mtim,
&fa_mst_ctim,
&mst_isdir,
&mst_islink
);
fa.mst_ino = fa_mst_ino;
fa.mst_size = fa_mst_size;
fa.mst_atime = fa_mst_atim;
fa.mst_mtime = fa_mst_mtim;
fa.mst_ctime = fa_mst_ctim;
fa.mst_isdir = mst_isdir;
fa.mst_islink = mst_islink;
}
#ifdef _WIN32
extern int futimens_winfh(HANDLE hFile, const struct timespec times[2]);
/**
* Windows下实现与Linux utimensat相同功能的函数
* @param dirfd: 目录文件描述符(Windows下忽略)
* @param pathname: 文件路径
* @param times: 时间数组,times[0]为访问时间,times[1]为修改时间
* @param flags: 标志位(Windows下仅支持AT_SYMLINK_NOFOLLOW)
* @return: 0表示成功,负数表示错误码
*/
int utimensat(int dirfd, const char* pathname, const struct timespec times[2], int flags)
{
// Windows下忽略dirfd,直接使用绝对路径或相对路径
std::wstring wpath = MyCA2W(pathname, CP_UTF8);
// 确定打开方式
DWORD desired_access = FILE_WRITE_ATTRIBUTES;
DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
DWORD creation_disposition = OPEN_EXISTING;
DWORD flags_and_attributes = FILE_FLAG_BACKUP_SEMANTICS;
//// 如果需要修改符号链接本身的时间
//if (flags & AT_SYMLINK_NOFOLLOW) {
// flags_and_attributes |= FILE_FLAG_OPEN_REPARSE_POINT;
//}
HANDLE hFile = CreateFileW(wpath.c_str(), desired_access, share_mode, NULL, creation_disposition, flags_and_attributes, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
DWORD win_err = GetLastError();
// 转换Windows错误码到errno
switch (win_err) {
case ERROR_ACCESS_DENIED:
errno = EACCES;
break;
case ERROR_FILE_NOT_FOUND:
errno = ENOENT;
break;
case ERROR_PATH_NOT_FOUND:
errno = ENOENT;
break;
case ERROR_SHARING_VIOLATION:
errno = EBUSY;
break;
default:
errno = EINVAL;
break;
}
return -1;
}
// 使用futimens实现相同逻辑
int result = futimens_winfh(hFile, times);
CloseHandle(hFile);
return result;
}
//// 定义renameat2的标志常量(与Linux对齐)
//enum {
// RENAME_NOREPLACE = 1 << 0, // 禁止覆盖已存在的目标文件
// RENAME_EXCHANGE = 1 << 1 // 交换源和目标文件(仅当两者都存在时)
//};
/**
* @brief 模拟Linux renameat2函数的行为(忽略fd参数,使用路径)
* @param oldpath 源文件路径
* @param newpath 目标文件路径
* @param flags 控制标志(RENAME_NOREPLACE/RENAME_EXCHANGE)
* @return 0成功,-1失败并设置errno
*/
int my_renameat2(const char* oldpath, const char* newpath, unsigned int flags) {
if (oldpath == nullptr || newpath == nullptr) {
errno = EINVAL;
return -1;
}
// 转换为宽字符路径(Windows API要求)
std::wstring w_oldpath = MyCA2W(oldpath, CP_UTF8);
std::wstring w_newpath = MyCA2W(newpath, CP_UTF8);
// 处理RENAME_EXCHANGE标志(交换两个文件)
if (flags & RENAME_EXCHANGE) {
// 检查两个文件是否都存在
if (GetFileAttributesW(w_oldpath.c_str()) == INVALID_FILE_ATTRIBUTES ||
GetFileAttributesW(w_newpath.c_str()) == INVALID_FILE_ATTRIBUTES) {
errno = ENOENT;
return -1;
}
// 创建临时文件名
wchar_t temp_path[MAX_PATH] = {};
wchar_t tmpDir[MAX_PATH] = {};
StringCchCopyW(tmpDir, MAX_PATH, w_oldpath.c_str());
PathAppendW(tmpDir, L"..");
if (!GetTempFileNameW(tmpDir, L"rnxtmp", 0, temp_path)) {
errno = EIO;
return -1;
}
// 交换逻辑:old -> temp -> new -> old
if (!MoveFileExW(w_oldpath.c_str(), temp_path, MOVEFILE_REPLACE_EXISTING)) {
DeleteFileW(temp_path);
errno = EIO;
return -1;
}
if (!MoveFileExW(w_newpath.c_str(), w_oldpath.c_str(), MOVEFILE_REPLACE_EXISTING)) {
MoveFileExW(temp_path, w_oldpath.c_str(), MOVEFILE_REPLACE_EXISTING);
DeleteFileW(temp_path);
errno = EIO;
return -1;
}
if (!MoveFileExW(temp_path, w_newpath.c_str(), MOVEFILE_REPLACE_EXISTING)) {
MoveFileExW(w_oldpath.c_str(), w_newpath.c_str(), MOVEFILE_REPLACE_EXISTING);
MoveFileExW(temp_path, w_oldpath.c_str(), MOVEFILE_REPLACE_EXISTING);
DeleteFileW(temp_path);
errno = EIO;
return -1;
}
DeleteFileW(temp_path);
return 0;
}
// 处理RENAME_NOREPLACE标志(禁止覆盖)
if (flags & RENAME_NOREPLACE) {
// 检查目标文件是否存在
DWORD attr = GetFileAttributesW(w_newpath.c_str());
if (attr != INVALID_FILE_ATTRIBUTES) {
errno = EEXIST; // 目标已存在
return -1;
}
}
// 执行重命名/移动操作
DWORD move_flags = MOVEFILE_REPLACE_EXISTING; // 默认允许覆盖
if (!MoveFileExW(w_oldpath.c_str(), w_newpath.c_str(), move_flags)) {
switch (GetLastError()) {
case ERROR_FILE_NOT_FOUND:
errno = ENOENT;
break;
case ERROR_ACCESS_DENIED:
errno = EACCES;
break;
case ERROR_ALREADY_EXISTS:
errno = EEXIST;
break;
case ERROR_NOT_SAME_DEVICE:
errno = EXDEV; // 跨设备移动
break;
default:
errno = EIO;
break;
}
return -1;
}
return 0;
}
#else//_WIN32
int my_renameat2(const char *oldpath, const char *newpath, unsigned int flags)
{
if (oldpath == nullptr || newpath == nullptr)
{
errno = EINVAL;
return -1;
}
// 处理RENAME_EXCHANGE标志(交换两个文件)
if (flags & RENAME_EXCHANGE)
{
// 检查两个文件是否都存在
struct stat st_old, st_new;
if (stat(oldpath, &st_old) != 0 || stat(newpath, &st_new) != 0)
{
errno = ENOENT;
return -1;
}
// 生成临时文件名(在newpath同目录下)
char temp_path[PATH_MAX];
char dir_path[PATH_MAX - 16];
// 提取目录路径
const char *last_slash = strrchr(newpath, '/');
if (last_slash != nullptr)
{
size_t dir_len = last_slash - newpath;
if (dir_len >= PATH_MAX)
{
errno = ENAMETOOLONG;
return -1;
}
strncpy(dir_path, newpath, dir_len);
dir_path[dir_len] = '\0';
}
else
{
strcpy(dir_path, ".");
}
// 创建临时文件名
snprintf(temp_path, PATH_MAX, "%s/.rnxtmp", dir_path);
int temp_fd = mkstemp(temp_path);
if (temp_fd == -1)
{
return -1;
}
close(temp_fd);
// 交换逻辑:old -> temp, new -> old, temp -> new
if (rename(oldpath, temp_path) != 0)
{
int saved_errno = errno;
unlink(temp_path);
errno = saved_errno;
return -1;
}
if (rename(newpath, oldpath) != 0)
{
int saved_errno = errno;
rename(temp_path, oldpath); // 回滚
unlink(temp_path);
errno = saved_errno;
return -1;
}
if (rename(temp_path, newpath) != 0)
{
int saved_errno = errno;
rename(oldpath, newpath); // 回滚第二步
rename(temp_path, oldpath); // 回滚第一步
unlink(temp_path);
errno = saved_errno;
return -1;
}
unlink(temp_path); // 清理临时文件(如果还存在)
return 0;
}
// 处理RENAME_NOREPLACE标志(禁止覆盖)
if (flags & RENAME_NOREPLACE)
{
// 检查目标文件是否存在
struct stat st;
if (stat(newpath, &st) == 0)
{
errno = EEXIST; // 目标已存在
return -1;
}
// 如果stat失败但不是因为文件不存在,需要处理
if (errno != ENOENT)
{
return -1;
}
}
// 执行标准重命名操作
return rename(oldpath, newpath);
}
#endif//_WIN32
std::string get_realitive_path(const std::string baseP, const std::string fullP)
{
string strRoot = baseP;
Utils::_FormatWindowsPathA(strRoot);
string strFull = fullP;
Utils::_FormatWindowsPathA(strFull);
if (strFull.length() > strRoot.length())
{
size_t rightLen = strFull.length() - strRoot.length();
string strLeft = strFull.substr(0, strRoot.length());
string strRight = strFull.substr(strFull.length() - rightLen);
if (Utils::MyStricmp(strLeft.c_str(), strRoot.c_str()) == 0 && strRight[0] == '\\')
{
return strRight.substr(1);
}
}
throw std::runtime_error("cant open link");
}
#ifdef _WIN32
unsigned int linux_open_flag_to_win(unsigned int lflg)
{
unsigned int ret = 0;
unsigned int acc = lflg & LNX_O_ACCMODE;
unsigned int accR = 0;
if (acc == LNX_O_RDONLY)
accR = O_RDONLY;
else if (acc == LNX_O_WRONLY)
accR = O_WRONLY;
else if (acc == LNX_O_RDWR)
accR = O_RDWR;
if (lflg & LNX_O_CREAT)
{
ret |= _O_CREAT;
if (lflg & LNX_O_EXCL)
{
// Windows不支持原子性的O_EXCL,需在调用后额外检查
ret |= _O_EXCL;
}
}
if (lflg & LNX_O_TRUNC)
ret |= O_TRUNC;
if (lflg & LNX_O_APPEND)
ret |= O_APPEND;
return ret | accR;
}
unsigned int linux_open_mode_to_win(unsigned int lmod)
{
unsigned int ret = 0;
if (lmod & (LNX_S_IRUSR | LNX_S_IRGRP | LNX_S_IROTH))
ret |= _S_IREAD;
if (lmod & (LNX_S_IWUSR | LNX_S_IWGRP | LNX_S_IWOTH))
ret |= _S_IWRITE;
//if (lmod & (LNX_S_IXUSR | LNX_S_IXGRP | LNX_S_IXOTH))
// ret |= _S_IEXEC;
return ret;
}
#endif//_WIN32
unsigned long long HashData(const char * buf, size_t size)
{
#ifdef CHECK_RW
unsigned long long byMd5[2] = { 0 };
MD5_CTX ctx;
QHMD5Init(&ctx);
QHMD5Update(&ctx, (unsigned char*)buf, (unsigned int)size);
QHMD5Final((unsigned char*)byMd5, &ctx);
return byMd5[0];
#else//CHECK_RW
return 0;
#endif // CHECK_RW
}
low-io
#ifdef _WIN32
#include <Windows.h>
#include "win_lowio.h"
#include <string>
#include "AWConv.h"
#include <new>
#include <filesystem>
#include "FS_Util.h"
#ifdef _WIN32
namespace fs = std::experimental::filesystem::v1;
#else//!_WIN32
namespace fs = std::filesystem;
#endif // _WIN32
struct WLO_HANDLEST
{
HANDLE hFile = INVALID_HANDLE_VALUE;
};
#define WLOHANDLEST(fh) (*((WLO_HANDLEST*)(fh)))
wlo_handle _alloc_osfhnd()
{
WLO_HANDLEST* p = new(std::nothrow) WLO_HANDLEST;
if (!p)
return -1;
return (wlo_handle)p;
}
void _free_osfhnd(wlo_handle h)
{
if (h && h != -1)
{
delete (WLO_HANDLEST*)(h);
}
}
DWORD const GENERIC_READ_WRITE = (GENERIC_READ | GENERIC_WRITE);
#define _VALIDATE_RETURN_ERRCODE(expr, err) \
do { \
if (!(expr)) { \
return err; \
} \
} while (0)
#define _VALIDATE_RETURN(expr, err, ret) \
do { \
if (!(expr)) { \
_set_errno(err); \
return ret; \
} \
} while (0)
struct file_options
{
// These are the flags that are used for the osflag of the CRT file
// object that is created.
char crt_flags;
// These are the flags that are eventually passed to CreateFile to tell
// the Operating System how to create the file:
DWORD access;
DWORD create;
DWORD share;
DWORD attributes;
DWORD flags;
};
#define _O_RDONLY 0x0000 // open for reading only
#define _O_WRONLY 0x0001 // open for writing only
#define _O_RDWR 0x0002 // open for reading and writing
#define _O_APPEND 0x0008 // writes done at eof
#define _O_CREAT 0x0100 // create and open file
#define _O_TRUNC 0x0200 // open and truncate
#define _O_EXCL 0x0400 // open only if file doesn't already exist
#define _O_TEXT 0x4000 // file mode is text (translated)
#define _O_BINARY 0x8000 // file mode is binary (untranslated)
#define _O_WTEXT 0x10000 // file mode is UTF16 (translated)
#define _O_U16TEXT 0x20000 // file mode is UTF16 no BOM (translated)
#define _O_U8TEXT 0x40000 // file mode is UTF8 no BOM (translated)
#define _O_NOINHERIT 0x0080 // child process doesn't inherit file
#define _O_TEMPORARY 0x0040 // temporary file bit (file is deleted when last handle is closed)
#define _O_SHORT_LIVED 0x1000 // temporary storage file, try not to flush
#define _O_OBTAIN_DIR 0x2000 // get information about a directory
#define _O_SEQUENTIAL 0x0020 // file access is primarily sequential
#define _O_RANDOM 0x0010 // file access is primarily random
#define _SH_DENYRW 0x10 // deny read/write mode
#define _SH_DENYWR 0x20 // deny write mode
#define _SH_DENYRD 0x30 // deny read mode
#define _SH_DENYNO 0x40 // deny none mode
#define _SH_SECURE 0x80 // secure mode
#define _S_IFMT 0xF000 // File type mask
#define _S_IFDIR 0x4000 // Directory
#define _S_IFCHR 0x2000 // Character special
#define _S_IFIFO 0x1000 // Pipe
#define _S_IFREG 0x8000 // Regular
#define _S_IREAD 0x0100 // Read permission, owner
#define _S_IWRITE 0x0080 // Write permission, owner
#define _S_IEXEC 0x0040 // Execute/search permission, owner
static DWORD decode_open_create_flags(int const oflag) throw()
{
switch (oflag & (_O_CREAT | _O_EXCL | _O_TRUNC))
{
case 0:
case _O_EXCL: // ignore EXCL w/o CREAT
return OPEN_EXISTING;
case _O_CREAT:
return OPEN_ALWAYS;
case _O_CREAT | _O_EXCL:
case _O_CREAT | _O_TRUNC | _O_EXCL:
return CREATE_NEW;
case _O_TRUNC:
case _O_TRUNC | _O_EXCL: // ignore EXCL w/o CREAT
return TRUNCATE_EXISTING;
case _O_CREAT | _O_TRUNC:
return CREATE_ALWAYS;
}
// This is unreachable, but the compiler can't tell.
_VALIDATE_RETURN(("Invalid open flag", 0), EINVAL, static_cast<DWORD>(-1));
return 0;
}
static DWORD decode_access_flags(int const oflag) throw()
{
switch (oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR))
{
case _O_RDONLY:
return GENERIC_READ;
case _O_WRONLY:
// If the file is being opened in append mode, we give read access as
// well because in append (a, not a+) mode, we need to read the BOM to
// determine the encoding (ANSI, UTF-8, or UTF-16).
if ((oflag & _O_APPEND) && (oflag & (_O_WTEXT | _O_U16TEXT | _O_U8TEXT)) != 0)
return GENERIC_READ | GENERIC_WRITE;
return GENERIC_WRITE;
case _O_RDWR:
return GENERIC_READ | GENERIC_WRITE;
}
// This is unreachable, but the compiler can't tell.
_VALIDATE_RETURN(("Invalid open flag", 0), EINVAL, static_cast<DWORD>(-1));
return 0;
}
static DWORD decode_sharing_flags(int const shflag, int const access) throw()
{
switch (shflag)
{
case _SH_DENYRW:
return 0;
case _SH_DENYWR:
return FILE_SHARE_READ;
case _SH_DENYRD:
return FILE_SHARE_WRITE;
case _SH_DENYNO:
return FILE_SHARE_READ | FILE_SHARE_WRITE;
case _SH_SECURE:
if (access == GENERIC_READ)
return FILE_SHARE_READ;
else
return 0;
}
_VALIDATE_RETURN(("Invalid sharing flag", 0), EINVAL, static_cast<DWORD>(-1));
return 0;
}
static file_options decode_options(int const oflag, int const shflag, int const pmode) throw()
{
file_options result;
result.crt_flags = 0;
result.access = decode_access_flags(oflag);
result.create = decode_open_create_flags(oflag);
result.share = decode_sharing_flags(shflag, result.access);
result.attributes = FILE_ATTRIBUTE_NORMAL;
result.flags = 0;
if (oflag & _O_TEMPORARY)
{
result.flags |= FILE_FLAG_DELETE_ON_CLOSE;
result.access |= DELETE;
result.share |= FILE_SHARE_DELETE;
}
if (oflag & _O_SHORT_LIVED)
{
result.attributes |= FILE_ATTRIBUTE_TEMPORARY;
}
if (oflag & _O_OBTAIN_DIR)
{
result.flags |= FILE_FLAG_BACKUP_SEMANTICS;
}
if (oflag & _O_SEQUENTIAL)
{
result.flags |= FILE_FLAG_SEQUENTIAL_SCAN;
}
else if (oflag & _O_RANDOM)
{
result.flags |= FILE_FLAG_RANDOM_ACCESS;
}
return result;
}
static HANDLE __cdecl create_file(
PCWSTR const path,
SECURITY_ATTRIBUTES* const security_attributes,
file_options const options
) throw()
{
return CreateFileW(
path,
options.access,
options.share,
security_attributes,
options.create,
options.flags | options.attributes,
nullptr);
}
static errno_t __cdecl _wsopen_nolock(
int* const punlock_flag,
wlo_handle* const pfh,
wchar_t const* const path,
int const oflag,
int const shflag,
int const pmode,
int const secure
)
{
UNREFERENCED_PARAMETER(secure);
// First, do the initial parse of the options. The only thing that can fail
// here is the parsing of the share options, in which case -1 is returned
// and errno is set.
file_options options = decode_options(oflag, shflag, pmode);
if (options.share == static_cast<DWORD>(-1))
{
_doserrno = 0;
*pfh = -1;
return errno;
}
// Allocate the CRT file handle. Note that if a handle is allocated, it is
// locked when it is returned by the allocation function. It is our caller's
// responsibility to unlock the file handle (we do not unlock it before
// returning).
*pfh = _alloc_osfhnd();
if (*pfh == -1)
{
_doserrno = 0;
*pfh = -1;
errno = EMFILE;
return errno;
}
// Beyond this point, do not change *pfh, even if an error occurs. Our
// caller requires the handle in order to release its lock.
*punlock_flag = 1;
SECURITY_ATTRIBUTES security_attributes;
security_attributes.nLength = sizeof(security_attributes);
security_attributes.lpSecurityDescriptor = nullptr;
security_attributes.bInheritHandle = (oflag & _O_NOINHERIT) == 0;
// Try to open or create the file:
HANDLE os_handle = create_file(path, &security_attributes, options);
if (os_handle == INVALID_HANDLE_VALUE)
{
if ((options.access & GENERIC_READ_WRITE) == GENERIC_READ_WRITE && (oflag & _O_WRONLY))
{
// The call may have failed because we may be trying to open
// something for reading that does not allow reading (e.g. a pipe or
// a device). So, we try again with just GENERIC_WRITE. If this
// succeeds, we will have to assume the default encoding because we
// will have no way to read the BOM.
options.access &= ~GENERIC_READ;
os_handle = create_file(path, &security_attributes, options);
}
}
if (os_handle == INVALID_HANDLE_VALUE)
{
_free_osfhnd(*pfh);
*pfh = -1;
return EACCES;
}
WLOHANDLEST(*pfh).hFile = os_handle;
return 0; // Success!
}
static errno_t __cdecl _sopen_nolock(
int* const punlock_flag,
wlo_handle* const pfh,
char const* const path,
int const oflag,
int const shflag,
int const pmode,
int const secure
)
{
std::wstring wide_path;
try
{
wide_path = MyCA2W(path, CP_UTF8);
}
catch (...)
{
return -1;
}
return _wsopen_nolock(punlock_flag, pfh, wide_path.c_str(), oflag, shflag, pmode, secure);
}
static int __cdecl common_sopen_dispatch(
char const* const path,
int const oflag,
int const shflag,
int const pmode,
wlo_handle* const pfh,
int const secure
) throw()
{
_VALIDATE_RETURN_ERRCODE(pfh != nullptr, EINVAL);
*pfh = -1;
_VALIDATE_RETURN_ERRCODE(path != nullptr, EINVAL);
if (secure)
{
_VALIDATE_RETURN_ERRCODE((pmode & (~(_S_IREAD | _S_IWRITE))) == 0, EINVAL);
}
int unlock_flag = 0;
errno_t error_code = 0;
error_code = _sopen_nolock(&unlock_flag, pfh, path, oflag, shflag, pmode, secure);
if (error_code != 0)
{
*pfh = -1;
}
return error_code;
}
static int __cdecl _sopen_dispatch(
char const* const path,
int const oflag,
int const shflag,
int const pmode,
wlo_handle* const pfh,
int const secure
)
{
return common_sopen_dispatch(path, oflag, shflag, pmode, pfh, secure);
}
static int __cdecl _sopen_s(
wlo_handle* const pfh,
char const* const path,
int const oflag,
int const shflag,
int const pmode
)
{
// The last argument is 1 so that pmode is validated in open_s:
return _sopen_dispatch(path, oflag, shflag, pmode, pfh, 1/*TRUE*/);
}
// Creates a new file.
//
// If the file does not exist, this function creates a new file with the given
// permission setting and opens it for writing. If the file already exists and
// its permission allows writing, this function truncates it to zero length and
// opens it for writing.
//
// The only XENIX mode bit supported is user write (S_IWRITE).
//
// On success, the handle for the newly created file is returned. On failure,
// -1 is returned and errno is set.
static wlo_handle common_creat(char const* const path, int const pmode) throw()
{
wlo_handle fh = -1;
errno_t e = _sopen_s(&fh, path, _O_CREAT + _O_TRUNC + _O_RDWR, _SH_DENYNO, pmode);
return e == 0 ? fh : -1;
}
wlo_handle wlo_create(char const* const path, int const pmode)
{
return common_creat(path, pmode);
}
void wlo_close(wlo_handle fh)
{
if (fh != -1 && fh)
{
if (WLOHANDLEST(fh).hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(WLOHANDLEST(fh).hFile);
WLOHANDLEST(fh).hFile = INVALID_HANDLE_VALUE;
}
_free_osfhnd(fh);
}
}
static wlo_handle __cdecl common_open(
char const* const path,
int const oflag,
int const pmode
) throw()
{
_VALIDATE_RETURN(path != nullptr, EINVAL, -1);
wlo_handle fh = -1;
int unlock_flag = 0;
errno_t error_code = 0;
error_code = _sopen_nolock(&unlock_flag, &fh, path, oflag, _SH_DENYNO, pmode, 0);
if (error_code != 0)
{
errno = error_code;
return -1;
}
return fh;
}
wlo_handle wlo_open(char const* const path, int const oflag, ...)
{
va_list arglist;
va_start(arglist, oflag);
int const pmode = va_arg(arglist, int);
va_end(arglist);
return common_open(path, oflag, pmode);
}
// 将文件指针设置到指定位置,返回新的偏移量
long long wlo_seek64(wlo_handle fh, long long offset, int _Origin)
{
// 验证句柄有效性
_VALIDATE_RETURN(fh != -1 && fh != 0, EBADF, static_cast<long long>(-1));
// 验证文件句柄有效性
if (WLOHANDLEST(fh).hFile == INVALID_HANDLE_VALUE)
{
errno = EBADF;
return static_cast<long long>(-1);
}
// 转换CRT的origin为Windows的DWORD类型
DWORD moveMethod;
switch (_Origin)
{
case SEEK_SET:
moveMethod = FILE_BEGIN;
break;
case SEEK_CUR:
moveMethod = FILE_CURRENT;
break;
case SEEK_END:
moveMethod = FILE_END;
break;
default:
_VALIDATE_RETURN(("Invalid seek origin", 0), EINVAL, static_cast<long long>(-1));
return static_cast<long long>(-1); // 编译器需要的返回值
}
// 使用SetFilePointerEx进行64位文件定位
LARGE_INTEGER liDistanceToMove;
LARGE_INTEGER liNewFilePointer;
liDistanceToMove.QuadPart = offset;
if (!SetFilePointerEx(WLOHANDLEST(fh).hFile, liDistanceToMove, &liNewFilePointer, moveMethod))
{
// 获取Windows错误码并转换为errno
DWORD win_err = GetLastError();
switch (win_err)
{
case ERROR_ACCESS_DENIED:
errno = EACCES;
break;
case ERROR_INVALID_PARAMETER:
errno = EINVAL;
break;
default:
errno = EIO;
break;
}
return static_cast<long long>(-1);
}
// 返回新的文件偏移量
return liNewFilePointer.QuadPart;
}
// 从文件中读取数据,返回实际读取的字节数
int wlo_read(wlo_handle fh, void* _DstBuf, unsigned int _MaxCharCount)
{
// 验证句柄有效性
_VALIDATE_RETURN(fh != -1 && fh != 0, EBADF, -1);
// 验证输出缓冲区指针
_VALIDATE_RETURN(_DstBuf != nullptr, EINVAL, -1);
// 验证文件句柄有效性
if (WLOHANDLEST(fh).hFile == INVALID_HANDLE_VALUE)
{
errno = EBADF;
return -1;
}
// 读取文件内容
DWORD bytesRead;
if (!ReadFile(WLOHANDLEST(fh).hFile, _DstBuf, static_cast<DWORD>(_MaxCharCount), &bytesRead, nullptr))
{
// 获取Windows错误码并转换为errno
DWORD win_err = GetLastError();
switch (win_err)
{
case ERROR_ACCESS_DENIED:
errno = EACCES;
break;
case ERROR_HANDLE_EOF:
return 0; // 文件已结束
default:
errno = EIO;
break;
}
return -1;
}
// 返回实际读取的字节数
return static_cast<int>(bytesRead);
}
// 向文件中写入数据,返回实际写入的字节数
int wlo_write(wlo_handle fh, void const* _Buf, unsigned int _MaxCharCount)
{
// 验证句柄有效性
_VALIDATE_RETURN(fh != -1 && fh != 0, EBADF, -1);
// 验证输入缓冲区指针
_VALIDATE_RETURN(_Buf != nullptr, EINVAL, -1);
// 验证文件句柄有效性
if (WLOHANDLEST(fh).hFile == INVALID_HANDLE_VALUE)
{
errno = EBADF;
return -1;
}
// 写入文件内容
DWORD bytesWritten;
if (!WriteFile(WLOHANDLEST(fh).hFile, _Buf, static_cast<DWORD>(_MaxCharCount), &bytesWritten, nullptr))
{
// 获取Windows错误码并转换为errno
DWORD win_err = GetLastError();
switch (win_err)
{
case ERROR_ACCESS_DENIED:
errno = EACCES;
break;
case ERROR_DISK_FULL:
errno = ENOSPC;
break;
default:
errno = EIO;
break;
}
return -1;
}
// 返回实际写入的字节数
return static_cast<int>(bytesWritten);
}
int wlo_sync(wlo_handle fh)
{
// 验证句柄有效性
_VALIDATE_RETURN(fh != -1 && fh != 0, EBADF, -1);
// 验证文件句柄有效性
if (WLOHANDLEST(fh).hFile == INVALID_HANDLE_VALUE)
{
errno = EBADF;
return -1;
}
if (FlushFileBuffers(WLOHANDLEST(fh).hFile))
{
return 0;
}
// 获取Windows错误码并转换为errno
DWORD win_err = GetLastError();
switch (win_err)
{
case ERROR_ACCESS_DENIED:
errno = EACCES;
break;
case ERROR_WRITE_PROTECT:
errno = EROFS;
break;
case ERROR_DISK_FULL:
errno = ENOSPC;
break;
case ERROR_INVALID_HANDLE:
errno = EBADF;
break;
default:
errno = EIO;
break;
}
return -1;
}
#ifdef _WIN32
// 定义与Linux兼容的常量
#define UTIME_NOW 1000000001
#define UTIME_OMIT 1000000002
int futimens_winfh(HANDLE hFile, const struct timespec times[2])
{
if (hFile == INVALID_HANDLE_VALUE) {
errno = EBADF;
return -1;
}
FILETIME ft_access, ft_modify;
SYSTEMTIME st;
// 获取当前时间(如果需要)
GetSystemTime(&st);
SystemTimeToFileTime(&st, &ft_access);
ft_modify = ft_access;
// 处理访问时间
if (times != NULL) {
if (times[0].tv_nsec == UTIME_NOW) {
// 设置为当前时间(已经获取)
}
else if (times[0].tv_nsec != UTIME_OMIT) {
// 转换timespec到FILETIME
ULARGE_INTEGER ul;
ul.QuadPart = (unsigned long long)times[0].tv_sec * 10000000ULL + 116444736000000000ULL;
ul.QuadPart += times[0].tv_nsec / 100; // 纳秒转换为100纳秒单位
ft_access.dwLowDateTime = ul.LowPart;
ft_access.dwHighDateTime = ul.HighPart;
}
}
// 处理修改时间
if (times != NULL) {
if (times[1].tv_nsec == UTIME_NOW) {
// 设置为当前时间(已经获取)
}
else if (times[1].tv_nsec != UTIME_OMIT) {
// 转换timespec到FILETIME
ULARGE_INTEGER ul;
ul.QuadPart = (unsigned long long)times[1].tv_sec * 10000000ULL + 116444736000000000ULL;
ul.QuadPart += times[1].tv_nsec / 100; // 纳秒转换为100纳秒单位
ft_modify.dwLowDateTime = ul.LowPart;
ft_modify.dwHighDateTime = ul.HighPart;
}
}
// 设置文件时间
BOOL result = SetFileTime(hFile, NULL, &ft_access, &ft_modify);
if (!result) {
DWORD win_err = GetLastError();
// 转换Windows错误码到errno
switch (win_err) {
case ERROR_ACCESS_DENIED:
errno = EACCES;
break;
case ERROR_FILE_NOT_FOUND:
errno = ENOENT;
break;
case ERROR_PATH_NOT_FOUND:
errno = ENOENT;
break;
case ERROR_INVALID_HANDLE:
errno = EBADF;
break;
default:
errno = EINVAL;
break;
}
return -1;
}
return 0;
}
// 定义符号链接重解析数据的相关常量
#define SYMBOLIC_LINK_REPARSE_TAG IO_REPARSE_TAG_SYMLINK
typedef struct _REPARSE_DATA_BUFFER {
DWORD ReparseTag;
WORD ReparseDataLength;
WORD Reserved;
union {
struct {
WORD SubstituteNameOffset;
WORD SubstituteNameLength;
WORD PrintNameOffset;
WORD PrintNameLength;
DWORD Flags;
WCHAR PathBuffer[1];
} SymbolicLinkReparseBuffer;
// 其他类型的重解析数据结构(此处省略)
} u;
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
// 符号链接标志定义
#define SYMLINK_FLAG_RELATIVE 0x00000001
/**
* @brief 获取Windows符号链接的目标路径
* @param full_path 符号链接的完整路径(UTF-8编码)
* @return 目标路径(UTF-8编码),如果是相对路径会转换为绝对路径
* @throws std::runtime_error 当无法获取目标路径时抛出异常,包含错误信息
*/
std::string GetSymbollnkTarget(const std::string& full_path)
{
// 将UTF-8路径转换为宽字符
std::wstring wpath = MyCA2W(full_path.c_str(), CP_UTF8);
// 打开符号链接文件,需要特定标志
HANDLE hFile = CreateFileW(
wpath.c_str(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
nullptr
);
if (hFile == INVALID_HANDLE_VALUE) {
throw std::runtime_error("cant open link");
}
// 准备接收重解析数据的缓冲区
std::vector<char> buffer(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
DWORD bytes_returned = 0;
// 发送IO控制码获取重解析数据
BOOL success = DeviceIoControl(
hFile,
FSCTL_GET_REPARSE_POINT,
nullptr,
0,
buffer.data(),
(DWORD)buffer.size(),
&bytes_returned,
nullptr
);
CloseHandle(hFile);
if (!success) {
DWORD error = GetLastError();
std::string error_msg = "DeviceIoControl failed: " + std::to_string(error);
if (error == ERROR_NOT_A_REPARSE_POINT) {
error_msg += " (Not a symbolic link)";
}
throw std::runtime_error(error_msg);
}
// 解析重解析数据
PREPARSE_DATA_BUFFER reparse_data = reinterpret_cast<PREPARSE_DATA_BUFFER>(buffer.data());
if (reparse_data->ReparseTag != SYMBOLIC_LINK_REPARSE_TAG) {
throw std::runtime_error("Not a symbolic link reparse point");
}
// 提取目标路径
const WCHAR* path_buffer = reparse_data->u.SymbolicLinkReparseBuffer.PathBuffer;
WORD substitute_name_offset = reparse_data->u.SymbolicLinkReparseBuffer.SubstituteNameOffset;
WORD substitute_name_length = reparse_data->u.SymbolicLinkReparseBuffer.SubstituteNameLength;
std::wstring target_path(
path_buffer + substitute_name_offset / sizeof(WCHAR),
substitute_name_length / sizeof(WCHAR)
);
// 处理相对路径
if (reparse_data->u.SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) {
// 获取符号链接所在的目录
fs::path link_path(full_path);
fs::path link_dir = link_path.parent_path();
// 组合成绝对路径
fs::path absolute_target = link_dir / target_path;
target_path = absolute_target.wstring();
}
return std::string(MyCW2A(target_path.c_str(), CP_UTF8));
}
#endif // _WIN32
int wlo_futimens(wlo_handle fh, const timespec times[2])
{
// 验证句柄有效性
_VALIDATE_RETURN(fh != -1 && fh != 0, EBADF, -1);
// 验证文件句柄有效性
if (WLOHANDLEST(fh).hFile == INVALID_HANDLE_VALUE)
{
errno = EBADF;
return -1;
}
HANDLE hFile = WLOHANDLEST(fh).hFile;
return futimens_winfh(hFile, times);
}
#endif//_WIN32
通讯协议
#pragma once
#include <cstdint>
#include <cstring>
#include <sys/stat.h>
#ifdef _WIN32
#include <fuse3/fuse.h>
#else//!_WIN32
#include <sys/time.h>
#endif // _WIN32
#include <pthread.h>
#define PATH_LEN 176
#ifndef _WIN32
#define _Xoff_t off_t
#endif//_WIN32
#define my_min(a,b) ( (a)<(b) ? (a):(b) )
#define my_offsetof(st,mb) ((unsigned int)(uint64_t) &(((st*)0)->mb) )
#ifdef _WIN32
#define lseek64 _lseeki64
#endif // _WIN32
extern int g_logLevel;
// 打印当前时间和线程ID
#define LOG_DEBUG 0
#define LOG_WARN 1
#define LOG_ERROR 2
void print_with_time_and_thread(int level, const char *format, ...);
// 创建线程
int create_thread(pthread_t *thread, void* (*start_routine)(void*), void *arg);
// 等待线程结束
int join_thread(pthread_t thread);
// 获取当前时间字符串
const char* get_current_time_str();
#pragma pack(push,1)
// 命令类型
enum class CommandType : uint32_t
{
unk,
AUTHENTICATE,
GET_ATTR,
SET_ATTR,
READ_DIR,
OPEN,
READ,
WRITE,
CREATE,
MKDIR,
RMDIR,
UNLINK,
RENAME,
CLOSE,
LSEEK,
TRUNCATE,
CHMOD,
CHOWN,
UTIMENS,
LINK,
SYMLINK,
READLINK,
SYNC,
RESPONSE
};
// 响应状态
enum class ResponseStatus : uint32_t
{
FU_SUCCESS,
FU_ERROR,
FU_NOT_FOUND,
FU_PERMISSION_DENIED,
FU_NOT_IMPLEMENTED,
FU_AUTH_FAILED
};
// 不同命令的参数结构体
struct AuthParams
{
char password[64];
};
struct GetAttrParams
{
uint32_t Flag_GetAttr;
};
struct my_time
{
uint64_t tv_sec;
};
struct myfuse_stat
{
uint64_t st_ino;
uint32_t st_mode;
uint16_t st_nlink;
uint64_t st_size;
union
{
my_time st_atim;
#ifdef _WIN32
uint64_t st_atime;
#endif//_WIN32
};
union
{
my_time st_mtim;
#ifdef _WIN32
uint64_t st_mtime;
#endif//_WIN32
};
union
{
my_time st_ctim;
#ifdef _WIN32
uint64_t st_ctime;
#endif//_WIN32
};
};
struct SetAttrParams
{
myfuse_stat fustbuf;
uint32_t valid; // 哪些属性是有效的,使用FUSE_SETATTR_*标志
};
struct OpenParams
{
uint32_t open_flags;
uint32_t open_mode;
};
struct ReadParams
{
uint32_t size;
int64_t offset; // 使用64位偏移量,支持大文件
};
struct WriteParams
{
uint32_t size;
int64_t offset; // 使用64位偏移量,支持大文件
};
struct TruncateParams
{
int64_t length; // 使用64位长度,支持大文件
};
struct UtimensParams
{
struct timespec times[2];
};
struct LinkParams
{
char newpath[PATH_LEN];
};
struct SymlinkParams
{
char target[PATH_LEN];
};
struct CreateParams
{
uint32_t create_mode;
};
struct MkdirParams
{
uint32_t mkdir_mode;
};
struct RmdirParams
{
uint32_t Flag_Rmdir;
};
struct UnlinkParams
{
uint32_t Flag_Ulink;
};
struct RenameParams
{
char newpath[PATH_LEN];
unsigned Flag_Rename;
};
struct ResponseParams
{
ResponseStatus status;
union
{
struct myfuse_stat mustat;
size_t entry_count;
uint64_t fd_id_out;
size_t bytes_transferred;
int64_t new_offset; // 使用64位偏移量,支持大文件
};
};
///////////////////////////////////////////////////////////////////////
// 统一通讯结构体
///////////////////////////////////////////////////////////////////////
struct CommunicationPacket
{
CommandType command = (CommandType)0;
uint32_t variable_data_size = 0; // 可变长数据的大小,0表示没有可变长数据
char path[PATH_LEN];
uint64_t fd_id_in = 0;
uint64_t serial = 0;
// 使用union存储不同命令的参数
union
{
AuthParams auth;
GetAttrParams getattr;
CreateParams create;
MkdirParams mkdir;
RmdirParams rmdir;
UnlinkParams unlink;
RenameParams rename;
LinkParams link;
SymlinkParams symlink;
ReadParams read;
WriteParams write;
OpenParams open;
UtimensParams utimens;
SetAttrParams setattr;
TruncateParams truncate;
};
// 可变长尾部数据,实际大小由variable_data_size指定
char variable_data[1];
////////////////////////STRUCT END//////
void init(CommandType cmd); // 初始化函数
bool set_variable_data(const char *data, uint32_t size, uint32_t allocSize); // 设置可变长数据 allocSize:结构分配长度
size_t get_total_need() const;//根据可变长度的大小确定总共需要多少数据
};
///////////////////////////////////////////////////////////////////////
// ResponsePkt
///////////////////////////////////////////////////////////////////////
struct ResponsePkt
{
CommandType commandSrc = (CommandType)0;
uint64_t serialSrc = 0;
uint32_t variable_data_size = 0; // 可变长数据的大小,0表示没有可变长数据
ResponseParams response;
// 可变长尾部数据,实际大小由variable_data_size指定
char variable_data[1];
////////////////////////STRUCT END//////
void init(const CommunicationPacket& src); // 初始化函数
bool set_variable_data(const char *data, uint32_t size, uint32_t allocSize); // 设置可变长数据 allocSize:结构分配长度
size_t get_total_need();
};
///////////////////////////////////////////////////////////////////////
// CmdResponse
///////////////////////////////////////////////////////////////////////
struct CmdResponse
{
CmdResponse();
~CmdResponse();
ResponsePkt *pPkt = 0;
bool prepare();//准备一个ResponsePkt大小的空间
bool extend_to(size_t newLen);//长度增长到 newLen
};
#pragma pack(pop)//1
#include "FuseCommon.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include <stdarg.h>
#include <new>
using namespace std;
#ifdef _WIN32
#include <Windows.h>
#endif // _WIN32
int g_logLevel = LOG_WARN;
// 打印当前时间和线程ID
void print_with_time_and_thread(int level, const char* format, ...)
{
if (level < g_logLevel)
return;
// 获取当前时间
time_t now;
time(&now);
struct tm* local_time = localtime(&now);
// 获取当前线程ID
pthread_t thread_id = pthread_self();
// 打印时间和线程ID
#ifdef _WIN32
printf("[%04d-%02d-%02d %02d:%02d:%02d] [Thread: %lu] ",
local_time->tm_year + 1900,
local_time->tm_mon + 1,
local_time->tm_mday,
local_time->tm_hour,
local_time->tm_min,
local_time->tm_sec,
(unsigned long)GetCurrentThreadId());
#else//!_WIN32
printf("[%04d-%02d-%02d %02d:%02d:%02d] [Thread: %lu] ",
local_time->tm_year + 1900,
local_time->tm_mon + 1,
local_time->tm_mday,
local_time->tm_hour,
local_time->tm_min,
local_time->tm_sec,
(unsigned long)thread_id);
#endif // _WIN32
// 打印格式化消息
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
}
// 创建线程
int create_thread(pthread_t* thread, void* (*start_routine)(void*), void* arg) {
print_with_time_and_thread(LOG_WARN, "Creating new thread\n");
return pthread_create(thread, NULL, start_routine, arg);
}
// 等待线程结束
int join_thread(pthread_t thread) {
print_with_time_and_thread(LOG_WARN, "Waiting for thread to finish\n");
return pthread_join(thread, NULL);
}
// 获取当前时间字符串
const char* get_current_time_str()
{
static char time_str[64] = {};
time_t now;
time(&now);
struct tm *local_time = localtime(&now);
snprintf(time_str, sizeof(time_str), "%04d-%02d-%02d %02d:%02d:%02d",
local_time->tm_year + 1900,
local_time->tm_mon + 1,
local_time->tm_mday,
local_time->tm_hour,
local_time->tm_min,
local_time->tm_sec
);
return time_str;
}
///////////////////////////////////////////////////////////////////////
// 统一通讯结构体
///////////////////////////////////////////////////////////////////////
void CommunicationPacket::init(CommandType cmd)
{
memset((char*)this, 0, sizeof(CommunicationPacket));
command = cmd;
variable_data_size = 0;
}
// 设置可变长数据
bool CommunicationPacket::set_variable_data(const char * data, uint32_t size, uint32_t allocSize)
{
//allocSize:结构分配长度
if (allocSize < sizeof(CommunicationPacket))
return false;
size_t remain = allocSize - my_offsetof(CommunicationPacket, variable_data);
if (size > remain) {
return false;
}
memcpy(variable_data, data, size);
variable_data_size = size;
return true;
}
size_t CommunicationPacket::get_total_need() const
{
//可变长度加上可变区域前的长度
return variable_data_size + my_offsetof(CommunicationPacket, variable_data);
}
///////////////////////////////////////////////////////////////////////
// ResponsePkt
///////////////////////////////////////////////////////////////////////
void ResponsePkt::init(const CommunicationPacket& src) // 初始化函数
{
memset((char*)this, 0, sizeof(ResponsePkt));
commandSrc = src.command;
serialSrc = src.serial;
}
bool ResponsePkt::set_variable_data(const char * data, uint32_t size, uint32_t allocSize)
{
//allocSize:结构分配长度
if (allocSize < sizeof(ResponsePkt))
return false;
size_t remain = allocSize - my_offsetof(ResponsePkt, variable_data);
if (size > remain) {
return false;
}
memcpy(variable_data, data, size);
variable_data_size = size;
return true;
}
size_t ResponsePkt::get_total_need()
{
return variable_data_size + my_offsetof(ResponsePkt, variable_data);
}
///////////////////////////////////////////////////////////////////////
// CmdResponse
///////////////////////////////////////////////////////////////////////
CmdResponse::CmdResponse() {}
CmdResponse::~CmdResponse() { if (pPkt)free(pPkt); }
bool CmdResponse::prepare()
{
pPkt = (ResponsePkt*)malloc(sizeof(ResponsePkt));
if (pPkt)
return true;
return false;
}
bool CmdResponse::extend_to(size_t newLen)
{
if (!pPkt)
return false;
if (newLen < sizeof(ResponsePkt))
return false;
ResponsePkt* pPkt2 = (ResponsePkt*)malloc(newLen);
if (!pPkt2)
return false;
memcpy(pPkt2, pPkt, sizeof(ResponsePkt));
free(pPkt);
pPkt = pPkt2;
return true;
}
连接管理
#include "SockMgr.h"
#ifdef _WIN32
#include <Windows.h>
#include <WinSock.h>
#include <io.h>
#else//!_WIN32
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif // _WIN32
#include "FuseCommon.h"
string SERVER_IP = "127.0.0.1"; // Windows服务器IP
int SERVER_PORT = 30876;
string AUTH_PASSWORD = "123456"; // 设置认证密码
string SHARE_DIR = "C:/share";
SocketManager & SocketManager::getInstance() {
static SocketManager instance;
return instance;
}
HSOCKET SocketManager::getSocket() {
std::lock_guard<std::mutex> lock(mutex_);
if (sock_ == BAD_SOCKET) {
sock_ = connect_to_server();
if (sock_ != BAD_SOCKET) {
// 连接成功后进行认证
if (!authenticate(sock_)) {
skapi_closesocket(sock_);
sock_ = BAD_SOCKET;
}
}
}
return sock_;
}
void SocketManager::resetSocket()
{
std::lock_guard<std::mutex> lock(mutex_);
if (sock_ != BAD_SOCKET)
{
skapi_closesocket(sock_);
sock_ = BAD_SOCKET;
}
}
SocketManager::SocketManager() : sock_(BAD_SOCKET) {}
HSOCKET SocketManager::connect_to_server() {
HSOCKET sock = skapi_socket();
if (sock == BAD_SOCKET) {
print_with_time_and_thread(LOG_ERROR, "Failed to create socket\n");
return BAD_SOCKET;
}
if (skapi_connect(sock, SERVER_IP.c_str(), (unsigned short)SERVER_PORT) < 0) {
print_with_time_and_thread(LOG_ERROR, "Failed to connect to server\n");
skapi_closesocket(sock);
return BAD_SOCKET;
}
print_with_time_and_thread(LOG_WARN, "Connected to server %s:%d\n", SERVER_IP.c_str(), SERVER_PORT);
return sock;
}
bool SocketManager::authenticate(HSOCKET sock) {
CommunicationPacket request;
request.init(CommandType::AUTHENTICATE);
strncpy(request.auth.password, AUTH_PASSWORD.c_str(), sizeof(request.auth.password) - 1);
skapi_send(sock, (char*)&request, sizeof(request));
ResponsePkt response;
ssize_t bytes_read = skapi_recv(sock, (char*)&response, sizeof(response));
if (bytes_read <= 0 || response.response.status != ResponseStatus::FU_SUCCESS) {
print_with_time_and_thread(LOG_ERROR, "Authentication failed\n");
skapi_closesocket(sock);
sock_ = BAD_SOCKET;
return false;
}
print_with_time_and_thread(LOG_WARN, "Authenticated successfully\n");
return true;
}
句柄管理
#include "HandleMgr.h"
#ifdef _WIN32
#include <WinSock2.h>
#include <Windows.h>
#include <io.h>
#else//!_WIN32
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif // _WIN32
#include "SnowflakeId.h"
FileHandleManager & FileHandleManager::getInstance() {
static FileHandleManager instance;
return instance;
}
long long FileHandleManager::addFileHandle(wlo_handle fd) {
std::lock_guard<std::mutex> lock(mutex_);
long long id = SnowflakeIdGenerator::getInstance().nextId();
FileHandleInfo info;
info.fd = fd;
fileHandles_[id] = info;
return id;
}
void FileHandleManager::removeFileHandle(long long id) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = fileHandles_.find(id);
if (it != fileHandles_.end()) {
wlo_close(it->second.fd);
fileHandles_.erase(it);
}
}
FileHandleManager::FileHandleInfo * FileHandleManager::getFileHandle(long long id) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = fileHandles_.find(id);
return it != fileHandles_.end() ? &it->second : nullptr;
}
#include "SnowflakeId.h"
SnowflakeIdGenerator & SnowflakeIdGenerator::getInstance() {
static SnowflakeIdGenerator instance;
return instance;
}
long long SnowflakeIdGenerator::nextId() {
std::lock_guard<std::mutex> lock(mutex_);
auto now = std::chrono::system_clock::now();
auto duration = now.time_since_epoch();
long long currentTime = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
if (currentTime < lastTime) {
throw std::runtime_error("Clock moved backwards");
}
if (currentTime == lastTime) {
sequence = (sequence + 1) & SEQUENCE_MASK;
if (sequence == 0) {
// 等待下一毫秒
while (currentTime == lastTime) {
now = std::chrono::system_clock::now();
duration = now.time_since_epoch();
currentTime = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
}
}
}
else {
sequence = 0;
}
lastTime = currentTime;
return ((currentTime - EPOCH) << TIMESTAMP_SHIFT) |
(WORKER_ID << WORKER_ID_SHIFT) |
(DATA_CENTER_ID << DATA_CENTER_ID_SHIFT) |
sequence;
}
SnowflakeIdGenerator::SnowflakeIdGenerator() : lastTime(0), sequence(0), WORKER_ID(1), DATA_CENTER_ID(1) {}

1607

被折叠的 条评论
为什么被折叠?



