/* * Copyright 2001-2008 pinc Software. All Rights Reserved. * Released under the terms of the MIT license. */ //! BFS Inode classes #include "Inode.h" #include "BPlusTree.h" #include #include #include #include #include #include #include #include #include class NodeGetter { public: NodeGetter(Inode* inode) : fInode(inode) { fInode->AcquireBuffer(); } ~NodeGetter() { fInode->ReleaseBuffer(); } private: Inode* fInode; }; // #pragma mark - Inode::Inode(Disk* disk, bfs_inode* inode, bool ownBuffer) : fDisk(disk), fInode(inode), fOwnBuffer(ownBuffer), fPath(NULL), fRefCount(1), fCurrentSmallData(NULL), fAttributes(NULL), fAttributeBuffer(NULL) { if (inode != NULL) fBlockRun = inode->inode_num; } Inode::Inode(const Inode& inode) : fDisk(inode.fDisk), fInode(inode.fInode), fOwnBuffer(false), fPath(NULL), fBlockRun(inode.fBlockRun), fRefCount(1), fCurrentSmallData(NULL), fAttributes(NULL), fAttributeBuffer(NULL) { } Inode::~Inode() { _Unset(); } void Inode::_Unset() { if (fOwnBuffer) free(fInode); fInode = NULL; fBlockRun.SetTo(0, 0, 0); free(fPath); fPath = NULL; delete fAttributes; fAttributes = NULL; } status_t Inode::SetTo(bfs_inode *inode) { _Unset(); fInode = inode; fBlockRun = inode->inode_num; return B_OK; } status_t Inode::InitCheck() const { if (!fInode) return B_ERROR; // test inode magic and flags if (fInode->magic1 != INODE_MAGIC1 || !(fInode->flags & INODE_IN_USE) || fInode->inode_num.length != 1) return B_ERROR; if (fDisk->BlockSize()) { // matches known block size? if (fInode->inode_size != fDisk->SuperBlock()->inode_size // parent resides on disk? || fInode->parent.allocation_group > fDisk->SuperBlock()->num_ags || fInode->parent.allocation_group < 0 || fInode->parent.start > (1L << fDisk->SuperBlock()->ag_shift) || fInode->parent.length != 1 // attributes, too? || fInode->attributes.allocation_group > fDisk->SuperBlock()->num_ags || fInode->attributes.allocation_group < 0 || fInode->attributes.start > (1L << fDisk->SuperBlock()->ag_shift)) return B_ERROR; } else { // is inode size one of the valid values? switch (fInode->inode_size) { case 1024: case 2048: case 4096: case 8192: break; default: return B_ERROR; } } return B_OK; // is inode on a boundary matching it's size? //return (Offset() % fInode->inode_size) == 0 ? B_OK : B_ERROR; } status_t Inode::CopyBuffer() { if (!fInode) return B_ERROR; bfs_inode *buffer = (bfs_inode *)malloc(fInode->inode_size); if (!buffer) return B_NO_MEMORY; memcpy(buffer, fInode, fInode->inode_size); fInode = buffer; fOwnBuffer = true; BufferClobbered(); // this must not be deleted anymore return B_OK; } /*static*/ bool Inode::_LowMemory() { static bigtime_t lastChecked; static int32 percentUsed; if (system_time() > lastChecked + 1000000LL) { system_info info; get_system_info(&info); percentUsed = 100 * info.used_pages / info.max_pages; } return percentUsed > 75; } void Inode::ReleaseBuffer() { if (atomic_add(&fRefCount, -1) != 1) return; if (fOwnBuffer) { if (!_LowMemory()) return; free(fInode); fInode = NULL; } } status_t Inode::AcquireBuffer() { if (atomic_add(&fRefCount, 1) != 0) return B_OK; if (!fOwnBuffer || fInode != NULL) return B_OK; fInode = (bfs_inode*)malloc(fDisk->BlockSize()); if (fInode == NULL) return B_NO_MEMORY; ssize_t bytesRead = fDisk->ReadAt(Offset(), fInode, fDisk->BlockSize()); if (bytesRead < B_OK) return bytesRead; return B_OK; } void Inode::BufferClobbered() { AcquireBuffer(); } void Inode::SetParent(const block_run& run) { fInode->parent = run; BufferClobbered(); } void Inode::SetBlockRun(const block_run& run) { fInode->inode_num = run; fBlockRun = run; BufferClobbered(); } void Inode::SetMode(uint32 mode) { fInode->mode = mode; BufferClobbered(); } status_t Inode::SetName(const char *name) { if (name == NULL || *name == '\0') return B_BAD_VALUE; small_data *data = fInode->small_data_start, *nameData = NULL; BufferClobbered(); while (!data->IsLast(fInode)) { if (data->type == FILE_NAME_TYPE && data->name_size == FILE_NAME_NAME_LENGTH && *data->Name() == FILE_NAME_NAME) nameData = data; data = data->Next(); } int32 oldLength = nameData == NULL ? 0 : nameData->data_size; int32 newLength = strlen(name) + (nameData == NULL ? sizeof(small_data) + 5 : 0); if ((addr_t)data + newLength - oldLength >= (addr_t)(fInode + fDisk->BlockSize())) return B_NO_MEMORY; if (nameData == NULL) { memmove(newLength + (uint8 *)fInode->small_data_start, fInode->small_data_start, (addr_t)data - (addr_t)fInode->small_data_start); nameData = fInode->small_data_start; } else { memmove(newLength + (uint8 *)nameData, nameData, (addr_t)data - (addr_t)fInode->small_data_start); } memset(nameData, 0, sizeof(small_data) + 5 + strlen(name)); nameData->type = FILE_NAME_TYPE; nameData->name_size = FILE_NAME_NAME_LENGTH; nameData->data_size = strlen(name); *nameData->Name() = FILE_NAME_NAME; strcpy((char *)nameData->Data(),name); return B_OK; } const char * Inode::Name() const { if (InitCheck() != B_OK) { puts("Not getting name because node is invalid"); return NULL; } small_data *data = fInode->small_data_start; while (!data->IsLast(fInode)) { if (data->type == FILE_NAME_TYPE && data->name_size == FILE_NAME_NAME_LENGTH && *data->Name() == FILE_NAME_NAME) return (const char *)data->Data(); data = data->Next(); } return NULL; } status_t Inode::GetNextSmallData(small_data **smallData) { if (!fInode) return B_ERROR; small_data *data = *smallData; // begin from the start? if (data == NULL) data = fInode->small_data_start; else data = data->Next(); // is already last item? if (data->IsLast(fInode)) return B_ENTRY_NOT_FOUND; *smallData = data; return B_OK; } status_t Inode::RewindAttributes() { fCurrentSmallData = NULL; if (fAttributes != NULL) fAttributes->Rewind(); return B_OK; } status_t Inode::GetNextAttribute(char *name, uint32 *type, void **data, size_t *length) { // read attributes out of the small data section if (fCurrentSmallData == NULL || !fCurrentSmallData->IsLast(fInode)) { if (fCurrentSmallData == NULL) fCurrentSmallData = fInode->small_data_start; else fCurrentSmallData = fCurrentSmallData->Next(); // skip name attribute if (!fCurrentSmallData->IsLast(fInode) && fCurrentSmallData->name_size == FILE_NAME_NAME_LENGTH && *fCurrentSmallData->Name() == FILE_NAME_NAME) fCurrentSmallData = fCurrentSmallData->Next(); if (!fCurrentSmallData->IsLast(fInode)) { strncpy(name,fCurrentSmallData->Name(), B_FILE_NAME_LENGTH); *type = fCurrentSmallData->type; *data = fCurrentSmallData->Data(); *length = fCurrentSmallData->data_size; return B_OK; } } // read attributes out of the attribute directory if (Attributes().IsZero()) return B_ENTRY_NOT_FOUND; if (fAttributes == NULL) fAttributes = (Directory *)Inode::Factory(fDisk, Attributes()); status_t status = fAttributes ? fAttributes->InitCheck() : B_ERROR; if (status < B_OK) return status; block_run run; status = fAttributes->GetNextEntry(name, &run); if (status < B_OK) { free(fAttributeBuffer); fAttributeBuffer = NULL; return status; } Attribute *attribute = (Attribute *)Inode::Factory(fDisk, run); if (attribute == NULL || attribute->InitCheck() < B_OK) return B_IO_ERROR; *type = attribute->Type(); void *buffer = realloc(fAttributeBuffer, attribute->Size()); if (buffer == NULL) { free(fAttributeBuffer); fAttributeBuffer = NULL; delete attribute; return B_NO_MEMORY; } fAttributeBuffer = buffer; ssize_t size = attribute->Read(fAttributeBuffer, attribute->Size()); delete attribute; *length = size; *data = fAttributeBuffer; return size < B_OK ? size : B_OK; } status_t Inode::_FindPath(Inode::Source *source) { BString path; block_run parent = Parent(); while (!parent.IsZero() && parent != fDisk->Root()) { Inode *inode; if (source) inode = source->InodeAt(parent); else inode = Inode::Factory(fDisk, parent); if (inode == NULL || inode->InitCheck() < B_OK || inode->Name() == NULL || !*inode->Name()) { BString sub; sub << "__recovered " << parent.allocation_group << ":" << (int32)parent.start << "/"; path.Prepend(sub); delete inode; break; } parent = inode->Parent(); path.Prepend("/"); path.Prepend(inode->Name()); delete inode; } fPath = strdup(path.String()); return B_OK; } const char * Inode::Path(Inode::Source *source) { if (fPath == NULL) _FindPath(source); return fPath; } status_t Inode::CopyTo(const char *root, bool fullPath, Inode::Source *source) { if (root == NULL) return B_ENTRY_NOT_FOUND; BString path; if (fullPath) path.Append(Path(source)); if (*(root + strlen(root) - 1) != '/') path.Prepend("/"); path.Prepend(root); return create_directory(path.String(), 0777); } status_t Inode::CopyAttributesTo(BNode *node) { // copy attributes RewindAttributes(); char name[B_FILE_NAME_LENGTH]; const uint32 kMaxBrokenAttributes = 64; // sanity max value uint32 count = 0; uint32 type; void *data; size_t size; status_t status; while ((status = GetNextAttribute(name, &type, &data, &size)) != B_ENTRY_NOT_FOUND) { if (status != B_OK) { printf("could not open attribute (possibly: %s): %s!\n", name, strerror(status)); if (count++ > kMaxBrokenAttributes) break; continue; } ssize_t written = node->WriteAttr(name, type, 0, data, size); if (written < B_OK) { printf("could not write attribute \"%s\": %s\n", name, strerror(written)); } else if ((size_t)written < size) { printf("could only write %ld bytes (from %ld) at attribute \"%s\"\n", written, size, name); } } // copy stats node->SetPermissions(fInode->mode); node->SetOwner(fInode->uid); node->SetGroup(fInode->gid); node->SetModificationTime(fInode->last_modified_time >> 16); node->SetCreationTime(fInode->create_time >> 16); return B_OK; } Inode * Inode::Factory(Disk *disk, bfs_inode *inode, bool ownBuffer) { // attributes (of a file) if ((inode->mode & (S_ATTR | S_ATTR_DIR)) == S_ATTR) return new Attribute(disk, inode, ownBuffer); // directories, attribute directories, indices if (S_ISDIR(inode->mode) || inode->mode & S_ATTR_DIR) return new Directory(disk, inode, ownBuffer); // regular files if (S_ISREG(inode->mode)) return new File(disk, inode, ownBuffer); // symlinks (short and link in data-stream) if (S_ISLNK(inode->mode)) return new Symlink(disk, inode, ownBuffer); return NULL; } Inode * Inode::Factory(Disk *disk, block_run run) { bfs_inode *inode = (bfs_inode *)malloc(disk->BlockSize()); if (!inode) return NULL; if (disk->ReadAt(disk->ToOffset(run), inode, disk->BlockSize()) <= 0) return NULL; Inode *object = Factory(disk, inode); if (object == NULL) free(inode); return object; } Inode * Inode::Factory(Disk *disk, Inode *inode, bool copyBuffer) { bfs_inode *inodeBuffer = inode->fInode; if (copyBuffer) { bfs_inode *inodeCopy = (bfs_inode *)malloc(inodeBuffer->inode_size); if (!inodeCopy) return NULL; memcpy(inodeCopy, inodeBuffer, inodeBuffer->inode_size); inodeBuffer = inodeCopy; } return Factory(disk, inodeBuffer, copyBuffer); } Inode * Inode::EmptyInode(Disk *disk, const char *name, int32 mode) { bfs_inode *inode = (bfs_inode *)malloc(disk->BlockSize()); if (!inode) return NULL; memset(inode, 0, sizeof(bfs_inode)); inode->magic1 = INODE_MAGIC1; inode->inode_size = disk->BlockSize(); inode->mode = mode; inode->flags = INODE_IN_USE | (mode & S_IFDIR ? INODE_LOGGED : 0); if (name) { small_data *data = inode->small_data_start; data->type = FILE_NAME_TYPE; data->name_size = FILE_NAME_NAME_LENGTH; *data->Name() = FILE_NAME_NAME; data->data_size = strlen(name); strcpy((char *)data->Data(), name); } Inode *object = new (std::nothrow) Inode(disk, inode); if (object == NULL) { free(inode); return NULL; } object->AcquireBuffer(); // this must not be deleted anymore! return object; } // #pragma mark - DataStream::DataStream(Disk *disk, bfs_inode *inode, bool ownBuffer) : Inode(disk,inode,ownBuffer), fCurrent(-1), fPosition(0LL) { } DataStream::DataStream(const Inode &inode) : Inode(inode), fCurrent(-1), fPosition(0LL) { } DataStream::~DataStream() { } status_t DataStream::FindBlockRun(off_t pos) { NodeGetter _(this); if (pos > fInode->data.size) return B_ENTRY_NOT_FOUND; if (fCurrent < 0) fLevel = 0; fRunBlockEnd = fCurrent >= 0 ? fRunFileOffset + (fRun.length << fDisk->BlockShift()) : 0LL; // access in current block run? if (fCurrent >= 0 && pos >= fRunFileOffset && pos < fRunBlockEnd) return B_OK; // find matching block run if (fInode->data.max_direct_range > 0 && pos >= fInode->data.max_direct_range) { if (fInode->data.max_double_indirect_range > 0 && pos >= fInode->data.max_indirect_range) { // read from double indirect blocks //printf("find double indirect block: %ld,%d!\n",fInode->data.double_indirect.allocation_group,fInode->data.double_indirect.start); block_run *indirect = (block_run *)fDisk->ReadBlockRun(fInode->data.double_indirect); if (indirect == NULL) return B_ERROR; off_t start = pos - fInode->data.max_indirect_range; int32 indirectSize = fDisk->BlockSize() * 16 * (fDisk->BlockSize() / sizeof(block_run)); int32 directSize = fDisk->BlockSize() * 4; int32 index = start / indirectSize; //printf("\tstart = %lld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index); //printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start); indirect = (block_run *)fDisk->ReadBlockRun(indirect[index]); if (indirect == NULL) return B_ERROR; fCurrent = (start % indirectSize) / directSize; fRunFileOffset = fInode->data.max_indirect_range + (index * indirectSize) + (fCurrent * directSize); fRunBlockEnd = fRunFileOffset + directSize; fRun = indirect[fCurrent]; //printf("\tfCurrent = %ld, fRunFileOffset = %lld, fRunBlockEnd = %lld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start); } else { // access from indirect blocks block_run *indirect = (block_run *)fDisk->ReadBlockRun(fInode->data.indirect); if (!indirect) return B_ERROR; int32 indirectRuns = (fInode->data.indirect.length << fDisk->BlockShift()) / sizeof(block_run); if (fLevel != 1 || pos < fRunFileOffset) { fRunBlockEnd = fInode->data.max_direct_range; fCurrent = -1; fLevel = 1; } while (++fCurrent < indirectRuns) { if (indirect[fCurrent].IsZero()) break; fRunFileOffset = fRunBlockEnd; fRunBlockEnd += indirect[fCurrent].length << fDisk->BlockShift(); if (fRunBlockEnd > pos) break; } if (fCurrent == indirectRuns || indirect[fCurrent].IsZero()) return B_ERROR; fRun = indirect[fCurrent]; //printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start); //printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %lld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset); } } else { // access from direct blocks if (fRunFileOffset > pos) { fRunBlockEnd = 0LL; fCurrent = -1; } fLevel = 0; while (++fCurrent < NUM_DIRECT_BLOCKS) { if (fInode->data.direct[fCurrent].IsZero()) break; fRunFileOffset = fRunBlockEnd; fRunBlockEnd += fInode->data.direct[fCurrent].length << fDisk->BlockShift(); if (fRunBlockEnd > pos) break; } if (fCurrent == NUM_DIRECT_BLOCKS || fInode->data.direct[fCurrent].IsZero()) return B_ERROR; fRun = fInode->data.direct[fCurrent]; //printf("### run[%ld] = (%ld,%d,%d), offset = %lld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset); } return B_OK; } ssize_t DataStream::ReadAt(off_t pos, void *buffer, size_t size) { NodeGetter _(this); //printf("DataStream::ReadAt(pos = %lld,buffer = %p,size = %ld);\n",pos,buffer,size); // truncate size to read if (pos + (off_t)size > fInode->data.size) { if (pos > fInode->data.size) // reading outside the file return B_ERROR; size = fInode->data.size - pos; if (!size) // there is nothing left to read return 0; } ssize_t read = 0; //printf("### read %ld bytes at %lld\n",size,pos); while (size > 0) { status_t status = FindBlockRun(pos); if (status < B_OK) return status; ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos); //printf("### read %ld bytes from %lld\n### --\n",bytes,fDisk->ToOffset(fRun) + pos - fRunFileOffset); bytes = fDisk->ReadAt(fDisk->ToOffset(fRun) + pos - fRunFileOffset, buffer, bytes); if (bytes <= 0) { if (bytes == 0) { printf("could not read bytes at: %" B_PRId32 ",%d\n", fRun.allocation_group, fRun.start); } return bytes < 0 ? bytes : B_BAD_DATA; } buffer = (void *)((uint8 *)buffer + bytes); size -= bytes; pos += bytes; read += bytes; } if (read >= 0) return read; return B_IO_ERROR; } ssize_t DataStream::WriteAt(off_t pos, const void *buffer, size_t size) { NodeGetter _(this); // FIXME: truncate size -> should enlargen the file if (pos + (off_t)size > fInode->data.size) { if (pos > fInode->data.size) // writing outside the file return B_ERROR; size = fInode->data.size - pos; if (!size) // there is nothing left to write return 0; } ssize_t written = 0; //printf("### write %ld bytes at %lld\n",size,pos); while (size > 0) { status_t status = FindBlockRun(pos); if (status < B_OK) return status; ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos); //printf("### write %ld bytes to %lld\n### --\n",bytes,fDisk->ToOffset(fRun) + pos - fRunFileOffset); bytes = fDisk->WriteAt(fDisk->ToOffset(fRun) + pos - fRunFileOffset,buffer,bytes); if (bytes < 0) return bytes; buffer = (void *)((uint8 *)buffer + bytes); size -= bytes; pos += bytes; written += bytes; } if (written >= 0) return written; return B_IO_ERROR; } off_t DataStream::Seek(off_t position, uint32 seekMode) { NodeGetter _(this); if (seekMode == SEEK_SET) fPosition = position; else if (seekMode == SEEK_END) fPosition = fInode->data.size + position; else fPosition += position; return fPosition; } off_t DataStream::Position() const { return fPosition; } status_t DataStream::SetSize(off_t size) { NodeGetter _(this); // FIXME: not yet supported if (size > fInode->data.size || size > fInode->data.max_direct_range) return B_ERROR; if (size == fInode->data.size) return B_OK; BufferClobbered(); fInode->data.size = size; fInode->data.max_direct_range = size; fInode->data.max_indirect_range = 0; fInode->data.max_double_indirect_range = 0; for (int32 i = 0;i < NUM_DIRECT_BLOCKS;i++) { if (size <= 0) fInode->data.direct[i].SetTo(0, 0, 0); else if ((fInode->data.direct[i].length << fDisk->BlockShift()) >= size) { off_t blocks = (size + fDisk->BlockSize() - 1) / fDisk->BlockSize(); fInode->data.direct[i].length = blocks; size = 0; } else size -= fInode->data.direct[i].length << fDisk->BlockShift(); } return B_OK; } // #pragma mark - File::File(Disk *disk, bfs_inode *inode,bool ownBuffer) : DataStream(disk,inode,ownBuffer) { } File::File(const Inode &inode) : DataStream(inode) { } File::~File() { } status_t File::InitCheck() const { status_t status = DataStream::InitCheck(); if (status == B_OK) return IsFile() ? B_OK : B_ERROR; return status; } status_t File::CopyTo(const char *root, bool fullPath, Inode::Source *source) { status_t status = Inode::CopyTo(root, fullPath, source); if (status < B_OK) return status; BPath path(root); if (fullPath && Path(source)) path.Append(Path(source)); char *name = (char *)Name(); if (name != NULL) { // changes the filename in the inode buffer (for deleted entries) if (!*name) *name = '_'; path.Append(name); } else { BString sub; sub << "__untitled " << BlockRun().allocation_group << ":" << (int32)BlockRun().start; path.Append(sub.String()); } printf("%" B_PRId32 ",%d -> %s\n", BlockRun().allocation_group, BlockRun().start, path.Path()); BFile file; status = file.SetTo(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS); if (status < B_OK) return status; char buffer[fDisk->BlockSize()]; ssize_t size; Seek(0, SEEK_SET); while ((size = Read(buffer, sizeof(buffer))) > B_OK) { ssize_t written = file.Write(buffer, size); if (written < B_OK) return written; } return CopyAttributesTo(&file); } // #pragma mark - Attribute::Attribute(Disk *disk, bfs_inode *inode, bool ownBuffer) : File(disk, inode, ownBuffer) { } Attribute::Attribute(const Inode &inode) : File(inode) { } Attribute::~Attribute() { } status_t Attribute::InitCheck() const { status_t status = DataStream::InitCheck(); if (status == B_OK) return IsAttribute() ? B_OK : B_ERROR; return status; } status_t Attribute::CopyTo(const char */*path*/, bool /*fullPath*/, Inode::Source */*source*/) { // files and directories already copy all attributes // eventually, this method should be implemented to recover lost // attributes on the disk return B_OK; } // #pragma mark - Directory::Directory(Disk *disk, bfs_inode *inode, bool ownBuffer) : DataStream(disk, inode, ownBuffer), fTree(NULL) { } Directory::Directory(const Inode &inode) : DataStream(inode), fTree(NULL) { } Directory::~Directory() { delete fTree; } status_t Directory::InitCheck() const { status_t status = DataStream::InitCheck(); if (status == B_OK) return (IsDirectory() || IsAttributeDirectory()) ? B_OK : B_ERROR; return status; } status_t Directory::CopyTo(const char *root, bool fullPath, Inode::Source *source) { // don't copy attributes or indices // the recovery program should make empty files to recover lost attributes if (IsAttributeDirectory() || IsIndex()) return B_OK; status_t status = Inode::CopyTo(root, fullPath, source); if (status < B_OK) return status; BPath path(root); if (fullPath && Path(source)) path.Append(Path(source)); char *name = (char *)Name(); if (name != NULL) { // changes the filename in the inode buffer (for deleted entries) if (!*name) *name = '_'; path.Append(name); } else { // create unique name BString sub; sub << "__untitled " << BlockRun().allocation_group << ":" << (int32)BlockRun().start; path.Append(sub.String()); } BEntry entry(path.Path()); BDirectory directory; if ((status = entry.GetParent(&directory)) < B_OK) return status; status = directory.CreateDirectory(path.Leaf(), NULL); if (status < B_OK && status != B_FILE_EXISTS) return status; if ((status = directory.SetTo(&entry)) < B_OK) return status; return CopyAttributesTo(&directory); } status_t Directory::Rewind() { if (!fTree) { status_t status = CreateTree(); if (status < B_OK) return status; } return fTree->Rewind(); } status_t Directory::GetNextEntry(char *name, block_run *run) { status_t status; if (!fTree) { if ((status = Rewind()) < B_OK) return status; } uint16 length; off_t offset; if ((status = fTree->GetNextEntry(name, &length, B_FILE_NAME_LENGTH - 1, &offset)) < B_OK) return status; *run = fDisk->ToBlockRun(offset); return B_OK; } status_t Directory::GetNextEntry(block_run *run) { char name[B_FILE_NAME_LENGTH]; return GetNextEntry(name, run); } status_t Directory::Contains(const block_run *run) { status_t status; if (!fTree) { if ((status = Rewind()) < B_OK) return status; } block_run searchRun; while (GetNextEntry(&searchRun) == B_OK) { if (searchRun == *run) return B_OK; } return B_ENTRY_NOT_FOUND; } status_t Directory::Contains(const Inode *inode) { status_t status; if (!fTree) { if ((status = CreateTree()) < B_OK) return status; } off_t value; const char *name = inode->Name(); status = B_ENTRY_NOT_FOUND; if (name && (status = fTree->Find((uint8 *)name, (uint16)strlen(name), &value)) == B_OK) { if (fDisk->ToBlockRun(value) == inode->InodeBuffer()->inode_num) return B_OK; printf("inode address do not match (%s)!\n", inode->Name()); } if (status != B_OK && status != B_ENTRY_NOT_FOUND) return status; return Contains(&inode->InodeBuffer()->inode_num); } status_t Directory::FindEntry(const char *name, block_run *run) { status_t status; if (!name) return B_BAD_VALUE; if (!fTree) { if ((status = CreateTree()) < B_OK) return status; } off_t value; if ((status = fTree->Find((uint8 *)name, (uint16)strlen(name), &value)) >= B_OK) { if (run) *run = fDisk->ToBlockRun(value); return B_OK; } return status; } status_t Directory::AddEntry(Inode *inode) { status_t status; bool created = false; if (!fTree) { status = CreateTree(); if (status == B_OK) status = fTree->Validate(); if (status == B_BAD_DATA) { //puts("bplustree corrupted!"); fTree = new BPlusTree(BPLUSTREE_STRING_TYPE, BPLUSTREE_NODE_SIZE, false); if ((status = fTree->InitCheck()) < B_OK) { delete fTree; fTree = NULL; } else created = true; } if (status < B_OK) return status; } // keep all changes in memory fTree->SetHoldChanges(true); if (created) { // add . and .. fTree->Insert(".", Block()); fTree->Insert("..", fDisk->ToBlock(Parent())); } if (inode->Flags() & INODE_DELETED) return B_ENTRY_NOT_FOUND; BString name = inode->Name(); if (name == "") { name << "__file " << inode->BlockRun().allocation_group << ":" << (int32)inode->BlockRun().start; } return fTree->Insert(name.String(), inode->Block()); } status_t Directory::CreateTree() { fTree = new BPlusTree(this); status_t status = fTree->InitCheck(); if (status < B_OK) { delete fTree; fTree = NULL; return status; } return B_OK; } status_t Directory::GetTree(BPlusTree **tree) { if (!fTree) { status_t status = CreateTree(); if (status < B_OK) return status; } *tree = fTree; return B_OK; } // #pragma mark - Symlink::Symlink(Disk *disk, bfs_inode *inode,bool ownBuffer) : Inode(disk,inode,ownBuffer) { } Symlink::Symlink(const Inode &inode) : Inode(inode) { } Symlink::~Symlink() { } status_t Symlink::InitCheck() const { status_t status = Inode::InitCheck(); if (status == B_OK) return IsSymlink() ? B_OK : B_ERROR; return status; } status_t Symlink::CopyTo(const char *root, bool fullPath,Inode::Source *source) { status_t status = Inode::CopyTo(root,fullPath,source); if (status < B_OK) return status; BPath path(root); if (fullPath && Path(source)) path.Append(Path(source)); char *name = (char *)Name(); if (name != NULL) { // changes the filename in the inode buffer (for deleted entries) if (!*name) *name = '_'; path.Append(name); } else { // create unique name BString sub; sub << "__symlink " << BlockRun().allocation_group << ":" << (int32)BlockRun().start; path.Append(sub.String()); } BEntry entry(path.Path()); BDirectory directory; if ((status = entry.GetParent(&directory)) < B_OK) return status; char to[2048]; if (LinksTo(to,sizeof(to)) < B_OK) return B_ERROR; BSymLink link; status = directory.CreateSymLink(path.Leaf(),to,&link); if (status < B_OK && status != B_FILE_EXISTS) return status; if ((status = link.SetTo(&entry)) < B_OK) return status; return CopyAttributesTo(&link); } status_t Symlink::LinksTo(char *to,size_t maxLength) { if ((fInode->flags & INODE_LONG_SYMLINK) == 0) { strcpy(to,fInode->short_symlink); return B_OK; } DataStream stream(*this); status_t status = stream.InitCheck(); if (status < B_OK) return status; status = stream.Read(to,maxLength); return status < B_OK ? status : B_OK; }