/* * 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 #endif #include "fs_descriptors.h" #include #include #include #include #include #include #include #include "fs_impl.h" using std::map; static const int kVirtualDescriptorStart = 10000; typedef map 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; } // #pragma mark - Descriptor // constructor Descriptor::~Descriptor() { } // IsSystemFD bool Descriptor::IsSystemFD() const { return false; } // GetPath status_t Descriptor::GetPath(string& path) const { return get_path(fd, NULL, path); } // GetNodeRef 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; } // #pragma mark - FileDescriptor // constructor FileDescriptor::FileDescriptor(int fd) { this->fd = fd; } // destructor FileDescriptor::~FileDescriptor() { Close(); } // Close status_t FileDescriptor::Close() { if (fd >= 0) { int oldFD = fd; fd = -1; if (close(oldFD) < 0) return errno; } return B_OK; } // Dup status_t FileDescriptor::Dup(Descriptor *&clone) { int dupFD = dup(fd); if (dupFD < 0) return errno; clone = new FileDescriptor(dupFD); return B_OK; } // GetStat status_t FileDescriptor::GetStat(bool traverseLink, struct stat *st) { if (fstat(fd, st) < 0) return errno; return B_OK; } // IsSystemFD bool FileDescriptor::IsSystemFD() const { return true; } // #pragma mark - DirectoryDescriptor // constructor DirectoryDescriptor::DirectoryDescriptor(DIR *dir, const NodeRef &ref) { this->dir = dir; this->ref = ref; } // destructor DirectoryDescriptor::~DirectoryDescriptor() { Close(); } // Close status_t DirectoryDescriptor::Close() { if (dir) { DIR *oldDir = dir; dir = NULL; if (closedir(oldDir) < 0) return errno; } return B_OK; } // Dup 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; } // GetStat status_t DirectoryDescriptor::GetStat(bool traverseLink, struct stat *st) { // get a usable path string realPath; status_t error = get_path(fd, NULL, realPath); if (error != B_OK) return error; // stat int result; result = stat(realPath.c_str(), st); if (result < 0) return errno; return B_OK; } // GetNodeRef status_t DirectoryDescriptor::GetNodeRef(NodeRef &ref) { ref = this->ref; return B_OK; } // #pragma mark - SymlinkDescriptor // constructor SymlinkDescriptor::SymlinkDescriptor(const char *path) { this->path = path; } // Close status_t SymlinkDescriptor::Close() { return B_OK; } // Dup status_t SymlinkDescriptor::Dup(Descriptor *&clone) { clone = new SymlinkDescriptor(path.c_str()); return B_OK; } // GetStat status_t SymlinkDescriptor::GetStat(bool traverseLink, struct stat *st) { // stat 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; } // GetPath status_t SymlinkDescriptor::GetPath(string& path) const { path = this->path; return B_OK; } // #pragma mark - AttributeDescriptor 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; // stat the attribute attr_info info; if (fs_stat_attr(fFileFD, fAttribute, &info) < 0) { if (errno == B_ENTRY_NOT_FOUND) { if ((fOpenMode & O_CREAT) == 0) return errno; // create the attribute if (fs_write_attr(fFileFD, fAttribute, fType, 0, NULL, 0) < 0) return errno; return B_OK; } return errno; } if ((fOpenMode & O_TRUNC) == 0) { // truncate the attribute if (fs_write_attr(fFileFD, fAttribute, fType, 0, NULL, 0) < 0) return errno; return B_OK; } // we have to read in the attribute data 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; } // we may need to resize the buffer 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; } // copy the data and write all of it 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; } // #pragma mark - AttrDirDescriptor // constructor AttrDirDescriptor::AttrDirDescriptor(DIR *dir, const NodeRef &ref) : DirectoryDescriptor(dir, ref) { } // destructor AttrDirDescriptor::~AttrDirDescriptor() { Close(); } // Close status_t AttrDirDescriptor::Close() { if (dir) { DIR *oldDir = dir; dir = NULL; if (fs_close_attr_dir(oldDir) < 0) return errno; } return B_OK; } // Dup status_t AttrDirDescriptor::Dup(Descriptor *&clone) { // we don't allow dup()int attr dir descriptors return B_FILE_ERROR; } // GetStat status_t AttrDirDescriptor::GetStat(bool traverseLink, struct stat *st) { // we don't allow stat()int attr dir descriptors return B_FILE_ERROR; } // GetNodeRef status_t AttrDirDescriptor::GetNodeRef(NodeRef &ref) { ref = this->ref; return B_OK; } // get_descriptor Descriptor * get_descriptor(int fd) { if (!sDescriptors) return NULL; DescriptorMap::iterator it = sDescriptors->find(fd); if (it == sDescriptors->end()) return NULL; return it->second; } // add_descriptor int add_descriptor(Descriptor *descriptor) { if (!sDescriptors) sDescriptors = new DescriptorMap; int fd = -1; if (FileDescriptor *file = dynamic_cast(descriptor)) { fd = file->fd; } else { // find a free slot for (fd = kVirtualDescriptorStart; sDescriptors->find(fd) != sDescriptors->end(); fd++) { } } (*sDescriptors)[fd] = descriptor; descriptor->fd = fd; return fd; } // delete_descriptor 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(); } } // namespace BPrivate