mirror of https://github.com/ksherlock/afp.git
829 lines
17 KiB
C++
829 lines
17 KiB
C++
#include "resource_fork.h"
|
|
|
|
#include <cstring>
|
|
|
|
#if defined(__CYGWIN__) || defined(__MSYS__)
|
|
#if !defined(_WIN32)
|
|
#define _WIN32
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#ifdef _WIN32
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#define XATTR_RESOURCEFORK_NAME "AFP_Resource"
|
|
#else
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include "xattr.h"
|
|
#endif
|
|
|
|
#ifdef __APPLE__
|
|
#include <sys/xattr.h>
|
|
#include <sys/paths.h>
|
|
#ifndef _PATH_RSRCFORKSPEC
|
|
#define _PATH_RSRCFORKSPEC "/..namedfork/rsrc"
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(__linux__)
|
|
#define XATTR_RESOURCEFORK_NAME "user.com.apple.ResourceFork"
|
|
#endif
|
|
|
|
|
|
#ifndef XATTR_RESOURCEFORK_NAME
|
|
#define XATTR_RESOURCEFORK_NAME "com.apple.ResourceFork"
|
|
#endif
|
|
|
|
#if defined(__linux__) || defined(__FreeBSD__) || defined(_AIX)
|
|
#define XATTR_RESOURCE_FORK
|
|
|
|
#include <vector>
|
|
|
|
#endif
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
// ENOATTR is not standard enough, so use ENODATA.
|
|
#if defined(ENOATTR) && ENOATTR != ENODATA
|
|
|
|
void remap_enoattr(std::error_code &ec) {
|
|
if (ec.value() == ENOATTR)
|
|
ec = std::make_error_code(std::errc::no_message_available);
|
|
}
|
|
#else
|
|
void remap_enoattr(std::error_code &ec) {}
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
#ifdef _MSC_VER
|
|
#define AFP_ERROR_FILE_NOT_FOUND ERROR_FILE_NOT_FOUND
|
|
#define remap_os_error(x) x
|
|
#else
|
|
#define AFP_ERROR_FILE_NOT_FOUND ENOENT
|
|
extern "C" int remap_os_error(unsigned long);
|
|
#endif
|
|
|
|
BOOL _(BOOL x, std::error_code &ec) {
|
|
if (!x) ec = std::error_code(remap_os_error(GetLastError()), std::system_category());
|
|
return x;
|
|
}
|
|
|
|
HANDLE _(HANDLE x, std::error_code &ec) {
|
|
if (x == INVALID_HANDLE_VALUE)
|
|
ec = std::error_code(remap_os_error(GetLastError()), std::system_category());
|
|
return x;
|
|
}
|
|
|
|
#undef CreateFile
|
|
#undef DeleteFile
|
|
|
|
template<class ...Args>
|
|
HANDLE CreateFile(const std::string &s, Args... args) {
|
|
return CreateFileA(s.c_str(), std::forward<Args>(args)...);
|
|
}
|
|
|
|
template<class ...Args>
|
|
HANDLE CreateFile(const std::wstring &s, Args... args) {
|
|
return CreateFileW(s.c_str(), std::forward<Args>(args)...);
|
|
}
|
|
|
|
template<class StringType>
|
|
HANDLE CreateFileX(const StringType &s, afp::resource_fork::open_mode mode, std::error_code &ec) {
|
|
|
|
|
|
DWORD access = 0;
|
|
DWORD create = 0;
|
|
|
|
switch (mode) {
|
|
case afp::resource_fork::read_only:
|
|
access = GENERIC_READ;
|
|
create = OPEN_EXISTING;
|
|
break;
|
|
case afp::resource_fork::read_write:
|
|
access = GENERIC_READ | GENERIC_WRITE;
|
|
create = OPEN_ALWAYS;
|
|
break;
|
|
case afp::resource_fork::write_only:
|
|
access = GENERIC_WRITE;
|
|
create = OPEN_ALWAYS;
|
|
break;
|
|
}
|
|
|
|
HANDLE h = _(CreateFile(s, access, FILE_SHARE_READ, nullptr, create, FILE_ATTRIBUTE_NORMAL, nullptr), ec);
|
|
|
|
return h;
|
|
}
|
|
|
|
BOOL DeleteFile(const std::string &path) { return DeleteFileA(path.c_str()); }
|
|
BOOL DeleteFile(const std::wstring &path) { return DeleteFileW(path.c_str()); }
|
|
|
|
template<class StringType>
|
|
bool DeleteFileX(const StringType &path, std::error_code &ec) {
|
|
return _(DeleteFile(path), ec);
|
|
}
|
|
|
|
|
|
DWORD GetFileAttributesX(const std::string &path) {
|
|
return GetFileAttributesA(path.c_str());
|
|
}
|
|
DWORD GetFileAttributesX(const std::wstring &path) {
|
|
return GetFileAttributesW(path.c_str());
|
|
}
|
|
|
|
template<class StringType>
|
|
bool regular_file(const StringType &path, std::error_code &ec) {
|
|
|
|
// make sure this isn't a directory.
|
|
DWORD st = GetFileAttributesX(path);
|
|
if (st == INVALID_FILE_ATTRIBUTES) {
|
|
ec = std::error_code(remap_os_error(GetLastError()), std::system_category());
|
|
return false;
|
|
}
|
|
if (st & FILE_ATTRIBUTE_DIRECTORY) {
|
|
ec = std::make_error_code(std::errc::is_a_directory);
|
|
return false;
|
|
}
|
|
if (st & FILE_ATTRIBUTE_DEVICE) {
|
|
ec = std::make_error_code(std::errc::invalid_seek);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
template<class T>
|
|
T _(T x, std::error_code &ec) {
|
|
if (x < 0) ec = std::error_code(errno, std::system_category());
|
|
return x;
|
|
}
|
|
|
|
bool regular_file(int fd, std::error_code &ec) {
|
|
struct stat st;
|
|
if (_(::fstat(fd, &st), ec) < 0) {
|
|
return false;
|
|
}
|
|
if (S_ISREG(st.st_mode)) return true;
|
|
|
|
if (S_ISDIR(st.st_mode)) {
|
|
ec = std::make_error_code(std::errc::is_a_directory);
|
|
} else {
|
|
ec = std::make_error_code(std::errc::invalid_seek); // ESPIPE.
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* opens a file read-only and verifies it's a regular file */
|
|
int openX(const std::string &path, std::error_code &ec) {
|
|
int fd = _(::open(path.c_str(), O_RDONLY | O_NONBLOCK), ec);
|
|
if (fd >= 0 && !regular_file(fd, ec)) {
|
|
::close(fd);
|
|
fd = -1;
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
namespace afp {
|
|
|
|
resource_fork::resource_fork(resource_fork &&rhs) {
|
|
std::swap(_fd, rhs._fd);
|
|
|
|
#if defined(__linux__) || defined(__FreeBSD__) || defined(_AIX)
|
|
std::swap(_offset, rhs._offset);
|
|
std::swap(_mode, rhs._mode);
|
|
#endif
|
|
}
|
|
|
|
resource_fork& resource_fork::operator=(resource_fork &&rhs) {
|
|
if (this != &rhs) {
|
|
close();
|
|
std::swap(_fd, rhs._fd);
|
|
|
|
#if defined(__linux__) || defined(__FreeBSD__) || defined(_AIX)
|
|
std::swap(_offset, rhs._offset);
|
|
std::swap(_mode, rhs._mode);
|
|
#endif
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
bool resource_fork::open(const std::string &path, open_mode mode, std::error_code &ec) {
|
|
ec.clear();
|
|
close();
|
|
|
|
if (!regular_file(path, ec)) return false;
|
|
|
|
std::string s(path);
|
|
s += ":" XATTR_RESOURCEFORK_NAME;
|
|
|
|
HANDLE h = CreateFileX(path, read_only, ec);
|
|
if (ec) return false;
|
|
_fd = CreateFileX(s, mode, ec);
|
|
CloseHandle(h);
|
|
if (ec) {
|
|
if (ec.value() == AFP_ERROR_FILE_NOT_FOUND)
|
|
ec = std::make_error_code(std::errc::no_message_available);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool resource_fork::open(const std::wstring &path, open_mode mode, std::error_code &ec) {
|
|
ec.clear();
|
|
close();
|
|
|
|
if (!regular_file(path, ec)) return false;
|
|
|
|
std::wstring s(path);
|
|
s += L":" XATTR_RESOURCEFORK_NAME;
|
|
|
|
HANDLE h = CreateFileX(path, read_only, ec);
|
|
if (ec) return false;
|
|
_fd = CreateFileX(s, mode, ec);
|
|
CloseHandle(h);
|
|
if (ec) {
|
|
if (ec.value() == AFP_ERROR_FILE_NOT_FOUND)
|
|
ec = std::make_error_code(std::errc::no_message_available);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
size_t resource_fork::write(const std::string &path, const void *buffer, size_t n, std::error_code &ec) {
|
|
|
|
ec.clear();
|
|
|
|
if (!regular_file(path, ec)) return 0;
|
|
|
|
std::string s(path);
|
|
s += ":" XATTR_RESOURCEFORK_NAME;
|
|
|
|
HANDLE h = CreateFileX(path, read_only, ec);
|
|
if (ec) return 0;
|
|
|
|
|
|
HANDLE fd = _(CreateFile(s, GENERIC_WRITE, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr), ec);
|
|
if (ec) return 0;
|
|
|
|
DWORD transferred = 0;
|
|
BOOL ok = _(WriteFile(fd, buffer, n, &transferred, nullptr), ec);
|
|
|
|
|
|
CloseHandle(h);
|
|
CloseHandle(fd);
|
|
|
|
|
|
if (ec) return 0;
|
|
return transferred;
|
|
}
|
|
|
|
size_t resource_fork::write(const std::wstring &path, const void *buffer, size_t n, std::error_code &ec) {
|
|
|
|
ec.clear();
|
|
|
|
if (!regular_file(path, ec)) return 0;
|
|
|
|
std::wstring s(path);
|
|
s += L":" XATTR_RESOURCEFORK_NAME;
|
|
|
|
HANDLE h = CreateFileX(path, read_only, ec);
|
|
if (ec) return 0;
|
|
|
|
|
|
HANDLE fd = _(CreateFile(s, GENERIC_WRITE, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr), ec);
|
|
if (ec) return 0;
|
|
|
|
DWORD transferred = 0;
|
|
BOOL ok = _(WriteFile(fd, buffer, n, &transferred, nullptr), ec);
|
|
|
|
|
|
CloseHandle(h);
|
|
CloseHandle(fd);
|
|
|
|
|
|
if (ec) return 0;
|
|
return transferred;
|
|
}
|
|
|
|
|
|
bool resource_fork::remove(const std::string &path, std::error_code &ec) {
|
|
ec.clear();
|
|
|
|
if (!regular_file(path, ec)) return false;
|
|
|
|
std::string s(path);
|
|
s += ":" XATTR_RESOURCEFORK_NAME;
|
|
|
|
HANDLE h = CreateFileX(path, read_only, ec);
|
|
if (ec) return false;
|
|
CloseHandle(h);
|
|
|
|
bool ok = DeleteFileX(s, ec);
|
|
if (ec.value() == ERROR_FILE_NOT_FOUND) {
|
|
ec = std::make_error_code(std::errc::no_message_available);
|
|
return true;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
bool resource_fork::remove(const std::wstring &path, std::error_code &ec) {
|
|
ec.clear();
|
|
|
|
if (!regular_file(path, ec)) return false;
|
|
|
|
std::wstring s(path);
|
|
s += L":" XATTR_RESOURCEFORK_NAME;
|
|
|
|
HANDLE h = CreateFileX(path, read_only, ec);
|
|
if (ec) return false;
|
|
CloseHandle(h);
|
|
|
|
bool ok = DeleteFileX(s, ec);
|
|
if (ec.value() == ERROR_FILE_NOT_FOUND) {
|
|
ec = std::make_error_code(std::errc::no_message_available);
|
|
return true;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
|
|
void resource_fork::close() {
|
|
CloseHandle(_fd);
|
|
_fd = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
size_t resource_fork::read(void *buffer, size_t n, std::error_code &ec) {
|
|
ec.clear();
|
|
DWORD transferred = 0;
|
|
BOOL ok = _(ReadFile(_fd, buffer, n, &transferred, nullptr), ec);
|
|
if (ec) return 0;
|
|
return transferred;
|
|
}
|
|
|
|
size_t resource_fork::write(const void *buffer, size_t n, std::error_code &ec) {
|
|
ec.clear();
|
|
DWORD transferred = 0;
|
|
BOOL ok = _(WriteFile(_fd, buffer, n, &transferred, nullptr), ec);
|
|
if (ec) return 0;
|
|
return transferred;
|
|
}
|
|
|
|
size_t resource_fork::size(std::error_code &ec) {
|
|
ec.clear();
|
|
LARGE_INTEGER ll = { };
|
|
BOOL ok = _(GetFileSizeEx(_fd, &ll), ec);
|
|
if (ec) return 0;
|
|
return static_cast<size_t>(ll.QuadPart);
|
|
}
|
|
|
|
bool resource_fork::truncate(size_t pos, std::error_code &ec) {
|
|
ec.clear();
|
|
|
|
LARGE_INTEGER ll;
|
|
BOOL ok;
|
|
|
|
ll.QuadPart = pos;
|
|
|
|
ok = _(SetFilePointerEx(_fd, ll, nullptr, FILE_BEGIN), ec);
|
|
if (ec) return false;
|
|
|
|
ok = _(SetEndOfFile(_fd), ec);
|
|
if (ec) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool resource_fork::seek(size_t pos, std::error_code &ec) {
|
|
ec.clear();
|
|
|
|
LARGE_INTEGER ll;
|
|
BOOL ok;
|
|
|
|
ll.QuadPart = pos;
|
|
|
|
ok = _(SetFilePointerEx(_fd, ll, nullptr, FILE_BEGIN), ec);
|
|
if (ec) return false;
|
|
return true;
|
|
}
|
|
#else
|
|
void resource_fork::close() {
|
|
::close(_fd);
|
|
_fd = -1;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef __sun__
|
|
#define FD_RESOURCE_FORK
|
|
bool resource_fork::open(const std::string &path, open_mode mode, std::error_code &ec) {
|
|
ec.clear();
|
|
close();
|
|
|
|
int umode = 0;
|
|
switch(mode) {
|
|
case read_only: umode = O_RDONLY; break;
|
|
case write_only: umode = O_WRONLY | O_CREAT; break;
|
|
case read_write: umode = O_RDWR | O_CREAT; break;
|
|
}
|
|
|
|
int fd = openX(path, ec);
|
|
if (ec) return false;
|
|
|
|
_fd = _(::openat(fd, XATTR_RESOURCEFORK_NAME, umode | O_XATTR, 0666), ec);
|
|
::close(fd);
|
|
|
|
if (ec) {
|
|
if (ec.value() == ENOENT)
|
|
ec = std::make_error_code(std::errc::no_message_available); // ENODATA.
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool resource_fork::remove(const std::string &path, std::error_code &ec) {
|
|
ec.clear();
|
|
|
|
|
|
int fd = openX(path, ec);
|
|
if (ec) return false;
|
|
|
|
int dirfd = _(::openat(fd, ".", O_RDONLY | O_XATTR), ec);
|
|
|
|
::close(fd);
|
|
if (ec) return false;
|
|
|
|
int ok = _(::unlinkat(dirfd, XATTR_RESOURCEFORK_NAME, 0), ec);
|
|
::close(dirfd);
|
|
|
|
if (ec.value() == ENOENT) {
|
|
ec = std::make_error_code(std::errc::no_message_available); // ENODATA.
|
|
return true;
|
|
}
|
|
return ok == 0;
|
|
}
|
|
|
|
|
|
size_t resource_fork::write(const std::string &path, const void *buffer, size_t n, std::error_code &ec) {
|
|
|
|
ec.clear();
|
|
|
|
int fd = openX(path, ec);
|
|
if (ec) return 0;
|
|
|
|
int rfd = _(::openat(fd, XATTR_RESOURCEFORK_NAME, O_WRONLY | O_CREAT | O_TRUNC | O_XATTR, 0666), ec);
|
|
::close(fd);
|
|
|
|
if (ec) {
|
|
if (ec.value() == ENOENT)
|
|
ec = std::make_error_code(std::errc::no_message_available); // ENODATA.
|
|
return 0;
|
|
}
|
|
|
|
auto rv = _(::write(rfd, buffer, n), ec);
|
|
::close(rfd);
|
|
if (rv < 0) return 0;
|
|
return rv;
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef __APPLE__
|
|
#define FD_RESOURCE_FORK
|
|
bool resource_fork::open(const std::string &path, open_mode mode, std::error_code &ec) {
|
|
ec.clear();
|
|
close();
|
|
|
|
std::string s(path);
|
|
s += _PATH_RSRCFORKSPEC;
|
|
|
|
int fd = openX(path, ec);
|
|
if (ec) return false;
|
|
|
|
int umode = 0;
|
|
switch(mode) {
|
|
case read_only: umode = O_RDONLY; break;
|
|
case write_only: umode = O_WRONLY | O_CREAT; break;
|
|
case read_write: umode = O_RDWR | O_CREAT; break;
|
|
}
|
|
|
|
_fd = _(::open(s.c_str(), umode, 0666), ec);
|
|
::close(fd);
|
|
if (ec) {
|
|
if (ec.value() == ENOENT)
|
|
ec = std::make_error_code(std::errc::no_message_available);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool resource_fork::remove(const std::string &path, std::error_code &ec) {
|
|
ec.clear();
|
|
|
|
std::string s(path);
|
|
s += _PATH_RSRCFORKSPEC;
|
|
|
|
int fd = openX(path, ec);
|
|
if (ec) return false;
|
|
::close(fd);
|
|
|
|
int ok = _(::unlink(s.c_str()), ec);
|
|
if (ec.value() == ENOENT) {
|
|
ec = std::make_error_code(std::errc::no_message_available);
|
|
return true;
|
|
}
|
|
return ok == 0;
|
|
}
|
|
|
|
size_t resource_fork::write(const std::string &path, const void *buffer, size_t n, std::error_code &ec) {
|
|
|
|
ec.clear();
|
|
|
|
std::string s(path);
|
|
s += _PATH_RSRCFORKSPEC;
|
|
|
|
int fd = openX(path, ec);
|
|
if (ec) return false;
|
|
::close(fd);
|
|
|
|
int rfd = _(::open(s.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666), ec);
|
|
if (rfd < 0) return 0;
|
|
|
|
auto rv = _(::write(rfd, buffer, n), ec);
|
|
::close(rfd);
|
|
if (rv < 0) return 0;
|
|
return rv;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef FD_RESOURCE_FORK
|
|
size_t resource_fork::read(void *buffer, size_t n, std::error_code &ec) {
|
|
ec.clear();
|
|
auto rv = _(::read(_fd, buffer, n), ec);
|
|
if (ec) return 0;
|
|
|
|
return rv;
|
|
}
|
|
|
|
size_t resource_fork::write(const void *buffer, size_t n, std::error_code &ec) {
|
|
ec.clear();
|
|
auto rv = _(::write(_fd, buffer, n), ec);
|
|
if (ec) return 0;
|
|
|
|
return rv;
|
|
}
|
|
|
|
bool resource_fork::truncate(size_t pos, std::error_code &ec) {
|
|
ec.clear();
|
|
_(::ftruncate(_fd, pos), ec);
|
|
if (ec) return false;
|
|
return true;
|
|
}
|
|
|
|
bool resource_fork::seek(size_t pos, std::error_code &ec) {
|
|
ec.clear();
|
|
_(::lseek(_fd, pos, SEEK_SET), ec);
|
|
if (ec) return false;
|
|
return true;
|
|
}
|
|
|
|
size_t resource_fork::size(std::error_code &ec) {
|
|
ec.clear();
|
|
struct stat st;
|
|
_(::fstat(_fd, &st), ec);
|
|
if (ec) return 0;
|
|
return st.st_size;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef XATTR_RESOURCE_FORK
|
|
namespace {
|
|
std::vector<uint8_t> read_rfork(int _fd, std::error_code &ec) {
|
|
std::vector<uint8_t> rv;
|
|
|
|
for(;;) {
|
|
ssize_t size = 0;
|
|
ssize_t tsize = 0;
|
|
|
|
rv.clear();
|
|
ec.clear();
|
|
size = _(::size_xattr(_fd, XATTR_RESOURCEFORK_NAME), ec);
|
|
if (ec) return rv;
|
|
|
|
if (size == 0) return rv;
|
|
rv.resize(size);
|
|
|
|
tsize = _(::read_xattr(_fd, XATTR_RESOURCEFORK_NAME, rv.data(), size), ec);
|
|
if (ec) {
|
|
if (ec.value() == ERANGE) continue;
|
|
rv.clear();
|
|
return rv;
|
|
}
|
|
rv.resize(tsize);
|
|
return rv;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool resource_fork::open(const std::string &path, open_mode mode, std::error_code &ec) {
|
|
close();
|
|
ec.clear();
|
|
|
|
_fd = openX(path, ec);
|
|
if (ec) return false;
|
|
|
|
_mode = mode;
|
|
_offset = 0;
|
|
return true;
|
|
}
|
|
|
|
size_t resource_fork::size(std::error_code &ec) {
|
|
ec.clear();
|
|
auto rv = _(::size_xattr(_fd, XATTR_RESOURCEFORK_NAME), ec);
|
|
if (ec) {
|
|
remap_enoattr(ec);
|
|
return 0;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
size_t resource_fork::read(void *buffer, size_t n, std::error_code &ec) {
|
|
ec.clear();
|
|
if (_fd < 0 || _mode == write_only) {
|
|
ec = std::make_error_code(std::errc::bad_file_descriptor);
|
|
return 0;
|
|
}
|
|
|
|
if (n == 0) return 0;
|
|
|
|
auto tmp = read_rfork(_fd, ec);
|
|
if (ec) {
|
|
remap_enoattr(ec);
|
|
return 0;
|
|
}
|
|
|
|
if (_offset >= tmp.size()) return 0;
|
|
size_t count = std::min(n, tmp.size() - _offset);
|
|
|
|
std::memcpy(buffer, tmp.data() + _offset, count);
|
|
_offset += count;
|
|
return count;
|
|
}
|
|
|
|
size_t resource_fork::write(const void *buffer, size_t n, std::error_code &ec) {
|
|
ec.clear();
|
|
if (_fd < 0 || _mode == read_only) {
|
|
ec = std::make_error_code(std::errc::bad_file_descriptor);
|
|
return 0;
|
|
}
|
|
|
|
if (n == 0) return 0;
|
|
|
|
auto tmp = read_rfork(_fd, ec);
|
|
if (ec) {
|
|
remap_enoattr(ec);
|
|
return 0;
|
|
}
|
|
|
|
if (_offset > tmp.size()) {
|
|
tmp.resize(_offset);
|
|
}
|
|
tmp.insert(tmp.end(), (const uint8_t *)buffer, (const uint8_t *)buffer + n);
|
|
|
|
auto rv = _(::write_xattr(_fd, XATTR_RESOURCEFORK_NAME, tmp.data(), tmp.size()), ec);
|
|
if (ec) {
|
|
remap_enoattr(ec);
|
|
return 0;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
bool resource_fork::truncate(size_t pos, std::error_code &ec) {
|
|
|
|
ec.clear();
|
|
if (_fd < 0 || _mode == read_only) {
|
|
ec = std::make_error_code(std::errc::bad_file_descriptor);
|
|
return 0;
|
|
}
|
|
|
|
// simple case..
|
|
if (pos == 0) {
|
|
auto rv = _(::remove_xattr(_fd, XATTR_RESOURCEFORK_NAME), ec);
|
|
if (ec) {
|
|
remap_enoattr(ec); // consider that ok?
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
auto tmp = read_rfork(_fd, ec);
|
|
if (ec) {
|
|
remap_enoattr(ec);
|
|
return false;
|
|
}
|
|
if (tmp.size() == pos) return true;
|
|
tmp.resize(pos);
|
|
auto rv = _(::write_xattr(_fd, XATTR_RESOURCEFORK_NAME, tmp.data(), pos), ec);
|
|
if (ec) {
|
|
remap_enoattr(ec);
|
|
return false;
|
|
}
|
|
|
|
_offset = pos;
|
|
return true;
|
|
}
|
|
bool resource_fork::seek(size_t pos, std::error_code &ec) {
|
|
ec.clear();
|
|
if (_fd < 0) {
|
|
ec = std::make_error_code(std::errc::bad_file_descriptor);
|
|
return 0;
|
|
}
|
|
|
|
_offset = pos;
|
|
return true;
|
|
}
|
|
|
|
|
|
bool resource_fork::remove(const std::string &path, std::error_code &ec) {
|
|
ec.clear();
|
|
|
|
int fd = openX(path, ec);
|
|
if (ec) return false;
|
|
|
|
int rv = _(::remove_xattr(fd, XATTR_RESOURCEFORK_NAME), ec);
|
|
::close(fd);
|
|
|
|
if (rv < 0) {
|
|
remap_enoattr(ec);
|
|
if (ec.value() == ENODATA) return true;
|
|
}
|
|
return rv == 0;
|
|
|
|
}
|
|
|
|
size_t resource_fork::write(const std::string &path, const void *buffer, size_t n, std::error_code &ec) {
|
|
ec.clear();
|
|
|
|
int fd = openX(path, ec);
|
|
if (ec) return false;
|
|
|
|
auto rv = _(::write_xattr(fd, XATTR_RESOURCEFORK_NAME, buffer, n), ec);
|
|
::close(fd);
|
|
|
|
if (rv < 0) {
|
|
remap_enoattr(ec);
|
|
return 0;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
size_t resource_fork::size(const std::string &path, std::error_code &ec) {
|
|
resource_fork rf;
|
|
rf.open(path, read_only, ec);
|
|
if (ec) return 0;
|
|
return rf.size(ec);
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
size_t resource_fork::size(const std::wstring &path, std::error_code &ec) {
|
|
resource_fork rf;
|
|
rf.open(path, read_only, ec);
|
|
if (ec) return 0;
|
|
return rf.size(ec);
|
|
}
|
|
#endif
|
|
|
|
}
|