#ifdef BUILDING_FS_SHELL
# include "compat.h"
# define B_OK 0
# define B_BAD_VALUE EINVAL
# define B_FILE_ERROR EBADF
# define B_ERROR EINVAL
# define B_ENTRY_NOT_FOUND ENOENT
# define B_NO_MEMORY ENOMEM
#else
# include <BeOSBuildCompatibility.h>
# include <syscalls.h>
# include "fs_impl.h"
# include "fs_descriptors.h"
#endif
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <map>
#include <string>
#include <fs_attr.h>
#if defined(HAIKU_HOST_PLATFORM_LINUX)
# include "fs_attr_xattr.h"
#elif defined(HAIKU_HOST_PLATFORM_FREEBSD) \
|| defined(HAIKU_HOST_PLATFORM_NETBSD)
# include "fs_attr_extattr.h"
#elif defined(HAIKU_HOST_PLATFORM_DARWIN)
# include "fs_attr_bsdxattr.h"
#else
# error No attribute support for this host platform!
#endif
namespace BPrivate {}
using namespace BPrivate;
using std::map;
using std::string;
static const size_t kMaxAttributeListingLength = 10240;
static const size_t kMaxAttributeLength = 10240 * 4;
#ifdef HEX_ENCODE_ATTRIBUTE_NAMES
static string
mangle_attribute_name(const char* name)
{
string mangledName = kAttributeNamespace;
static const char* const kHexChars = "0123456789abcdef";
for (int i = 0; name[i] != '\0'; i++) {
mangledName += kHexChars[name[i] >> 4];
mangledName += kHexChars[name[i] & 0xf];
}
return mangledName;
}
static bool
demangle_attribute_name(const char* name, string& demangledName)
{
if (strncmp(name, kAttributeNamespace, kAttributeNamespaceLen) != 0)
return false;
name += kAttributeNamespaceLen;
demangledName = "";
for (int i = 0; name[i] != '\0'; i += 2) {
char c[3];
c[2] = '\0';
memcpy(c, name + i, 2);
char out;
out = strtol(c, NULL, 16);
if (out == '\0')
return false;
demangledName += out;
}
return true;
}
#else
static string
mangle_attribute_name(const char* name)
{
string mangledName = kAttributeNamespace;
for (int i = 0; name[i] != '\0'; i++) {
char c = name[i];
switch (c) {
case '/':
mangledName += "%\\";
break;
case '%':
mangledName += "%%";
break;
default:
mangledName += c;
break;
}
}
return mangledName;
}
static bool
demangle_attribute_name(const char* name, string& demangledName)
{
if (strncmp(name, kAttributeNamespace, kAttributeNamespaceLen) != 0)
return false;
name += kAttributeNamespaceLen;
demangledName = "";
for (int i = 0; name[i] != '\0'; i++) {
char c = name[i];
if (c == '%') {
c = name[++i];
if (c == '%')
demangledName += c;
else if (c == '\\')
demangledName += '/';
else
return false;
} else
demangledName += c;
}
return true;
}
#endif
namespace {
class AttributeDirectory;
typedef map<DIR*, AttributeDirectory*> AttrDirMap;
static AttrDirMap sAttributeDirectories;
struct LongDirEntry {
char _[sizeof(struct dirent) + B_FILE_NAME_LENGTH + 1];
struct dirent* dirent() { return (struct dirent*)_; }
};
struct AttributeHeader {
uint32 type;
};
class AttributeDirectory {
public:
AttributeDirectory()
: fFileFD(-1),
fFakeDir(NULL),
fListing(NULL),
fListingLength(-1),
fListingIndex(0)
{
}
~AttributeDirectory()
{
if (fFileFD >= 0)
close(fFileFD);
if (fFakeDir) {
AttrDirMap::iterator it = sAttributeDirectories.find(fFakeDir);
if (it != sAttributeDirectories.end())
sAttributeDirectories.erase(it);
closedir(fFakeDir);
}
free(fListing);
}
static AttributeDirectory* Get(DIR* dir)
{
AttrDirMap::iterator it = sAttributeDirectories.find(dir);
if (it == sAttributeDirectories.end())
return NULL;
return it->second;
}
status_t Init(const char* path, int fileFD)
{
if (!fFakeDir) {
fFakeDir = opendir(".");
if (!fFakeDir)
return B_ERROR;
sAttributeDirectories[fFakeDir] = this;
}
string tempPath;
if (!path) {
Descriptor* descriptor = get_descriptor(fileFD);
if (descriptor && !descriptor->IsSystemFD()) {
status_t error = descriptor->GetPath(tempPath);
if (error != B_OK)
return error;
path = tempPath.c_str();
fileFD = -1;
}
}
if (path) {
struct stat st;
if (lstat(path, &st))
return B_ENTRY_NOT_FOUND;
if (S_ISLNK(st.st_mode)) {
fFileFD = -1;
fPath = path;
} else {
fFileFD = open(path, O_RDONLY);
if (fFileFD < 0)
return errno;
fPath = "";
}
} else {
fFileFD = dup(fileFD);
if (fFileFD < 0)
return errno;
fPath = "";
}
fListingLength = -1;
fListingIndex = 0;
return B_OK;
}
DIR* FakeDir() const { return fFakeDir; }
status_t ReadDir(struct dirent** _entry)
{
status_t error = _CheckListing();
if (error != B_OK)
return error;
while (fListingIndex < fListingLength) {
const char* name = fListing + fListingIndex;
int nameLen = strlen(name);
fListingIndex += nameLen + 1;
string demangledName;
if (!demangle_attribute_name(name, demangledName))
continue;
name = demangledName.c_str();
nameLen = demangledName.length();
if (nameLen == 0) {
return B_ERROR;
}
strcpy(fDirent.dirent()->d_name, name);
fDirent.dirent()->d_ino = 0;
*_entry = fDirent.dirent();
return B_OK;
}
*_entry = NULL;
return B_OK;
}
void RewindDir()
{
fListingIndex = 0;
}
private:
status_t _CheckListing()
{
if (fListing && fListingLength >= 0)
return B_OK;
char listing[kMaxAttributeListingLength];
ssize_t length = list_attributes(fFileFD, fPath.c_str(), listing,
kMaxAttributeListingLength);
if (length < 0)
return errno;
char* newListing = (char*)realloc(fListing, length);
if (!newListing)
return B_NO_MEMORY;
memcpy(newListing, listing, length);
fListing = newListing;
fListingLength = length;
fListingIndex = 0;
return B_OK;
}
private:
int fFileFD;
string fPath;
DIR* fFakeDir;
LongDirEntry fDirent;
char* fListing;
int fListingLength;
int fListingIndex;
};
#include "LocalFD.h"
}
DIR *
fs_open_attr_dir(const char *path)
{
AttributeDirectory* attrDir = new AttributeDirectory;
status_t error = attrDir->Init(path, -1);
if (error != B_OK) {
errno = error;
delete attrDir;
return NULL;
}
return attrDir->FakeDir();
}
DIR *
fs_fopen_attr_dir(int fd)
{
AttributeDirectory* attrDir = new AttributeDirectory;
status_t error = attrDir->Init(NULL, fd);
if (error != B_OK) {
errno = error;
delete attrDir;
return NULL;
}
return attrDir->FakeDir();
}
int
fs_close_attr_dir(DIR *dir)
{
AttributeDirectory* attrDir = AttributeDirectory::Get(dir);
if (!attrDir) {
errno = B_BAD_VALUE;
return -1;
}
delete attrDir;
return 0;
}
struct dirent *
fs_read_attr_dir(DIR *dir)
{
AttributeDirectory* attrDir = AttributeDirectory::Get(dir);
if (!attrDir) {
errno = B_BAD_VALUE;
return NULL;
}
dirent* entry = NULL;
status_t error = attrDir->ReadDir(&entry);
if (error != B_OK) {
errno = error;
return NULL;
}
return entry;
}
void
fs_rewind_attr_dir(DIR *dir)
{
AttributeDirectory* attrDir = AttributeDirectory::Get(dir);
if (attrDir)
attrDir->RewindDir();
}
int
fs_fopen_attr(int fd, const char *attribute, uint32 type, int openMode)
{
if (fd < 0) {
errno = B_BAD_VALUE;
return -1;
}
AttributeDescriptor* descriptor = new(std::nothrow) AttributeDescriptor(fd,
attribute, type, openMode);
if (descriptor == NULL) {
errno = B_NO_MEMORY;
return -1;
}
status_t error = descriptor->Init();
if (error != B_OK) {
delete descriptor;
errno = error;
return -1;
}
int attributeFD = add_descriptor(descriptor);
if (attributeFD < 0) {
delete descriptor;
errno = B_NO_MEMORY;
return -1;
}
return attributeFD;
}
int
fs_close_attr(int fd)
{
status_t error = delete_descriptor(fd);
if (error != 0) {
errno = error;
return -1;
}
return 0;
}
ssize_t
fs_read_attr(int fd, const char *_attribute, uint32 type, off_t pos,
void *buffer, size_t readBytes)
{
if (pos < 0 || pos + readBytes > kMaxAttributeLength
|| !_attribute || !buffer) {
errno = B_BAD_VALUE;
return -1;
}
LocalFD localFD;
status_t error = localFD.Init(fd);
if (error != B_OK) {
errno = error;
return -1;
}
string attribute = mangle_attribute_name(_attribute);
char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength];
ssize_t bytesRead = sizeof(attributeBuffer);
if (localFD.Path()) {
bytesRead = get_attribute(-1, localFD.Path(), attribute.c_str(),
attributeBuffer, bytesRead);
} else {
bytesRead = get_attribute(localFD.FD(), NULL, attribute.c_str(),
attributeBuffer, bytesRead);
}
if (bytesRead < 0) {
if (errno == ENOATTR || errno == ENODATA)
errno = B_ENTRY_NOT_FOUND;
return -1;
}
if ((size_t)bytesRead < sizeof(AttributeHeader)) {
fprintf(stderr, "fs_read_attr(): attribute \"%s\" is shorter than the "
"AttributeHeader!\n", attribute.c_str());
errno = B_ERROR;
return -1;
}
bytesRead -= sizeof(AttributeHeader) + pos;
if (bytesRead < 0) {
errno = B_BAD_VALUE;
return -1;
}
if (bytesRead > 0) {
if ((size_t)bytesRead > readBytes)
bytesRead = readBytes;
memcpy(buffer, attributeBuffer + sizeof(AttributeHeader) + pos,
bytesRead);
}
return bytesRead;
}
ssize_t
fs_write_attr(int fd, const char *_attribute, uint32 type, off_t pos,
const void *buffer, size_t writeBytes)
{
if (pos < 0 || pos + writeBytes > kMaxAttributeLength
|| _attribute == NULL || (writeBytes > 0 && buffer == NULL)) {
errno = B_BAD_VALUE;
return -1;
}
LocalFD localFD;
status_t error = localFD.Init(fd);
if (error != B_OK) {
errno = error;
return -1;
}
string attribute = mangle_attribute_name(_attribute);
char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength];
AttributeHeader* header = (AttributeHeader*)attributeBuffer;
header->type = type;
memset(attributeBuffer + sizeof(AttributeHeader), 0, pos);
if (writeBytes > 0) {
memcpy(attributeBuffer + sizeof(AttributeHeader) + pos, buffer,
writeBytes);
}
ssize_t toWrite = sizeof(AttributeHeader) + pos + writeBytes;
int result;
if (localFD.Path()) {
result = set_attribute(-1, localFD.Path(), attribute.c_str(),
attributeBuffer, toWrite);
} else {
result = set_attribute(localFD.FD(), NULL, attribute.c_str(),
attributeBuffer, toWrite);
}
if (result < 0) {
if (localFD.IsSymlink() && strcmp(_attribute, "BEOS:TYPE") == 0)
return writeBytes;
return -1;
}
return writeBytes;
}
int
fs_remove_attr(int fd, const char *_attribute)
{
if (!_attribute) {
errno = B_BAD_VALUE;
return -1;
}
LocalFD localFD;
status_t error = localFD.Init(fd);
if (error != B_OK) {
errno = error;
return -1;
}
string attribute = mangle_attribute_name(_attribute);
int result;
if (localFD.Path())
result = remove_attribute(-1, localFD.Path(), attribute.c_str());
else
result = remove_attribute(localFD.FD(), NULL, attribute.c_str());
if (result < 0) {
if (errno == ENOATTR || errno == ENODATA)
errno = B_ENTRY_NOT_FOUND;
return -1;
}
return 0;
}
int
fs_stat_attr(int fd, const char *_attribute, struct attr_info *attrInfo)
{
if (!_attribute || !attrInfo) {
errno = B_BAD_VALUE;
return -1;
}
LocalFD localFD;
status_t error = localFD.Init(fd);
if (error != B_OK) {
errno = error;
return -1;
}
string attribute = mangle_attribute_name(_attribute);
char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength];
ssize_t bytesRead = sizeof(AttributeHeader) + kMaxAttributeLength;
if (localFD.Path()) {
bytesRead = get_attribute(-1, localFD.Path(), attribute.c_str(),
attributeBuffer, bytesRead);
} else {
bytesRead = get_attribute(localFD.FD(), NULL, attribute.c_str(),
attributeBuffer, bytesRead);
}
if (bytesRead < 0) {
if (errno == ENOATTR || errno == ENODATA)
errno = B_ENTRY_NOT_FOUND;
return -1;
}
if ((size_t)bytesRead < sizeof(AttributeHeader)) {
fprintf(stderr, "fs_stat_attr(): attribute \"%s\" is shorter than the "
"AttributeHeader!\n", attribute.c_str());
errno = B_ERROR;
return -1;
}
attrInfo->size = bytesRead - sizeof(AttributeHeader);
attrInfo->type = ((AttributeHeader*)attributeBuffer)->type;
return 0;
}
#ifndef BUILDING_FS_SHELL
int
_kern_open_attr_dir(int fd, const char *path)
{
struct stat st;
status_t error = _kern_read_stat(fd, path, false, &st,
sizeof(struct stat));
if (error != B_OK) {
errno = error;
return -1;
}
NodeRef ref(st);
DIR* dir;
if (path) {
string realPath;
status_t error = get_path(fd, path, realPath);
if (error != B_OK)
return error;
dir = fs_open_attr_dir(realPath.c_str());
} else
dir = fs_fopen_attr_dir(fd);
if (!dir)
return errno;
AttrDirDescriptor *descriptor = new AttrDirDescriptor(dir, ref);
return add_descriptor(descriptor);
}
status_t
_kern_rename_attr(int fromFile, const char *fromName, int toFile,
const char *toName)
{
return B_BAD_VALUE;
}
status_t
_kern_remove_attr(int fd, const char *name)
{
if (!name)
return B_BAD_VALUE;
if (fs_remove_attr(fd, name) < 0)
return errno;
return B_OK;
}
#endif