void collectFiles(Disk &disk,Directory *directory) { if (directory == NULL) return; directory->Rewind(); char name[B_FILE_NAME_LENGTH]; block_run run; while (directory->GetNextEntry(name,&run) >= B_OK) { if (!strcmp(name,".") || !strcmp(name,"..")) continue; gHashtable.Put(run); if (++gCount % 50 == 0) printf(" %7Ld%s1A\n",gCount,gEscape); Inode *inode = Inode::Factory(&disk,run); if (inode != NULL) { if (inode->IsDirectory()) collectFiles(disk,static_cast<Directory *>(inode)); delete inode; } else printf(" Directory \"%s\" (%ld, %d) points to corrupt inode \"%s\" (%ld, %d)\n", directory->Name(),directory->BlockRun().allocation_group,directory->BlockRun().start, name,run.allocation_group,run.start); } }
static status_t ext2_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie) { Volume* volume = (Volume*)_volume->private_volume; Inode* inode = (Inode*)_node->private_node; // opening a directory read-only is allowed, although you can't read // any data from it. if (inode->IsDirectory() && (openMode & O_RWMASK) != 0) return B_IS_A_DIRECTORY; status_t status = inode->CheckPermissions(open_mode_to_access(openMode) | (openMode & O_TRUNC ? W_OK : 0)); if (status != B_OK) return status; // Prepare the cookie file_cookie* cookie = new(std::nothrow) file_cookie; if (cookie == NULL) return B_NO_MEMORY; ObjectDeleter<file_cookie> cookieDeleter(cookie); cookie->open_mode = openMode & EXT2_OPEN_MODE_USER_MASK; cookie->last_size = inode->Size(); cookie->last_notification = system_time(); MethodDeleter<Inode, status_t> fileCacheEnabler(&Inode::EnableFileCache); if ((openMode & O_NOCACHE) != 0) { status = inode->DisableFileCache(); if (status != B_OK) return status; fileCacheEnabler.SetTo(inode); } // Should we truncate the file? if ((openMode & O_TRUNC) != 0) { if ((openMode & O_RWMASK) == O_RDONLY) return B_NOT_ALLOWED; Transaction transaction(volume->GetJournal()); inode->WriteLockInTransaction(transaction); status_t status = inode->Resize(transaction, 0); if (status == B_OK) status = inode->WriteBack(transaction); if (status == B_OK) status = transaction.Done(); if (status != B_OK) return status; // TODO: No need to notify file size changed? } fileCacheEnabler.Detach(); cookieDeleter.Detach(); *_cookie = cookie; return B_OK; }
static status_t btrfs_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos, void* buffer, size_t* _length) { Inode* inode = (Inode*)_node->private_node; if (!inode->IsFile()) { *_length = 0; return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE; } return inode->ReadAt(pos, (uint8*)buffer, _length); }
static status_t ext2_write(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos, const void* buffer, size_t* _length) { TRACE("ext2_write()\n"); Volume* volume = (Volume*)_volume->private_volume; Inode* inode = (Inode*)_node->private_node; if (volume->IsReadOnly()) return B_READ_ONLY_DEVICE; if (inode->IsDirectory()) { *_length = 0; return B_IS_A_DIRECTORY; } TRACE("ext2_write(): Preparing cookie\n"); file_cookie* cookie = (file_cookie*)_cookie; if ((cookie->open_mode & O_APPEND) != 0) pos = inode->Size(); TRACE("ext2_write(): Creating transaction\n"); Transaction transaction; status_t status = inode->WriteAt(transaction, pos, (const uint8*)buffer, _length); if (status == B_OK) status = transaction.Done(); if (status == B_OK) { TRACE("ext2_write(): Finalizing\n"); ReadLocker lock(*inode->Lock()); if (cookie->last_size != inode->Size() && system_time() > cookie->last_notification + INODE_NOTIFICATION_INTERVAL) { notify_stat_changed(volume->ID(), -1, inode->ID(), B_STAT_MODIFICATION_TIME | B_STAT_SIZE | B_STAT_INTERIM_UPDATE); cookie->last_size = inode->Size(); cookie->last_notification = system_time(); } } TRACE("ext2_write(): Done\n"); return status; }
static status_t ext2_open_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie) { Inode* inode = (Inode*)_node->private_node; status_t status = inode->CheckPermissions(R_OK); if (status < B_OK) return status; if (!inode->IsDirectory()) return B_NOT_A_DIRECTORY; DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode); if (iterator == NULL) return B_NO_MEMORY; *_cookie = iterator; return B_OK; }
static status_t ext2_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name, int mode) { TRACE("ext2_create_dir()\n"); Volume* volume = (Volume*)_volume->private_volume; Inode* directory = (Inode*)_directory->private_node; if (volume->IsReadOnly()) return B_READ_ONLY_DEVICE; if (!directory->IsDirectory()) return B_BAD_TYPE; status_t status = directory->CheckPermissions(W_OK); if (status != B_OK) return status; TRACE("ext2_create_dir(): Starting transaction\n"); Transaction transaction(volume->GetJournal()); ino_t id; status = Inode::Create(transaction, directory, name, S_DIRECTORY | (mode & S_IUMSK), 0, EXT2_TYPE_DIRECTORY, NULL, &id); if (status != B_OK) return status; put_vnode(volume->FSVolume(), id); entry_cache_add(volume->ID(), directory->ID(), name, id); status = transaction.Done(); if (status != B_OK) { entry_cache_remove(volume->ID(), directory->ID(), name); return status; } notify_entry_created(volume->ID(), directory->ID(), name, id); TRACE("ext2_create_dir(): Done\n"); return B_OK; }
static status_t ext2_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter) { TRACE("ext2_remove_vnode()\n"); Volume* volume = (Volume*)_volume->private_volume; Inode* inode = (Inode*)_node->private_node; ObjectDeleter<Inode> inodeDeleter(inode); if (!inode->IsDeleted()) return B_OK; TRACE("ext2_remove_vnode(): Starting transaction\n"); Transaction transaction(volume->GetJournal()); if (!inode->IsSymLink() || inode->Size() >= EXT2_SHORT_SYMLINK_LENGTH) { TRACE("ext2_remove_vnode(): Truncating\n"); status_t status = inode->Resize(transaction, 0); if (status != B_OK) return status; } TRACE("ext2_remove_vnode(): Removing from orphan list\n"); status_t status = volume->RemoveOrphan(transaction, inode->ID()); if (status != B_OK) return status; TRACE("ext2_remove_vnode(): Setting deletion time\n"); inode->Node().SetDeletionTime(real_time_clock()); status = inode->WriteBack(transaction); if (status != B_OK) return status; TRACE("ext2_remove_vnode(): Freeing inode\n"); status = volume->FreeInode(transaction, inode->ID(), inode->IsDirectory()); // TODO: When Transaction::Done() fails, do we have to re-add the vnode? if (status == B_OK) status = transaction.Done(); return status; }
static status_t btrfs_open(fs_volume* /*_volume*/, fs_vnode* _node, int openMode, void** _cookie) { Inode* inode = (Inode*)_node->private_node; // opening a directory read-only is allowed, although you can't read // any data from it. if (inode->IsDirectory() && (openMode & O_RWMASK) != 0) return B_IS_A_DIRECTORY; status_t status = inode->CheckPermissions(open_mode_to_access(openMode) | (openMode & O_TRUNC ? W_OK : 0)); if (status != B_OK) return status; // Prepare the cookie file_cookie* cookie = new(std::nothrow) file_cookie; if (cookie == NULL) return B_NO_MEMORY; ObjectDeleter<file_cookie> cookieDeleter(cookie); cookie->open_mode = openMode & BTRFS_OPEN_MODE_USER_MASK; cookie->last_size = inode->Size(); cookie->last_notification = system_time(); if ((openMode & O_NOCACHE) != 0 && inode->FileCache() != NULL) { // Disable the file cache, if requested? status = file_cache_disable(inode->FileCache()); if (status != B_OK) return status; } cookieDeleter.Detach(); *_cookie = cookie; return B_OK; }
/*static*/ status_t Inode::Create(Transaction& transaction, Inode* parent, const char* name, int32 mode, int openMode, uint8 type, bool* _created, ino_t* _id, Inode** _inode, fs_vnode_ops* vnodeOps, uint32 publishFlags) { TRACE("Inode::Create()\n"); Volume* volume = transaction.GetVolume(); DirectoryIterator* entries = NULL; ObjectDeleter<DirectoryIterator> entriesDeleter; if (parent != NULL) { parent->WriteLockInTransaction(transaction); TRACE("Inode::Create(): Looking up entry destination\n"); HTree htree(volume, parent); status_t status = htree.Lookup(name, &entries); if (status == B_ENTRY_NOT_FOUND) { panic("We need to add the first node.\n"); return B_ERROR; } if (status != B_OK) return status; entriesDeleter.SetTo(entries); TRACE("Inode::Create(): Looking up to see if file already exists\n"); ino_t entryID; status = entries->FindEntry(name, &entryID); if (status == B_OK) { // File already exists TRACE("Inode::Create(): File already exists\n"); if (S_ISDIR(mode) || S_ISLNK(mode) || (openMode & O_EXCL) != 0) return B_FILE_EXISTS; Vnode vnode(volume, entryID); Inode* inode; status = vnode.Get(&inode); if (status != B_OK) { TRACE("Inode::Create() Failed to get the inode from the " "vnode\n"); return B_ENTRY_NOT_FOUND; } if (inode->IsDirectory() && (openMode & O_RWMASK) != O_RDONLY) return B_IS_A_DIRECTORY; if ((openMode & O_DIRECTORY) != 0 && !inode->IsDirectory()) return B_NOT_A_DIRECTORY; if (inode->CheckPermissions(open_mode_to_access(openMode) | ((openMode & O_TRUNC) != 0 ? W_OK : 0)) != B_OK) return B_NOT_ALLOWED; if ((openMode & O_TRUNC) != 0) { // Truncate requested TRACE("Inode::Create(): Truncating file\n"); inode->WriteLockInTransaction(transaction); status = inode->Resize(transaction, 0); if (status != B_OK) return status; } if (_created != NULL) *_created = false; if (_id != NULL) *_id = inode->ID(); if (_inode != NULL) *_inode = inode; if (_id != NULL || _inode != NULL) vnode.Keep(); TRACE("Inode::Create(): Done opening file\n"); return B_OK; /*} else if ((mode & S_ATTR_DIR) == 0) { TRACE("Inode::Create(): (mode & S_ATTR_DIR) == 0\n"); return B_BAD_VALUE;*/ } else if ((openMode & O_DIRECTORY) != 0) { TRACE("Inode::Create(): (openMode & O_DIRECTORY) != 0\n"); return B_ENTRY_NOT_FOUND; } // Return to initial position TRACE("Inode::Create(): Restarting iterator\n"); entries->Restart(); } status_t status; if (parent != NULL) { status = parent->CheckPermissions(W_OK); if (status != B_OK) return status; } TRACE("Inode::Create(): Allocating inode\n"); ino_t id; status = volume->AllocateInode(transaction, parent, mode, id); if (status != B_OK) { ERROR("Inode::Create(): AllocateInode() failed\n"); return status; } if (entries != NULL) { size_t nameLength = strlen(name); status = entries->AddEntry(transaction, name, nameLength, id, type); if (status != B_OK) { ERROR("Inode::Create(): AddEntry() failed\n"); return status; } } TRACE("Inode::Create(): Creating inode\n"); Inode* inode = new(std::nothrow) Inode(volume); if (inode == NULL) return B_NO_MEMORY; TRACE("Inode::Create(): Getting node structure\n"); ext2_inode& node = inode->Node(); TRACE("Inode::Create(): Initializing inode data\n"); memset(&node, 0, sizeof(ext2_inode)); node.SetMode(mode); node.SetUserID(geteuid()); node.SetGroupID(parent != NULL ? parent->Node().GroupID() : getegid()); node.SetNumLinks(inode->IsDirectory() ? 2 : 1); TRACE("Inode::Create(): Updating time\n"); struct timespec timespec; _BigtimeToTimespec(real_time_clock_usecs(), ×pec); inode->SetAccessTime(×pec); inode->SetCreationTime(×pec); inode->SetModificationTime(×pec); node.SetFlags(parent->Flags() & EXT2_INODE_INHERITED); if (volume->HasExtentsFeature() && (inode->IsDirectory() || inode->IsFile())) { node.SetFlag(EXT2_INODE_EXTENTS); ExtentStream stream(volume, &node.extent_stream, 0); stream.Init(); ASSERT(stream.Check()); } if (sizeof(ext2_inode) < volume->InodeSize()) node.SetExtraInodeSize(sizeof(ext2_inode) - EXT2_INODE_NORMAL_SIZE); TRACE("Inode::Create(): Updating ID\n"); inode->fID = id; if (inode->IsDirectory()) { TRACE("Inode::Create(): Initializing directory\n"); status = inode->InitDirectory(transaction, parent); if (status != B_OK) { ERROR("Inode::Create(): InitDirectory() failed\n"); delete inode; return status; } } // TODO: Maybe it can be better /*if (volume->HasExtendedAttributes()) { TRACE("Inode::Create(): Initializing extended attributes\n"); uint32 blockGroup = 0; uint32 pos = 0; uint32 allocated; status = volume->AllocateBlocks(transaction, 1, 1, blockGroup, pos, allocated); if (status != B_OK) return status; // Clear the new block uint32 blockNum = volume->FirstDataBlock() + pos + volume->BlocksPerGroup() * blockGroup; CachedBlock cached(volume); cached.SetToWritable(transaction, blockNum, true); node.SetExtendedAttributesBlock(blockNum); }*/ TRACE("Inode::Create(): Saving inode\n"); status = inode->WriteBack(transaction); if (status != B_OK) { delete inode; return status; } TRACE("Inode::Create(): Creating vnode\n"); Vnode vnode; status = vnode.Publish(transaction, inode, vnodeOps, publishFlags); if (status != B_OK) return status; if (!inode->IsSymLink()) { // Vnode::Publish doesn't publish symlinks if (!inode->IsDirectory()) { status = inode->EnableFileCache(); if (status != B_OK) return status; } inode->WriteLockInTransaction(transaction); } if (_created) *_created = true; if (_id != NULL) *_id = id; if (_inode != NULL) *_inode = inode; if (_id != NULL || _inode != NULL) vnode.Keep(); TRACE("Inode::Create(): Deleting entries iterator\n"); DirectoryIterator* iterator = entriesDeleter.Detach(); TRACE("Inode::Create(): Entries iterator: %p\n", entries); delete iterator; TRACE("Inode::Create(): Done\n"); return B_OK; }
int main(int argc, char **argv) { puts("Copyright (c) 2001-2010 pinc Software."); if (argc < 2 || !strcmp(argv[1], "--help")) { char *filename = strrchr(argv[0],'/'); fprintf(stderr,"usage: %s [-srib] <device> [allocation_group start]\n" "\t-s\tdump superblock\n" "\t-r\tdump root node\n" " the following options need the allocation_group/start " "parameters:\n" "\t-i\tdump inode\n" "\t-b\tdump b+tree\n" "\t-v\tvalidate b+tree\n" "\t-h\thexdump\n" "\t-o\tshow disk offsets\n", filename ? filename + 1 : argv[0]); return -1; } bool dumpRootNode = false; bool dumpInode = false; bool dumpSuperBlock = false; bool dumpBTree = false; bool validateBTree = false; bool dumpHex = false; bool showOffsets = false; while (*++argv) { char *arg = *argv; if (*arg == '-') { while (*++arg && isalpha(*arg)) { switch (*arg) { case 's': dumpSuperBlock = true; break; case 'r': dumpRootNode = true; break; case 'i': dumpInode = true; break; case 'b': dumpBTree = true; break; case 'v': validateBTree = true; break; case 'h': dumpHex = true; break; case 'o': showOffsets = true; break; } } } else break; } Disk disk(argv[0]); if (disk.InitCheck() < B_OK) { fprintf(stderr, "Could not open device or file: %s\n", strerror(disk.InitCheck())); return -1; } putchar('\n'); if (!dumpSuperBlock && !dumpRootNode && !dumpInode && !dumpBTree && !dumpHex) { printf(" Name:\t\t\t\"%s\"\n", disk.SuperBlock()->name); printf(" (disk is %s)\n\n", disk.ValidateSuperBlock() == B_OK ? "valid" : "invalid!!"); printf(" Block Size:\t\t%" B_PRIu32 " bytes\n", disk.BlockSize()); printf(" Number of Blocks:\t%12" B_PRIdOFF "\t%10g MB\n", disk.NumBlocks(), disk.NumBlocks() * disk.BlockSize() / (1024.0*1024)); if (disk.BlockBitmap() != NULL) { printf(" Used Blocks:\t\t%12" B_PRIdOFF "\t%10g MB\n", disk.BlockBitmap()->UsedBlocks(), disk.BlockBitmap()->UsedBlocks() * disk.BlockSize() / (1024.0*1024)); printf(" Free Blocks:\t\t%12" B_PRIdOFF "\t%10g MB\n", disk.BlockBitmap()->FreeBlocks(), disk.BlockBitmap()->FreeBlocks() * disk.BlockSize() / (1024.0*1024)); } int32 size = (disk.AllocationGroups() * disk.SuperBlock()->blocks_per_ag); printf(" Bitmap Size:\t\t%" B_PRIu32 " bytes (%" B_PRId32 " blocks, %" B_PRId32 " per allocation group)\n", disk.BlockSize() * size, size, disk.SuperBlock()->blocks_per_ag); printf(" Allocation Groups:\t%" B_PRIu32 "\n\n", disk.AllocationGroups()); dump_block_run(" Log:\t\t\t", disk.Log()); printf(" (was %s)\n\n", disk.SuperBlock()->flags == SUPER_BLOCK_CLEAN ? "cleanly unmounted" : "not unmounted cleanly!"); dump_block_run(" Root Directory:\t", disk.Root()); putchar('\n'); } else if (dumpSuperBlock) { dump_super_block(disk.SuperBlock()); putchar('\n'); } if (disk.ValidateSuperBlock() < B_OK) { fprintf(stderr, "The disk's superblock is corrupt (or it's not a BFS " "device)!\n"); return 0; } if (dumpRootNode) { bfs_inode inode; if (disk.ReadAt(disk.ToOffset(disk.Root()), (void *)&inode, sizeof(bfs_inode)) < B_OK) { fprintf(stderr,"Could not read root node from disk!\n"); } else { puts("Root node:\n-----------------------------------------"); dump_inode(NULL, &inode, showOffsets); dump_indirect_stream(disk, &inode, showOffsets); putchar('\n'); } } char buffer[disk.BlockSize()]; bfs_inode* bfsInode = (bfs_inode*)buffer; block_run run; Inode *inode = NULL; if (dumpInode || dumpBTree || dumpHex || validateBTree) { // Set the block_run to the right value (as specified on the command // line) if (!argv[1]) { fprintf(stderr, "The -i/b/f options need the allocation group and " "starting offset (or the block number) of the node to dump!\n"); return -1; } run = parseBlockRun(disk, argv[1], argv[2]); if (disk.ReadAt(disk.ToOffset(run), buffer, disk.BlockSize()) <= 0) { fprintf(stderr,"Could not read node from disk!\n"); return -1; } inode = Inode::Factory(&disk, bfsInode, false); if (inode == NULL || inode->InitCheck() < B_OK) { fprintf(stderr,"Not a valid inode!\n"); delete inode; inode = NULL; } } if (dumpInode) { printf("Inode at block %" B_PRIdOFF ":\n------------------------------" "-----------\n", disk.ToBlock(run)); dump_inode(inode, bfsInode, showOffsets); dump_indirect_stream(disk, bfsInode, showOffsets); dump_double_indirect_stream(disk, bfsInode, showOffsets); dump_small_data(inode); putchar('\n'); } if (dumpBTree && inode != NULL) { printf("B+Tree at block %" B_PRIdOFF ":\n-----------------------------" "------------\n", disk.ToBlock(run)); if (inode->IsDirectory() || inode->IsAttributeDirectory()) { dump_bplustree(disk, (Directory *)inode, inode->Size(), dumpHex); putchar('\n'); } else fprintf(stderr, "Inode is not a directory!\n"); } if (validateBTree && inode != NULL) { printf("Validating B+Tree at block %" B_PRIdOFF ":\n------------------" "-----------------------\n", disk.ToBlock(run)); if (inode->IsDirectory() || inode->IsAttributeDirectory()) { BPlusTree *tree; if (((Directory *)inode)->GetTree(&tree) == B_OK) { if (tree->Validate(true) < B_OK) puts("B+Tree is corrupt!"); else puts("B+Tree seems to be okay."); } } else fprintf(stderr, "Inode is not a directory!\n"); } if (dumpHex) { printf("Hexdump from inode at block %" B_PRIdOFF ":\n-----------------" "------------------------\n", disk.ToBlock(run)); dump_block(buffer, disk.BlockSize()); putchar('\n'); } delete inode; return 0; }
static status_t ext2_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName, fs_vnode* _newDir, const char* newName) { TRACE("ext2_rename()\n"); Volume* volume = (Volume*)_volume->private_volume; Inode* oldDirectory = (Inode*)_oldDir->private_node; Inode* newDirectory = (Inode*)_newDir->private_node; if (oldDirectory == newDirectory && strcmp(oldName, newName) == 0) return B_OK; Transaction transaction(volume->GetJournal()); oldDirectory->WriteLockInTransaction(transaction); if (oldDirectory != newDirectory) newDirectory->WriteLockInTransaction(transaction); status_t status = oldDirectory->CheckPermissions(W_OK); if (status == B_OK) status = newDirectory->CheckPermissions(W_OK); if (status != B_OK) return status; HTree oldHTree(volume, oldDirectory); DirectoryIterator* oldIterator; status = oldHTree.Lookup(oldName, &oldIterator); if (status != B_OK) return status; ObjectDeleter<DirectoryIterator> oldIteratorDeleter(oldIterator); ino_t oldID; status = oldIterator->FindEntry(oldName, &oldID); if (status != B_OK) return status; if (oldDirectory != newDirectory) { TRACE("ext2_rename(): Different parent directories\n"); CachedBlock cached(volume); ino_t parentID = newDirectory->ID(); ino_t oldDirID = oldDirectory->ID(); do { Vnode vnode(volume, parentID); Inode* parent; status = vnode.Get(&parent); if (status != B_OK) return B_IO_ERROR; fsblock_t blockNum; status = parent->FindBlock(0, blockNum); if (status != B_OK) return status; const HTreeRoot* data = (const HTreeRoot*)cached.SetTo(blockNum); parentID = data->dotdot.InodeID(); } while (parentID != oldID && parentID != oldDirID && parentID != EXT2_ROOT_NODE); if (parentID == oldID) return B_BAD_VALUE; } HTree newHTree(volume, newDirectory); DirectoryIterator* newIterator; status = newHTree.Lookup(newName, &newIterator); if (status != B_OK) return status; ObjectDeleter<DirectoryIterator> newIteratorDeleter(newIterator); Vnode vnode(volume, oldID); Inode* inode; status = vnode.Get(&inode); if (status != B_OK) return status; uint8 fileType; // TODO: Support all file types? if (inode->IsDirectory()) fileType = EXT2_TYPE_DIRECTORY; else if (inode->IsSymLink()) fileType = EXT2_TYPE_SYMLINK; else fileType = EXT2_TYPE_FILE; // Add entry in destination directory ino_t existentID; status = newIterator->FindEntry(newName, &existentID); if (status == B_OK) { if (existentID == oldID) { // Remove entry in oldID // return inode->Unlink(); return B_BAD_VALUE; } Vnode vnodeExistent(volume, existentID); Inode* existent; if (vnodeExistent.Get(&existent) != B_OK) return B_NAME_IN_USE; if (existent->IsDirectory() != inode->IsDirectory()) { return existent->IsDirectory() ? B_IS_A_DIRECTORY : B_NOT_A_DIRECTORY; } // TODO: Perhaps we have to revert this in case of error? status = newIterator->ChangeEntry(transaction, oldID, fileType); if (status != B_OK) return status; notify_entry_removed(volume->ID(), newDirectory->ID(), newName, existentID); } else if (status == B_ENTRY_NOT_FOUND) { newIterator->Restart(); status = newIterator->AddEntry(transaction, newName, strlen(newName), oldID, fileType); if (status != B_OK) return status; } else return status; // Remove entry from source folder status = oldIterator->RemoveEntry(transaction); if (status != B_OK) return status; inode->WriteLockInTransaction(transaction); if (oldDirectory != newDirectory && inode->IsDirectory()) { DirectoryIterator inodeIterator(inode); status = inodeIterator.FindEntry(".."); if (status == B_ENTRY_NOT_FOUND) { TRACE("Corrupt file system. Missing \"..\" in directory %" B_PRIdINO "\n", inode->ID()); return B_BAD_DATA; } else if (status != B_OK) return status; inodeIterator.ChangeEntry(transaction, newDirectory->ID(), (uint8)EXT2_TYPE_DIRECTORY); } status = inode->WriteBack(transaction); if (status != B_OK) return status; entry_cache_remove(volume->ID(), oldDirectory->ID(), oldName); entry_cache_add(volume->ID(), newDirectory->ID(), newName, oldID); status = transaction.Done(); if (status != B_OK) { entry_cache_remove(volume->ID(), oldDirectory->ID(), newName); entry_cache_add(volume->ID(), newDirectory->ID(), oldName, oldID); return status; } notify_entry_moved(volume->ID(), oldDirectory->ID(), oldName, newDirectory->ID(), newName, oldID); return B_OK; }
static status_t ext2_create_symlink(fs_volume* _volume, fs_vnode* _directory, const char* name, const char* path, int mode) { TRACE("ext2_create_symlink()\n"); Volume* volume = (Volume*)_volume->private_volume; Inode* directory = (Inode*)_directory->private_node; if (volume->IsReadOnly()) return B_READ_ONLY_DEVICE; if (!directory->IsDirectory()) return B_BAD_TYPE; status_t status = directory->CheckPermissions(W_OK); if (status != B_OK) return status; TRACE("ext2_create_symlink(): Starting transaction\n"); Transaction transaction(volume->GetJournal()); Inode* link; ino_t id; status = Inode::Create(transaction, directory, name, S_SYMLINK | 0777, 0, (uint8)EXT2_TYPE_SYMLINK, NULL, &id, &link); if (status != B_OK) return status; // TODO: We have to prepare the link before publishing? size_t length = strlen(path); TRACE("ext2_create_symlink(): Path (%s) length: %d\n", path, (int)length); if (length < EXT2_SHORT_SYMLINK_LENGTH) { strcpy(link->Node().symlink, path); link->Node().SetSize((uint32)length); TRACE("ext2_create_symlink(): Publishing vnode\n"); publish_vnode(volume->FSVolume(), id, link, &gExt2VnodeOps, link->Mode(), 0); put_vnode(volume->FSVolume(), id); } else { TRACE("ext2_create_symlink(): Publishing vnode\n"); publish_vnode(volume->FSVolume(), id, link, &gExt2VnodeOps, link->Mode(), 0); put_vnode(volume->FSVolume(), id); if (!link->HasFileCache()) { status = link->CreateFileCache(); if (status != B_OK) return status; } size_t written = length; status = link->WriteAt(transaction, 0, (const uint8*)path, &written); if (status == B_OK && written != length) status = B_IO_ERROR; } if (status == B_OK) status = link->WriteBack(transaction); entry_cache_add(volume->ID(), directory->ID(), name, id); status = transaction.Done(); if (status != B_OK) { entry_cache_remove(volume->ID(), directory->ID(), name); return status; } notify_entry_created(volume->ID(), directory->ID(), name, id); TRACE("ext2_create_symlink(): Done\n"); return status; }
static status_t ext2_create(fs_volume* _volume, fs_vnode* _directory, const char* name, int openMode, int mode, void** _cookie, ino_t* _vnodeID) { Volume* volume = (Volume*)_volume->private_volume; Inode* directory = (Inode*)_directory->private_node; TRACE("ext2_create()\n"); if (volume->IsReadOnly()) return B_READ_ONLY_DEVICE; if (!directory->IsDirectory()) return B_BAD_TYPE; TRACE("ext2_create(): Creating cookie\n"); // Allocate cookie file_cookie* cookie = new(std::nothrow) file_cookie; if (cookie == NULL) return B_NO_MEMORY; ObjectDeleter<file_cookie> cookieDeleter(cookie); cookie->open_mode = openMode; cookie->last_size = 0; cookie->last_notification = system_time(); TRACE("ext2_create(): Starting transaction\n"); Transaction transaction(volume->GetJournal()); TRACE("ext2_create(): Creating inode\n"); Inode* inode; bool created; status_t status = Inode::Create(transaction, directory, name, S_FILE | (mode & S_IUMSK), openMode, EXT2_TYPE_FILE, &created, _vnodeID, &inode, &gExt2VnodeOps); if (status != B_OK) return status; TRACE("ext2_create(): Created inode\n"); if ((openMode & O_NOCACHE) != 0) { status = inode->DisableFileCache(); if (status != B_OK) return status; } entry_cache_add(volume->ID(), directory->ID(), name, *_vnodeID); status = transaction.Done(); if (status != B_OK) { entry_cache_remove(volume->ID(), directory->ID(), name); return status; } *_cookie = cookie; cookieDeleter.Detach(); if (created) notify_entry_created(volume->ID(), directory->ID(), name, *_vnodeID); return B_OK; }
status_t ext2_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat, uint32 mask) { TRACE("ext2_write_stat\n"); Volume* volume = (Volume*)_volume->private_volume; if (volume->IsReadOnly()) return B_READ_ONLY_DEVICE; Inode* inode = (Inode*)_node->private_node; ext2_inode& node = inode->Node(); bool updateTime = false; uid_t uid = geteuid(); bool isOwnerOrRoot = uid == 0 || uid == (uid_t)node.UserID(); bool hasWriteAccess = inode->CheckPermissions(W_OK) == B_OK; TRACE("ext2_write_stat: Starting transaction\n"); Transaction transaction(volume->GetJournal()); inode->WriteLockInTransaction(transaction); if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) { if (inode->IsDirectory()) return B_IS_A_DIRECTORY; if (!inode->IsFile()) return B_BAD_VALUE; if (!hasWriteAccess) return B_NOT_ALLOWED; TRACE("ext2_write_stat: Old size: %ld, new size: %ld\n", (long)inode->Size(), (long)stat->st_size); off_t oldSize = inode->Size(); status_t status = inode->Resize(transaction, stat->st_size); if(status != B_OK) return status; if ((mask & B_STAT_SIZE_INSECURE) == 0) { rw_lock_write_unlock(inode->Lock()); inode->FillGapWithZeros(oldSize, inode->Size()); rw_lock_write_lock(inode->Lock()); } updateTime = true; } if ((mask & B_STAT_MODE) != 0) { // only the user or root can do that if (!isOwnerOrRoot) return B_NOT_ALLOWED; node.UpdateMode(stat->st_mode, S_IUMSK); updateTime = true; } if ((mask & B_STAT_UID) != 0) { // only root should be allowed if (uid != 0) return B_NOT_ALLOWED; node.SetUserID(stat->st_uid); updateTime = true; } if ((mask & B_STAT_GID) != 0) { // only the user or root can do that if (!isOwnerOrRoot) return B_NOT_ALLOWED; node.SetGroupID(stat->st_gid); updateTime = true; } if ((mask & B_STAT_MODIFICATION_TIME) != 0 || updateTime || (mask & B_STAT_CHANGE_TIME) != 0) { // the user or root can do that or any user with write access if (!isOwnerOrRoot && !hasWriteAccess) return B_NOT_ALLOWED; struct timespec newTimespec = { 0, 0}; if ((mask & B_STAT_MODIFICATION_TIME) != 0) newTimespec = stat->st_mtim; if ((mask & B_STAT_CHANGE_TIME) != 0 && stat->st_ctim.tv_sec > newTimespec.tv_sec) newTimespec = stat->st_ctim; if (newTimespec.tv_sec == 0) Inode::_BigtimeToTimespec(real_time_clock_usecs(), &newTimespec); inode->SetModificationTime(&newTimespec); } if ((mask & B_STAT_CREATION_TIME) != 0) { // the user or root can do that or any user with write access if (!isOwnerOrRoot && !hasWriteAccess) return B_NOT_ALLOWED; inode->SetCreationTime(&stat->st_crtim); } status_t status = inode->WriteBack(transaction); if (status == B_OK) status = transaction.Done(); if (status == B_OK) notify_stat_changed(volume->ID(), -1, inode->ID(), mask); return status; }
void checkFiles(Disk &disk,BPlusTree &tree,char *attribute) { block_run *runs = (block_run *)malloc(gCount * sizeof(block_run)); if (runs == NULL) { fprintf(stderr," Not enough memory!\n"); return; } // copy hashtable to array block_run *run = NULL; int32 index = 0; gHashtable.Rewind(); while (gHashtable.GetNextEntry((void **)&run) == B_OK) { runs[index++] = *run; } // sort array to speed up disk access qsort(runs,index,sizeof(block_run),(int (*)(const void *,const void *))compareBlockRuns); bool sizeIndex = !strcmp(attribute,"size"); bool nameIndex = !strcmp(attribute,"name"); bool modifiedIndex = !strcmp(attribute,"last_modified"); char key[B_FILE_NAME_LENGTH]; uint16 keyLength = 0; Inode *inode = NULL; for (int32 i = 0;i < index;i++) { if (i % 50 == 0) printf(" %7ld%s1A\n",i,gEscape); delete inode; inode = Inode::Factory(&disk,runs[i]); if (inode == NULL || inode->InitCheck() < B_OK) { fprintf(stderr," inode at (%ld, %d) is corrupt!\n",runs[i].allocation_group,runs[i].start); delete inode; continue; } // check indices not based on standard attributes if (sizeIndex) { if (inode->IsDirectory()) continue; memcpy(key,&inode->InodeBuffer()->data.size,sizeof(off_t)); keyLength = sizeof(off_t); } else if (nameIndex) { strcpy(key,inode->Name()); keyLength = strlen(key); } else if (modifiedIndex) { if (inode->IsDirectory()) continue; memcpy(key,&inode->InodeBuffer()->last_modified_time,sizeof(off_t)); keyLength = sizeof(off_t); } else // iterate through all attributes to find the right one (damn slow, sorry...) { inode->RewindAttributes(); char name[B_FILE_NAME_LENGTH]; uint32 type; void *data; size_t length; bool found = false; while (inode->GetNextAttribute(name,&type,&data,&length) == B_OK) { if (!strcmp(name,attribute)) { strncpy(key,(char *)data,B_FILE_NAME_LENGTH - 1); key[B_FILE_NAME_LENGTH - 1] = '\0'; keyLength = length > B_FILE_NAME_LENGTH ? B_FILE_NAME_LENGTH : length; found = true; break; } } if (!found) continue; } off_t value; if (tree.Find((uint8 *)key,keyLength,&value) < B_OK) { if (*inode->Name()) fprintf(stderr," inode at (%ld, %d) name \"%s\" is not in index!\n",runs[i].allocation_group,runs[i].start,inode->Name()); else { // inode is obviously deleted! block_run parent = inode->Parent(); Directory *directory = (Directory *)Inode::Factory(&disk,parent); if (directory != NULL && directory->InitCheck() == B_OK) { BPlusTree *parentTree; if (directory->GetTree(&parentTree) == B_OK) { char name[B_FILE_NAME_LENGTH]; uint16 length; off_t offset,searchOffset = disk.ToBlock(runs[i]); bool found = false; while (parentTree->GetNextEntry(name,&length,B_FILE_NAME_LENGTH,&offset) == B_OK) { if (offset == searchOffset) { fprintf(stderr," inode at (%ld, %d) name \"%s\" was obviously deleted, but the parent \"%s\" still contains it!\n",runs[i].allocation_group,runs[i].start,name,directory->Name()); found = true; break; } } if (!found) fprintf(stderr," inode at (%ld, %d) was obviously deleted, and the parent \"%s\" obviously doesn't contain it anymore!\n",runs[i].allocation_group,runs[i].start,directory->Name()); } else fprintf(stderr," inode at (%ld, %d) was obviously deleted, but the parent \"%s\" is invalid and still contains it!\n",runs[i].allocation_group,runs[i].start,directory->Name()); } else { // not that this would be really possible... - but who knows fprintf(stderr," inode at (%ld, %d) is not in index and has invalid parent!\n",runs[i].allocation_group,runs[i].start); } delete directory; } } else { if (bplustree_node::LinkType(value) == BPLUSTREE_NODE) { if (disk.ToBlockRun(value) != runs[i]) fprintf(stderr," offset in index and inode offset doesn't match for inode \"%s\" at (%ld, %d)\n",inode->Name(),runs[i].allocation_group,runs[i].start); } else { // search duplicates char name[B_FILE_NAME_LENGTH]; uint16 length; off_t offset; bool found = false,duplicates = false; //puts("++"); tree.Rewind(); while (tree.GetNextEntry(name,&length,B_FILE_NAME_LENGTH,&offset) == B_OK) { //printf("search for = %ld, key = %ld -> value = %Ld (%ld, %d)\n",*(int32 *)&key,*(int32 *)&name,offset,disk.ToBlockRun(offset).allocation_group,disk.ToBlockRun(offset).start); if (keyLength == length && !memcmp(key,name,keyLength)) { duplicates = true; if (disk.ToBlockRun(offset) == runs[i]) { found = true; break; } } //else if (duplicates) // break; } if (!found) { printf(" inode \"%s\" at (%ld, %d) not found in duplicates!\n",inode->Name(),runs[i].allocation_group,runs[i].start); // return; } } } } delete inode; printf(" %7Ld files processed.\n",gCount); }