163db34c8SAxel Dörfler/*
2db1b905eSJérôme Duval * Copyright 2010, J��r��me Duval, korli@users.berlios.de.
363db34c8SAxel Dörfler * Copyright 2008, Axel D��rfler, axeld@pinc-software.de.
463db34c8SAxel Dörfler * This file may be used under the terms of the MIT License.
563db34c8SAxel Dörfler */
663db34c8SAxel Dörfler
763db34c8SAxel Dörfler
863db34c8SAxel Dörfler#include <dirent.h>
9af206cf2SJérôme Duval#include <util/kernel_cpp.h>
1063db34c8SAxel Dörfler#include <string.h>
1163db34c8SAxel Dörfler
12919f9c41SJérôme Duval#include <AutoDeleter.h>
1363db34c8SAxel Dörfler#include <fs_cache.h>
1463db34c8SAxel Dörfler#include <fs_info.h>
15a1b0ec30SJérôme Duval#include <io_requests.h>
16a1b0ec30SJérôme Duval#include <NodeMonitor.h>
17a1b0ec30SJérôme Duval#include <util/AutoLock.h>
1863db34c8SAxel Dörfler
19db1b905eSJérôme Duval#include "Attribute.h"
20a1b0ec30SJérôme Duval#include "CachedBlock.h"
2163db34c8SAxel Dörfler#include "DirectoryIterator.h"
2263db34c8SAxel Dörfler#include "ext2.h"
23919f9c41SJérôme Duval#include "HTree.h"
2463db34c8SAxel Dörfler#include "Inode.h"
25a1b0ec30SJérôme Duval#include "Journal.h"
26a1b0ec30SJérôme Duval#include "Utility.h"
2763db34c8SAxel Dörfler
2863db34c8SAxel Dörfler
29b026d219SFrançois Revol//#define TRACE_EXT2
30b026d219SFrançois Revol#ifdef TRACE_EXT2
31b026d219SFrançois Revol#	define TRACE(x...) dprintf("\33[34mext2:\33[0m " x)
32b026d219SFrançois Revol#else
33b026d219SFrançois Revol#	define TRACE(x...) ;
34b026d219SFrançois Revol#endif
35f7218c21SJérôme Duval#define ERROR(x...) dprintf("\33[34mext2:\33[0m " x)
36b026d219SFrançois Revol
37b026d219SFrançois Revol
3863db34c8SAxel Dörfler#define EXT2_IO_SIZE	65536
3963db34c8SAxel Dörfler
4063db34c8SAxel Dörfler
4163db34c8SAxel Dörflerstruct identify_cookie {
4263db34c8SAxel Dörfler	ext2_super_block super_block;
4363db34c8SAxel Dörfler};
4463db34c8SAxel Dörfler
4563db34c8SAxel Dörfler
46a1b0ec30SJérôme Duval//!	ext2_io() callback hook
47a1b0ec30SJérôme Duvalstatic status_t
48a1b0ec30SJérôme Duvaliterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset,
49a1b0ec30SJérôme Duval	size_t size, struct file_io_vec* vecs, size_t* _count)
5063db34c8SAxel Dörfler{
51a1b0ec30SJérôme Duval	Inode* inode = (Inode*)cookie;
52a1b0ec30SJérôme Duval
53a1b0ec30SJérôme Duval	return file_map_translate(inode->Map(), offset, size, vecs, _count,
54a1b0ec30SJérôme Duval		inode->GetVolume()->BlockSize());
55a1b0ec30SJérôme Duval}
5663db34c8SAxel Dörfler
57a1b0ec30SJérôme Duval
58a1b0ec30SJérôme Duval//!	ext2_io() callback hook
59a1b0ec30SJérôme Duvalstatic status_t
60a1b0ec30SJérôme Duvaliterative_io_finished_hook(void* cookie, io_request* request, status_t status,
61a1b0ec30SJérôme Duval	bool partialTransfer, size_t bytesTransferred)
62a1b0ec30SJérôme Duval{
63a1b0ec30SJérôme Duval	Inode* inode = (Inode*)cookie;
64a1b0ec30SJérôme Duval	rw_lock_read_unlock(inode->Lock());
65a1b0ec30SJérôme Duval	return B_OK;
6663db34c8SAxel Dörfler}
6763db34c8SAxel Dörfler
6863db34c8SAxel Dörfler
6963db34c8SAxel Dörfler//	#pragma mark - Scanning
7063db34c8SAxel Dörfler
7163db34c8SAxel Dörfler
7263db34c8SAxel Dörflerstatic float
7363db34c8SAxel Dörflerext2_identify_partition(int fd, partition_data *partition, void **_cookie)
7463db34c8SAxel Dörfler{
75ce4e12caSJérôme Duval	STATIC_ASSERT(sizeof(struct ext2_super_block) == 1024);
76ce4e12caSJérôme Duval	STATIC_ASSERT(sizeof(struct ext2_block_group) == 64);
77ce4e12caSJérôme Duval
7863db34c8SAxel Dörfler	ext2_super_block superBlock;
7963db34c8SAxel Dörfler	status_t status = Volume::Identify(fd, &superBlock);
8063db34c8SAxel Dörfler	if (status != B_OK)
8153ef70a0SIngo Weinhold		return -1;
8263db34c8SAxel Dörfler
8363db34c8SAxel Dörfler	identify_cookie *cookie = new identify_cookie;
8463db34c8SAxel Dörfler	memcpy(&cookie->super_block, &superBlock, sizeof(ext2_super_block));
8563db34c8SAxel Dörfler
8663db34c8SAxel Dörfler	*_cookie = cookie;
8763db34c8SAxel Dörfler	return 0.8f;
8863db34c8SAxel Dörfler}
8963db34c8SAxel Dörfler
9063db34c8SAxel Dörfler
9163db34c8SAxel Dörflerstatic status_t
9263db34c8SAxel Dörflerext2_scan_partition(int fd, partition_data *partition, void *_cookie)
9363db34c8SAxel Dörfler{
9463db34c8SAxel Dörfler	identify_cookie *cookie = (identify_cookie *)_cookie;
9563db34c8SAxel Dörfler
9663db34c8SAxel Dörfler	partition->status = B_PARTITION_VALID;
9763db34c8SAxel Dörfler	partition->flags |= B_PARTITION_FILE_SYSTEM;
98d8772e0cSJérôme Duval	partition->content_size = cookie->super_block.NumBlocks(
99d8772e0cSJérôme Duval		(cookie->super_block.CompatibleFeatures()
100d8772e0cSJérôme Duval			& EXT2_INCOMPATIBLE_FEATURE_64BIT) != 0)
101d8772e0cSJérôme Duval			<< cookie->super_block.BlockShift();
10263db34c8SAxel Dörfler	partition->block_size = 1UL << cookie->super_block.BlockShift();
10363db34c8SAxel Dörfler	partition->content_name = strdup(cookie->super_block.name);
10463db34c8SAxel Dörfler	if (partition->content_name == NULL)
10563db34c8SAxel Dörfler		return B_NO_MEMORY;
10663db34c8SAxel Dörfler
10763db34c8SAxel Dörfler	return B_OK;
10863db34c8SAxel Dörfler}
10963db34c8SAxel Dörfler
11063db34c8SAxel Dörfler
11163db34c8SAxel Dörflerstatic void
11263db34c8SAxel Dörflerext2_free_identify_partition_cookie(partition_data* partition, void* _cookie)
11363db34c8SAxel Dörfler{
11463db34c8SAxel Dörfler	delete (identify_cookie*)_cookie;
11563db34c8SAxel Dörfler}
11663db34c8SAxel Dörfler
11763db34c8SAxel Dörfler
11863db34c8SAxel Dörfler//	#pragma mark -
11963db34c8SAxel Dörfler
12063db34c8SAxel Dörfler
12163db34c8SAxel Dörflerstatic status_t
12263db34c8SAxel Dörflerext2_mount(fs_volume* _volume, const char* device, uint32 flags,
12363db34c8SAxel Dörfler	const char* args, ino_t* _rootID)
12463db34c8SAxel Dörfler{
12585f15b4bSJérôme Duval	Volume* volume = new(std::nothrow) Volume(_volume);
12663db34c8SAxel Dörfler	if (volume == NULL)
12763db34c8SAxel Dörfler		return B_NO_MEMORY;
12863db34c8SAxel Dörfler
12963db34c8SAxel Dörfler	// TODO: this is a bit hacky: we can't use publish_vnode() to publish
13063db34c8SAxel Dörfler	// the root node, or else its file cache cannot be created (we could
13163db34c8SAxel Dörfler	// create it later, though). Therefore we're using get_vnode() in Mount(),
13263db34c8SAxel Dörfler	// but that requires us to export our volume data before calling it.
13363db34c8SAxel Dörfler	_volume->private_volume = volume;
13463db34c8SAxel Dörfler	_volume->ops = &gExt2VolumeOps;
13563db34c8SAxel Dörfler
13663db34c8SAxel Dörfler	status_t status = volume->Mount(device, flags);
13763db34c8SAxel Dörfler	if (status != B_OK) {
138f7218c21SJérôme Duval		ERROR("Failed mounting the volume. Error: %s\n", strerror(status));
13963db34c8SAxel Dörfler		delete volume;
14063db34c8SAxel Dörfler		return status;
14163db34c8SAxel Dörfler	}
14263db34c8SAxel Dörfler
14363db34c8SAxel Dörfler	*_rootID = volume->RootNode()->ID();
14463db34c8SAxel Dörfler	return B_OK;
14563db34c8SAxel Dörfler}
14663db34c8SAxel Dörfler
14763db34c8SAxel Dörfler
14863db34c8SAxel Dörflerstatic status_t
14963db34c8SAxel Dörflerext2_unmount(fs_volume *_volume)
15063db34c8SAxel Dörfler{
15163db34c8SAxel Dörfler	Volume* volume = (Volume *)_volume->private_volume;
15263db34c8SAxel Dörfler
15363db34c8SAxel Dörfler	status_t status = volume->Unmount();
15463db34c8SAxel Dörfler	delete volume;
15563db34c8SAxel Dörfler
15663db34c8SAxel Dörfler	return status;
15763db34c8SAxel Dörfler}
15863db34c8SAxel Dörfler
15963db34c8SAxel Dörfler
16063db34c8SAxel Dörflerstatic status_t
16163db34c8SAxel Dörflerext2_read_fs_info(fs_volume* _volume, struct fs_info* info)
16263db34c8SAxel Dörfler{
16363db34c8SAxel Dörfler	Volume* volume = (Volume*)_volume->private_volume;
16463db34c8SAxel Dörfler
16563db34c8SAxel Dörfler	// File system flags
166886d6531SJérôme Duval	info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR
16763db34c8SAxel Dörfler		| (volume->IsReadOnly() ? B_FS_IS_READONLY : 0);
16863db34c8SAxel Dörfler	info->io_size = EXT2_IO_SIZE;
16963db34c8SAxel Dörfler	info->block_size = volume->BlockSize();
17063db34c8SAxel Dörfler	info->total_blocks = volume->NumBlocks();
171a1b0ec30SJérôme Duval	info->free_blocks = volume->NumFreeBlocks();
17263db34c8SAxel Dörfler
17363db34c8SAxel Dörfler	// Volume name
17463db34c8SAxel Dörfler	strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
17563db34c8SAxel Dörfler
17663db34c8SAxel Dörfler	// File system name
17763db34c8SAxel Dörfler	strlcpy(info->fsh_name, "ext2", sizeof(info->fsh_name));
17863db34c8SAxel Dörfler
17963db34c8SAxel Dörfler	return B_OK;
18063db34c8SAxel Dörfler}
18163db34c8SAxel Dörfler
18263db34c8SAxel Dörfler
183823a2382SJérôme Duvalstatic status_t
184823a2382SJérôme Duvalext2_write_fs_info(fs_volume* _volume, const struct fs_info* info, uint32 mask)
185823a2382SJérôme Duval{
186823a2382SJérôme Duval	Volume* volume = (Volume*)_volume->private_volume;
187823a2382SJérôme Duval
188823a2382SJérôme Duval	if (volume->IsReadOnly())
189823a2382SJérôme Duval		return B_READ_ONLY_DEVICE;
190823a2382SJérôme Duval
191823a2382SJérôme Duval	MutexLocker locker(volume->Lock());
192823a2382SJérôme Duval
193823a2382SJérôme Duval	status_t status = B_BAD_VALUE;
194823a2382SJérôme Duval
195823a2382SJérôme Duval	if (mask & FS_WRITE_FSINFO_NAME) {
196823a2382SJérôme Duval		Transaction transaction(volume->GetJournal());
197823a2382SJérôme Duval		volume->SetName(info->volume_name);
198823a2382SJérôme Duval		status = volume->WriteSuperBlock(transaction);
199823a2382SJérôme Duval		transaction.Done();
200823a2382SJérôme Duval	}
201823a2382SJérôme Duval	return status;
202823a2382SJérôme Duval}
203823a2382SJérôme Duval
204823a2382SJérôme Duval
20582b3bdd4SJérôme Duvalstatic status_t
20682b3bdd4SJérôme Duvalext2_sync(fs_volume* _volume)
20782b3bdd4SJérôme Duval{
20882b3bdd4SJérôme Duval	Volume* volume = (Volume*)_volume->private_volume;
20982b3bdd4SJérôme Duval	return volume->Sync();
21082b3bdd4SJérôme Duval}
21182b3bdd4SJérôme Duval
21282b3bdd4SJérôme Duval
21363db34c8SAxel Dörfler//	#pragma mark -
21463db34c8SAxel Dörfler
21563db34c8SAxel Dörfler
21663db34c8SAxel Dörflerstatic status_t
21763db34c8SAxel Dörflerext2_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type,
21863db34c8SAxel Dörfler	uint32* _flags, bool reenter)
21963db34c8SAxel Dörfler{
22063db34c8SAxel Dörfler	Volume* volume = (Volume*)_volume->private_volume;
22163db34c8SAxel Dörfler
22217aad616SJérôme Duval	if (id < 2 || id > volume->NumInodes()) {
223a130bab3SJérôme Duval		ERROR("invalid inode id %" B_PRIdINO " requested!\n", id);
224583f39e9SJérôme Duval		return B_BAD_VALUE;
22563db34c8SAxel Dörfler	}
22663db34c8SAxel Dörfler
22785f15b4bSJérôme Duval	Inode* inode = new(std::nothrow) Inode(volume, id);
22863db34c8SAxel Dörfler	if (inode == NULL)
22963db34c8SAxel Dörfler		return B_NO_MEMORY;
23063db34c8SAxel Dörfler
23163db34c8SAxel Dörfler	status_t status = inode->InitCheck();
232230b9a69SJérôme Duval	if (status != B_OK)
23363db34c8SAxel Dörfler		delete inode;
23463db34c8SAxel Dörfler
23563db34c8SAxel Dörfler	if (status == B_OK) {
23663db34c8SAxel Dörfler		_node->private_node = inode;
23763db34c8SAxel Dörfler		_node->ops = &gExt2VnodeOps;
23863db34c8SAxel Dörfler		*_type = inode->Mode();
23963db34c8SAxel Dörfler		*_flags = 0;
240f7218c21SJérôme Duval	} else
241f7218c21SJérôme Duval		ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status));
24263db34c8SAxel Dörfler
24363db34c8SAxel Dörfler	return status;
24463db34c8SAxel Dörfler}
24563db34c8SAxel Dörfler
24663db34c8SAxel Dörfler
24763db34c8SAxel Dörflerstatic status_t
24863db34c8SAxel Dörflerext2_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
24963db34c8SAxel Dörfler{
25063db34c8SAxel Dörfler	delete (Inode*)_node->private_node;
25163db34c8SAxel Dörfler	return B_OK;
25263db34c8SAxel Dörfler}
25363db34c8SAxel Dörfler
25463db34c8SAxel Dörfler
255a1b0ec30SJérôme Duvalstatic status_t
256a1b0ec30SJérôme Duvalext2_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
257a1b0ec30SJérôme Duval{
258a1b0ec30SJérôme Duval	TRACE("ext2_remove_vnode()\n");
259a1b0ec30SJérôme Duval	Volume* volume = (Volume*)_volume->private_volume;
260a1b0ec30SJérôme Duval	Inode* inode = (Inode*)_node->private_node;
261a1b0ec30SJérôme Duval	ObjectDeleter<Inode> inodeDeleter(inode);
262a1b0ec30SJérôme Duval
263a1b0ec30SJérôme Duval	if (!inode->IsDeleted())
264a1b0ec30SJérôme Duval		return B_OK;
265a1b0ec30SJérôme Duval
266a1b0ec30SJérôme Duval	TRACE("ext2_remove_vnode(): Starting transaction\n");
267a1b0ec30SJérôme Duval	Transaction transaction(volume->GetJournal());
268a1b0ec30SJérôme Duval
269a1b0ec30SJérôme Duval	if (!inode->IsSymLink() || inode->Size() >= EXT2_SHORT_SYMLINK_LENGTH) {
270a1b0ec30SJérôme Duval		TRACE("ext2_remove_vnode(): Truncating\n");
271a1b0ec30SJérôme Duval		status_t status = inode->Resize(transaction, 0);
272a1b0ec30SJérôme Duval		if (status != B_OK)
273a1b0ec30SJérôme Duval			return status;
274a1b0ec30SJérôme Duval	}
275a1b0ec30SJérôme Duval
276a1b0ec30SJérôme Duval	TRACE("ext2_remove_vnode(): Removing from orphan list\n");
277a1b0ec30SJérôme Duval	status_t status = volume->RemoveOrphan(transaction, inode->ID());
278a1b0ec30SJérôme Duval	if (status != B_OK)
279a1b0ec30SJérôme Duval		return status;
280a1b0ec30SJérôme Duval
281a1b0ec30SJérôme Duval	TRACE("ext2_remove_vnode(): Setting deletion time\n");
282a1b0ec30SJérôme Duval	inode->Node().SetDeletionTime(real_time_clock());
283a1b0ec30SJérôme Duval
284a1b0ec30SJérôme Duval	status = inode->WriteBack(transaction);
285a1b0ec30SJérôme Duval	if (status != B_OK)
286a1b0ec30SJérôme Duval		return status;
287ea75a051SAxel Dörfler
288a1b0ec30SJérôme Duval	TRACE("ext2_remove_vnode(): Freeing inode\n");
289a1b0ec30SJérôme Duval	status = volume->FreeInode(transaction, inode->ID(), inode->IsDirectory());
290a1b0ec30SJérôme Duval
291a1b0ec30SJérôme Duval	// TODO: When Transaction::Done() fails, do we have to re-add the vnode?
292a1b0ec30SJérôme Duval	if (status == B_OK)
293a1b0ec30SJérôme Duval		status = transaction.Done();
294a1b0ec30SJérôme Duval
295a1b0ec30SJérôme Duval	return status;
296a1b0ec30SJérôme Duval}
297a1b0ec30SJérôme Duval
298a1b0ec30SJérôme Duval
29963db34c8SAxel Dörflerstatic bool
30063db34c8SAxel Dörflerext2_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie)
30163db34c8SAxel Dörfler{
30263db34c8SAxel Dörfler	return true;
30363db34c8SAxel Dörfler}
30463db34c8SAxel Dörfler
30563db34c8SAxel Dörfler
30663db34c8SAxel Dörflerstatic status_t
30763db34c8SAxel Dörflerext2_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
308e6bd90c5SIngo Weinhold	off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
30963db34c8SAxel Dörfler{
31063db34c8SAxel Dörfler	Volume* volume = (Volume*)_volume->private_volume;
31163db34c8SAxel Dörfler	Inode* inode = (Inode*)_node->private_node;
31263db34c8SAxel Dörfler
31363db34c8SAxel Dörfler	if (inode->FileCache() == NULL)
31463db34c8SAxel Dörfler		return B_BAD_VALUE;
31563db34c8SAxel Dörfler
316e6bd90c5SIngo Weinhold	rw_lock_read_lock(inode->Lock());
31763db34c8SAxel Dörfler
31863db34c8SAxel Dörfler	uint32 vecIndex = 0;
31963db34c8SAxel Dörfler	size_t vecOffset = 0;
32063db34c8SAxel Dörfler	size_t bytesLeft = *_numBytes;
32163db34c8SAxel Dörfler	status_t status;
32263db34c8SAxel Dörfler
32363db34c8SAxel Dörfler	while (true) {
32463db34c8SAxel Dörfler		file_io_vec fileVecs[8];
325a130bab3SJérôme Duval		size_t fileVecCount = 8;
32663db34c8SAxel Dörfler
32763db34c8SAxel Dörfler		status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
32846124337SIngo Weinhold			&fileVecCount, 0);
32963db34c8SAxel Dörfler		if (status != B_OK && status != B_BUFFER_OVERFLOW)
33063db34c8SAxel Dörfler			break;
33163db34c8SAxel Dörfler
33263db34c8SAxel Dörfler		bool bufferOverflow = status == B_BUFFER_OVERFLOW;
33363db34c8SAxel Dörfler
33463db34c8SAxel Dörfler		size_t bytes = bytesLeft;
33563db34c8SAxel Dörfler		status = read_file_io_vec_pages(volume->Device(), fileVecs,
33663db34c8SAxel Dörfler			fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
33763db34c8SAxel Dörfler		if (status != B_OK || !bufferOverflow)
33863db34c8SAxel Dörfler			break;
33963db34c8SAxel Dörfler
34063db34c8SAxel Dörfler		pos += bytes;
34163db34c8SAxel Dörfler		bytesLeft -= bytes;
34263db34c8SAxel Dörfler	}
34363db34c8SAxel Dörfler
344e6bd90c5SIngo Weinhold	rw_lock_read_unlock(inode->Lock());
34563db34c8SAxel Dörfler
34663db34c8SAxel Dörfler	return status;
34763db34c8SAxel Dörfler}
34863db34c8SAxel Dörfler
34963db34c8SAxel Dörfler
350a1b0ec30SJérôme Duvalstatic status_t
351a1b0ec30SJérôme Duvalext2_write_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
352a1b0ec30SJérôme Duval	off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
353a1b0ec30SJérôme Duval{
354a1b0ec30SJérôme Duval	Volume* volume = (Volume*)_volume->private_volume;
355a1b0ec30SJérôme Duval	Inode* inode = (Inode*)_node->private_node;
356a1b0ec30SJérôme Duval
357a1b0ec30SJérôme Duval	if (volume->IsReadOnly())
358a1b0ec30SJérôme Duval		return B_READ_ONLY_DEVICE;
359a1b0ec30SJérôme Duval
360a1b0ec30SJérôme Duval	if (inode->FileCache() == NULL)
361a1b0ec30SJérôme Duval		return B_BAD_VALUE;
362a1b0ec30SJérôme Duval
363a1b0ec30SJérôme Duval	rw_lock_read_lock(inode->Lock());
364a1b0ec30SJérôme Duval
365a1b0ec30SJérôme Duval	uint32 vecIndex = 0;
366a1b0ec30SJérôme Duval	size_t vecOffset = 0;
367a1b0ec30SJérôme Duval	size_t bytesLeft = *_numBytes;
368a1b0ec30SJérôme Duval	status_t status;
369a1b0ec30SJérôme Duval
370a1b0ec30SJérôme Duval	while (true) {
371a1b0ec30SJérôme Duval		file_io_vec fileVecs[8];
372a1b0ec30SJérôme Duval		size_t fileVecCount = 8;
373a1b0ec30SJérôme Duval
374a1b0ec30SJérôme Duval		status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
375a1b0ec30SJérôme Duval			&fileVecCount, 0);
376a1b0ec30SJérôme Duval		if (status != B_OK && status != B_BUFFER_OVERFLOW)
377a1b0ec30SJérôme Duval			break;
378a1b0ec30SJérôme Duval
379a1b0ec30SJérôme Duval		bool bufferOverflow = status == B_BUFFER_OVERFLOW;
380a1b0ec30SJérôme Duval
381a1b0ec30SJérôme Duval		size_t bytes = bytesLeft;
382a1b0ec30SJérôme Duval		status = write_file_io_vec_pages(volume->Device(), fileVecs,
383a1b0ec30SJérôme Duval			fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
384a1b0ec30SJérôme Duval		if (status != B_OK || !bufferOverflow)
385a1b0ec30SJérôme Duval			break;
386a1b0ec30SJérôme Duval
387a1b0ec30SJérôme Duval		pos += bytes;
388a1b0ec30SJérôme Duval		bytesLeft -= bytes;
389a1b0ec30SJérôme Duval	}
390a1b0ec30SJérôme Duval
391a1b0ec30SJérôme Duval	rw_lock_read_unlock(inode->Lock());
392a1b0ec30SJérôme Duval
393a1b0ec30SJérôme Duval	return status;
394a1b0ec30SJérôme Duval}
395a1b0ec30SJérôme Duval
396a1b0ec30SJérôme Duval
397a1b0ec30SJérôme Duvalstatic status_t
398a1b0ec30SJérôme Duvalext2_io(fs_volume* _volume, fs_vnode* _node, void* _cookie, io_request* request)
399a1b0ec30SJérôme Duval{
400a1b0ec30SJérôme Duval	Volume* volume = (Volume*)_volume->private_volume;
401a1b0ec30SJérôme Duval	Inode* inode = (Inode*)_node->private_node;
402a1b0ec30SJérôme Duval
403a1b0ec30SJérôme Duval#ifndef EXT2_SHELL
404a1b0ec30SJérôme Duval	if (io_request_is_write(request) && volume->IsReadOnly()) {
405a1b0ec30SJérôme Duval		notify_io_request(request, B_READ_ONLY_DEVICE);
406a1b0ec30SJérôme Duval		return B_READ_ONLY_DEVICE;
407a1b0ec30SJérôme Duval	}
408a1b0ec30SJérôme Duval#endif
409a1b0ec30SJérôme Duval
410a1b0ec30SJérôme Duval	if (inode->FileCache() == NULL) {
411a1b0ec30SJérôme Duval#ifndef EXT2_SHELL
412