static status_t ext2_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset, size_t size, struct file_io_vec* vecs, size_t* _count) { TRACE("ext2_get_file_map()\n"); Volume* volume = (Volume*)_volume->private_volume; Inode* inode = (Inode*)_node->private_node; size_t index = 0, max = *_count; while (true) { fsblock_t block; uint32 count = 1; status_t status = inode->FindBlock(offset, block, &count); if (status != B_OK) return status; if (block > volume->NumBlocks()) { panic("ext2_get_file_map() found block %" B_PRIu64 " for offset %" B_PRIdOFF "\n", block, offset); } off_t blockOffset = block << volume->BlockShift(); uint32 blockLength = volume->BlockSize() * count; if (index > 0 && (vecs[index - 1].offset == blockOffset - vecs[index - 1].length || (vecs[index - 1].offset == -1 && block == 0))) { vecs[index - 1].length += blockLength; } else { if (index >= max) { // we're out of file_io_vecs; let's bail out *_count = index; return B_BUFFER_OVERFLOW; } // 'block' is 0 for sparse blocks if (block != 0) vecs[index].offset = blockOffset; else vecs[index].offset = -1; vecs[index].length = blockLength; index++; } offset += blockLength; if (offset >= inode->Size() || size <= blockLength) { // We're done! *_count = index; TRACE("ext2_get_file_map for inode %" B_PRIdINO "\n", inode->ID()); return B_OK; } size -= blockLength; } // can never get here return B_ERROR; }
static status_t btrfs_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset, size_t size, struct file_io_vec* vecs, size_t* _count) { TRACE("btrfs_get_file_map()\n"); Inode* inode = (Inode*)_node->private_node; size_t index = 0, max = *_count; while (true) { off_t blockOffset; off_t blockLength; status_t status = inode->FindBlock(offset, blockOffset, &blockLength); if (status != B_OK) return status; if (index > 0 && (vecs[index - 1].offset == blockOffset - vecs[index - 1].length)) { vecs[index - 1].length += blockLength; } else { if (index >= max) { // we're out of file_io_vecs; let's bail out *_count = index; return B_BUFFER_OVERFLOW; } vecs[index].offset = blockOffset; vecs[index].length = blockLength; index++; } offset += blockLength; size -= blockLength; if ((off_t)size <= vecs[index - 1].length || offset >= inode->Size()) { // We're done! *_count = index; TRACE("btrfs_get_file_map for inode %" B_PRIdINO "\n", inode->ID()); return B_OK; } } // can never get here return B_ERROR; }
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; }