mirror of
https://github.com/ksherlock/afp.git
synced 2025-02-08 09:31:00 +00:00
first version (sort of)
This commit is contained in:
commit
f398ac94f0
12
CMakeLists.txt
Normal file
12
CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||
set(CMAKE_CXX_EXTENSIONS FALSE)
|
||||
|
||||
|
||||
add_library(afp src/finder_info.cpp src/resource_fork.cpp src/xattr.c)
|
||||
|
||||
target_include_directories(afp INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include/)
|
||||
target_include_directories(afp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/afp)
|
147
include/afp/finder_info.h
Normal file
147
include/afp/finder_info.h
Normal file
@ -0,0 +1,147 @@
|
||||
#ifndef __afp_finder_info_h__
|
||||
#define __afp_finder_info_h__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
#include <system_error>
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
#pragma pack(push, 2)
|
||||
struct AFP_Info {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint32_t file_id;
|
||||
uint32_t backup_date;
|
||||
uint8_t finder_info[32];
|
||||
uint16_t prodos_file_type;
|
||||
uint32_t prodos_aux_type;
|
||||
uint8_t reserved[6];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
||||
|
||||
namespace afp {
|
||||
class finder_info {
|
||||
|
||||
public:
|
||||
|
||||
enum open_mode {
|
||||
read_only = 1,
|
||||
write_only = 2,
|
||||
read_write = 3,
|
||||
};
|
||||
|
||||
finder_info();
|
||||
~finder_info();
|
||||
|
||||
finder_info(const finder_info &) = delete;
|
||||
finder_info(finder_info &&) = delete;
|
||||
|
||||
finder_info& operator=(const finder_info &) = delete;
|
||||
finder_info& operator=(finder_info &&) = delete;
|
||||
|
||||
|
||||
const uint8_t *data() const {
|
||||
#if defined(_WIN32)
|
||||
return _afp.finder_info;
|
||||
#else
|
||||
return _finder_info;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint8_t *data() {
|
||||
#if defined(_WIN32)
|
||||
return _afp.finder_info;
|
||||
#else
|
||||
return _finder_info;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool read(const std::string &fname, std::error_code &ec) {
|
||||
return open(fname, read_only, ec);
|
||||
}
|
||||
|
||||
bool write(const std::string &fname, std::error_code &ec);
|
||||
|
||||
bool open(const std::string &fname, open_mode perm, std::error_code &ec);
|
||||
bool open(const std::string &fname, std::error_code &ec) {
|
||||
return open(fname, read_only, ec);
|
||||
}
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
bool read(const std::wstring &pathName, std::error_code &ec) {
|
||||
return open(pathName, read_only, ec);
|
||||
}
|
||||
|
||||
bool write(const std::wstring &pathName, std::error_code &ec);
|
||||
|
||||
bool open(const std::wstring &fname, open_mode perm, std::error_code &ec);
|
||||
bool open(const std::wstring &fname, std::error_code &ec) {
|
||||
return open(fname, read_only, ec);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
bool write(std::error_code &ec);
|
||||
|
||||
uint32_t creator_type() const;
|
||||
uint32_t file_type() const;
|
||||
|
||||
uint16_t prodos_file_type() const {
|
||||
#if defined(_WIN32)
|
||||
return _afp.prodos_file_type;
|
||||
#else
|
||||
return _prodos_file_type;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t prodos_aux_type() const {
|
||||
#if defined(_WIN32)
|
||||
return _afp.prodos_aux_type;
|
||||
#else
|
||||
return _prodos_aux_type;
|
||||
#endif
|
||||
}
|
||||
|
||||
void set_prodos_file_type(uint16_t);
|
||||
void set_prodos_file_type(uint16_t, uint32_t);
|
||||
|
||||
void set_file_type(uint32_t);
|
||||
void set_creator_type(uint32_t);
|
||||
|
||||
bool is_text() const;
|
||||
bool is_binary() const;
|
||||
|
||||
private:
|
||||
|
||||
void close();
|
||||
|
||||
#if defined(_WIN32)
|
||||
bool write(void *handle, std::error_code &ec);
|
||||
bool read(void *handle, std::error_code &ec);
|
||||
#else
|
||||
bool write(int fd, std::error_code &ec);
|
||||
bool read(int fd, std::error_code &ec);
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
void *_fd = (void *)-1;
|
||||
AFP_Info _afp;
|
||||
#else
|
||||
int _fd = -1;
|
||||
|
||||
uint16_t _prodos_file_type = 0;
|
||||
uint32_t _prodos_aux_type = 0;
|
||||
uint8_t _finder_info[32] = {};
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
70
include/afp/resource_fork.h
Normal file
70
include/afp/resource_fork.h
Normal file
@ -0,0 +1,70 @@
|
||||
#ifndef __afp_resource_fork_h__
|
||||
#define __afp_resource_fork_h__
|
||||
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
namespace afp {
|
||||
|
||||
class resource_fork {
|
||||
|
||||
public:
|
||||
enum open_mode {
|
||||
read_only = 1,
|
||||
write_only = 2,
|
||||
read_write = 3,
|
||||
};
|
||||
|
||||
resource_fork() = default;
|
||||
resource_fork(const resource_fork &) = delete;
|
||||
resource_fork(resource_fork &&rhs);
|
||||
|
||||
resource_fork& operator=(const resource_fork &rhs) = delete;
|
||||
resource_fork& operator=(resource_fork &&rhs);
|
||||
|
||||
|
||||
~resource_fork() { close(); }
|
||||
|
||||
static size_t size(const std::string &path, std::error_code &ec);
|
||||
#ifdef _WIN32
|
||||
static size_t size(const std::wstring &path, std::error_code &ec);
|
||||
#endif
|
||||
|
||||
bool open(const std::string &s, open_mode mode, std::error_code &ec);
|
||||
|
||||
bool open(const std::string &s, std::error_code &ec) {
|
||||
return open(s, read_only, ec);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
bool open(const std::wstring &s, open_mode mode, std::error_code &ec);
|
||||
bool open(const std::wstring &s, std::error_code &ec) {
|
||||
return open(s, read_only, ec);
|
||||
}
|
||||
#endif
|
||||
|
||||
void close();
|
||||
|
||||
size_t read(void *buffer, size_t n, std::error_code &);
|
||||
size_t write(const void *buffer, size_t n, std::error_code &);
|
||||
bool truncate(size_t pos, std::error_code &ec);
|
||||
bool seek(size_t pos, std::error_code &ec);
|
||||
size_t size(std::error_code &ec);
|
||||
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
void *_fd = (void *)-1;
|
||||
#else
|
||||
int _fd = -1;
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(_AIX)
|
||||
size_t _offset = 0;
|
||||
open_mode _mode = read_only;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
27
include/afp/xattr.h
Normal file
27
include/afp/xattr.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef xattr_h
|
||||
#define xattr_h
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef ENOATTR
|
||||
#define ENOATTR ENODATA
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
ssize_t size_xattr(int fd, const char *xattr);
|
||||
ssize_t read_xattr(int fd, const char *xattr, void *buffer, size_t size);
|
||||
ssize_t write_xattr(int fd, const char *xattr, const void *buffer, size_t size);
|
||||
int remove_xattr(int fd, const char *xattr);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
743
src/finder_info.cpp
Normal file
743
src/finder_info.cpp
Normal file
@ -0,0 +1,743 @@
|
||||
#include "finder_info.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <string>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <sys/xattr.h>
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/xattr.h>
|
||||
#define XATTR_FINDERINFO_NAME "user.com.apple.FinderInfo"
|
||||
#define XATTR_RESOURCEFORK_NAME "user.com.apple.ResourceFork"
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
#include <sys/types.h>
|
||||
#include <sys/extattr.h>
|
||||
#endif
|
||||
|
||||
#if defined(_AIX)
|
||||
#include <sys/ea.h>
|
||||
#endif
|
||||
|
||||
|
||||
#if defined (_WIN32)
|
||||
#include <windows.h>
|
||||
#define XATTR_FINDERINFO_NAME "AFP_AfpInfo"
|
||||
#endif
|
||||
|
||||
#ifndef XATTR_FINDERINFO_NAME
|
||||
#define XATTR_FINDERINFO_NAME "com.apple.FinderInfo"
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define _prodos_file_type _afp.prodos_file_type
|
||||
#define _prodos_aux_type _afp.prodos_aux_type
|
||||
#define _finder_info _afp.finder_info
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
/*
|
||||
|
||||
tech note PT515
|
||||
ProDOS -> Macintosh conversion
|
||||
|
||||
ProDOS Macintosh
|
||||
Filetype Auxtype Creator Filetype
|
||||
$00 $0000 'pdos' 'BINA'
|
||||
$B0 (SRC) (any) 'pdos' 'TEXT'
|
||||
$04 (TXT) $0000 'pdos' 'TEXT'
|
||||
$FF (SYS) (any) 'pdos' 'PSYS'
|
||||
$B3 (S16) (any) 'pdos' 'PS16'
|
||||
$uv $wxyz 'pdos' 'p' $uv $wx $yz
|
||||
|
||||
Programmer's Reference for System 6.0:
|
||||
|
||||
ProDOS Macintosh
|
||||
File Type Auxiliary Type Creator Type File Type
|
||||
$00 $0000 'pdos' 'BINA'
|
||||
$04 (TXT) $0000 'pdos' 'TEXT'
|
||||
$FF (SYS) (any) 'pdos' 'PSYS'
|
||||
$B3 (S16) $DByz 'pdos' 'p' $B3 $DB $yz
|
||||
$B3 (S16) (any) 'pdos' 'PS16'
|
||||
$D7 $0000 'pdos' 'MIDI'
|
||||
$D8 $0000 'pdos' 'AIFF'
|
||||
$D8 $0001 'pdos' 'AIFC'
|
||||
$E0 $0005 'dCpy' 'dImg'
|
||||
$FF (SYS) (any) 'pdos' 'PSYS'
|
||||
$uv $wxyz 'pdos' 'p' $uv $wx $yz
|
||||
|
||||
|
||||
mpw standard:
|
||||
$uv (any) "pdos" printf("%02x ",$uv)
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
int hex(uint8_t c)
|
||||
{
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'a' && c <= 'f') return c + 10 - 'a';
|
||||
if (c >= 'A' && c <= 'F') return c + 10 - 'A';
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool finder_info_to_filetype(const uint8_t *buffer, uint16_t *file_type, uint32_t *aux_type) {
|
||||
|
||||
if (!memcmp("pdos", buffer + 4, 4))
|
||||
{
|
||||
if (buffer[0] == 'p') {
|
||||
*file_type = buffer[1];
|
||||
*aux_type = (buffer[2] << 8) | buffer[3];
|
||||
return true;
|
||||
}
|
||||
if (!memcmp("PSYS", buffer, 4)) {
|
||||
*file_type = 0xff;
|
||||
*aux_type = 0x0000;
|
||||
return true;
|
||||
}
|
||||
if (!memcmp("PS16", buffer, 4)) {
|
||||
*file_type = 0xb3;
|
||||
*aux_type = 0x0000;
|
||||
return true;
|
||||
}
|
||||
|
||||
// old mpw method for encoding.
|
||||
if (!isxdigit(buffer[0]) && isxdigit(buffer[1]) && buffer[2] == ' ' && buffer[3] == ' ')
|
||||
{
|
||||
*file_type = (hex(buffer[0]) << 8) | hex(buffer[1]);
|
||||
*aux_type = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!memcmp("TEXT", buffer, 4)) {
|
||||
*file_type = 0x04;
|
||||
*aux_type = 0x0000;
|
||||
return true;
|
||||
}
|
||||
if (!memcmp("BINA", buffer, 4)) {
|
||||
*file_type = 0x00;
|
||||
*aux_type = 0x0000;
|
||||
return true;
|
||||
}
|
||||
if (!memcmp("dImgdCpy", buffer, 8)) {
|
||||
*file_type = 0xe0;
|
||||
*aux_type = 0x0005;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!memcmp("MIDI", buffer, 4)) {
|
||||
*file_type = 0xd7;
|
||||
*aux_type = 0x0000;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!memcmp("AIFF", buffer, 4)) {
|
||||
*file_type = 0xd8;
|
||||
*aux_type = 0x0000;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!memcmp("AIFC", buffer, 4)) {
|
||||
*file_type = 0xd8;
|
||||
*aux_type = 0x0001;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool file_type_to_finder_info(uint8_t *buffer, uint16_t file_type, uint32_t aux_type) {
|
||||
if (file_type > 0xff || aux_type > 0xffff) return false;
|
||||
|
||||
if (!file_type && aux_type == 0x0000) {
|
||||
memcpy(buffer, "BINApdos", 8);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (file_type == 0x04 && aux_type == 0x0000) {
|
||||
memcpy(buffer, "TEXTpdos", 8);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (file_type == 0xff && aux_type == 0x0000) {
|
||||
memcpy(buffer, "PSYSpdos", 8);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (file_type == 0xb3 && aux_type == 0x0000) {
|
||||
memcpy(buffer, "PS16pdos", 8);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (file_type == 0xd7 && aux_type == 0x0000) {
|
||||
memcpy(buffer, "MIDIpdos", 8);
|
||||
return true;
|
||||
}
|
||||
if (file_type == 0xd8 && aux_type == 0x0000) {
|
||||
memcpy(buffer, "AIFFpdos", 8);
|
||||
return true;
|
||||
}
|
||||
if (file_type == 0xd8 && aux_type == 0x0001) {
|
||||
memcpy(buffer, "AIFCpdos", 8);
|
||||
return true;
|
||||
}
|
||||
if (file_type == 0xe0 && aux_type == 0x0005) {
|
||||
memcpy(buffer, "dImgdCpy", 8);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
memcpy(buffer, "p pdos", 8);
|
||||
buffer[1] = (file_type) & 0xff;
|
||||
buffer[2] = (aux_type >> 8) & 0xff;
|
||||
buffer[3] = (aux_type) & 0xff;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
T _(const T t, std::error_code &ec) {
|
||||
if (t < 0) ec = std::error_code(errno, std::generic_category());
|
||||
return t;
|
||||
}
|
||||
|
||||
/*
|
||||
* extended attributes functions.
|
||||
*/
|
||||
#if defined(__APPLE__)
|
||||
ssize_t size_xattr(int fd, const char *xattr) {
|
||||
return fgetxattr(fd, xattr, NULL, 0, 0, 0);
|
||||
}
|
||||
|
||||
ssize_t read_xattr(int fd, const char *xattr, void *buffer, size_t size) {
|
||||
return fgetxattr(fd, xattr, buffer, size, 0, 0);
|
||||
}
|
||||
|
||||
ssize_t write_xattr(int fd, const char *xattr, const void *buffer, size_t size) {
|
||||
if (fsetxattr(fd, xattr, buffer, size, 0, 0) < 0) return -1;
|
||||
return size;
|
||||
}
|
||||
|
||||
int remove_xattr(int fd, const char *xattr) {
|
||||
return fremovexattr(fd, xattr, 0);
|
||||
}
|
||||
|
||||
#elif defined(__linux__)
|
||||
ssize_t size_xattr(int fd, const char *xattr) {
|
||||
return fgetxattr(fd, xattr, NULL, 0);
|
||||
}
|
||||
|
||||
ssize_t read_xattr(int fd, const char *xattr, void *buffer, size_t size) {
|
||||
return fgetxattr(fd, xattr, buffer, size);
|
||||
}
|
||||
|
||||
ssize_t write_xattr(int fd, const char *xattr, const void *buffer, size_t size) {
|
||||
if (fsetxattr(fd, xattr, buffer, size, 0) < 0) return -1;
|
||||
return size;
|
||||
}
|
||||
|
||||
int remove_xattr(int fd, const char *xattr) {
|
||||
return fremovexattr(fd, xattr);
|
||||
}
|
||||
|
||||
#elif defined(__FreeBSD__)
|
||||
ssize_t size_xattr(int fd, const char *xattr) {
|
||||
return extattr_get_fd(fd, EXTATTR_NAMESPACE_USER, xattr, NULL, 0);
|
||||
}
|
||||
|
||||
ssize_t read_xattr(int fd, const char *xattr, void *buffer, size_t size) {
|
||||
return extattr_get_fd(fd, EXTATTR_NAMESPACE_USER, xattr, buffer, size);
|
||||
}
|
||||
|
||||
ssize_t write_xattr(int fd, const char *xattr, const void *buffer, size_t size) {
|
||||
return extattr_set_fd(fd, EXTATTR_NAMESPACE_USER, xattr, buffer, size);
|
||||
}
|
||||
|
||||
int remove_xattr(int fd, const char *xattr) {
|
||||
return extattr_delete_fd(fd, EXTATTR_NAMESPACE_USER, xattr);
|
||||
}
|
||||
|
||||
#elif defined(_AIX)
|
||||
ssize_t size_xattr(int fd, const char *xattr) {
|
||||
/*
|
||||
struct stat64x st;
|
||||
if (fstatea(fd, xattr, &st) < 0) return -1;
|
||||
return st.st_size;
|
||||
*/
|
||||
return fgetea(fd, xattr, NULL, 0);
|
||||
}
|
||||
|
||||
ssize_t read_xattr(int fd, const char *xattr, void *buffer, size_t size) {
|
||||
return fgetea(fd, xattr, buffer, size);
|
||||
}
|
||||
|
||||
ssize_t write_xattr(int fd, const char *xattr, const void *buffer, size_t size) {
|
||||
if (fsetea(fd, xattr, buffer, size, 0) < 0) return -1;
|
||||
return size;
|
||||
}
|
||||
|
||||
int remove_xattr(int fd, const char *xattr) {
|
||||
return fremoveea(fd, xattr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void set_or_throw_error(std::error_code *ec, int error, const std::string &what) {
|
||||
if (ec) *ec = std::error_code(error, std::system_category());
|
||||
else throw std::system_error(error, std::system_category(), what);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
/*
|
||||
* allocating a new string could reset GetLastError() to 0.
|
||||
*/
|
||||
void set_or_throw_error(std::error_code *ec, const char *what) {
|
||||
auto e = GetLastError();
|
||||
set_or_throw_error(ec, e, what);
|
||||
}
|
||||
|
||||
void set_or_throw_error(std::error_code *ec, const std::string &what) {
|
||||
auto e = GetLastError();
|
||||
set_or_throw_error(ec, e, what);
|
||||
}
|
||||
|
||||
template<class ...Args>
|
||||
HANDLE CreateFileX(const std::string &s, Args... args) {
|
||||
return CreateFileA(s.c_str(), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class ...Args>
|
||||
HANDLE CreateFileX(const std::wstring &s, Args... args) {
|
||||
return CreateFileW(s.c_str(), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void fi_close(void *fd) {
|
||||
CloseHandle(fd);
|
||||
}
|
||||
|
||||
void *fi_open(const std::string &path, int perm, std::error_code &ec) {
|
||||
|
||||
ec.clear();
|
||||
|
||||
std::string s(path);
|
||||
s.append(":" XATTR_FINDERINFO_NAME);
|
||||
|
||||
HANDLE fh;
|
||||
bool ro = perm == 1;
|
||||
|
||||
fh = CreateFileX(s,
|
||||
ro ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ,
|
||||
nullptr,
|
||||
ro ? OPEN_EXISTING : OPEN_ALWAYS,
|
||||
ro ? FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL,
|
||||
nullptr);
|
||||
|
||||
if (fh == INVALID_HANDLE_VALUE)
|
||||
set_or_throw_error(&ec, "CreateFile");
|
||||
|
||||
return fh;
|
||||
}
|
||||
|
||||
void *fi_open(const std::wstring &path, int perm, std::error_code &ec) {
|
||||
|
||||
ec.clear();
|
||||
|
||||
std::wstring s(path);
|
||||
s.append(L":" XATTR_FINDERINFO_NAME);
|
||||
|
||||
HANDLE fh;
|
||||
bool ro = perm == 1;
|
||||
|
||||
fh = CreateFileX(s,
|
||||
ro ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ,
|
||||
nullptr,
|
||||
ro ? OPEN_EXISTING : OPEN_ALWAYS,
|
||||
ro ? FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL,
|
||||
nullptr);
|
||||
|
||||
if (fh == INVALID_HANDLE_VALUE)
|
||||
set_or_throw_error(&ec, "CreateFile");
|
||||
|
||||
return fh;
|
||||
}
|
||||
|
||||
int fi_write(void *handle, const void *data, int length, std::error_code &ec) {
|
||||
|
||||
ec.clear();
|
||||
DWORD rv = 0;
|
||||
BOOL ok;
|
||||
|
||||
LARGE_INTEGER zero = { 0 };
|
||||
|
||||
ok = SetFilePointerEx(handle, zero, nullptr, FILE_BEGIN);
|
||||
if (!ok) {
|
||||
set_or_throw_error(&ec, "SetFilePointerEx");
|
||||
return 0;
|
||||
}
|
||||
ok = WriteFile(handle, data, length, &rv, nullptr);
|
||||
if (!ok) {
|
||||
set_or_throw_error(&ec, "WriteFile");
|
||||
return 0;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
int fi_read(void *handle, void *data, int length, std::error_code &ec) {
|
||||
|
||||
ec.clear();
|
||||
DWORD rv = 0;
|
||||
BOOL ok;
|
||||
LARGE_INTEGER zero = { 0 };
|
||||
|
||||
ok = SetFilePointerEx(handle, zero, nullptr, FILE_BEGIN);
|
||||
if (!ok) {
|
||||
set_or_throw_error(&ec, "SetFilePointerEx");
|
||||
return 0;
|
||||
}
|
||||
ok = ReadFile(handle, data, length, &rv, nullptr);
|
||||
if (!ok) {
|
||||
set_or_throw_error(&ec, "ReadFile");
|
||||
return 0;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void fi_close(int fd) {
|
||||
close(fd);
|
||||
}
|
||||
|
||||
int fi_open(const std::string &path, int perm, std::error_code &ec) {
|
||||
|
||||
#if defined(__sun__)
|
||||
if (perm == 1) return attropen(path.c_str(), XATTR_FINDERINFO_NAME, O_RDONLY);
|
||||
else return attropen(path.c_str(), XATTR_FINDERINFO_NAME, O_RDWR | O_CREAT, 0666);
|
||||
#else
|
||||
// linux needs to open as read/write to write it?
|
||||
//return open(path.c_str(), read_only ? O_RDONLY : O_RDWR);
|
||||
return open(path.c_str(), O_RDONLY);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int fi_write(int fd, const void *data, int length, std::error_code &ec) {
|
||||
#if defined(__sun__)
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
return write(fd, data, length);
|
||||
#else
|
||||
return write_xattr(fd, XATTR_FINDERINFO_NAME, data, length);
|
||||
#endif
|
||||
}
|
||||
|
||||
int fi_read(int fd, void *data, int length, std::error_code &ec) {
|
||||
#if defined(__sun__)
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
return read(fd, data, length);
|
||||
#else
|
||||
return read_xattr(fd, XATTR_FINDERINFO_NAME, data, length);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
void afp_init(struct AFP_Info *info) {
|
||||
//static_assert(sizeof(AFP_Info) == 60, "Incorrect AFP_Info size");
|
||||
memset(info, 0, sizeof(*info));
|
||||
info->magic = 0x00504641;
|
||||
info->version = 0x00010000;
|
||||
info->backup_date = 0x80000000;
|
||||
}
|
||||
|
||||
int afp_verify(struct AFP_Info *info) {
|
||||
if (!info) return 0;
|
||||
|
||||
if (info->magic != 0x00504641) return 0;
|
||||
if (info->version != 0x00010000) return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int afp_to_filetype(struct AFP_Info *info, uint16_t *file_type, uint32_t *aux_type) {
|
||||
// check for prodos ftype/auxtype...
|
||||
if (info->prodos_file_type || info->prodos_aux_type) {
|
||||
*file_type = info->prodos_file_type;
|
||||
*aux_type = info->prodos_aux_type;
|
||||
return 0;
|
||||
}
|
||||
int ok = finder_info_to_filetype(info->finder_info, file_type, aux_type);
|
||||
if (ok == 0) {
|
||||
info->prodos_file_type = *file_type;
|
||||
info->prodos_aux_type = *aux_type;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
trust_prodos,
|
||||
trust_hfs
|
||||
};
|
||||
|
||||
void afp_synchronize(struct AFP_Info *info, int trust) {
|
||||
// if ftype/auxtype is inconsistent between prodos and finder info, use
|
||||
// prodos as source of truth.
|
||||
uint16_t f = 0;
|
||||
uint32_t a = 0;
|
||||
if (finder_info_to_filetype(info->finder_info, &f, &a)) {
|
||||
if (f == info->prodos_file_type && a == info->prodos_aux_type) return;
|
||||
}
|
||||
|
||||
if (trust == trust_prodos)
|
||||
file_type_to_finder_info(info->finder_info, info->prodos_file_type, info->prodos_aux_type);
|
||||
else {
|
||||
info->prodos_file_type = f;
|
||||
info->prodos_aux_type = a;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
namespace afp {
|
||||
|
||||
finder_info::finder_info() {
|
||||
#if defined(_WIN32)
|
||||
afp_init(&_afp);
|
||||
#else
|
||||
memset(&_finder_info, 0, sizeof(_finder_info));
|
||||
#endif
|
||||
}
|
||||
|
||||
void finder_info::close() {
|
||||
#if _WIN32
|
||||
if (_fd != INVALID_HANDLE_VALUE) CloseHandle(_fd);
|
||||
_fd = INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
if (_fd >= 0) close(_fd);
|
||||
_fd = -1;
|
||||
#endif
|
||||
|
||||
}
|
||||
finder_info::~finder_info() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool finder_info::open(const std::string &name, open_mode perm, std::error_code &ec) {
|
||||
|
||||
ec.clear();
|
||||
|
||||
close();
|
||||
|
||||
if (perm < 1 || perm > 3) {
|
||||
ec = std::make_error_code(std::errc::invalid_argument);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto fd = fi_open(name, perm, ec);
|
||||
if (ec) return false;
|
||||
|
||||
// win32 should read even if write-only.
|
||||
bool ok = read(_fd, ec);
|
||||
|
||||
if (perm == read_only) {
|
||||
fi_close(fd);
|
||||
return ok;
|
||||
}
|
||||
|
||||
// write mode, so it's ok if it doesn't exist.
|
||||
if (!ok) ec.clear();
|
||||
_fd = fd;
|
||||
return true;
|
||||
}
|
||||
#if _WIN32
|
||||
|
||||
bool finder_info::open(const std::wstring &name, open_mode perm, std::error_code &ec) {
|
||||
|
||||
ec.clear();
|
||||
|
||||
close();
|
||||
|
||||
if (perm < 1 || perm > 3) {
|
||||
ec = std::make_error_code(std::errc::invalid_argument);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto fd = fi_open(name, perm, ec);
|
||||
if (ec) return false;
|
||||
|
||||
// win32 should read even if write-only.
|
||||
bool ok = read(_fd, ec);
|
||||
|
||||
if (perm == read_only) {
|
||||
fi_close(fd);
|
||||
return ok;
|
||||
}
|
||||
|
||||
// write mode, so it's ok if it doesn't exist.
|
||||
if (!ok) ec.clear();
|
||||
_fd = fd;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool finder_info::read(void *fd, std::error_code &ec) {
|
||||
|
||||
int ok = fi_read(fd, &_afp, sizeof(_afp), ec);
|
||||
if (ec) {
|
||||
afp_init(&_afp);
|
||||
return false;
|
||||
}
|
||||
if (ok < sizeof(_afp) || !afp_verify(&_afp)) {
|
||||
ec = std::make_error_code(std::errc::illegal_byte_sequence); // close enough!
|
||||
afp_init(&_afp);
|
||||
return false;
|
||||
}
|
||||
if (!_afp.prodos_file_type && !_afp.prodos_aux_type)
|
||||
afp_synchronize(&_afp, trust_hfs);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool finder_info::write(void *fd, std::error_code &ec) {
|
||||
return fi_write(fd, &_afp, sizeof(_afp), ec) == sizeof(_afp);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
bool finder_info::read(int fd, std::error_code &ec) {
|
||||
|
||||
int ok = fi_read(fd, &_finder_info, sizeof(_finder_info), ec);
|
||||
if (ok < 0) {
|
||||
memset(&_finder_info, 0, sizeof(_finder_info));
|
||||
return false;
|
||||
}
|
||||
finder_info_to_filetype(_finder_info, &_prodos_file_type, &_prodos_aux_type);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool finder_info::write(int fd, std::error_code &ec) {
|
||||
return fi_write(fd, &_finder_info, sizeof(_finder_info), ec) == sizeof(_finder_info);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool finder_info::write(std::error_code &ec) {
|
||||
ec.clear();
|
||||
return write(_fd, ec);
|
||||
}
|
||||
|
||||
|
||||
bool finder_info::write(const std::string &name, std::error_code &ec) {
|
||||
ec.clear();
|
||||
auto fd = fi_open(name, write_only, ec);
|
||||
|
||||
if (ec)
|
||||
return false;
|
||||
|
||||
bool ok = write(fd, ec);
|
||||
fi_close(fd);
|
||||
return ok;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
bool finder_info::write(const std::wstring &name, std::error_code &ec) {
|
||||
ec.clear();
|
||||
auto fd = fi_open(name, write_only, ec);
|
||||
|
||||
if (ec)
|
||||
return false;
|
||||
|
||||
bool ok = write(fd, ec);
|
||||
fi_close(fd);
|
||||
return ok;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void finder_info::set_prodos_file_type(uint16_t ftype, uint32_t atype) {
|
||||
_prodos_file_type = ftype;
|
||||
_prodos_aux_type = atype;
|
||||
file_type_to_finder_info(_finder_info, ftype, atype);
|
||||
}
|
||||
|
||||
|
||||
void finder_info::set_prodos_file_type(uint16_t ftype) {
|
||||
set_prodos_file_type(ftype, _prodos_aux_type);
|
||||
}
|
||||
|
||||
bool finder_info::is_text() const {
|
||||
if (memcmp(_finder_info, "TEXT", 4) == 0) return true;
|
||||
if (_prodos_file_type == 0x04) return true;
|
||||
if (_prodos_file_type == 0xb0) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool finder_info::is_binary() const {
|
||||
if (is_text()) return false;
|
||||
if (_prodos_file_type || _prodos_aux_type) return true;
|
||||
|
||||
if (memcmp(_finder_info, "\x00\x00\x00\x00\x00\x00\x00\x00", 8) == 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint32_t finder_info::file_type() const {
|
||||
uint32_t rv = 0;
|
||||
for (unsigned i = 0; i < 4; ++i) {
|
||||
rv <<= 8;
|
||||
rv |= _finder_info[i];
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
uint32_t finder_info::creator_type() const {
|
||||
uint32_t rv = 0;
|
||||
for (unsigned i = 4; i < 8; ++i) {
|
||||
rv <<= 8;
|
||||
rv |= _finder_info[i];
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
void finder_info::set_file_type(uint32_t x) {
|
||||
_finder_info[0] = x >> 24;
|
||||
_finder_info[1] = x >> 16;
|
||||
_finder_info[2] = x >> 8;
|
||||
_finder_info[3] = x >> 0;
|
||||
}
|
||||
|
||||
|
||||
void finder_info::set_creator_type(uint32_t x) {
|
||||
_finder_info[4] = x >> 24;
|
||||
_finder_info[5] = x >> 16;
|
||||
_finder_info[6] = x >> 8;
|
||||
_finder_info[7] = x >> 0;
|
||||
}
|
||||
|
||||
}
|
535
src/resource_fork.cpp
Normal file
535
src/resource_fork.cpp
Normal file
@ -0,0 +1,535 @@
|
||||
#include "resource_fork.h"
|
||||
#include "xattr.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#define XATTR_RESOURCEFORK_NAME "AFP_ResourceFork"
|
||||
#else
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE_
|
||||
#include <sys/xattr.h>
|
||||
#include <sys/paths.h>
|
||||
#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 {
|
||||
|
||||
void set_or_throw_error(std::error_code *ec, int error, const std::string &what) {
|
||||
if (ec) *ec = std::error_code(error, std::system_category());
|
||||
else throw std::system_error(error, std::system_category(), what);
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
/*
|
||||
* allocating a new string could reset GetLastError() to 0.
|
||||
*/
|
||||
void set_or_throw_error(std::error_code *ec, const char *what) {
|
||||
auto e = GetLastError();
|
||||
set_or_throw_error(ec, e, what);
|
||||
}
|
||||
|
||||
void set_or_throw_error(std::error_code *ec, const std::string &what) {
|
||||
auto e = GetLastError();
|
||||
set_or_throw_error(ec, e, what);
|
||||
}
|
||||
|
||||
template<class ...Args>
|
||||
HANDLE CreateFileX(const std::string &s, Args... args) {
|
||||
return CreateFileA(s.c_str(), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class ...Args>
|
||||
HANDLE CreateFileX(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 = CreateFileX(s, access, FILE_SHARE_READ, nullptr, create, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
set_or_throw_error(&ec, "CreateFile");
|
||||
}
|
||||
return h;
|
||||
}
|
||||
#else
|
||||
void set_or_throw_error(std::error_code *ec, const char *what) {
|
||||
auto e = errno
|
||||
set_or_throw_error(ec, e, what);
|
||||
}
|
||||
|
||||
void set_or_throw_error(std::error_code *ec, const std::string &what) {
|
||||
auto e = errno
|
||||
set_or_throw_error(ec, e, what);
|
||||
}
|
||||
|
||||
#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) {
|
||||
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();
|
||||
|
||||
std::string s(path);
|
||||
s += ":" XATTR_RESOURCEFORK_NAME;
|
||||
|
||||
_fd = CreateFileX(s, mode, ec);
|
||||
if (ec) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool resource_fork::open(const std::wstring &path, open_mode mode, std::error_code &ec) {
|
||||
ec.clear();
|
||||
close();
|
||||
|
||||
std::wstring s(path);
|
||||
s += L":" XATTR_RESOURCEFORK_NAME;
|
||||
|
||||
_fd = CreateFileX(s, mode, ec);
|
||||
if (ec) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
if (!ok) {
|
||||
set_or_throw_error(&ec, "ReadFile");
|
||||
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);
|
||||
if (!ok) {
|
||||
set_or_throw_error(&ec, "WriteFile");
|
||||
return 0;
|
||||
}
|
||||
return transferred;
|
||||
}
|
||||
|
||||
size_t resource_fork::size(std::error_code &ec) {
|
||||
ec.clear();
|
||||
LARGE_INTEGER ll = { };
|
||||
BOOL ok = GetFileSizeEx(_fd, &ll);
|
||||
if (!ok) {
|
||||
set_or_throw_error(&ec, "GetFileSizeEx");
|
||||
return 0;
|
||||
}
|
||||
return 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);
|
||||
if (!ok) {
|
||||
set_or_throw_error(&ec, "SetFilePointerEx");
|
||||
return false;
|
||||
}
|
||||
|
||||
ok = SetEndOfFile(_fd);
|
||||
if (!ok) {
|
||||
set_or_throw_error(&ec, "SetEndOfFile");
|
||||
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);
|
||||
if (!ok) {
|
||||
set_or_throw_error(&ec, "SetFilePointerEx");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
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;
|
||||
}
|
||||
_fd = attropen(path.c_str(), XATTR_RESOURCEFORK_NAME, umode, 0666);
|
||||
if (_fd < 0) {
|
||||
set_or_throw_error(ec, "attropen");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define FD_RESOURCE_FORK
|
||||
bool open(const std::string &path, open_mode mode, std::error_code &ec) {
|
||||
ec.clear();
|
||||
close();
|
||||
|
||||
std::string s(path);
|
||||
s += _PATH_RSRCFORKSPEC;
|
||||
|
||||
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);
|
||||
if (_fd < 0) {
|
||||
set_or_throw_error(ec, "open");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef FD_RESOURCE_FORK
|
||||
size_t resource_fork::read(void *buffer, size_t n, std::error_code &) {
|
||||
ec.clear();
|
||||
auto rv = read(_fd, buffer, n);
|
||||
if (rv < 0) {
|
||||
set_or_throw_error(ec, "read");
|
||||
return 0;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
size_t resource_fork::write(const void *buffer, size_t n, std::error_code &) {
|
||||
ec.clear();
|
||||
auto rv = write(_fd, buffer, n);
|
||||
if (rv < 0) {
|
||||
set_or_throw_error(ec, "write");
|
||||
return 0;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool resource_fork::truncate(size_t pos, std::error_code &ec) {
|
||||
ec.clear();
|
||||
if (ftruncate(_fd, pos) < 0) {
|
||||
set_or_throw_error(ec, "ftruncate");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool resource_fork::seek(size_t pos, std::error_code &ec) {
|
||||
ec.clear();
|
||||
if (lseek(_fd, pos, SEEK_SET) < 0) {
|
||||
set_or_throw_error(ec, "lseek");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t resource_fork::size(std::error_code &ec) {
|
||||
ec.clear();
|
||||
struct stat st;
|
||||
if (fstat(_fd, &st) < 0) {
|
||||
set_or_throw_error(ec, "fstat");
|
||||
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;
|
||||
|
||||
ec.clear();
|
||||
|
||||
for(;;) {
|
||||
ssize_t size = 0;
|
||||
ssize_t tsize = 0;
|
||||
|
||||
for(;;) {
|
||||
rv.clear();
|
||||
size = size_xattr(_fd, XATTR_RESOURCEFORK_NAME);
|
||||
if (size < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
if (errno == ENOATTR) {
|
||||
return rv;
|
||||
}
|
||||
set_or_throw_error(ec, "size_xattr");
|
||||
return rv;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (size == 0) return rv;
|
||||
rv.resize(size);
|
||||
|
||||
for(;;) {
|
||||
tsize = read_xattr(_fd, XATTR_RESOURCEFORK_NAME, tmp.data(), size);
|
||||
if (tsize < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
if (errno == ERANGE) break;
|
||||
if (errno == ENOATTR) {
|
||||
rv.clear();
|
||||
return rv;
|
||||
}
|
||||
set_or_throw_error(ec, "read_xattr");
|
||||
rv.clear();
|
||||
return rv;
|
||||
}
|
||||
rv.resize(tsize);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool resource_fork::open(const std::string &s, open_mode mode, std::error_code &ec) {
|
||||
close();
|
||||
ec.clear();
|
||||
_fd = open(s.c_str(), O_RDONLY);
|
||||
if (_fd < 0) {
|
||||
set_or_throw_error(ec, "open");
|
||||
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);
|
||||
if (rv < 0) {
|
||||
set_or_throw_error(ec, "size_xattr");
|
||||
return 0;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
size_t resource_fork::read(void *buffer, size_t n, std::error_code &ec) {
|
||||
ec.clear();
|
||||
if (_fd < 0) {
|
||||
set_or_throw_error(ec, EBADF, "read");
|
||||
return 0;
|
||||
}
|
||||
if (_mode == write_only) {
|
||||
set_or_throw_error(ec, EPERM, "read");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (n == 0) return 0;
|
||||
|
||||
auto tmp = read_rfork(_fd, ec);
|
||||
if (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) {
|
||||
set_or_throw_error(ec, EBADF, "write");
|
||||
return 0;
|
||||
}
|
||||
if (_mode == read_only) {
|
||||
set_or_throw_error(ec, EPERM, "write");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (n == 0) return 0;
|
||||
|
||||
auto tmp = read_rfork(_fd, ec);
|
||||
if (ec) return ec;
|
||||
|
||||
if (_offset > tmp.size()) {
|
||||
tmp.resize(_offset);
|
||||
}
|
||||
tmp.append((const uint8_t *)buffer, (const uint8_t *buffer) + n);
|
||||
|
||||
auto rv = write_xattr(_fd, XATTR_RESOURCEFORK_NAME, tmp.data(), tmp.size());
|
||||
if (rv < 0) {
|
||||
set_or_throw_error(ec, "write_xattr");
|
||||
return 0;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
bool resource_fork::truncate(size_t pos, std::error_code &ec) {
|
||||
|
||||
ec.clear();
|
||||
if (_fd < 0) {
|
||||
set_or_throw_error(ec, EBADF, "resource_fork::truncate");
|
||||
return 0;
|
||||
}
|
||||
if (_mode == read_only) {
|
||||
set_or_throw_error(ec, EPERM, "resource_fork::truncate");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// simple case..
|
||||
if (pos == 0) {
|
||||
auto rv = remove_xattr(_fd, XATTR_RESOURCEFORK_NAME);
|
||||
if (rv < 0) {
|
||||
set_or_throw_error(ec, "remove_xattr");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
auto tmp = read_rfork(_fd, ec);
|
||||
if (ec) return false;
|
||||
if (tmp.size() == pos) return true;
|
||||
tmp.resize(pos);
|
||||
auto rv = write_xattr(_fd, XATTR_RESOURCEFORK_NAME, tmp.data(), pos);
|
||||
if (rv < 0) {
|
||||
set_or_throw_error(ec, "write_xattr");
|
||||
return false;
|
||||
}
|
||||
_offset = pos;
|
||||
return true;
|
||||
}
|
||||
bool resource_fork::seek(size_t pos, std::error_code &ec) {
|
||||
ec.clear();
|
||||
if (_fd < 0) {
|
||||
set_or_throw_error(ec, EBADF, "truncate");
|
||||
return 0;
|
||||
}
|
||||
|
||||
_offset = pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#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);
|
||||