* Copyright 2005-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifdef BUILDING_FS_SHELL
# include "compat.h"
# define B_OK 0
# define B_FILE_ERROR EBADF
#else
# include <BeOSBuildCompatibility.h>
#endif
#include "fs_descriptors.h"
#include <map>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fs_attr.h>
#include <syscalls.h>
#include "fs_impl.h"
using std::map;
static const int kVirtualDescriptorStart = 10000;
typedef map<int, BPrivate::Descriptor*> DescriptorMap;
static DescriptorMap *sDescriptors;
namespace BPrivate {
static int
dup_maybe_system(int fd)
{
if (get_descriptor(fd) != NULL)
return _kern_dup(fd);
int clonedFD = dup(fd);
return clonedFD >= 0 ? clonedFD : errno;
}
static status_t
close_maybe_system(int fd)
{
if (get_descriptor(fd) != NULL)
return _kern_close(fd);
return close(fd) == 0 ? B_OK : errno;
}
Descriptor::~Descriptor()
{
}
bool
Descriptor::IsSystemFD() const
{
return false;
}
status_t
Descriptor::GetPath(string& path) const
{
return get_path(fd, NULL, path);
}
status_t
Descriptor::GetNodeRef(NodeRef &ref)
{
struct stat st;
status_t error = GetStat(false, &st);
if (error != B_OK)
return error;
ref = NodeRef(st);
return B_OK;
}
FileDescriptor::FileDescriptor(int fd)
{
this->fd = fd;
}
FileDescriptor::~FileDescriptor()
{
Close();
}
status_t
FileDescriptor::Close()
{
if (fd >= 0) {
int oldFD = fd;
fd = -1;
if (close(oldFD) < 0)
return errno;
}
return B_OK;
}
status_t
FileDescriptor::Dup(Descriptor *&clone)
{
int dupFD = dup(fd);
if (dupFD < 0)
return errno;
clone = new FileDescriptor(dupFD);
return B_OK;
}
status_t
FileDescriptor::GetStat(bool traverseLink, struct stat *st)
{
if (fstat(fd, st) < 0)
return errno;
return B_OK;
}
bool
FileDescriptor::IsSystemFD() const
{
return true;
}
DirectoryDescriptor::DirectoryDescriptor(DIR *dir, const NodeRef &ref)
{
this->dir = dir;
this->ref = ref;
}
DirectoryDescriptor::~DirectoryDescriptor()
{
Close();
}
status_t
DirectoryDescriptor::Close()
{
if (dir) {
DIR *oldDir = dir;
dir = NULL;
if (closedir(oldDir) < 0)
return errno;
}
return B_OK;
}
status_t
DirectoryDescriptor::Dup(Descriptor *&clone)
{
string path;
status_t error = get_path(fd, NULL, path);
if (error != B_OK)
return error;
DIR *dupDir = opendir(path.c_str());
if (!dupDir)
return errno;
clone = new DirectoryDescriptor(dupDir, ref);
return B_OK;
}
status_t
DirectoryDescriptor::GetStat(bool traverseLink, struct stat *st)
{
string realPath;
status_t error = get_path(fd, NULL, realPath);
if (error != B_OK)
return error;
int result;
result = stat(realPath.c_str(), st);
if (result < 0)
return errno;
return B_OK;
}
status_t
DirectoryDescriptor::GetNodeRef(NodeRef &ref)
{
ref = this->ref;
return B_OK;
}
SymlinkDescriptor::SymlinkDescriptor(const char *path)
{
this->path = path;
}
status_t
SymlinkDescriptor::Close()
{
return B_OK;
}
status_t
SymlinkDescriptor::Dup(Descriptor *&clone)
{
clone = new SymlinkDescriptor(path.c_str());
return B_OK;
}
status_t
SymlinkDescriptor::GetStat(bool traverseLink, struct stat *st)
{
int result;
if (traverseLink)
result = stat(path.c_str(), st);
else
result = lstat(path.c_str(), st);
if (result < 0)
return errno;
return B_OK;
}
status_t
SymlinkDescriptor::GetPath(string& path) const
{
path = this->path;
return B_OK;
}
AttributeDescriptor::AttributeDescriptor(int fileFD, const char* attribute,
uint32 type, int openMode)
:
fFileFD(dup_maybe_system(fileFD)),
fType(type),
fOpenMode(openMode),
fData(NULL),
fDataSize(0)
{
strlcpy(fAttribute, attribute, sizeof(fAttribute));
}
AttributeDescriptor::~AttributeDescriptor()
{
Close();
}
status_t
AttributeDescriptor::Init()
{
if (fFileFD < 0)
return B_IO_ERROR;
attr_info info;
if (fs_stat_attr(fFileFD, fAttribute, &info) < 0) {
if (errno == B_ENTRY_NOT_FOUND) {
if ((fOpenMode & O_CREAT) == 0)
return errno;
if (fs_write_attr(fFileFD, fAttribute, fType, 0, NULL, 0) < 0)
return errno;
return B_OK;
}
return errno;
}
if ((fOpenMode & O_TRUNC) == 0) {
if (fs_write_attr(fFileFD, fAttribute, fType, 0, NULL, 0) < 0)
return errno;
return B_OK;
}
if (info.size == 0)
return B_OK;
fData = (uint8*)malloc(info.size);
if (fData == NULL)
return B_NO_MEMORY;
fDataSize = info.size;
ssize_t bytesRead = fs_read_attr(fFileFD, fAttribute, fType, 0, fData,
fDataSize);
if (bytesRead < 0)
return errno;
if ((size_t)bytesRead != fDataSize)
return B_IO_ERROR;
return B_OK;
}
status_t
AttributeDescriptor::Write(off_t offset, const void* buffer, size_t bufferSize)
{
if (offset < 0)
return B_BAD_VALUE;
if ((fOpenMode & O_ACCMODE) != O_WRONLY
&& (fOpenMode & O_ACCMODE) != O_RDWR) {
return B_NOT_ALLOWED;
}
size_t minSize = (size_t)offset + bufferSize;
if (minSize > fDataSize) {
uint8* data = (uint8*)realloc(fData, minSize);
if (data == NULL)
return B_NO_MEMORY;
if ((size_t)offset > fDataSize)
memset(data + offset, 0, offset - fDataSize);
fData = data;
fDataSize = minSize;
}
if (bufferSize == 0)
return B_OK;
memcpy((uint8*)fData + offset, buffer, bufferSize);
ssize_t bytesWritten = fs_write_attr(fFileFD, fAttribute, fType, 0,
fData, fDataSize);
if (bytesWritten < 0)
return errno;
if ((size_t)bytesWritten != fDataSize)
return B_IO_ERROR;
return B_OK;
}
status_t
AttributeDescriptor::Close()
{
if (fFileFD < 0)
return B_BAD_VALUE;
close_maybe_system(fFileFD);
fFileFD = -1;
free(fData);
fData = NULL;
fDataSize = 0;
return B_OK;
}
status_t
AttributeDescriptor::Dup(Descriptor*& clone)
{
return B_NOT_SUPPORTED;
}
status_t
AttributeDescriptor::GetStat(bool traverseLink, struct stat* st)
{
return B_NOT_SUPPORTED;
}
AttrDirDescriptor::AttrDirDescriptor(DIR *dir, const NodeRef &ref)
: DirectoryDescriptor(dir, ref)
{
}
AttrDirDescriptor::~AttrDirDescriptor()
{
Close();
}
status_t
AttrDirDescriptor::Close()
{
if (dir) {
DIR *oldDir = dir;
dir = NULL;
if (fs_close_attr_dir(oldDir) < 0)
return errno;
}
return B_OK;
}
status_t
AttrDirDescriptor::Dup(Descriptor *&clone)
{
return B_FILE_ERROR;
}
status_t
AttrDirDescriptor::GetStat(bool traverseLink, struct stat *st)
{
return B_FILE_ERROR;
}
status_t
AttrDirDescriptor::GetNodeRef(NodeRef &ref)
{
ref = this->ref;
return B_OK;
}
Descriptor *
get_descriptor(int fd)
{
if (!sDescriptors)
return NULL;
DescriptorMap::iterator it = sDescriptors->find(fd);
if (it == sDescriptors->end())
return NULL;
return it->second;
}
int
add_descriptor(Descriptor *descriptor)
{
if (!sDescriptors)
sDescriptors = new DescriptorMap;
int fd = -1;
if (FileDescriptor *file = dynamic_cast<FileDescriptor*>(descriptor)) {
fd = file->fd;
} else {
for (fd = kVirtualDescriptorStart;
sDescriptors->find(fd) != sDescriptors->end();
fd++) {
}
}
(*sDescriptors)[fd] = descriptor;
descriptor->fd = fd;
return fd;
}
status_t
delete_descriptor(int fd)
{
DescriptorMap::iterator it = sDescriptors->find(fd);
if (it == sDescriptors->end())
return B_FILE_ERROR;
status_t error = it->second->Close();
delete it->second;
sDescriptors->erase(it);
if (sDescriptors->size() == 0) {
delete sDescriptors;
sDescriptors = NULL;
}
return error;
}
bool
is_unknown_or_system_descriptor(int fd)
{
Descriptor* descriptor = get_descriptor(fd);
return descriptor == NULL || descriptor->IsSystemFD();
}
}