1/*
2 * Copyright 2010, J��r��me Duval, korli@users.berlios.de.
3 * Copyright 2008, Axel D��rfler, axeld@pinc-software.de.
4 * This file may be used under the terms of the MIT License.
5 */
6
7
8#include <algorithm>
9#include <dirent.h>
10#include <sys/ioctl.h>
11#include <util/kernel_cpp.h>
12#include <string.h>
13
14#include <AutoDeleter.h>
15#include <fs_cache.h>
16#include <fs_info.h>
17#include <io_requests.h>
18#include <NodeMonitor.h>
19#include <util/AutoLock.h>
20
21#include "Attribute.h"
22#include "CachedBlock.h"
23#include "DirectoryIterator.h"
24#include "ext2.h"
25#include "HTree.h"
26#include "Inode.h"
27#include "Journal.h"
28#include "Utility.h"
29
30
31//#define TRACE_EXT2
32#ifdef TRACE_EXT2
33#	define TRACE(x...) dprintf("\33[34mext2:\33[0m " x)
34#else
35#	define TRACE(x...) ;
36#endif
37#define ERROR(x...) dprintf("\33[34mext2:\33[0m " x)
38
39
40#define EXT2_IO_SIZE	65536
41
42
43struct identify_cookie {
44	ext2_super_block super_block;
45};
46
47
48//	#pragma mark - Scanning
49
50
51static float
52ext2_identify_partition(int fd, partition_data *partition, void **_cookie)
53{
54	STATIC_ASSERT(sizeof(struct ext2_super_block) == 1024);
55	STATIC_ASSERT(sizeof(struct ext2_block_group) == 64);
56
57	ext2_super_block superBlock;
58	status_t status = Volume::Identify(fd, &superBlock);
59	if (status != B_OK)
60		return -1;
61
62	identify_cookie *cookie = new identify_cookie;
63	memcpy(&cookie->super_block, &superBlock, sizeof(ext2_super_block));
64
65	*_cookie = cookie;
66	return 0.8f;
67}
68
69
70static status_t
71ext2_scan_partition(int fd, partition_data *partition, void *_cookie)
72{
73	identify_cookie *cookie = (identify_cookie *)_cookie;
74
75	partition->status = B_PARTITION_VALID;
76	partition->flags |= B_PARTITION_FILE_SYSTEM;
77	partition->content_size = cookie->super_block.NumBlocks(
78		(cookie->super_block.CompatibleFeatures()
79			& EXT2_INCOMPATIBLE_FEATURE_64BIT) != 0)
80			<< cookie->super_block.BlockShift();
81	partition->block_size = 1UL << cookie->super_block.BlockShift();
82	partition->content_name = strdup(cookie->super_block.name);
83	if (partition->content_name == NULL)
84		return B_NO_MEMORY;
85
86	return B_OK;
87}
88
89
90static void
91ext2_free_identify_partition_cookie(partition_data* partition, void* _cookie)
92{
93	delete (identify_cookie*)_cookie;
94}
95
96
97//	#pragma mark -
98
99
100static status_t
101ext2_mount(fs_volume* _volume, const char* device, uint32 flags,
102	const char* args, ino_t* _rootID)
103{
104	Volume* volume = new(std::nothrow) Volume(_volume);
105	if (volume == NULL)
106		return B_NO_MEMORY;
107
108	// TODO: this is a bit hacky: we can't use publish_vnode() to publish
109	// the root node, or else its file cache cannot be created (we could
110	// create it later, though). Therefore we're using get_vnode() in Mount(),
111	// but that requires us to export our volume data before calling it.
112	_volume->private_volume = volume;
113	_volume->ops = &gExt2VolumeOps;
114
115	status_t status = volume->Mount(device, flags);
116	if (status != B_OK) {
117		ERROR("Failed mounting the volume. Error: %s\n", strerror(status));
118		delete volume;
119		return status;
120	}
121
122	*_rootID = volume->RootNode()->ID();
123	return B_OK;
124}
125
126
127static status_t
128ext2_unmount(fs_volume *_volume)
129{
130	Volume* volume = (Volume *)_volume->private_volume;
131
132	status_t status = volume->Unmount();
133	delete volume;
134
135	return status;
136}
137
138
139static status_t
140ext2_read_fs_info(fs_volume* _volume, struct fs_info* info)
141{
142	Volume* volume = (Volume*)_volume->private_volume;
143
144	// File system flags
145	info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR
146		| (volume->IsReadOnly() ? B_FS_IS_READONLY : 0);
147	info->io_size = EXT2_IO_SIZE;
148	info->block_size = volume->BlockSize();
149	info->total_blocks = volume->NumBlocks();
150	info->free_blocks = volume->NumFreeBlocks();
151
152	// Volume name
153	strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
154
155	// File system name
156	if (volume->HasExtentsFeature())
157		strlcpy(info->fsh_name, "ext4", sizeof(info->fsh_name));
158	else if (volume->HasJournalFeature())
159		strlcpy(info->fsh_name, "ext3", sizeof(info->fsh_name));
160	else
161		strlcpy(info->fsh_name, "ext2", sizeof(info->fsh_name));
162
163	return B_OK;
164}
165
166
167static status_t
168ext2_write_fs_info(fs_volume* _volume, const struct fs_info* info, uint32 mask)
169{
170	Volume* volume = (Volume*)_volume->private_volume;
171
172	if (volume->IsReadOnly())
173		return B_READ_ONLY_DEVICE;
174
175	MutexLocker locker(volume->Lock());
176
177	status_t status = B_BAD_VALUE;
178
179	if (mask & FS_WRITE_FSINFO_NAME) {
180		Transaction transaction(volume->GetJournal());
181		volume->SetName(info->volume_name);
182		status = volume->WriteSuperBlock(transaction);
183		transaction.Done();
184	}
185	return status;
186}
187
188
189static status_t
190ext2_sync(fs_volume* _volume)
191{
192	Volume* volume = (Volume*)_volume->private_volume;
193	return volume->Sync();
194}
195
196
197//	#pragma mark -
198
199
200static status_t
201ext2_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type,
202	uint32* _flags, bool reenter)
203{
204	Volume* volume = (Volume*)_volume->private_volume;
205
206	if (id < 2 || id > volume->NumInodes()) {
207		ERROR("invalid inode id %" B_PRIdINO " requested!\n", id);
208		return B_BAD_VALUE;
209	}
210
211	Inode* inode = new(std::nothrow) Inode(volume, id);
212	if (inode == NULL)
213		return B_NO_MEMORY;
214
215	status_t status = inode->InitCheck();
216	if (status != B_OK)
217		delete inode;
218
219	if (status == B_OK) {
220		_node->private_node = inode;
221		_node->ops = &gExt2VnodeOps;
222		*_type = inode->Mode();
223		*_flags = 0;
224	} else
225		ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status));
226
227	return status;
228}
229
230
231static status_t
232ext2_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
233{
234	delete (Inode*)_node->private_node;
235	return B_OK;
236}
237
238
239static status_t
240ext2_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
241{
242	TRACE("ext2_remove_vnode()\n");
243	Volume* volume = (Volume*)_volume->private_volume;
244	Inode* inode = (Inode*)_node->private_node;
245	ObjectDeleter<Inode> inodeDeleter(inode);
246
247	if (!inode->IsDeleted())
248		return B_OK;
249
250	TRACE("ext2_remove_vnode(): Starting transaction\n");
251	Transaction transaction(volume->GetJournal());
252
253	if (!inode->IsSymLink() || inode->Size() >= EXT2_SHORT_SYMLINK_LENGTH) {
254		TRACE("ext2_remove_vnode(): Truncating\n");
255		status_t status = inode->Resize(transaction, 0);
256		if (status != B_OK)
257			return status;
258	}
259
260	TRACE("ext2_remove_vnode(): Removing from orphan list\n");
261	status_t status = volume->RemoveOrphan(transaction, inode->ID());
262	if (status != B_OK)
263		return status;
264
265	TRACE("ext2_remove_vnode(): Setting deletion time\n");
266	inode->Node().SetDeletionTime(real_time_clock());
267
268	status = inode->WriteBack(transaction);
269	if (status != B_OK)
270		return status;
271
272	TRACE("ext2_remove_vnode(): Freeing inode\n");
273	status = volume->FreeInode(transaction, inode->ID(), inode->IsDirectory());
274
275	// TODO: When Transaction::Done() fails, do we have to re-add the vnode?
276	if (status == B_OK)
277		status = transaction.Done();
278
279	return status;
280}
281
282
283static bool
284ext2_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie)
285{
286	return true;
287}
288
289
290static status_t
291ext2_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
292	off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
293{
294	Volume* volume = (Volume*)_volume->private_volume;
295	Inode* inode = (Inode*)_node->private_node;
296
297	if (inode->FileCache() == NULL)
298		return B_BAD_VALUE;
299
300	rw_lock_read_lock(inode->Lock());
301
302	uint32 vecIndex = 0;
303	size_t vecOffset = 0;
304	size_t bytesLeft = *_numBytes;
305	status_t status;
306
307	while (true) {
308		file_io_vec fileVecs[8];
309		size_t fileVecCount = 8;
310
311		status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
312			&fileVecCount, 0);
313		if (status != B_OK && status != B_BUFFER_OVERFLOW)
314			break;
315
316		bool bufferOverflow = status == B_BUFFER_OVERFLOW;
317
318		size_t bytes = bytesLeft;
319		status = read_file_io_vec_pages(volume->Device(), fileVecs,
320			fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
321		if (status != B_OK || !bufferOverflow)
322			break;
323
324		pos += bytes;
325		bytesLeft -= bytes;
326	}
327
328	rw_lock_read_unlock(inode->Lock());
329
330	return status;
331}
332
333
334static status_t
335ext2_write_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
336	off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
337{
338	Volume* volume = (Volume*)_volume->private_volume;
339	Inode* inode = (Inode*)_node->private_node;
340
341	if (volume->IsReadOnly())
342		return B_READ_ONLY_DEVICE;
343
344	if (inode->FileCache() == NULL)
345		return B_BAD_VALUE;
346
347	rw_lock_read_lock(inode->Lock());
348
349	uint32 vecIndex = 0;
350	size_t vecOffset = 0;
351	size_t bytesLeft = *_numBytes;
352	status_t status;
353
354	while (true) {
355		file_io_vec fileVecs[8];
356		size_t fileVecCount = 8;
357
358		status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
359			&fileVecCount, 0);
360		if (status != B_OK && status != B_BUFFER_OVERFLOW)
361			break;
362
363		bool bufferOverflow = status == B_BUFFER_OVERFLOW;
364
365		size_t bytes = bytesLeft;
366		status = write_file_io_vec_pages(volume->Device(), fileVecs,
367			fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
368		if (status != B_OK || !bufferOverflow)
369			break;
370
371		pos += bytes;
372		bytesLeft -= bytes;
373	}
374
375	rw_lock_read_unlock(inode->Lock());
376
377	return status;
378}
379
380
381static status_t
382ext2_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset,
383	size_t size, struct file_io_vec* vecs, size_t* _count)
384{
385	TRACE("ext2_get_file_map()\n");
386	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