116359Sasami/*
256711Snyan * Copyright 2010, J��r��me Duval, korli@users.berlios.de.
316359Sasami * Copyright 2008, Axel D��rfler, axeld@pinc-software.de.
450518Snyan * This file may be used under the terms of the MIT License.
550518Snyan */
616359Sasami
760717Snyan
850518Snyan#include <algorithm>
950518Snyan#include <dirent.h>
1050518Snyan#include <sys/ioctl.h>
1160717Snyan#include <util/kernel_cpp.h>
1250518Snyan#include <string.h>
1350518Snyan
1450518Snyan#include <AutoDeleter.h>
1550518Snyan#include <fs_cache.h>
1617973Sasami#include <fs_info.h>
1717973Sasami#include <io_requests.h>
1850477Speter#include <NodeMonitor.h>
1916359Sasami#include <util/AutoLock.h>
2046044Skato
2146044Skato#include "Attribute.h"
2246044Skato#include "CachedBlock.h"
2346044Skato#include "DirectoryIterator.h"
2446044Skato#include "ext2.h"
2560027Snyan#include "HTree.h"
2638832Skato#include "Inode.h"
2716359Sasami#include "Journal.h"
2861640Speter#include "Utility.h"
2961640Speter
3046044Skato
3145783Skato//#define TRACE_EXT2
3250518Snyan#ifdef TRACE_EXT2
3350518Snyan#	define TRACE(x...) dprintf("\33[34mext2:\33[0m " x)
3450518Snyan#else
3557655Skato#	define TRACE(x...) ;
3650518Snyan#endif
3750518Snyan#define ERROR(x...) dprintf("\33[34mext2:\33[0m " x)
3854877Skato
3953744Snyan
4050518Snyan#define EXT2_IO_SIZE	65536
4156533Skato
4250518Snyan
4350518Snyanstruct identify_cookie {
4456533Skato	ext2_super_block super_block;
4550518Snyan};
4650518Snyan
4756711Snyan
4850518Snyan//	#pragma mark - Scanning
4950518Snyan
5050518Snyan
5150518Snyanstatic float
5250518Snyanext2_identify_partition(int fd, partition_data *partition, void **_cookie)
5350518Snyan{
5456533Skato	STATIC_ASSERT(sizeof(struct ext2_super_block) == 1024);
5552833Snyan	STATIC_ASSERT(sizeof(struct ext2_block_group) == 64);
5652833Snyan
5756327Snyan	ext2_super_block superBlock;
5859530Snyan	status_t status = Volume::Identify(fd, &superBlock);
5956323Skato	if (status != B_OK)
6060717Snyan		return -1;
6118846Sasami
6242157Skato	identify_cookie *cookie = new identify_cookie;
6350518Snyan	memcpy(&cookie->super_block, &superBlock, sizeof(ext2_super_block));
6450518Snyan
6542157Skato	*_cookie = cookie;
6650518Snyan	return 0.8f;
6750518Snyan}
6850518Snyan
6950518Snyan
7042157Skatostatic status_t
7156442Speterext2_scan_partition(int fd, partition_data *partition, void *_cookie)
7256442Speter{
7358290Skato	identify_cookie *cookie = (identify_cookie *)_cookie;
7458290Skato
7516359Sasami	partition->status = B_PARTITION_VALID;
7650518Snyan	partition->flags |= B_PARTITION_FILE_SYSTEM;
7761640Speter	partition->content_size = cookie->super_block.NumBlocks(
7816359Sasami		(cookie->super_block.CompatibleFeatures()
7950518Snyan			& EXT2_INCOMPATIBLE_FEATURE_64BIT) != 0)
8061640Speter			<< cookie->super_block.BlockShift();
8116359Sasami	partition->block_size = 1UL << cookie->super_block.BlockShift();
8256516Speter	partition->content_name = strdup(cookie->super_block.name);
8356442Speter	if (partition->content_name == NULL)
8456442Speter		return B_NO_MEMORY;
8556442Speter
8616359Sasami	return B_OK;
8750518Snyan}
8856442Speter
8956442Speter
9056442Speterstatic void
9156442Speterext2_free_identify_partition_cookie(partition_data* partition, void* _cookie)
9256442Speter{
9356442Speter	delete (identify_cookie*)_cookie;
9418846Sasami}
9520128Sasami
9620128Sasami
9720128Sasami//	#pragma mark -
9820128Sasami
9920128Sasami
10020128Sasamistatic status_t
10161741Skatoext2_mount(fs_volume* _volume, const char* device, uint32 flags,
10220128Sasami	const char* args, ino_t* _rootID)
10361640Speter{
10461640Speter	Volume* volume = new(std::nothrow) Volume(_volume);
10561640Speter	if (volume == NULL)
10661640Speter		return B_NO_MEMORY;
10761640Speter
10820128Sasami	// TODO: this is a bit hacky: we can't use publish_vnode() to publish
10961640Speter	// the root node, or else its file cache cannot be created (we could
11061640Speter	// create it later, though). Therefore we're using get_vnode() in Mount(),
11161640Speter	// but that requires us to export our volume data before calling it.
11261640Speter	_volume->private_volume = volume;
11361640Speter	_volume->ops = &gExt2VolumeOps;
11420128Sasami
11561640Speter	status_t status = volume->Mount(device, flags);
11661640Speter	if (status != B_OK) {
11761640Speter		ERROR("Failed mounting the volume. Error: %s\n", strerror(status));
11861640Speter		delete volume;
11961640Speter		return status;
12020128Sasami	}
12161640Speter
12261640Speter	*_rootID = volume->RootNode()->ID();
12361640Speter	return B_OK;
12461640Speter}
12561640Speter
12620128Sasami
12761640Speterstatic status_t
12861640Speterext2_unmount(fs_volume *_volume)
12961640Speter{
13061640Speter	Volume* volume = (Volume *)_volume->private_volume;
13161640Speter
13261640Speter	status_t status = volume->Unmount();
13361640Speter	delete volume;
13421272Skato
13561640Speter	return status;
13661640Speter}
13761640Speter
13861640Speter
13961640Speterstatic status_t
14020128Sasamiext2_read_fs_info(fs_volume* _volume, struct fs_info* info)
14158789Snyan{
14261640Speter	Volume* volume = (Volume*)_volume->private_volume;
14358789Snyan
14450518Snyan	// File system flags
14556442Speter	info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR
14656442Speter		| (volume->IsReadOnly() ? B_FS_IS_READONLY : 0);
14756442Speter	info->io_size = EXT2_IO_SIZE;
14856442Speter	info->block_size = volume->BlockSize();
14956442Speter	info->total_blocks = volume->NumBlocks();
15016359Sasami	info->free_blocks = volume->NumFreeBlocks();
15154030Snyan
15260717Snyan	// Volume name
15356442Speter	strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
15456442Speter
15554030Snyan	// File system name
15642795Skato	if (volume->HasExtentsFeature())
15761741Skato		strlcpy(info->fsh_name, "ext4", sizeof(info->fsh_name));
15816359Sasami	else if (volume->HasJournalFeature())
15961640Speter		strlcpy(info->fsh_name, "ext3", sizeof(info->fsh_name));
16042795Skato	else
16145783Skato		strlcpy(info->fsh_name, "ext2", sizeof(info->fsh_name));
16261640Speter
16342795Skato	return B_OK;
16453120Snyan}
16561640Speter
16642795Skato
16750518Snyanstatic status_t
16861640Speterext2_write_fs_info(fs_volume* _volume, const struct fs_info* info, uint32 mask)
16916359Sasami{
17050518Snyan	Volume* volume = (Volume*)_volume->private_volume;
17161640Speter
17224432Skato	if (volume->IsReadOnly())
17316359Sasami		return B_READ_ONLY_DEVICE;
17456442Speter
17561640Speter	MutexLocker locker(volume->Lock());
17616359Sasami
17750518Snyan	status_t status = B_BAD_VALUE;
17842730Skato
17953120Snyan	if (mask & FS_WRITE_FSINFO_NAME) {
18053120Snyan		Transaction transaction(volume->GetJournal());
18142730Skato		volume->SetName(info->volume_name);
18261640Speter		status = volume->WriteSuperBlock(transaction);
18342730Skato		transaction.Done();
18461640Speter	}
18561640Speter	return status;
18661640Speter}
18761640Speter
18842730Skato
18961640Speterstatic status_t
19061640Speterext2_sync(fs_volume* _volume)
19161640Speter{
19261640Speter	Volume* volume = (Volume*)_volume->private_volume;
19361640Speter	return volume->Sync();
19461640Speter}
19561640Speter
19661640Speter
19742730Skato//	#pragma mark -
19861640Speter
19961640Speter
20061640Speterstatic status_t
20161640Speterext2_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type,
20242730Skato	uint32* _flags, bool reenter)
20361640Speter{
20461640Speter	Volume* volume = (Volume*)_volume->private_volume;
20561640Speter
20661640Speter	if (id < 2 || id > volume->NumInodes()) {
20761640Speter		ERROR("invalid inode id %" B_PRIdINO " requested!\n", id);
20861640Speter		return B_BAD_VALUE;
20961640Speter	}
21042730Skato
21161640Speter	Inode* inode = new(std::nothrow) Inode(volume, id);
21261640Speter	if (inode == NULL)
21361640Speter		return B_NO_MEMORY;
21461640Speter
21561640Speter	status_t status = inode->InitCheck();
21661640Speter	if (status != B_OK)
21742730Skato		delete inode;
21861640Speter
21961640Speter	if (status == B_OK) {
22061640Speter		_node->private_node = inode;
22161640Speter		_node->ops = &gExt2VnodeOps;
22261640Speter		*_type = inode->Mode();
22361640Speter		*_flags = 0;
22442730Skato	} else
22561640Speter		ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status));
22661640Speter
22761640Speter	return status;
22861640Speter}
22961640Speter
23061640Speter
23161640Speterstatic status_t
23242730Skatoext2_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
23353120Snyan{
23461640Speter	delete (Inode*)_node->private_node;
23561640Speter	return B_OK;
23661640Speter}
23761640Speter
23816359Sasami
23961640Speterstatic status_t
24050518Snyanext2_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
24148006Skato{
24261640Speter	TRACE("ext2_remove_vnode()\n");
24356711Snyan	Volume* volume = (Volume*)_volume->private_volume;
24456711Snyan	Inode* inode = (Inode*)_node->private_node;
24556711Snyan	ObjectDeleter<Inode> inodeDeleter(inode);
24656711Snyan
24756711Snyan	if (!inode->IsDeleted())
24848006Skato		return B_OK;
24948006Skato
25061741Skato	TRACE("ext2_remove_vnode(): Starting transaction\n");
25116359Sasami	Transaction transaction(volume->GetJournal());
25250513Skato
25350518Snyan	if (!inode->IsSymLink() || inode->Size() >= EXT2_SHORT_SYMLINK_LENGTH) {
25456442Speter		TRACE("ext2_remove_vnode(): Truncating\n");
25556442Speter		status_t status = inode->Resize(transaction, 0);
25656442Speter		if (status != B_OK)
25756442Speter			return status;
25856442Speter	}
25951222Skato
26051222Skato	TRACE("ext2_remove_vnode(): Removing from orphan list\n");
26156442Speter	status_t status = volume->RemoveOrphan(transaction, inode->ID());
26256442Speter	if (status != B_OK)
26356442Speter		return status;
26456442Speter
26556442Speter	TRACE("ext2_remove_vnode(): Setting deletion time\n");
26656442Speter	inode->Node().SetDeletionTime(real_time_clock());
26756442Speter
26856442Speter	status = inode->WriteBack(transaction);
26956442Speter	if (status != B_OK)
27056442Speter		return status;
27116359Sasami
27253056Snyan	TRACE("ext2_remove_vnode(): Freeing inode\n");
27318095Sasami	status = volume->FreeInode(transaction, inode->ID(), inode->IsDirectory());
27418095Sasami
27540071Skato	// TODO: When Transaction::Done() fails, do we have to re-add the vnode?
27656372Snyan	if (status == B_OK)
27718095Sasami		status = transaction.Done();
27818095Sasami
27918095Sasami	return status;
28018095Sasami}
28118095Sasami
28218095Sasami
28318095Sasamistatic bool
28418846Sasamiext2_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie)
28518846Sasami{
28656372Snyan	return true;
28756372Snyan}
28853120Snyan
28961640Speter
29055107Skatostatic status_t
29156372Snyanext2_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
29255107Skato	off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
29355107Skato{
29455107Skato	Volume* volume = (Volume*)_volume->private_volume;
29555107Skato	Inode* inode = (Inode*)_node->private_node;
29655107Skato
29755107Skato	if (inode->FileCache() == NULL)
29855107Skato		return B_BAD_VALUE;
29955107Skato
30055107Skato	rw_lock_read_lock(inode->Lock());
30156372Snyan
30255107Skato	uint32 vecIndex = 0;
30356372Snyan	size_t vecOffset = 0;
30456372Snyan	size_t bytesLeft = *_numBytes;
30556372Snyan	status_t status;
30656372Snyan
30756372Snyan	while (true) {
30856372Snyan		file_io_vec fileVecs[8];
30956372Snyan		size_t fileVecCount = 8;
31056372Snyan
31155326Snyan		status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
31261640Speter			&fileVecCount, 0);
31355326Snyan		if (status != B_OK && status != B_BUFFER_OVERFLOW)
31461640Speter			break;
31561640Speter
31660717Snyan		bool bufferOverflow = status == B_BUFFER_OVERFLOW;
31755326Snyan
31855086Skato		size_t bytes = bytesLeft;
31954877Skato		status = read_file_io_vec_pages(volume->Device(), fileVecs,
32054877Skato			fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
32156711Snyan		if (status != B_OK || !bufferOverflow)
32256323Skato			break;
32356323Skato
32456323Skato		pos += bytes;
32556323Skato		bytesLeft -= bytes;
32656323Skato	}
32756711Snyan
32860717Snyan	rw_lock_read_unlock(inode->Lock());
32960717Snyan
33060717Snyan	return status;
33160717Snyan}
33218095Sasami
33356372Snyan
33456442Speterstatic status_t
33561640Speterext2_write_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
33661640Speter	off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
33761640Speter{
33861640Speter	Volume* volume = (Volume*)_volume->private_volume;
33961640Speter	Inode* inode = (Inode*)_node->private_node;
34061640Speter
34161640Speter	if (volume->IsReadOnly())
34216359Sasami		return B_READ_ONLY_DEVICE;
34361640Speter
34458458Snyan	if (inode->FileCache() == NULL)
34560711Snyan		return B_BAD_VALUE;
34660711Snyan
34760711Snyan	rw_lock_read_lock(inode->Lock());
34861640Speter
34960711Snyan	uint32 vecIndex = 0;
35060711Snyan	size_t vecOffset = 0;
35160711Snyan	size_t bytesLeft = *_numBytes;
35261640Speter	status_t status;
35360711Snyan
35450518Snyan	while (true) {
35561640Speter		file_io_vec fileVecs[8];
35661640Speter		size_t fileVecCount = 8;
35761640Speter
35861640Speter		status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
35961640Speter			&fileVecCount, 0);
36061640Speter		if (status != B_OK && status != B_BUFFER_OVERFLOW)
36161640Speter			break;
36261640Speter
36361640Speter		bool bufferOverflow = status == B_BUFFER_OVERFLOW;
36417256Sasami
36561640Speter		size_t bytes = bytesLeft;
36649519Skato		status = write_file_io_vec_pages(volume->Device(), fileVecs,
36761640Speter			fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
36850551Sphk		if (status != B_OK || !bufferOverflow)
36961640Speter			break;
37055326Snyan
37155326Snyan		pos += bytes;
37256442Speter		bytesLeft -= bytes;
37356442Speter	}
37456442Speter
37560717Snyan	rw_lock_read_unlock(inode->Lock());
37656442Speter
37756442Speter	return status;
37856442Speter}
37956442Speter
38056978Skato
38156442Speterstatic status_t
38260717Snyanext2_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset,
38357655Skato	size_t size, struct file_io_vec* vecs, size_t* _count)
38456442Speter{
38556442Speter	TRACE("ext2_get_file_map()\n");
38656442Speter	Volume* volume = (Volume*)_volume->private_volume;
387	Inode* inode = (Inode*)_node->private_node;
388	size_t index = 0, max = *_count;
389
390	while (true) {
391		fsblock_t block;
392		uint32 count = 1;
393		status_t status = inode->FindBlock(offset, block, &count);
394		if (status != B_OK)
395			return status;
396
397		if (block > volume->NumBlocks()) {
398			panic("ext2_get_file_map() found block %" B_PRIu64 " for offset %"
399				B_PRIdOFF "\n", block, offset);
400		}
401
402		off_t blockOffset = block << volume->BlockShift();
403		uint32 blockLength = volume->BlockSize() * count;
404
405		if (index > 0 && (vecs[index - 1].offset
406				== blockOffset - vecs[index - 1].length
407				|| (vecs[index - 1].offset == -1 && block == 0))) {
408			vecs[index - 1].length += blockLength;
409		} else {
410			if (index >= max) {
411				// we're out of file_io_vecs; let's bail out
412				*_count = index;
413				return B_BUFFER_OVERFLOW;
414			}
415
416			// 'block' is 0 for sparse blocks
417			if (block != 0)
418				vecs[index].offset = blockOffset;
419			else
420				vecs[index].offset = -1;
421
422			vecs[index].length = blockLength;
423			index++;
424		}
425
426		offset += blockLength;
427
428		if (offset >= inode->Size() || size <= blockLength) {
429			// We're done!
430			*_count = index;
431			TRACE("ext2_get_file_map for inode %" B_PRIdINO "\n", inode->ID());
432			return B_OK;
433		}
434
435		size -= blockLength;
436	}
437
438	// can never get here
439	return B_ERROR;
440}
441
442
443//	#pragma mark -
444
445
446static status_t
447ext2_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name,
448	ino_t* _vnodeID)
449{
450	TRACE("ext2_lookup: name address: %p\n", name);
451	TRACE("ext2_lookup: name: %s\n", name);
452	Volume* volume = (Volume*)_volume->private_volume;
453	Inode* directory = (Inode*)_directory->private_node;
454
455	// check access permissions
456	status_t status = directory->CheckPermissions(X_OK);
457	if (status < B_OK)
458		return status;
459
460	HTree htree(volume, directory);
461	DirectoryIterator* iterator;
462
463	status = htree.Lookup(name, &iterator);
464	if (status != B_OK)
465		return status;
466
467	ObjectDeleter<DirectoryIterator> iteratorDeleter(iterator);
468
469	status = iterator->FindEntry(name, _vnodeID);
470	if (status != B_OK) {
471		if (status == B_ENTRY_NOT_FOUND)
472			entry_cache_add_missing(volume->ID(), directory->ID(), name);
473		return status;
474	}
475	entry_cache_add(volume->ID(), directory->ID(), name, *_vnodeID);
476
477	return get_vnode(volume->FSVolume(), *_vnodeID, NULL);
478}
479
480
481static status_t
482ext2_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd,
483	void* buffer, size_t bufferLength)
484{
485	TRACE("ioctl: %" B_PRIu32 "\n", cmd);
486
487	Volume* volume = (Volume*)_volume->private_volume;
488	switch (cmd) {
489		case 56742:
490		{
491			TRACE("ioctl: Test the block allocator\n");
492			// Test the block allocator
493			TRACE("ioctl: Creating transaction\n");
494			Transaction transaction(volume->GetJournal());
495			TRACE("ioctl: Creating cached block\n");
496			CachedBlock cached(volume);
497			uint32 blocksPerGroup = volume->BlocksPerGroup();
498			uint32 blockSize  = volume->BlockSize();
499			uint32 firstBlock = volume->FirstDataBlock();
500			fsblock_t start = 0;
501			uint32 group = 0;
502			uint32 length;
503
504			TRACE("ioctl: blocks per group: %" B_PRIu32 ", block size: %"
505				B_PRIu32 ", first block: %" B_PRIu32 ", start: %" B_PRIu64
506				", group: %" B_PRIu32 "\n", blocksPerGroup,
507				blockSize, firstBlock, start, group);
508
509			while (volume->AllocateBlocks(transaction, 1, 2048, group, start,
510					length) == B_OK) {
511				TRACE("ioctl: Allocated blocks in group %" B_PRIu32 ": %"
512					B_PRIu64 "-%" B_PRIu64 "\n", group, start, start + length);
513				off_t blockNum = start + group * blocksPerGroup - firstBlock;
514
515				for (uint32 i = 0; i < length; ++i) {
516					uint8* block = cached.SetToWritable(transaction, blockNum);
517					memset(block, 0, blockSize);
518					blockNum++;
519				}
520
521				TRACE("ioctl: Blocks cleared\n");
522
523				transaction.Done();
524				transaction.Start(volume->GetJournal());
525			}
526
527			TRACE("ioctl: Done\n");
528
529			return B_OK;
530		}
531
532		case FIOSEEKDATA:
533		case FIOSEEKHOLE:
534		{
535			off_t* offset = (off_t*)buffer;
536			Inode* inode = (Inode*)_node->private_node;
537
538			if (*offset >= inode->Size())
539				return ENXIO;
540
541			while (*offset < inode->Size()) {
542				fsblock_t block;
543				uint32 count = 1;
544				status_t status = inode->FindBlock(*offset, block, &count);
545				if (status != B_OK)
546					return status;
547				if ((block != 0 && cmd == FIOSEEKDATA)
548					|| (block == 0 && cmd == FIOSEEKHOLE)) {
549					return B_OK;
550				}
551				*offset += count * volume->BlockSize();
552			}
553
554			if (*offset > inode->Size())
555				*offset = inode->Size();
556			return cmd == FIOSEEKDATA ? ENXIO : B_OK;
557		}
558	}
559
560	return B_DEV_INVALID_IOCTL;
561}
562
563
564/*!	Sets the open-mode flags for the open file cookie - only
565	supports O_APPEND currently, but that should be sufficient
566	for a file system.
567*/
568static status_t
569ext2_set_flags(fs_volume* _volume, fs_vnode* _node, void* _cookie, int flags)
570{
571	file_cookie* cookie = (file_cookie*)_cookie;
572	cookie->open_mode = (cookie->open_mode & ~O_APPEND) | (flags & O_APPEND);
573
574	return B_OK;
575}
576
577
578static status_t
579ext2_fsync(fs_volume* _volume, fs_vnode* _node)
580{
581	Inode* inode = (Inode*)_node->private_node;
582	return inode->Sync();
583}
584
585
586static status_t
587ext2_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat)
588{
589	Inode* inode = (Inode*)_node->private_node;
590	const ext2_inode& node = inode->Node();
591
592	stat->st_dev = inode->GetVolume()->ID();
593	stat->st_ino = inode->ID();
594	stat->st_nlink = node.NumLinks();
595	stat->st_blksize = EXT2_IO_SIZE;
596
597	stat->st_uid = node.UserID();
598	stat->st_gid = node.GroupID();
599	stat->st_mode = node.Mode();
600	stat->st_type = 0;
601
602	inode->GetAccessTime(&stat->st_atim);
603	inode->GetModificationTime(&stat->st_mtim);
604	inode->GetChangeTime(&stat->st_ctim);
605	inode->GetCreationTime(&stat->st_crtim);
606
607	stat->st_size = inode->Size();
608	stat->st_blocks = inode->NumBlocks();
609
610	return B_OK;
611}
612
613
614status_t
615ext2_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
616	uint32 mask)
617{
618	TRACE("ext2_write_stat\n");
619	Volume* volume = (Volume*)_volume->private_volume;
620
621	if (volume->IsReadOnly())
622		return B_READ_ONLY_DEVICE;
623
624	Inode* inode = (Inode*)_node->private_node;
625
626	ext2_inode& node = inode->Node();
627	bool updateTime = false;
628	uid_t uid = geteuid();
629
630	bool isOwnerOrRoot = uid == 0 || uid == (uid_t)node.UserID();
631	bool hasWriteAccess = inode->CheckPermissions(W_OK) == B_OK;
632
633	TRACE("ext2_write_stat: Starting transaction\n");
634	Transaction transaction(volume->GetJournal());
635	inode->WriteLockInTransaction(transaction);
636
637	if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) {
638		if (inode->IsDirectory())
639			return B_IS_A_DIRECTORY;
640		if (!inode->IsFile())
641			return B_BAD_VALUE;
642		if (!hasWriteAccess)
643			return B_NOT_ALLOWED;
644
645		TRACE("ext2_write_stat: Old size: %ld, new size: %ld\n",
646			(long)inode->Size(), (long)stat->st_size);
647
648		off_t oldSize = inode->Size();
649
650		status_t status = inode->Resize(transaction, stat->st_size);
651		if (status != B_OK)
652			return status;
653
654		if ((mask & B_STAT_SIZE_INSECURE) == 0) {
655			rw_lock_write_unlock(inode->Lock());
656			inode->FillGapWithZeros(oldSize, inode->Size());
657			rw_lock_write_lock(inode->Lock());
658		}
659
660		updateTime = true;
661	}
662
663	if ((mask & B_STAT_MODE) != 0) {
664		// only the user or root can do that
665		if (!isOwnerOrRoot)
666			return B_NOT_ALLOWED;
667		node.UpdateMode(stat->st_mode, S_IUMSK);
668		updateTime = true;
669	}
670
671	if ((mask & B_STAT_UID) != 0) {
672		// only root should be allowed
673		if (uid != 0)
674			return B_NOT_ALLOWED;
675		node.SetUserID(stat->st_uid);
676		updateTime = true;
677	}
678
679	if ((mask & B_STAT_GID) != 0) {
680		// only the user or root can do that
681		if (!isOwnerOrRoot)
682			return B_NOT_ALLOWED;
683		node.SetGroupID(stat->st_gid);
684		updateTime = true;
685	}
686
687	if ((mask & B_STAT_MODIFICATION_TIME) != 0 || updateTime
688		|| (mask & B_STAT_CHANGE_TIME) != 0) {
689		// the user or root can do that or any user with write access
690		if (!isOwnerOrRoot && !hasWriteAccess)
691			return B_NOT_ALLOWED;
692		struct timespec newTimespec = { 0, 0};
693
694		if ((mask & B_STAT_MODIFICATION_TIME) != 0)
695			newTimespec = stat->st_mtim;
696
697		if ((mask & B_STAT_CHANGE_TIME) != 0
698			&& stat->st_ctim.tv_sec > newTimespec.tv_sec)
699			newTimespec = stat->st_ctim;
700
701		if (newTimespec.tv_sec == 0)
702			Inode::_BigtimeToTimespec(real_time_clock_usecs(), &newTimespec);
703
704		inode->SetModificationTime(&newTimespec);
705	}
706	if ((mask & B_STAT_CREATION_TIME) != 0) {
707		// the user or root can do that or any user with write access
708		if (!isOwnerOrRoot && !hasWriteAccess)
709			return B_NOT_ALLOWED;
710		inode->SetCreationTime(&stat->st_crtim);
711	}
712
713	status_t status = inode->WriteBack(transaction);
714	if (status == B_OK)
715		status = transaction.Done();
716	if (status == B_OK)
717		notify_stat_changed(volume->ID(), -1, inode->ID(), mask);
718
719	return status;
720}
721
722
723static status_t
724ext2_create(fs_volume* _volume, fs_vnode* _directory, const char* name,
725	int openMode, int mode, void** _cookie, ino_t* _vnodeID)
726{
727	Volume* volume = (Volume*)_volume->private_volume;
728	Inode* directory = (Inode*)_directory->private_node;
729
730	TRACE("ext2_create()\n");
731
732	if (volume->IsReadOnly())
733		return B_READ_ONLY_DEVICE;
734
735	if (!directory->IsDirectory())
736		return B_BAD_TYPE;
737
738	TRACE("ext2_create(): Creating cookie\n");
739
740	// Allocate cookie
741	file_cookie* cookie = new(std::nothrow) file_cookie;
742	if (cookie == NULL)
743		return B_NO_MEMORY;
744	ObjectDeleter<file_cookie> cookieDeleter(cookie);
745
746	cookie->open_mode = openMode;
747	cookie->last_size = 0;
748	cookie->last_notification = system_time();
749
750	TRACE("ext2_create(): Starting transaction\n");
751
752	Transaction transaction(volume->GetJournal());
753
754	TRACE("ext2_create(): Creating inode\n");
755
756	Inode* inode;
757	bool created;
758	status_t status = Inode::Create(transaction, directory, name,
759		S_FILE | (mode & S_IUMSK), openMode, EXT2_TYPE_FILE, &created, _vnodeID,
760		&inode, &gExt2VnodeOps);
761	if (status != B_OK)
762		return status;
763
764	TRACE("ext2_create(): Created inode\n");
765
766	if ((openMode & O_NOCACHE) != 0) {
767		status = inode->DisableFileCache();
768		if (status != B_OK)
769			return status;
770	}
771
772	entry_cache_add(volume->ID(), directory->ID(), name, *_vnodeID);
773
774	status = transaction.Done();
775	if (status != B_OK) {
776		entry_cache_remove(volume->ID(), directory->ID(), name);
777		return status;
778	}
779
780	*_cookie = cookie;
781	cookieDeleter.Detach();
782
783	if (created)
784		notify_entry_created(volume->ID(), directory->ID(), name, *_vnodeID);
785
786	return B_OK;
787}
788
789
790static status_t
791ext2_create_symlink(fs_volume* _volume, fs_vnode* _directory, const char* name,
792	const char* path, int mode)
793{
794	TRACE("ext2_create_symlink()\n");
795
796	Volume* volume = (Volume*)_volume->private_volume;
797	Inode* directory = (Inode*)_directory->private_node;
798
799	if (volume->IsReadOnly())
800		return B_READ_ONLY_DEVICE;
801
802	if (!directory->IsDirectory())
803		return B_BAD_TYPE;
804
805	status_t status = directory->CheckPermissions(W_OK);
806	if (status != B_OK)
807		return status;
808
809	TRACE("ext2_create_symlink(): Starting transaction\n");
810	Transaction transaction(volume->GetJournal());
811
812	Inode* link;
813	ino_t id;
814	status = Inode::Create(transaction, directory, name, S_SYMLINK | 0777,
815		0, (uint8)EXT2_TYPE_SYMLINK, NULL, &id, &link);
816	if (status != B_OK)
817		return status;
818
819	// TODO: We have to prepare the link before publishing?
820
821	size_t length = strlen(path);
822	TRACE("ext2_create_symlink(): Path (%s) length: %d\n", path, (int)length);
823	if (length < EXT2_SHORT_SYMLINK_LENGTH) {
824		strcpy(link->Node().symlink, path);
825		link->Node().SetSize((uint32)length);
826	} else {
827		if (!link->HasFileCache()) {
828			status = link->CreateFileCache();
829			if (status != B_OK)
830				return status;
831		}
832
833		size_t written = length;
834		status = link->WriteAt(transaction, 0, (const uint8*)path, &written);
835		if (status == B_OK && written != length)
836			status = B_IO_ERROR;
837	}
838
839	if (status == B_OK)
840		status = link->WriteBack(transaction);
841
842	TRACE("ext2_create_symlink(): Publishing vnode\n");
843	publish_vnode(volume->FSVolume(), id, link, &gExt2VnodeOps,
844		link->Mode(), 0);
845	put_vnode(volume->FSVolume(), id);
846
847	if (status == B_OK) {
848		entry_cache_add(volume->ID(), directory->ID(), name, id);
849
850		status = transaction.Done();
851		if (status == B_OK)
852			notify_entry_created(volume->ID(), directory->ID(), name, id);
853		else
854			entry_cache_remove(volume->ID(), directory->ID(), name);
855	}
856	TRACE("ext2_create_symlink(): Done\n");
857
858	return status;
859}
860
861
862static status_t
863ext2_link(fs_volume* volume, fs_vnode* dir, const char* name, fs_vnode* node)
864{
865	// TODO
866
867	return B_UNSUPPORTED;
868}
869
870
871static status_t
872ext2_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name)
873{
874	TRACE("ext2_unlink()\n");
875	if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
876		return B_NOT_ALLOWED;
877
878	Volume* volume = (Volume*)_volume->private_volume;
879	Inode* directory = (Inode*)_directory->private_node;
880
881	status_t status = directory->CheckPermissions(W_OK);
882	if (status != B_OK)
883		return status;
884
885	TRACE("ext2_unlink(): Starting transaction\n");
886	Transaction transaction(volume->GetJournal());
887
888	directory->WriteLockInTransaction(transaction);
889
890	TRACE("ext2_unlink(): Looking up for directory entry\n");
891	HTree htree(volume, directory);
892	DirectoryIterator* directoryIterator;
893
894	status = htree.Lookup(name, &directoryIterator);
895	if (status != B_OK)
896		return status;
897
898	ino_t id;
899	status = directoryIterator->FindEntry(name, &id);
900	if (status != B_OK)
901		return status;
902
903	{
904		Vnode vnode(volume, id);
905		Inode* inode;
906		status = vnode.Get(&inode);
907		if (status != B_OK)
908			return status;
909
910		inode->WriteLockInTransaction(transaction);
911
912		status = inode->Unlink(transaction);
913		if (status != B_OK)
914			return status;
915
916		status = directoryIterator->RemoveEntry(transaction);
917		if (status != B_OK)
918			return status;
919	}
920	entry_cache_remove(volume->ID(), directory->ID(), name);
921
922	status = transaction.Done();
923	if (status != B_OK)
924		entry_cache_add(volume->ID(), directory->ID(), name, id);
925	else
926		notify_entry_removed(volume->ID(), directory->ID(), name, id);
927
928	return status;
929}
930
931
932static status_t
933ext2_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName,
934	fs_vnode* _newDir, const char* newName)
935{
936	TRACE("ext2_rename()\n");
937
938	Volume* volume = (Volume*)_volume->private_volume;
939	Inode* oldDirectory = (Inode*)_oldDir->private_node;
940	Inode* newDirectory = (Inode*)_newDir->private_node;
941
942	if (oldDirectory == newDirectory && strcmp(oldName, newName) == 0)
943		return B_OK;
944
945	Transaction transaction(volume->GetJournal());
946
947	oldDirectory->WriteLockInTransaction(transaction);
948	if (oldDirectory != newDirectory)
949		newDirectory->WriteLockInTransaction(transaction);
950
951	status_t status = oldDirectory->CheckPermissions(W_OK);
952	if (status == B_OK)
953		status = newDirectory->CheckPermissions(W_OK);
954	if (status != B_OK)
955		return status;
956
957	HTree oldHTree(volume, oldDirectory);
958	DirectoryIterator* oldIterator;
959
960	status = oldHTree.Lookup(oldName, &oldIterator);
961	if (status != B_OK)
962		return status;
963
964	ObjectDeleter<DirectoryIterator> oldIteratorDeleter(oldIterator);
965
966	ino_t oldID;
967	status = oldIterator->FindEntry(oldName, &oldID);
968	if (status != B_OK)
969		return status;
970
971	TRACE("ext2_rename(): found entry to rename\n");
972
973	if (oldDirectory != newDirectory) {
974		TRACE("ext2_rename(): Different parent directories\n");
975		CachedBlock cached(volume);
976
977		ino_t parentID = newDirectory->ID();
978		ino_t oldDirID = oldDirectory->ID();
979
980		do {
981			Vnode vnode(volume, parentID);
982			Inode* parent;
983
984			status = vnode.Get(&parent);
985			if (status != B_OK)
986				return B_IO_ERROR;
987
988			fsblock_t blockNum;
989			status = parent->FindBlock(0, blockNum);
990			if (status != B_OK)
991				return status;
992
993			const HTreeRoot* data = (const HTreeRoot*)cached.SetTo(blockNum);
994			parentID = data->dotdot.InodeID();
995		} while (parentID != oldID && parentID != oldDirID
996			&& parentID != EXT2_ROOT_NODE);
997
998		if (parentID == oldID)
999			return B_BAD_VALUE;
1000	}
1001
1002	HTree newHTree(volume, newDirectory);
1003	DirectoryIterator* newIterator;
1004
1005	status = newHTree.Lookup(newName, &newIterator);
1006	if (status != B_OK)
1007		return status;
1008
1009	TRACE("ext2_rename(): found new directory\n");
1010
1011	ObjectDeleter<DirectoryIterator> newIteratorDeleter(newIterator);
1012
1013	Vnode vnode(volume, oldID);
1014	Inode* inode;
1015
1016	status = vnode.Get(&inode);
1017	if (status != B_OK)
1018		return status;
1019
1020	uint8 fileType;
1021
1022	// TODO: Support all file types?
1023	if (inode->IsDirectory())
1024		fileType = EXT2_TYPE_DIRECTORY;
1025	else if (inode->IsSymLink())
1026		fileType = EXT2_TYPE_SYMLINK;
1027	else
1028		fileType = EXT2_TYPE_FILE;
1029
1030	// Add entry in destination directory
1031	ino_t existentID;
1032	status = newIterator->FindEntry(newName, &existentID);
1033	if (status == B_OK) {
1034		TRACE("ext2_rename(): found existing new entry\n");
1035		if (existentID == oldID) {
1036			// Remove entry in oldID
1037			// return inode->Unlink();
1038			return B_BAD_VALUE;
1039		}
1040
1041		Vnode vnodeExistent(volume, existentID);
1042		Inode* existent;
1043
1044		if (vnodeExistent.Get(&existent) != B_OK)
1045			return B_NAME_IN_USE;
1046
1047		if (existent->IsDirectory() != inode->IsDirectory()) {
1048			return existent->IsDirectory() ? B_IS_A_DIRECTORY
1049				: B_NOT_A_DIRECTORY;
1050		}
1051
1052		// TODO: Perhaps we have to revert this in case of error?
1053		status = newIterator->ChangeEntry(transaction, oldID, fileType);
1054		if (status != B_OK)
1055			return status;
1056
1057		status = existent->Unlink(transaction);
1058		if (status != B_OK)
1059			ERROR("Error while unlinking existing destination\n");
1060
1061		entry_cache_remove(volume->ID(), newDirectory->ID(), newName);
1062
1063		notify_entry_removed(volume->ID(), newDirectory->ID(), newName,
1064			existentID);
1065	} else if (status == B_ENTRY_NOT_FOUND) {
1066		newIterator->Restart();
1067
1068		status = newIterator->AddEntry(transaction, newName, strlen(newName),
1069			oldID, fileType);
1070		if (status != B_OK)
1071			return status;
1072
1073	} else
1074		return status;
1075
1076	if (oldDirectory == newDirectory) {
1077		status = oldHTree.Lookup(oldName, &oldIterator);
1078		if (status != B_OK)
1079			return status;
1080
1081		oldIteratorDeleter.SetTo(oldIterator);
1082		status = oldIterator->FindEntry(oldName, &oldID);
1083		if (status != B_OK)
1084			return status;
1085	}
1086
1087	// Remove entry from source folder
1088	status = oldIterator->RemoveEntry(transaction);
1089	if (status != B_OK)
1090		return status;
1091
1092	inode->WriteLockInTransaction(transaction);
1093
1094	if (oldDirectory != newDirectory && inode->IsDirectory()) {
1095		DirectoryIterator inodeIterator(inode);
1096
1097		status = inodeIterator.FindEntry("..");
1098		if (status == B_ENTRY_NOT_FOUND) {
1099			ERROR("Corrupt file system. Missing \"..\" in directory %"
1100				B_PRIdINO "\n", inode->ID());
1101			return B_BAD_DATA;
1102		} else if (status != B_OK)
1103			return status;
1104
1105		inodeIterator.ChangeEntry(transaction, newDirectory->ID(),
1106			(uint8)EXT2_TYPE_DIRECTORY);
1107		// Decrement hardlink count on the source folder
1108		status = oldDirectory->Unlink(transaction);
1109		if (status != B_OK)
1110			ERROR("Error while decrementing hardlink count on the source folder\n");
1111		// Increment hardlink count on the destination folder
1112		newDirectory->IncrementNumLinks(transaction);
1113		status = newDirectory->WriteBack(transaction);
1114		if (status != B_OK)
1115			ERROR("Error while writing back the destination folder inode\n");
1116	}
1117
1118	status = inode->WriteBack(transaction);
1119	if (status != B_OK)
1120		return status;
1121
1122	entry_cache_remove(volume->ID(), oldDirectory->ID(), oldName);
1123	entry_cache_add(volume->ID(), newDirectory->ID(), newName, oldID);
1124
1125	status = transaction.Done();
1126	if (status != B_OK) {
1127		entry_cache_remove(volume->ID(), oldDirectory->ID(), newName);
1128		entry_cache_add(volume->ID(), newDirectory->ID(), oldName, oldID);
1129
1130		return status;
1131	}
1132
1133	notify_entry_moved(volume->ID(), oldDirectory->ID(), oldName,
1134		newDirectory->ID(), newName, oldID);
1135
1136	return B_OK;
1137}
1138
1139
1140static status_t
1141ext2_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie)
1142{
1143	Volume* volume = (Volume*)_volume->private_volume;
1144	Inode* inode = (Inode*)_node->private_node;
1145
1146	// opening a directory read-only is allowed, although you can't read
1147	// any data from it.
1148	if (inode->IsDirectory() && (openMode & O_RWMASK) != 0)
1149		return B_IS_A_DIRECTORY;
1150
1151	status_t status =  inode->CheckPermissions(open_mode_to_access(openMode)
1152		| (openMode & O_TRUNC ? W_OK : 0));
1153	if (status != B_OK)
1154		return status;
1155
1156	// Prepare the cookie
1157	file_cookie* cookie = new(std::nothrow) file_cookie;
1158	if (cookie == NULL)
1159		return B_NO_MEMORY;
1160	ObjectDeleter<file_cookie> cookieDeleter(cookie);
1161
1162	cookie->open_mode = openMode & EXT2_OPEN_MODE_USER_MASK;
1163	cookie->last_size = inode->Size();
1164	cookie->last_notification = system_time();
1165
1166	MethodDeleter<Inode, status_t, &Inode::EnableFileCache> fileCacheEnabler;
1167	if ((openMode & O_NOCACHE) != 0) {
1168		status = inode->DisableFileCache();
1169		if (status != B_OK)
1170			return status;
1171		fileCacheEnabler.SetTo(inode);
1172	}
1173
1174	// Should we truncate the file?
1175	if ((openMode & O_TRUNC) != 0) {
1176		if ((openMode & O_RWMASK) == O_RDONLY)
1177			return B_NOT_ALLOWED;
1178
1179		Transaction transaction(volume->GetJournal());
1180		inode->WriteLockInTransaction(transaction);
1181
1182		status_t status = inode->Resize(transaction, 0);
1183		if (status == B_OK)
1184			status = inode->WriteBack(transaction);
1185		if (status == B_OK)
1186			status = transaction.Done();
1187		if (status != B_OK)
1188			return status;
1189
1190		// TODO: No need to notify file size changed?
1191	}
1192
1193	fileCacheEnabler.Detach();
1194	cookieDeleter.Detach();
1195	*_cookie = cookie;
1196
1197	return B_OK;
1198}
1199
1200
1201static status_t
1202ext2_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
1203	void* buffer, size_t* _length)
1204{
1205	Inode* inode = (Inode*)_node->private_node;
1206
1207	if (!inode->IsFile()) {
1208		*_length = 0;
1209		return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE;
1210	}
1211
1212	return inode->ReadAt(pos, (uint8*)buffer, _length);
1213}
1214
1215
1216static status_t
1217ext2_write(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
1218	const void* buffer, size_t* _length)
1219{
1220	TRACE("ext2_write()\n");
1221	Volume* volume = (Volume*)_volume->private_volume;
1222	Inode* inode = (Inode*)_node->private_node;
1223
1224	if (volume->IsReadOnly())
1225		return B_READ_ONLY_DEVICE;
1226
1227	if (inode->IsDirectory()) {
1228		*_length = 0;
1229		return B_IS_A_DIRECTORY;
1230	}
1231
1232	TRACE("ext2_write(): Preparing cookie\n");
1233
1234	file_cookie* cookie = (file_cookie*)_cookie;
1235
1236	if ((cookie->open_mode & O_APPEND) != 0)
1237		pos = inode->Size();
1238
1239	TRACE("ext2_write(): Creating transaction\n");
1240	Transaction transaction;
1241
1242	status_t status = inode->WriteAt(transaction, pos, (const uint8*)buffer,
1243		_length);
1244	if (status == B_OK)
1245		status = transaction.Done();
1246	if (status == B_OK) {
1247		TRACE("ext2_write(): Finalizing\n");
1248		ReadLocker lock(*inode->Lock());
1249
1250		if (cookie->last_size != inode->Size()
1251			&& system_time() > cookie->last_notification
1252				+ INODE_NOTIFICATION_INTERVAL) {
1253			notify_stat_changed(volume->ID(), -1, inode->ID(),
1254				B_STAT_MODIFICATION_TIME | B_STAT_SIZE | B_STAT_INTERIM_UPDATE);
1255			cookie->last_size = inode->Size();
1256			cookie->last_notification = system_time();
1257		}
1258	}
1259
1260	TRACE("ext2_write(): Done\n");
1261
1262	return status;
1263}
1264
1265
1266static status_t
1267ext2_close(fs_volume *_volume, fs_vnode *_node, void *_cookie)
1268{
1269	return B_OK;
1270}
1271
1272
1273static status_t
1274ext2_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1275{
1276	file_cookie* cookie = (file_cookie*)_cookie;
1277	Volume* volume = (Volume*)_volume->private_volume;
1278	Inode* inode = (Inode*)_node->private_node;
1279
1280	if (inode->Size() != cookie->last_size)
1281		notify_stat_changed(volume->ID(), -1, inode->ID(), B_STAT_SIZE);
1282
1283	if ((cookie->open_mode & O_NOCACHE) != 0)
1284		inode->EnableFileCache();
1285
1286	delete cookie;
1287	return B_OK;
1288}
1289
1290
1291static status_t
1292ext2_access(fs_volume* _volume, fs_vnode* _node, int accessMode)
1293{
1294	Inode* inode = (Inode*)_node->private_node;
1295	return inode->CheckPermissions(accessMode);
1296}
1297
1298
1299static status_t
1300ext2_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer,
1301	size_t *_bufferSize)
1302{
1303	Inode* inode = (Inode*)_node->private_node;
1304
1305	if (!inode->IsSymLink())
1306		return B_BAD_VALUE;
1307
1308	if (inode->Size() > EXT2_SHORT_SYMLINK_LENGTH) {
1309		status_t result = inode->ReadAt(0, reinterpret_cast<uint8*>(buffer),
1310			_bufferSize);
1311		if (result != B_OK)
1312			return result;
1313	} else {
1314		size_t bytesToCopy = std::min(static_cast<size_t>(inode->Size()),
1315			*_bufferSize);
1316
1317		memcpy(buffer, inode->Node().symlink, bytesToCopy);
1318	}
1319
1320	*_bufferSize = inode->Size();
1321	return B_OK;
1322}
1323
1324
1325//	#pragma mark - Directory functions
1326
1327
1328static status_t
1329ext2_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name,
1330	int mode)
1331{
1332	TRACE("ext2_create_dir()\n");
1333	Volume* volume = (Volume*)_volume->private_volume;
1334	Inode* directory = (Inode*)_directory->private_node;
1335
1336	if (volume->IsReadOnly())
1337		return B_READ_ONLY_DEVICE;
1338
1339	if (!directory->IsDirectory())
1340		return B_BAD_TYPE;
1341
1342	status_t status = directory->CheckPermissions(W_OK);
1343	if (status != B_OK)
1344		return status;
1345
1346	TRACE("ext2_create_dir(): Starting transaction\n");
1347	Transaction transaction(volume->GetJournal());
1348
1349	ino_t id;
1350	status = Inode::Create(transaction, directory, name,
1351		S_DIRECTORY | (mode & S_IUMSK), 0, EXT2_TYPE_DIRECTORY, NULL, &id);
1352	if (status != B_OK)
1353		return status;
1354
1355	put_vnode(volume->FSVolume(), id);
1356
1357	entry_cache_add(volume->ID(), directory->ID(), name, id);
1358
1359	status = transaction.Done();
1360	if (status != B_OK) {
1361		entry_cache_remove(volume->ID(), directory->ID(), name);
1362		return status;
1363	}
1364
1365	notify_entry_created(volume->ID(), directory->ID(), name, id);
1366
1367	TRACE("ext2_create_dir(): Done\n");
1368
1369	return B_OK;
1370}
1371
1372
1373static status_t
1374ext2_remove_dir(fs_volume* _volume, fs_vnode* _directory, const char* name)
1375{
1376	TRACE("ext2_remove_dir()\n");
1377
1378	Volume* volume = (Volume*)_volume->private_volume;
1379	Inode* directory = (Inode*)_directory->private_node;
1380
1381	status_t status = directory->CheckPermissions(W_OK);
1382	if (status != B_OK)
1383		return status;
1384
1385	TRACE("ext2_remove_dir(): Starting transaction\n");
1386	Transaction transaction(volume->GetJournal());
1387
1388	directory->WriteLockInTransaction(transaction);
1389
1390	TRACE("ext2_remove_dir(): Looking up for directory entry\n");
1391	HTree htree(volume, directory);
1392	DirectoryIterator* directoryIterator;
1393
1394	status = htree.Lookup(name, &directoryIterator);
1395	if (status != B_OK)
1396		return status;
1397
1398	ino_t id;
1399	status = directoryIterator->FindEntry(name, &id);
1400	if (status != B_OK)
1401		return status;
1402
1403	Vnode vnode(volume, id);
1404	Inode* inode;
1405	status = vnode.Get(&inode);
1406	if (status != B_OK)
1407		return status;
1408
1409	inode->WriteLockInTransaction(transaction);
1410
1411	status = inode->Unlink(transaction);
1412	if (status != B_OK)
1413		return status;
1414
1415	status = directory->Unlink(transaction);
1416	if (status != B_OK)
1417		return status;
1418
1419	status = directoryIterator->RemoveEntry(transaction);
1420	if (status != B_OK)
1421		return status;
1422
1423	entry_cache_remove(volume->ID(), directory->ID(), name);
1424	entry_cache_remove(volume->ID(), id, "..");
1425
1426	status = transaction.Done();
1427	if (status != B_OK) {
1428		entry_cache_add(volume->ID(), directory->ID(), name, id);
1429		entry_cache_add(volume->ID(), id, "..", id);
1430	} else
1431		notify_entry_removed(volume->ID(), directory->ID(), name, id);
1432
1433	return status;
1434}
1435
1436
1437static status_t
1438ext2_open_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie)
1439{
1440	Inode* inode = (Inode*)_node->private_node;
1441	status_t status = inode->CheckPermissions(R_OK);
1442	if (status < B_OK)
1443		return status;
1444
1445	if (!inode->IsDirectory())
1446		return B_NOT_A_DIRECTORY;
1447
1448	DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode);
1449	if (iterator == NULL)
1450		return B_NO_MEMORY;
1451
1452	*_cookie = iterator;
1453	return B_OK;
1454}
1455
1456
1457static status_t
1458ext2_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie,
1459	struct dirent *dirent, size_t bufferSize, uint32 *_num)
1460{
1461	DirectoryIterator *iterator = (DirectoryIterator *)_cookie;
1462	Volume* volume = (Volume*)_volume->private_volume;
1463
1464	uint32 maxCount = *_num;
1465	uint32 count = 0;
1466
1467	while (count < maxCount && bufferSize > sizeof(struct dirent)) {
1468
1469		size_t length = bufferSize - offsetof(struct dirent, d_name);
1470		ino_t id;
1471
1472		status_t status = iterator->GetNext(dirent->d_name, &length, &id);
1473		if (status == B_ENTRY_NOT_FOUND)
1474			break;
1475
1476		if (status == B_BUFFER_OVERFLOW) {
1477			// the remaining name buffer length was too small
1478			if (count == 0)
1479				return B_BUFFER_OVERFLOW;
1480			break;
1481		}
1482
1483		if (status != B_OK)
1484			return status;
1485
1486		status = iterator->Next();
1487		if (status != B_OK && status != B_ENTRY_NOT_FOUND)
1488			return status;
1489
1490		dirent->d_dev = volume->ID();
1491		dirent->d_ino = id;
1492
1493		dirent = next_dirent(dirent, length, bufferSize);
1494		count++;
1495	}
1496
1497	*_num = count;
1498	return B_OK;
1499}
1500
1501
1502static status_t
1503ext2_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie)
1504{
1505	DirectoryIterator *iterator = (DirectoryIterator *)_cookie;
1506	return iterator->Rewind();
1507}
1508
1509
1510static status_t
1511ext2_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void * /*_cookie*/)
1512{
1513	return B_OK;
1514}
1515
1516
1517static status_t
1518ext2_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
1519{
1520	delete (DirectoryIterator*)_cookie;
1521	return B_OK;
1522}
1523
1524
1525static status_t
1526ext2_open_attr_dir(fs_volume *_volume, fs_vnode *_node, void **_cookie)
1527{
1528	Inode* inode = (Inode*)_node->private_node;
1529	Volume* volume = (Volume*)_volume->private_volume;
1530	TRACE("%s()\n", __FUNCTION__);
1531
1532	if (!(volume->SuperBlock().CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR))
1533		return ENOSYS;
1534
1535	// on directories too ?
1536	if (!inode->IsFile())
1537		return EINVAL;
1538
1539	int32 *index = new(std::nothrow) int32;
1540	if (index == NULL)
1541		return B_NO_MEMORY;
1542	*index = 0;
1543	*(int32**)_cookie = index;
1544	return B_OK;
1545}
1546
1547static status_t
1548ext2_close_attr_dir(fs_volume* _volume, fs_vnode* _node, void* cookie)
1549{
1550	TRACE("%s()\n", __FUNCTION__);
1551	return B_OK;
1552}
1553
1554
1555static status_t
1556ext2_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1557{
1558	TRACE("%s()\n", __FUNCTION__);
1559	delete (int32 *)_cookie;
1560	return B_OK;
1561}
1562
1563
1564static status_t
1565ext2_read_attr_dir(fs_volume* _volume, fs_vnode* _node,
1566				void* _cookie, struct dirent* dirent, size_t bufferSize,
1567				uint32* _num)
1568{
1569	Inode* inode = (Inode*)_node->private_node;
1570	int32 index = *(int32 *)_cookie;
1571	Attribute attribute(inode);
1572	TRACE("%s()\n", __FUNCTION__);
1573
1574	size_t length = bufferSize;
1575	status_t status = attribute.Find(index);
1576	if (status == B_ENTRY_NOT_FOUND) {
1577		*_num = 0;
1578		return B_OK;
1579	} else if (status != B_OK)
1580		return status;
1581
1582	status = attribute.GetName(dirent->d_name, &length);
1583	if (status != B_OK)
1584		return B_OK;
1585
1586	Volume* volume = (Volume*)_volume->private_volume;
1587
1588	dirent->d_dev = volume->ID();
1589	dirent->d_ino = inode->ID();
1590	dirent->d_reclen = offsetof(struct dirent, d_name) + length + 1;
1591
1592	*_num = 1;
1593	*(int32*)_cookie = index + 1;
1594	return B_OK;
1595}
1596
1597
1598static status_t
1599ext2_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1600{
1601	*(int32*)_cookie = 0;
1602	TRACE("%s()\n", __FUNCTION__);
1603	return B_OK;
1604}
1605
1606
1607	/* attribute operations */
1608static status_t
1609ext2_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name,
1610	int openMode, void** _cookie)
1611{
1612	TRACE("%s()\n", __FUNCTION__);
1613
1614	Volume* volume = (Volume*)_volume->private_volume;
1615	Inode* inode = (Inode*)_node->private_node;
1616	Attribute attribute(inode);
1617
1618	if (!(volume->SuperBlock().CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR))
1619		return ENOSYS;
1620
1621	return attribute.Open(name, openMode, (attr_cookie**)_cookie);
1622}
1623
1624
1625static status_t
1626ext2_close_attr(fs_volume* _volume, fs_vnode* _node,
1627	void* cookie)
1628{
1629	return B_OK;
1630}
1631
1632
1633static status_t
1634ext2_free_attr_cookie(fs_volume* _volume, fs_vnode* _node,
1635	void* cookie)
1636{
1637	delete (attr_cookie*)cookie;
1638	return B_OK;
1639}
1640
1641
1642static status_t
1643ext2_read_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie,
1644	off_t pos, void* buffer, size_t* _length)
1645{
1646	TRACE("%s()\n", __FUNCTION__);
1647
1648	attr_cookie* cookie = (attr_cookie*)_cookie;
1649	Inode* inode = (Inode*)_node->private_node;
1650
1651	Attribute attribute(inode, cookie);
1652
1653	return attribute.Read(cookie, pos, (uint8*)buffer, _length);
1654}
1655
1656
1657static status_t
1658ext2_read_attr_stat(fs_volume* _volume, fs_vnode* _node,
1659	void* _cookie, struct stat* stat)
1660{
1661	attr_cookie* cookie = (attr_cookie*)_cookie;
1662	Inode* inode = (Inode*)_node->private_node;
1663
1664	Attribute attribute(inode, cookie);
1665
1666	return attribute.Stat(*stat);
1667}
1668
1669
1670fs_volume_ops gExt2VolumeOps = {
1671	&ext2_unmount,
1672	&ext2_read_fs_info,
1673	&ext2_write_fs_info,
1674	&ext2_sync,
1675	&ext2_get_vnode,
1676};
1677
1678
1679fs_vnode_ops gExt2VnodeOps = {
1680	/* vnode operations */
1681	&ext2_lookup,
1682	NULL,
1683	&ext2_put_vnode,
1684	&ext2_remove_vnode,
1685
1686	/* VM file access */
1687	&ext2_can_page,
1688	&ext2_read_pages,
1689	&ext2_write_pages,
1690
1691	NULL,	// io()
1692	NULL,	// cancel_io()
1693
1694	&ext2_get_file_map,
1695
1696	&ext2_ioctl,
1697	&ext2_set_flags,
1698	NULL,	// fs_select
1699	NULL,	// fs_deselect
1700	&ext2_fsync,
1701
1702	&ext2_read_link,
1703	&ext2_create_symlink,
1704
1705	&ext2_link,
1706	&ext2_unlink,
1707	&ext2_rename,
1708
1709	&ext2_access,
1710	&ext2_read_stat,
1711	&ext2_write_stat,
1712	NULL,	// fs_preallocate
1713
1714	/* file operations */
1715	&ext2_create,
1716	&ext2_open,
1717	&ext2_close,
1718	&ext2_free_cookie,
1719	&ext2_read,
1720	&ext2_write,
1721
1722	/* directory operations */
1723	&ext2_create_dir,
1724	&ext2_remove_dir,
1725	&ext2_open_dir,
1726	&ext2_close_dir,
1727	&ext2_free_dir_cookie,
1728	&ext2_read_dir,
1729	&ext2_rewind_dir,
1730
1731	/* attribute directory operations */
1732	&ext2_open_attr_dir,
1733	&ext2_close_attr_dir,
1734	&ext2_free_attr_dir_cookie,
1735	&ext2_read_attr_dir,
1736	&ext2_rewind_attr_dir,
1737
1738	/* attribute operations */
1739	NULL,
1740	&ext2_open_attr,
1741	&ext2_close_attr,
1742	&ext2_free_attr_cookie,
1743	&ext2_read_attr,
1744	NULL,
1745	&ext2_read_attr_stat,
1746	NULL,
1747	NULL,
1748	NULL,
1749};
1750
1751
1752static file_system_module_info sExt2FileSystem = {
1753	{
1754		"file_systems/ext2" B_CURRENT_FS_API_VERSION,
1755		0,
1756		NULL,
1757	},
1758
1759	"ext4",								// short_name
1760	"Linux Extended File System 2/3/4",	// pretty_name
1761	B_DISK_SYSTEM_SUPPORTS_WRITING
1762		| B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME,	// DDM flags
1763
1764	// scanning
1765	ext2_identify_partition,
1766	ext2_scan_partition,
1767	ext2_free_identify_partition_cookie,
1768	NULL,	// free_partition_content_cookie()
1769
1770	&ext2_mount,
1771
1772	NULL,
1773};
1774
1775
1776module_info *modules[] = {
1777	(module_info *)&sExt2FileSystem,
1778	NULL,
1779};
1780