2022-12-15 20:49:01 +09:00

995 lines
26 KiB
C

/**
* @file winposix.c
*
* @copyright 2015-2022 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
*
* You can redistribute it and/or modify it under the terms of the GNU
* General Public License version 3 as published by the Free Software
* Foundation.
*
* Licensees holding a valid commercial license may use this software
* in accordance with the commercial license agreement provided in
* conjunction with the software. The terms and conditions of any such
* commercial license agreement shall govern, supersede, and render
* ineffective any application of the GPLv3 license to this software,
* notwithstanding of any reference thereto in the software or
* associated repository.
*/
/*
* This is a very simple Windows POSIX layer. It handles all the POSIX
* file API's required to implement passthrough-fuse in POSIX, however
* the API handling is rather unsophisticated.
*
* Ways to improve it: use the FspPosix* API's to properly handle
* file names and security.
*/
#include <winfsp/winfsp.h>
#include <fcntl.h>
#include <fuse.h>
#include "winposix.h"
#pragma comment(lib, "ntdll.lib")
typedef struct _FILE_GET_EA_INFORMATION
{
ULONG NextEntryOffset;
UCHAR EaNameLength;
CHAR EaName[1];
} FILE_GET_EA_INFORMATION, *PFILE_GET_EA_INFORMATION;
NTSYSAPI NTSTATUS NTAPI NtQueryEaFile(
IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID Buffer,
IN ULONG Length,
IN BOOLEAN ReturnSingleEntry,
IN PVOID EaList OPTIONAL,
IN ULONG EaListLength,
IN PULONG EaIndex OPTIONAL,
IN BOOLEAN RestartScan);
NTSYSAPI NTSTATUS NTAPI NtSetEaFile(
IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PVOID EaBuffer,
IN ULONG EaBufferSize);
#define NEXT_EA(Ea, EaEnd) \
(0 != (Ea)->NextEntryOffset ? (PVOID)((PUINT8)(Ea) + (Ea)->NextEntryOffset) : (EaEnd))
struct _DIR
{
HANDLE h, fh;
struct dirent de;
char path[];
};
#if defined(FSP_FUSE_USE_STAT_EX)
static inline uint32_t MapFileAttributesToFlags(UINT32 FileAttributes)
{
uint32_t flags = 0;
if (FileAttributes & FILE_ATTRIBUTE_READONLY)
flags |= FSP_FUSE_UF_READONLY;
if (FileAttributes & FILE_ATTRIBUTE_HIDDEN)
flags |= FSP_FUSE_UF_HIDDEN;
if (FileAttributes & FILE_ATTRIBUTE_SYSTEM)
flags |= FSP_FUSE_UF_SYSTEM;
if (FileAttributes & FILE_ATTRIBUTE_ARCHIVE)
flags |= FSP_FUSE_UF_ARCHIVE;
return flags;
}
static inline UINT32 MapFlagsToFileAttributes(uint32_t flags)
{
UINT32 FileAttributes = 0;
if (flags & FSP_FUSE_UF_READONLY)
FileAttributes |= FILE_ATTRIBUTE_READONLY;
if (flags & FSP_FUSE_UF_HIDDEN)
FileAttributes |= FILE_ATTRIBUTE_HIDDEN;
if (flags & FSP_FUSE_UF_SYSTEM)
FileAttributes |= FILE_ATTRIBUTE_SYSTEM;
if (flags & FSP_FUSE_UF_ARCHIVE)
FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
return FileAttributes;
}
#endif
static int maperror(int winerrno);
static inline void *error0(void)
{
errno = maperror(GetLastError());
return 0;
}
static inline int error(void)
{
errno = maperror(GetLastError());
return -1;
}
char *realpath(const char *path, char *resolved)
{
char *result;
if (0 == resolved)
{
result = malloc(PATH_MAX * 4); /* sets errno */
if (0 == result)
return 0;
}
else
result = resolved;
int err = 0;
WCHAR PathBuf[PATH_MAX];
WCHAR ResultBuf[PATH_MAX];
if (0 < MultiByteToWideChar(CP_UTF8, 0, path, -1, PathBuf, PATH_MAX))
{
DWORD len = GetFullPathNameW(PathBuf, PATH_MAX, ResultBuf, 0);
if (0 == len)
err = GetLastError();
else if (PATH_MAX < len)
err = ERROR_INVALID_PARAMETER;
WideCharToMultiByte(CP_UTF8, 0, ResultBuf, -1, result, PATH_MAX * 4, 0, 0);
}
else
err = GetLastError();
if (0 == err)
{
HANDLE h = CreateFileW(ResultBuf,
FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (INVALID_HANDLE_VALUE != h)
CloseHandle(h);
else
err = GetLastError();
}
if (0 != err)
{
if (result != resolved)
free(result);
errno = maperror(err);
result = 0;
}
return result;
}
int uncpath(const char *path, WCHAR *buf, int nchar)
{
if (4 < nchar &&
0 < MultiByteToWideChar(CP_UTF8, 0, path, -1, &buf[4], nchar - 4))
{
buf[0] = L'\\';
buf[1] = L'\\';
buf[2] = L'?';
buf[3] = L'\\';
int i = 4;
while (buf[i])
{
if (L'/' == buf[i])
buf[i] = L'\\';
i++;
}
return 1;
}
if (0 < nchar)
buf[0] = 0;
return 0;
}
int statvfs(const char *path, struct fuse_statvfs *stbuf)
{
WCHAR root[PATH_MAX];
DWORD
VolumeSerialNumber,
MaxComponentLength,
SectorsPerCluster,
BytesPerSector,
NumberOfFreeClusters,
TotalNumberOfClusters;
WCHAR PathBuf[PATH_MAX];
uncpath(path, PathBuf, PATH_MAX);
if (!GetVolumePathNameW(PathBuf, root, PATH_MAX) ||
!GetVolumeInformationW(root, 0, 0, &VolumeSerialNumber, &MaxComponentLength, 0, 0, 0) ||
!GetDiskFreeSpaceW(root, &SectorsPerCluster, &BytesPerSector,
&NumberOfFreeClusters, &TotalNumberOfClusters))
{
return error();
}
memset(stbuf, 0, sizeof *stbuf);
stbuf->f_bsize = SectorsPerCluster * BytesPerSector;
stbuf->f_frsize = SectorsPerCluster * BytesPerSector;
stbuf->f_blocks = TotalNumberOfClusters;
stbuf->f_bfree = NumberOfFreeClusters;
stbuf->f_bavail = TotalNumberOfClusters;
stbuf->f_fsid = VolumeSerialNumber;
stbuf->f_namemax = MaxComponentLength;
return 0;
}
int open(const char *path, int oflag, ...)
{
static DWORD da[] = { GENERIC_READ, GENERIC_WRITE, GENERIC_READ | GENERIC_WRITE, 0 };
static DWORD cd[] = { OPEN_EXISTING, OPEN_ALWAYS, TRUNCATE_EXISTING, CREATE_ALWAYS };
DWORD DesiredAccess = 0 == (oflag & _O_APPEND) ?
da[oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR)] :
(da[oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR)] & ~FILE_WRITE_DATA) | FILE_APPEND_DATA;
DWORD CreationDisposition = (_O_CREAT | _O_EXCL) == (oflag & (_O_CREAT | _O_EXCL)) ?
CREATE_NEW :
cd[(oflag & (_O_CREAT | _O_TRUNC)) >> 8];
WCHAR PathBuf[PATH_MAX];
uncpath(path, PathBuf, PATH_MAX);
HANDLE h = CreateFileW(PathBuf,
DesiredAccess, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0/* default security */,
CreationDisposition, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 0);
if (INVALID_HANDLE_VALUE == h)
return error();
return (int)(intptr_t)h;
}
int fgetpath(int fd, char *buf, size_t size)
{
HANDLE h = (HANDLE)(intptr_t)fd;
WCHAR FileName[FSP_FSCTL_TRANSACT_PATH_SIZEMAX / sizeof(WCHAR)];
DWORD FileNameLength;
FileNameLength = GetFinalPathNameByHandleW(h, FileName, sizeof FileName / sizeof(WCHAR),
VOLUME_NAME_NONE | FILE_NAME_NORMALIZED);
if (0 == FileNameLength)
return error();
FspPosixEncodeWindowsPath(FileName, FileNameLength);
size = WideCharToMultiByte(CP_UTF8, 0, FileName, FileNameLength, buf, (int)(size - 1), 0, 0);
if (0 == size)
return error();
buf[size] = '\0';
return 0;
}
int fstat(int fd, struct fuse_stat *stbuf)
{
HANDLE h = (HANDLE)(intptr_t)fd;
BY_HANDLE_FILE_INFORMATION FileInfo;
if (!GetFileInformationByHandle(h, &FileInfo))
return error();
memset(stbuf, 0, sizeof *stbuf);
stbuf->st_mode = 0777 |
((FileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 0040000/* S_IFDIR */ : 0);
stbuf->st_nlink = 1;
stbuf->st_size = ((UINT64)FileInfo.nFileSizeHigh << 32) | ((UINT64)FileInfo.nFileSizeLow);
FspPosixFileTimeToUnixTime(*(PUINT64)&FileInfo.ftCreationTime, (void *)&stbuf->st_birthtim);
FspPosixFileTimeToUnixTime(*(PUINT64)&FileInfo.ftLastAccessTime, (void *)&stbuf->st_atim);
FspPosixFileTimeToUnixTime(*(PUINT64)&FileInfo.ftLastWriteTime, (void *)&stbuf->st_mtim);
FspPosixFileTimeToUnixTime(*(PUINT64)&FileInfo.ftLastWriteTime, (void *)&stbuf->st_ctim);
#if defined(FSP_FUSE_USE_STAT_EX)
stbuf->st_flags = MapFileAttributesToFlags(FileInfo.dwFileAttributes);
#endif
return 0;
}
int ftruncate(int fd, fuse_off_t size)
{
HANDLE h = (HANDLE)(intptr_t)fd;
FILE_END_OF_FILE_INFO EndOfFileInfo;
EndOfFileInfo.EndOfFile.QuadPart = size;
if (!SetFileInformationByHandle(h, FileEndOfFileInfo, &EndOfFileInfo, sizeof EndOfFileInfo))
return error();
return 0;
}
int pread(int fd, void *buf, size_t nbyte, fuse_off_t offset)
{
HANDLE h = (HANDLE)(intptr_t)fd;
OVERLAPPED Overlapped = { 0 };
DWORD BytesTransferred;
Overlapped.Offset = (DWORD)offset;
Overlapped.OffsetHigh = (DWORD)(offset >> 32);
if (!ReadFile(h, buf, (DWORD)nbyte, &BytesTransferred, &Overlapped))
{
if (ERROR_HANDLE_EOF == GetLastError())
return 0;
return error();
}
return BytesTransferred;
}
int pwrite(int fd, const void *buf, size_t nbyte, fuse_off_t offset)
{
HANDLE h = (HANDLE)(intptr_t)fd;
OVERLAPPED Overlapped = { 0 };
DWORD BytesTransferred;
Overlapped.Offset = (DWORD)offset;
Overlapped.OffsetHigh = (DWORD)(offset >> 32);
if (!WriteFile(h, buf, (DWORD)nbyte, &BytesTransferred, &Overlapped))
return error();
return BytesTransferred;
}
int fsync(int fd)
{
HANDLE h = (HANDLE)(intptr_t)fd;
if (!FlushFileBuffers(h))
return error();
return 0;
}
int close(int fd)
{
HANDLE h = (HANDLE)(intptr_t)fd;
if (!CloseHandle(h))
return error();
return 0;
}
int getpath(const char *path, char *buf, size_t size)
{
WCHAR PathBuf[PATH_MAX];
uncpath(path, PathBuf, PATH_MAX);
HANDLE h = CreateFileW(PathBuf,
FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (INVALID_HANDLE_VALUE == h)
return error();
int res = fgetpath((int)(intptr_t)h, buf, size);
CloseHandle(h);
return res;
}
int lstat(const char *path, struct fuse_stat *stbuf)
{
WCHAR PathBuf[PATH_MAX];
uncpath(path, PathBuf, PATH_MAX);
HANDLE h = CreateFileW(PathBuf,
FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (INVALID_HANDLE_VALUE == h)
return error();
int res = fstat((int)(intptr_t)h, stbuf);
CloseHandle(h);
return res;
}
int chmod(const char *path, fuse_mode_t mode)
{
/* we do not support file security */
return 0;
}
int lchown(const char *path, fuse_uid_t uid, fuse_gid_t gid)
{
/* we do not support file security */
return 0;
}
int lchflags(const char *path, uint32_t flags)
{
#if defined(FSP_FUSE_USE_STAT_EX)
UINT32 FileAttributes = MapFlagsToFileAttributes(flags);
if (0 == FileAttributes)
FileAttributes = FILE_ATTRIBUTE_NORMAL;
WCHAR PathBuf[PATH_MAX];
uncpath(path, PathBuf, PATH_MAX);
if (!SetFileAttributesW(PathBuf, FileAttributes))
return error();
#endif
return 0;
}
int truncate(const char *path, fuse_off_t size)
{
WCHAR PathBuf[PATH_MAX];
uncpath(path, PathBuf, PATH_MAX);
HANDLE h = CreateFileW(PathBuf,
FILE_WRITE_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (INVALID_HANDLE_VALUE == h)
return error();
int res = ftruncate((int)(intptr_t)h, size);
CloseHandle(h);
return res;
}
int utime(const char *path, const struct fuse_utimbuf *timbuf)
{
if (0 == timbuf)
return utimensat(AT_FDCWD, path, 0, AT_SYMLINK_NOFOLLOW);
else
{
struct fuse_timespec times[2];
times[0].tv_sec = timbuf->actime;
times[0].tv_nsec = 0;
times[1].tv_sec = timbuf->modtime;
times[1].tv_nsec = 0;
return utimensat(AT_FDCWD, path, times, AT_SYMLINK_NOFOLLOW);
}
}
int utimensat(int dirfd, const char *path, const struct fuse_timespec times[2], int flag)
{
/* ignore dirfd and assume that it is always AT_FDCWD */
/* ignore flag and assume that it is always AT_SYMLINK_NOFOLLOW */
WCHAR PathBuf[PATH_MAX];
uncpath(path, PathBuf, PATH_MAX);
HANDLE h = CreateFileW(PathBuf,
FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (INVALID_HANDLE_VALUE == h)
return error();
UINT64 LastAccessTime, LastWriteTime;
if (0 == times)
{
FILETIME FileTime;
GetSystemTimeAsFileTime(&FileTime);
LastAccessTime = LastWriteTime = *(PUINT64)&FileTime;
}
else
{
FspPosixUnixTimeToFileTime((void *)&times[0], &LastAccessTime);
FspPosixUnixTimeToFileTime((void *)&times[1], &LastWriteTime);
}
int res = SetFileTime(h,
0, (PFILETIME)&LastAccessTime, (PFILETIME)&LastWriteTime) ? 0 : error();
CloseHandle(h);
return res;
}
int setcrtime(const char *path, const struct fuse_timespec *tv)
{
WCHAR PathBuf[PATH_MAX];
uncpath(path, PathBuf, PATH_MAX);
HANDLE h = CreateFileW(PathBuf,
FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (INVALID_HANDLE_VALUE == h)
return error();
UINT64 CreationTime;
FspPosixUnixTimeToFileTime((void *)tv, &CreationTime);
int res = SetFileTime(h,
(PFILETIME)&CreationTime, 0, 0) ? 0 : error();
CloseHandle(h);
return res;
}
int unlink(const char *path)
{
WCHAR PathBuf[PATH_MAX];
uncpath(path, PathBuf, PATH_MAX);
if (!DeleteFileW(PathBuf))
return error();
return 0;
}
int rename(const char *oldpath, const char *newpath)
{
WCHAR OldPathBuf[PATH_MAX];
WCHAR NewPathBuf[PATH_MAX];
uncpath(oldpath, OldPathBuf, PATH_MAX);
uncpath(newpath, NewPathBuf, PATH_MAX);
if (!MoveFileExW(OldPathBuf, NewPathBuf, MOVEFILE_REPLACE_EXISTING))
return error();
return 0;
}
static int lsetea(const char *path, PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength)
{
WCHAR PathBuf[PATH_MAX];
uncpath(path, PathBuf, PATH_MAX);
HANDLE h = CreateFileW(PathBuf,
FILE_WRITE_EA | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (INVALID_HANDLE_VALUE == h)
return error();
IO_STATUS_BLOCK Iosb;
NTSTATUS Status = NtSetEaFile(h, &Iosb, Ea, EaLength);
CloseHandle(h);
if (!NT_SUCCESS(Status))
switch (Status)
{
case STATUS_INVALID_EA_NAME:
case STATUS_EA_LIST_INCONSISTENT:
case STATUS_EA_CORRUPT_ERROR:
case STATUS_NONEXISTENT_EA_ENTRY:
case STATUS_NO_MORE_EAS:
case STATUS_NO_EAS_ON_FILE:
errno = EINVAL;
return -1;
default:
SetLastError(RtlNtStatusToDosError(Status));
return error();
}
return 0;
}
static int lgetea(const char *path,
PFILE_GET_EA_INFORMATION GetEa, ULONG GetEaLength,
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength)
{
WCHAR PathBuf[PATH_MAX];
uncpath(path, PathBuf, PATH_MAX);
HANDLE h = CreateFileW(PathBuf,
FILE_READ_EA | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (INVALID_HANDLE_VALUE == h)
return error();
IO_STATUS_BLOCK Iosb;
NTSTATUS Status = NtQueryEaFile(h, &Iosb, Ea, EaLength, FALSE, GetEa, GetEaLength, 0, TRUE);
CloseHandle(h);
if (!NT_SUCCESS(Status))
switch (Status)
{
case STATUS_INVALID_EA_NAME:
case STATUS_EA_LIST_INCONSISTENT:
case STATUS_EA_CORRUPT_ERROR:
case STATUS_NONEXISTENT_EA_ENTRY:
case STATUS_NO_MORE_EAS:
errno = EINVAL;
return -1;
case STATUS_NO_EAS_ON_FILE:
if (0 == GetEa)
return 0;
else
{
errno = ENODATA;
return -1;
}
default:
SetLastError(RtlNtStatusToDosError(Status));
return error();
}
else if (0 == GetEa &&
(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) > Iosb.Information || 0 == Ea->EaValueLength))
{
errno = ENODATA;
return -1;
}
return (ULONG)Iosb.Information;
}
int lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags)
{
union
{
FILE_FULL_EA_INFORMATION V;
UINT8 B[1024];
} EaBuf;
PFILE_FULL_EA_INFORMATION Ea = &EaBuf.V;
ULONG EaLength;
size_t namelen = strlen(name);
if (254 < namelen || 0xffff < size)
{
errno = EINVAL;
return -1;
}
EaLength = (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + namelen + 1 + size);
if (sizeof EaBuf < EaLength)
{
Ea = malloc(EaLength); /* sets errno */
if (0 == Ea)
return -1;
}
memset(Ea, 0, sizeof(FILE_FULL_EA_INFORMATION));
Ea->EaNameLength = (UCHAR)namelen;
Ea->EaValueLength = (USHORT)size;
memcpy(Ea->EaName, name, namelen + 1);
memcpy(Ea->EaName + namelen + 1, value, size);
int res = lsetea(path, Ea, EaLength); /* sets errno */
if (&EaBuf.V != Ea)
free(Ea);
return res;
}
int lgetxattr(const char *path, const char *name, void *value, size_t size0)
{
size_t size = 0 == size0 || 0xffff < size0 ? 0xffff : size0;
union
{
FILE_GET_EA_INFORMATION V;
UINT8 B[FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + 255];
} GetEaBuf;
PFILE_GET_EA_INFORMATION GetEa = &GetEaBuf.V;
union
{
FILE_FULL_EA_INFORMATION V;
UINT8 B[1024];
} EaBuf;
PFILE_FULL_EA_INFORMATION Ea = &EaBuf.V;
ULONG GetEaLength, EaLength;
size_t namelen = strlen(name);
if (254 < namelen)
{
errno = EINVAL;
return -1;
}
EaLength = (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + namelen + 1 + size);
if (sizeof EaBuf < EaLength)
{
Ea = malloc(EaLength); /* sets errno */
if (0 == Ea)
return -1;
}
GetEaLength = (ULONG)(FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + namelen + 1);
memset(GetEa, 0, sizeof(FILE_GET_EA_INFORMATION));
GetEa->EaNameLength = (UCHAR)namelen;
memcpy(GetEa->EaName, name, namelen + 1);
int res = lgetea(path, GetEa, GetEaLength, Ea, EaLength);
if (0 < res)
{
res = Ea->EaValueLength;
if (0 == size0)
;
else if (res <= size0)
memcpy(value, Ea->EaName + Ea->EaNameLength + 1, res);
else
{
errno = ERANGE;
res = -1;
}
}
else if (0 == res) /* should not happen! */
{
}
if (&EaBuf.V != Ea)
free(Ea);
return res;
}
int llistxattr(const char *path, char *namebuf, size_t size)
{
PFILE_FULL_EA_INFORMATION Ea = 0;
ULONG EaLength;
EaLength = (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + 254 + 1 + 0xffff);
Ea = malloc(EaLength); /* sets errno */
if (0 == Ea)
return -1;
int res = lgetea(path, 0, 0, Ea, EaLength);
if (0 < res)
{
PFILE_FULL_EA_INFORMATION EaEnd = (PVOID)((PUINT8)Ea + res);
res = 0;
for (PFILE_FULL_EA_INFORMATION EaPtr = Ea; EaEnd > EaPtr; EaPtr = NEXT_EA(EaPtr, EaEnd))
res += EaPtr->EaNameLength + 1;
if (0 == size)
;
else if (res <= size)
{
char *p = namebuf;
for (PFILE_FULL_EA_INFORMATION EaPtr = Ea; EaEnd > EaPtr; EaPtr = NEXT_EA(EaPtr, EaEnd))
{
memcpy(p, EaPtr->EaName, EaPtr->EaNameLength + 1);
p += EaPtr->EaNameLength + 1;
}
}
else
{
errno = ERANGE;
res = -1;
}
}
free(Ea);
return res;
}
int lremovexattr(const char *path, const char *name)
{
return lsetxattr(path, name, 0, 0, 0);
}
int mkdir(const char *path, fuse_mode_t mode)
{
WCHAR PathBuf[PATH_MAX];
uncpath(path, PathBuf, PATH_MAX);
if (!CreateDirectoryW(PathBuf, 0/* default security */))
return error();
return 0;
}
int rmdir(const char *path)
{
WCHAR PathBuf[PATH_MAX];
uncpath(path, PathBuf, PATH_MAX);
if (!RemoveDirectoryW(PathBuf))
return error();
return 0;
}
DIR *opendir(const char *path)
{
WCHAR PathBuf[PATH_MAX];
uncpath(path, PathBuf, PATH_MAX);
HANDLE h = CreateFileW(PathBuf,
FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (INVALID_HANDLE_VALUE == h)
return error0();
size_t pathlen = strlen(path);
if (0 < pathlen && '/' == path[pathlen - 1])
pathlen--;
DIR *dirp = malloc(sizeof *dirp + pathlen + 3); /* sets errno */
if (0 == dirp)
{
CloseHandle(h);
return 0;
}
memset(dirp, 0, sizeof *dirp);
dirp->h = h;
dirp->fh = INVALID_HANDLE_VALUE;
memcpy(dirp->path, path, pathlen);
dirp->path[pathlen + 0] = '/';
dirp->path[pathlen + 1] = '*';
dirp->path[pathlen + 2] = '\0';
return dirp;
}
int dirfd(DIR *dirp)
{
return (int)(intptr_t)dirp->h;
}
void rewinddir(DIR *dirp)
{
if (INVALID_HANDLE_VALUE != dirp->fh)
{
FindClose(dirp->fh);
dirp->fh = INVALID_HANDLE_VALUE;
}
}
struct dirent *readdir(DIR *dirp)
{
WIN32_FIND_DATAW FindData;
struct fuse_stat *stbuf = &dirp->de.d_stat;
if (INVALID_HANDLE_VALUE == dirp->fh)
{
WCHAR PathBuf[PATH_MAX];
uncpath(dirp->path, PathBuf, PATH_MAX);
dirp->fh = FindFirstFileW(PathBuf, &FindData);
if (INVALID_HANDLE_VALUE == dirp->fh)
return error0();
}
else
{
if (!FindNextFileW(dirp->fh, &FindData))
{
if (ERROR_NO_MORE_FILES == GetLastError())
return 0;
return error0();
}
}
memset(stbuf, 0, sizeof *stbuf);
stbuf->st_mode = 0777 |
((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 0040000/* S_IFDIR */ : 0);
stbuf->st_nlink = 1;
stbuf->st_size = ((UINT64)FindData.nFileSizeHigh << 32) | ((UINT64)FindData.nFileSizeLow);
FspPosixFileTimeToUnixTime(*(PUINT64)&FindData.ftCreationTime, (void *)&stbuf->st_birthtim);
FspPosixFileTimeToUnixTime(*(PUINT64)&FindData.ftLastAccessTime, (void *)&stbuf->st_atim);
FspPosixFileTimeToUnixTime(*(PUINT64)&FindData.ftLastWriteTime, (void *)&stbuf->st_mtim);
FspPosixFileTimeToUnixTime(*(PUINT64)&FindData.ftLastWriteTime, (void *)&stbuf->st_ctim);
#if defined(FSP_FUSE_USE_STAT_EX)
stbuf->st_flags = MapFileAttributesToFlags(FindData.dwFileAttributes);
#endif
WideCharToMultiByte(CP_UTF8, 0, FindData.cFileName, -1, dirp->de.d_name, 255 * 4, 0, 0);
return &dirp->de;
}
int closedir(DIR *dirp)
{
if (INVALID_HANDLE_VALUE != dirp->fh)
FindClose(dirp->fh);
CloseHandle(dirp->h);
free(dirp);
return 0;
}
static int maperror(int winerrno)
{
switch (winerrno)
{
case ERROR_INVALID_FUNCTION:
return EINVAL;
case ERROR_FILE_NOT_FOUND:
return ENOENT;
case ERROR_PATH_NOT_FOUND:
return ENOENT;
case ERROR_TOO_MANY_OPEN_FILES:
return EMFILE;
case ERROR_ACCESS_DENIED:
return EACCES;
case ERROR_INVALID_HANDLE:
return EBADF;
case ERROR_ARENA_TRASHED:
return ENOMEM;
case ERROR_NOT_ENOUGH_MEMORY:
return ENOMEM;
case ERROR_INVALID_BLOCK:
return ENOMEM;
case ERROR_BAD_ENVIRONMENT:
return E2BIG;
case ERROR_BAD_FORMAT:
return ENOEXEC;
case ERROR_INVALID_ACCESS:
return EINVAL;
case ERROR_INVALID_DATA:
return EINVAL;
case ERROR_INVALID_DRIVE:
return ENOENT;
case ERROR_CURRENT_DIRECTORY:
return EACCES;
case ERROR_NOT_SAME_DEVICE:
return EXDEV;
case ERROR_NO_MORE_FILES:
return ENOENT;
case ERROR_LOCK_VIOLATION:
return EACCES;
case ERROR_BAD_NETPATH:
return ENOENT;
case ERROR_NETWORK_ACCESS_DENIED:
return EACCES;
case ERROR_BAD_NET_NAME:
return ENOENT;
case ERROR_FILE_EXISTS:
return EEXIST;
case ERROR_CANNOT_MAKE:
return EACCES;
case ERROR_FAIL_I24:
return EACCES;
case ERROR_INVALID_PARAMETER:
return EINVAL;
case ERROR_NO_PROC_SLOTS:
return EAGAIN;
case ERROR_DRIVE_LOCKED:
return EACCES;
case ERROR_BROKEN_PIPE:
return EPIPE;
case ERROR_DISK_FULL:
return ENOSPC;
case ERROR_INVALID_TARGET_HANDLE:
return EBADF;
case ERROR_WAIT_NO_CHILDREN:
return ECHILD;
case ERROR_CHILD_NOT_COMPLETE:
return ECHILD;
case ERROR_DIRECT_ACCESS_HANDLE:
return EBADF;
case ERROR_NEGATIVE_SEEK:
return EINVAL;
case ERROR_SEEK_ON_DEVICE:
return EACCES;
case ERROR_DIR_NOT_EMPTY:
return ENOTEMPTY;
case ERROR_NOT_LOCKED:
return EACCES;
case ERROR_BAD_PATHNAME:
return ENOENT;
case ERROR_MAX_THRDS_REACHED:
return EAGAIN;
case ERROR_LOCK_FAILED:
return EACCES;
case ERROR_ALREADY_EXISTS:
return EEXIST;
case ERROR_FILENAME_EXCED_RANGE:
return ENOENT;
case ERROR_NESTING_NOT_ALLOWED:
return EAGAIN;
case ERROR_NOT_ENOUGH_QUOTA:
return ENOMEM;
default:
if (ERROR_WRITE_PROTECT <= winerrno && winerrno <= ERROR_SHARING_BUFFER_EXCEEDED)
return EACCES;
else if (ERROR_INVALID_STARTING_CODESEG <= winerrno && winerrno <= ERROR_INFLOOP_IN_RELOC_CHAIN)
return ENOEXEC;
else
return EINVAL;
}
}
long WinFspLoad(void)
{
return FspLoad(0);
}