/* * Copyright 2002-2009, Haiku Inc. * Distributed under the terms of the MIT License. * * Authors: * Tyler Dauwalder * Ingo Weinhold, bonefish@users.sf.net * Axel Dörfler, axeld@pinc-software.de */ #include "storage_support.h" #include #include #include #include #include #include #include #include #include BDirectory::BDirectory() : fDirFd(-1) { } BDirectory::BDirectory(const BDirectory& dir) : fDirFd(-1) { *this = dir; } BDirectory::BDirectory(const entry_ref* ref) : fDirFd(-1) { SetTo(ref); } BDirectory::BDirectory(const node_ref* nref) : fDirFd(-1) { SetTo(nref); } BDirectory::BDirectory(const BEntry* entry) : fDirFd(-1) { SetTo(entry); } BDirectory::BDirectory(const char* path) : fDirFd(-1) { SetTo(path); } BDirectory::BDirectory(const BDirectory* dir, const char* path) : fDirFd(-1) { SetTo(dir, path); } BDirectory::~BDirectory() { // Also called by the BNode destructor, but we rather try to avoid // problems with calling virtual functions in the base class destructor. // Depending on the compiler implementation an object may be degraded to // an object of the base class after the destructor of the derived class // has been executed. close_fd(); } status_t BDirectory::SetTo(const entry_ref* ref) { // open node status_t error = _SetTo(ref, true); if (error != B_OK) return error; // open dir error = set_dir_fd(_kern_open_dir_entry_ref(ref->device, ref->directory, ref->name)); if (error < 0) { Unset(); return (fCStatus = error); } // set close on exec flag on dir FD fcntl(fDirFd, F_SETFD, FD_CLOEXEC); return B_OK; } status_t BDirectory::SetTo(const node_ref* nref) { Unset(); status_t error = (nref ? B_OK : B_BAD_VALUE); if (error == B_OK) { entry_ref ref(nref->device, nref->node, "."); error = SetTo(&ref); } set_status(error); return error; } status_t BDirectory::SetTo(const BEntry* entry) { if (!entry) { Unset(); return (fCStatus = B_BAD_VALUE); } // open node status_t error = _SetTo(entry->fDirFd, entry->fName, true); if (error != B_OK) return error; // open dir error = set_dir_fd(_kern_open_dir(entry->fDirFd, entry->fName)); if (error < 0) { Unset(); return (fCStatus = error); } // set close on exec flag on dir FD fcntl(fDirFd, F_SETFD, FD_CLOEXEC); return B_OK; } status_t BDirectory::SetTo(const char* path) { // open node status_t error = _SetTo(-1, path, true); if (error != B_OK) return error; // open dir error = set_dir_fd(_kern_open_dir(-1, path)); if (error < 0) { Unset(); return (fCStatus = error); } // set close on exec flag on dir FD fcntl(fDirFd, F_SETFD, FD_CLOEXEC); return B_OK; } status_t BDirectory::SetTo(const BDirectory* dir, const char* path) { if (!dir || !path || BPrivate::Storage::is_absolute_path(path)) { Unset(); return (fCStatus = B_BAD_VALUE); } int dirFD = dir->fDirFd; if (dir == this) { // prevent that our file descriptor goes away in _SetTo() fDirFd = -1; } // open node status_t error = _SetTo(dirFD, path, true); if (error != B_OK) return error; // open dir error = set_dir_fd(_kern_open_dir(dir->fDirFd, path)); if (error < 0) { Unset(); return (fCStatus = error); } if (dir == this) { // cleanup after _SetTo() _kern_close(dirFD); } // set close on exec flag on dir FD fcntl(fDirFd, F_SETFD, FD_CLOEXEC); return B_OK; } status_t BDirectory::GetEntry(BEntry* entry) const { if (!entry) return B_BAD_VALUE; if (InitCheck() != B_OK) return B_NO_INIT; return entry->SetTo(this, ".", false); } status_t BDirectory::FindEntry(const char* path, BEntry* entry, bool traverse) const { if (path == NULL || entry == NULL) return B_BAD_VALUE; entry->Unset(); // init a potentially abstract entry status_t status; if (InitCheck() == B_OK) status = entry->SetTo(this, path, traverse); else status = entry->SetTo(path, traverse); // fail, if entry is abstract if (status == B_OK && !entry->Exists()) { status = B_ENTRY_NOT_FOUND; entry->Unset(); } return status; } bool BDirectory::Contains(const char* path, int32 nodeFlags) const { // check initialization and parameters if (InitCheck() != B_OK) return false; if (!path) return true; // mimic R5 behavior // turn the path into a BEntry and let the other version do the work BEntry entry; if (BPrivate::Storage::is_absolute_path(path)) entry.SetTo(path); else entry.SetTo(this, path); return Contains(&entry, nodeFlags); } bool BDirectory::Contains(const BEntry* entry, int32 nodeFlags) const { // check, if the entry exists at all if (entry == NULL || !entry->Exists() || InitCheck() != B_OK) return false; if (nodeFlags != B_ANY_NODE) { // test the node kind bool result = false; if ((nodeFlags & B_FILE_NODE) != 0) result = entry->IsFile(); if (!result && (nodeFlags & B_DIRECTORY_NODE) != 0) result = entry->IsDirectory(); if (!result && (nodeFlags & B_SYMLINK_NODE) != 0) result = entry->IsSymLink(); if (!result) return false; } // If the directory is initialized, get the canonical paths of the dir and // the entry and check, if the latter is a prefix of the first one. BPath dirPath(this, ".", true); BPath entryPath(entry); if (dirPath.InitCheck() != B_OK || entryPath.InitCheck() != B_OK) return false; uint32 dirLen = strlen(dirPath.Path()); if (!strncmp(dirPath.Path(), entryPath.Path(), dirLen)) { // if the paths are identical, return a match to stay consistent with // BeOS behavior. if (entryPath.Path()[dirLen] == '\0' || entryPath.Path()[dirLen] == '/') return true; } return false; } status_t BDirectory::GetNextEntry(BEntry* entry, bool traverse) { if (entry == NULL) return B_BAD_VALUE; entry_ref ref; status_t status = GetNextRef(&ref); if (status != B_OK) { entry->Unset(); return status; } return entry->SetTo(&ref, traverse); } status_t BDirectory::GetNextRef(entry_ref* ref) { if (ref == NULL) return B_BAD_VALUE; if (InitCheck() != B_OK) return B_FILE_ERROR; BPrivate::Storage::LongDirEntry longEntry; struct dirent* entry = longEntry.dirent(); bool next = true; while (next) { if (GetNextDirents(entry, sizeof(longEntry), 1) != 1) return B_ENTRY_NOT_FOUND; next = (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")); } ref->device = fDirNodeRef.device; ref->directory = fDirNodeRef.node; return ref->set_name(entry->d_name); } int32 BDirectory::GetNextDirents(dirent* buf, size_t bufSize, int32 count) { if (buf == NULL) return B_BAD_VALUE; if (InitCheck() != B_OK) return B_FILE_ERROR; return _kern_read_dir(fDirFd, buf, bufSize, count); } status_t BDirectory::Rewind() { if (InitCheck() != B_OK) return B_FILE_ERROR; return _kern_rewind_dir(fDirFd); } int32 BDirectory::CountEntries() { status_t error = Rewind(); if (error != B_OK) return error; int32 count = 0; BPrivate::Storage::LongDirEntry longEntry; struct dirent* entry = longEntry.dirent(); while (error == B_OK) { if (GetNextDirents(entry, sizeof(longEntry), 1) != 1) break; if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) count++; } Rewind(); return (error == B_OK ? count : error); } status_t BDirectory::CreateDirectory(const char* path, BDirectory* dir) { if (!path) return B_BAD_VALUE; // create the dir status_t error = _kern_create_dir(fDirFd, path, (S_IRWXU | S_IRWXG | S_IRWXO)); if (error != B_OK) return error; if (dir == NULL) return B_OK; // init the supplied BDirectory if (InitCheck() != B_OK || BPrivate::Storage::is_absolute_path(path)) return dir->SetTo(path); return dir->SetTo(this, path); } status_t BDirectory::CreateFile(const char* path, BFile* file, bool failIfExists) { if (!path) return B_BAD_VALUE; // Let BFile do the dirty job. uint32 openMode = B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE | (failIfExists ? B_FAIL_IF_EXISTS : 0); BFile tmpFile; BFile* realFile = file ? file : &tmpFile; status_t error = B_OK; if (InitCheck() == B_OK && !BPrivate::Storage::is_absolute_path(path)) error = realFile->SetTo(this, path, openMode); else error = realFile->SetTo(path, openMode); if (error != B_OK && file) // mimic R5 behavior file->Unset(); return error; } status_t BDirectory::CreateSymLink(const char* path, const char* linkToPath, BSymLink* link) { if (!path || !linkToPath) return B_BAD_VALUE; // create the symlink status_t error = _kern_create_symlink(fDirFd, path, linkToPath, (S_IRWXU | S_IRWXG | S_IRWXO)); if (error != B_OK) return error; if (link == NULL) return B_OK; // init the supplied BSymLink if (InitCheck() != B_OK || BPrivate::Storage::is_absolute_path(path)) return link->SetTo(path); return link->SetTo(this, path); } BDirectory& BDirectory::operator=(const BDirectory& dir) { if (&dir != this) { // no need to assign us to ourselves Unset(); if (dir.InitCheck() == B_OK) SetTo(&dir, "."); } return *this; } status_t BDirectory::GetStatFor(const char* path, struct stat* st) const { if (!st) return B_BAD_VALUE; if (InitCheck() != B_OK) return B_NO_INIT; if (path != NULL) { if (path[0] == '\0') return B_ENTRY_NOT_FOUND; return _kern_read_stat(fDirFd, path, false, st, sizeof(struct stat)); } return GetStat(st); } // FBC void BDirectory::_ErectorDirectory1() {} void BDirectory::_ErectorDirectory2() {} void BDirectory::_ErectorDirectory3() {} void BDirectory::_ErectorDirectory4() {} void BDirectory::_ErectorDirectory5() {} void BDirectory::_ErectorDirectory6() {} //! Closes the BDirectory's file descriptor. void BDirectory::close_fd() { if (fDirFd >= 0) { _kern_close(fDirFd); fDirFd = -1; } BNode::close_fd(); } int BDirectory::get_fd() const { return fDirFd; } status_t BDirectory::set_dir_fd(int fd) { if (fd < 0) return fd; fDirFd = fd; status_t error = GetNodeRef(&fDirNodeRef); if (error != B_OK) close_fd(); return error; } // #pragma mark - C functions // TODO: Check this method for efficiency. status_t create_directory(const char* path, mode_t mode) { if (!path) return B_BAD_VALUE; // That's the strategy: We start with the first component of the supplied // path, create a BPath object from it and successively add the following // components. Each time we get a new path, we check, if the entry it // refers to exists and is a directory. If it doesn't exist, we try // to create it. This goes on, until we're done with the input path or // an error occurs. BPath dirPath; char* component; int32 nextComponent; do { // get the next path component status_t error = BPrivate::Storage::parse_first_path_component(path, component, nextComponent); if (error != B_OK) return error; // append it to the BPath if (dirPath.InitCheck() == B_NO_INIT) // first component error = dirPath.SetTo(component); else error = dirPath.Append(component); delete[] component; if (error != B_OK) return error; path += nextComponent; // create a BEntry from the BPath BEntry entry; error = entry.SetTo(dirPath.Path(), true); if (error != B_OK) return error; // check, if it exists if (entry.Exists()) { // yep, it exists if (!entry.IsDirectory()) // but is no directory return B_NOT_A_DIRECTORY; } else { // it doesn't exist -- create it error = _kern_create_dir(-1, dirPath.Path(), mode); if (error != B_OK) return error; } } while (nextComponent != 0); return B_OK; }