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 <dirent.h>
9#include <util/kernel_cpp.h>
10#include <string.h>
11
12#include <AutoDeleter.h>
13#include <fs_cache.h>
14#include <fs_info.h>
15#include <io_requests.h>
16#include <NodeMonitor.h>
17#include <util/AutoLock.h>
18
19#include "Attribute.h"
20#include "CachedBlock.h"
21#include "DirectoryIterator.h"
22#include "ext2.h"
23#include "HTree.h"
24#include "Inode.h"
25#include "Journal.h"
26#include "Utility.h"
27
28
29//#define TRACE_EXT2
30#ifdef TRACE_EXT2
31#	define TRACE(x...) dprintf("\33[34mext2:\33[0m " x)
32#else
33#	define TRACE(x...) ;
34#endif
35#define ERROR(x...) dprintf("\33[34mext2:\33[0m " x)
36
37
38#define EXT2_IO_SIZE	65536
39
40
41struct identify_cookie {
42	ext2_super_block super_block;
43};
44
45
46//!	ext2_io() callback hook
47static status_t
48iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset,
49	size_t size, struct file_io_vec* vecs, size_t* _count)
50{
51	Inode* inode = (Inode*)cookie;
52
53	return file_map_translate(inode->Map(), offset, size, vecs, _count,
54		inode->GetVolume()->BlockSize());
55}
56
57
58//!	ext2_io() callback hook
59static status_t
60iterative_io_finished_hook(void* cookie, io_request* request, status_t status,
61	bool partialTransfer, size_t bytesTransferred)
62{
63	Inode* inode = (Inode*)cookie;
64	rw_lock_read_unlock(inode->Lock());
65	return B_OK;
66}
67
68
69//	#pragma mark - Scanning
70
71
72static float
73ext2_identify_partition(int fd, partition_data *partition, void **_cookie)
74{
75	STATIC_ASSERT(sizeof(struct ext2_super_block) == 1024);
76	STATIC_ASSERT(sizeof(struct ext2_block_group) == 64);
77
78	ext2_super_block superBlock;
79	status_t status = Volume::Identify(fd, &superBlock);
80	if (status != B_OK)
81		return -1;
82
83	identify_cookie *cookie = new identify_cookie;
84	memcpy(&cookie->super_block, &superBlock, sizeof(ext2_super_block));
85
86	*_cookie = cookie;
87	return 0.8f;
88}
89
90
91static status_t
92ext2_scan_partition(int fd, partition_data *partition, void *_cookie)
93{
94	identify_cookie *cookie = (identify_cookie *)_cookie;
95
96	partition->status = B_PARTITION_VALID;
97	partition->flags |= B_PARTITION_FILE_SYSTEM;
98	partition->content_size = cookie->super_block.NumBlocks(
99		(cookie->super_block.CompatibleFeatures()
100			& EXT2_INCOMPATIBLE_FEATURE_64BIT) != 0)
101			<< cookie->super_block.BlockShift();
102	partition->block_size = 1UL << cookie->super_block.BlockShift();
103	partition->content_name = strdup(cookie->super_block.name);
104	if (partition->content_name == NULL)
105		return B_NO_MEMORY;
106
107	return B_OK;
108}
109
110
111static void
112ext2_free_identify_partition_cookie(partition_data* partition, void* _cookie)
113{
114	delete (identify_cookie*)_cookie;
115}
116
117
118//	#pragma mark -
119
120
121static status_t
122ext2_mount(fs_volume* _volume, const char* device, uint32 flags,
123	const char* args, ino_t* _rootID)
124{
125	Volume* volume = new(std::nothrow) Volume(_volume);
126	if (volume == NULL)
127		return B_NO_MEMORY;
128
129	// TODO: this is a bit hacky: we can't use publish_vnode() to publish
130	// the root node, or else its file cache cannot be created (we could
131	// create it later, though). Therefore we're using get_vnode() in Mount(),
132	// but that requires us to export our volume data before calling it.
133	_volume->private_volume = volume;
134	_volume->ops = &gExt2VolumeOps;
135
136	status_t status = volume->Mount(device, flags);
137	if (status != B_OK) {
138		ERROR("Failed mounting the volume. Error: %s\n", strerror(status));
139		delete volume;
140		return status;
141	}
142
143	*_rootID = volume->RootNode()->ID();
144	return B_OK;
145}
146
147
148static status_t
149ext2_unmount(fs_volume *_volume)
150{
151	Volume* volume = (Volume *)_volume->private_volume;
152
153	status_t status = volume->Unmount();
154	delete volume;
155
156	return status;
157}
158
159
160static status_t
161ext2_read_fs_info(fs_volume* _volume, struct fs_info* info)
162{
163	Volume* volume = (Volume*)_volume->private_volume;
164
165	// File system flags
166	info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR
167		| (volume->IsReadOnly() ? B_FS_IS_READONLY : 0);
168	info->io_size = EXT2_IO_SIZE;
169	info->block_size = volume->BlockSize();
170	info->total_blocks = volume->NumBlocks();
171	info->free_blocks = volume->NumFreeBlocks();
172
173	// Volume name
174	strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
175
176	// File system name
177	strlcpy(info->fsh_name, "ext2", sizeof(info->fsh_name));
178
179	return B_OK;
180}
181
182
183static status_t
184ext2_write_fs_info(fs_volume* _volume, const struct fs_info* info, uint32 mask)
185{
186	Volume* volume = (Volume*)_volume->private_volume;
187
188	if (volume->IsReadOnly())
189		return B_READ_ONLY_DEVICE;
190
191	MutexLocker locker(volume->Lock());
192
193	status_t status = B_BAD_VALUE;
194
195	if (mask & FS_WRITE_FSINFO_NAME) {
196		Transaction transaction(volume->GetJournal());
197		volume->SetName(info->volume_name);
198		status = volume->WriteSuperBlock(transaction);
199		transaction.Done();
200	}
201	return status;
202}
203
204
205static status_t
206ext2_sync(fs_volume* _volume)
207{
208	Volume* volume = (Volume*)_volume->private_volume;
209	return volume->Sync();
210}
211
212
213//	#pragma mark -
214
215
216static status_t
217ext2_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type,
218	uint32* _flags, bool reenter)
219{
220	Volume* volume = (Volume*)_volume->private_volume;
221
222	if (id < 2 || id > volume->NumInodes()) {
223		ERROR("invalid inode id %" B_PRIdINO " requested!\n", id);
224		return B_BAD_VALUE;
225	}
226
227	Inode* inode = new(std::nothrow) Inode(volume, id);
228	if (inode == NULL)
229		return B_NO_MEMORY;
230
231	status_t status = inode->InitCheck();
232	if (status != B_OK)
233		delete inode;
234
235	if (status == B_OK) {
236		_node->private_node = inode;
237		_node->ops = &gExt2VnodeOps;
238		*_type = inode->Mode();
239		*_flags = 0;
240	} else
241		ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status));
242
243	return status;
244}
245
246
247static status_t
248ext2_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
249{
250	delete (Inode*)_node->private_node;
251	return B_OK;
252}
253
254
255static status_t
256ext2_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
257{
258	TRACE("ext2_remove_vnode()\n");
259	Volume* volume = (Volume*)_volume->private_volume;
260	Inode* inode = (Inode*)_node->private_node;
261	ObjectDeleter<Inode> inodeDeleter(inode);
262
263	if (!inode->IsDeleted())
264		return B_OK;
265
266	TRACE("ext2_remove_vnode(): Starting transaction\n");
267	Transaction transaction(volume->GetJournal());
268
269	if (!inode->IsSymLink() || inode->Size() >= EXT2_SHORT_SYMLINK_LENGTH) {
270		TRACE("ext2_remove_vnode(): Truncating\n");
271		status_t status = inode->Resize(transaction, 0);
272		if (status != B_OK)
273			return status;
274	}
275
276	TRACE("ext2_remove_vnode(): Removing from orphan list\n");
277	status_t status = volume->RemoveOrphan(transaction, inode->ID());
278	if (status != B_OK)
279		return status;
280
281	TRACE("ext2_remove_vnode(): Setting deletion time\n");
282	inode->Node().SetDeletionTime(real_time_clock());
283
284	status = inode->WriteBack(transaction);
285	if (status != B_OK)
286		return status;
287
288	TRACE("ext2_remove_vnode(): Freeing inode\n");
289	status = volume->FreeInode(transaction, inode->ID(), inode->IsDirectory());
290
291	// TODO: When Transaction::Done() fails, do we have to re-add the vnode?
292	if (status == B_OK)
293		status = transaction.Done();
294
295	return status;
296}
297
298
299static bool
300ext2_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie)
301{
302	return true;
303}
304
305
306static status_t
307ext2_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
308	off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
309{
310	Volume* volume = (Volume*)_volume->private_volume;
311	Inode* inode = (Inode*)_node->private_node;
312
313	if (inode->FileCache() == NULL)
314		return B_BAD_VALUE;
315
316	rw_lock_read_lock(inode->Lock());
317
318	uint32 vecIndex = 0;
319	size_t vecOffset = 0;
320	size_t bytesLeft = *_numBytes;
321	status_t status;
322
323	while (true) {
324		file_io_vec fileVecs[8];
325		size_t fileVecCount = 8;
326
327		status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
328			&fileVecCount, 0);
329		if (status != B_OK && status != B_BUFFER_OVERFLOW)
330			break;
331
332		bool bufferOverflow = status == B_BUFFER_OVERFLOW;
333
334		size_t bytes = bytesLeft;
335		status = read_file_io_vec_pages(volume->Device(), fileVecs,
336			fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
337		if (status != B_OK || !bufferOverflow)
338			break;
339
340		pos += bytes;
341		bytesLeft -= bytes;
342	}
343
344	rw_lock_read_unlock(inode->Lock());
345
346	return status;
347}
348
349
350static status_t
351ext2_write_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
352	off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
353{
354	Volume* volume = (Volume*)_volume->private_volume;
355	Inode* inode = (Inode*)_node->private_node;
356
357	if (volume->IsReadOnly())
358		return B_READ_ONLY_DEVICE;
359
360	if (inode->FileCache() == NULL)
361		return B_BAD_VALUE;
362
363	rw_lock_read_lock(inode->Lock());
364
365	uint32 vecIndex = 0;
366	size_t vecOffset = 0;
367	size_t bytesLeft = *_numBytes;
368	status_t status;
369
370	while (true) {
371		file_io_vec fileVecs[8];
372		size_t fileVecCount = 8;
373
374		status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
375			&fileVecCount, 0);
376		if (status != B_OK && status != B_BUFFER_OVERFLOW)
377			break;
378
379		bool bufferOverflow = status == B_BUFFER_OVERFLOW;
380
381		size_t bytes = bytesLeft;
382		status = write_file_io_vec_pages(volume->Device(), fileVecs,
383			fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
384		if (status != B_OK || !bufferOverflow)
385			break;
386
387		pos += bytes;
388		bytesLeft -= bytes;
389	}
390
391	rw_lock_read_unlock(inode->Lock());
392
393	return status;
394}
395
396
397static status_t
398ext2_io(fs_volume* _volume, fs_vnode* _node, void* _cookie, io_request* request)
399{
400	Volume* volume = (Volume*)_volume->private_volume;
401	Inode* inode = (Inode*)_node->private_node;
402
403#ifndef EXT2_SHELL
404	if (io_request_is_write(request) && volume->IsReadOnly()) {
405		notify_io_request(request, B_READ_ONLY_DEVICE);
406		return B_READ_ONLY_DEVICE;
407	}
408#endif
409
410	if (inode->FileCache() == NULL) {
411#ifndef EXT2_SHELL
412		notify_io_request(request, B_BAD_VALUE);
413#endif
414		return B_BAD_VALUE;
415	}
416
417	// We lock the node here and will unlock it in the "finished" hook.
418	rw_lock_read_lock(inode->Lock());
419
420	return do_iterative_fd_io(volume->Device(), request,
421		iterative_io_get_vecs_hook, iterative_io_finished_hook, inode);
422}
423
424
425static status_t
426ext2_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset,
427	size_t size, struct file_io_vec* vecs, size_t* _count)
428{
429	TRACE("ext2_get_file_map()\n");
430	Volume* volume = (Volume*)_volume->private_volume;
431	Inode* inode = (Inode*)_node->private_node;
432	size_t index = 0, max = *_count;
433
434	while (true) {
435		fsblock_t block;
436		uint32 count = 1;
437		status_t status = inode->FindBlock(offset, block, &count);
438		if (status != B_OK)
439			return status;
440
441		if (block > volume->NumBlocks()) {
442			panic("ext2_get_file_map() found block %" B_PRIu64 " for offset %"
443				B_PRIdOFF "\n", block, offset);
444		}
445
446		off_t blockOffset = block << volume->BlockShift();
447		uint32 blockLength = volume->BlockSize() * count;
448
449		if (index > 0 && (vecs[index - 1].offset
450				== blockOffset - vecs[index - 1].length
451				|| (vecs[index - 1].offset == -1 && block == 0))) {
452			vecs[index - 1].length += blockLength;
453		} else {
454			if (index >= max) {
455				// we're out of file_io_vecs; let's bail out
456				*_count = index;
457				return B_BUFFER_OVERFLOW;
458			}
459
460			// 'block' is 0 for sparse blocks
461			if (block != 0)
462				vecs[index].offset = blockOffset;
463			else
464				vecs[index].offset = -1;
465
466			vecs[index].length = blockLength;
467			index++;
468		}
469
470		offset += blockLength;
471
472		if (offset >= inode->Size() || size <= blockLength) {
473			// We're done!
474			*_count = index;
475			TRACE("ext2_get_file_map for inode %" B_PRIdINO "\n", inode->ID());
476			return B_OK;
477		}
478
479		size -= blockLength;
480	}
481
482	// can never get here
483	return B_ERROR;
484}
485
486
487//	#pragma mark -
488
489
490static status_t
491ext2_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name,
492	ino_t* _vnodeID)
493{
494	TRACE("ext2_lookup: name address: %p\n", name);
495	TRACE("ext2_lookup: name: %s\n", name);
496	Volume* volume = (Volume*)_volume->private_volume;
497	Inode* directory = (Inode*)_directory->private_node;
498
499	// check access permissions
500	status_t status = directory->CheckPermissions(X_OK);
501	if (status < B_OK)
502		return status;
503
504	HTree htree(volume, directory);
505	DirectoryIterator* iterator;
506
507	status = htree.Lookup(name, &iterator);
508	if (status != B_OK)
509		return status;
510
511	ObjectDeleter<DirectoryIterator> iteratorDeleter(iterator);
512
513	status = iterator->FindEntry(name, _vnodeID);
514	if (status != B_OK)
515		return status;
516
517	return get_vnode(volume->FSVolume(), *_vnodeID, NULL);
518}
519
520
521static status_t
522ext2_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd,
523	void* buffer, size_t bufferLength)
524{
525	TRACE("ioctl: %" B_PRIu32 "\n", cmd);
526
527	Volume* volume = (Volume*)_volume->private_volume;
528	switch (cmd) {
529		case 56742:
530		{
531			TRACE("ioctl: Test the block allocator\n");
532			// Test the block allocator
533			TRACE("ioctl: Creating transaction\n");
534			Transaction transaction(volume->GetJournal());
535			TRACE("ioctl: Creating cached block\n");
536			CachedBlock cached(volume);
537			uint32 blocksPerGroup = volume->BlocksPerGroup();
538			uint32 blockSize  = volume->BlockSize();
539			uint32 firstBlock = volume->FirstDataBlock();
540			fsblock_t start = 0;
541			uint32 group = 0;
542			uint32 length;
543
544			TRACE("ioctl: blocks per group: %" B_PRIu32 ", block size: %"
545				B_PRIu32 ", first block: %" B_PRIu32 ", start: %" B_PRIu64
546				", group: %" B_PRIu32 "\n", blocksPerGroup,
547				blockSize, firstBlock, start, group);
548
549			while (volume->AllocateBlocks(transaction, 1, 2048, group, start,
550					length) == B_OK) {
551				TRACE("ioctl: Allocated blocks in group %" B_PRIu32 ": %"
552					B_PRIu64 "-%" B_PRIu64 "\n", group, start, start + length);
553				off_t blockNum = start + group * blocksPerGroup - firstBlock;
554
555				for (uint32 i = 0; i < length; ++i) {
556					uint8* block = cached.SetToWritable(transaction, blockNum);
557					memset(block, 0, blockSize);
558					blockNum++;
559				}
560
561				TRACE("ioctl: Blocks cleared\n");
562
563				transaction.Done();
564				transaction.Start(volume->GetJournal());
565			}
566
567			TRACE("ioctl: Done\n");
568
569			return B_OK;
570		}
571	}
572
573	return B_DEV_INVALID_IOCTL;
574}
575
576
577static status_t
578ext2_fsync(fs_volume* _volume, fs_vnode* _node)
579{
580	Inode* inode = (Inode*)_node->private_node;
581	return inode->Sync();
582}
583
584
585static status_t
586ext2_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat)
587{
588	Inode* inode = (Inode*)_node->private_node;
589	const ext2_inode& node = inode->Node();
590
591	stat->st_dev = inode->GetVolume()->ID();
592	stat->st_ino = inode->ID();
593	stat->st_nlink = node.NumLinks();
594	stat->st_blksize = EXT2_IO_SIZE;
595
596	stat->st_uid = node.UserID();
597	stat->st_gid = node.GroupID();
598	stat->st_mode = node.Mode();
599	stat->st_type = 0;
600
601	inode->GetAccessTime(&stat->st_atim);
602	inode->GetModificationTime(&stat->st_mtim);
603	inode->GetChangeTime(&stat->st_ctim);
604	inode->GetCreationTime(&stat->st_crtim);
605
606	stat->st_size = inode->Size();
607	stat->st_blocks = (inode->Size() + 511) / 512;
608
609	return B_OK;
610}
611
612
613status_t
614ext2_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
615	uint32 mask)
616{
617	TRACE("ext2_write_stat\n");
618	Volume* volume = (Volume*)_volume->private_volume;
619
620	if (volume->IsReadOnly())
621		return B_READ_ONLY_DEVICE;
622
623	Inode* inode = (Inode*)_node->private_node;
624
625	ext2_inode& node = inode->Node();
626	bool updateTime = false;
627	uid_t uid = geteuid();
628
629	bool isOwnerOrRoot = uid == 0 || uid == (uid_t)node.UserID();
630	bool hasWriteAccess = inode->CheckPermissions(W_OK) == B_OK;
631
632	TRACE("ext2_write_stat: Starting transaction\n");
633	Transaction transaction(volume->GetJournal());
634	inode->WriteLockInTransaction(transaction);
635
636	if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) {
637		if (inode->IsDirectory())
638			return B_IS_A_DIRECTORY;
639		if (!inode->IsFile())
640			return B_BAD_VALUE;
641		if (!hasWriteAccess)
642			return B_NOT_ALLOWED;
643
644		TRACE("ext2_write_stat: Old size: %ld, new size: %ld\n",
645			(long)inode->Size(), (long)stat->st_size);
646
647		off_t oldSize = inode->Size();
648
649		status_t status = inode->Resize(transaction, stat->st_size);
650		if (status != B_OK)
651			return status;
652
653		if ((mask & B_STAT_SIZE_INSECURE) == 0) {
654			rw_lock_write_unlock(inode->Lock());
655			inode->FillGapWithZeros(oldSize, inode->Size());
656			rw_lock_write_lock(inode->Lock());
657		}
658
659		updateTime = true;
660	}
661
662	if ((mask & B_STAT_MODE) != 0) {
663		// only the user or root can do that
664		if (!isOwnerOrRoot)
665			return B_NOT_ALLOWED;
666		node.UpdateMode(stat->st_mode, S_IUMSK);
667		updateTime = true;
668	}
669
670	if ((mask & B_STAT_UID) != 0) {
671		// only root should be allowed
672		if (uid != 0)
673			return B_NOT_ALLOWED;
674		node.SetUserID(stat->st_uid);
675		updateTime = true;
676	}
677
678	if ((mask & B_STAT_GID) != 0) {
679		// only the user or root can do that
680		if (!isOwnerOrRoot)
681			return B_NOT_ALLOWED;
682		node.SetGroupID(stat->st_gid);
683		updateTime = true;
684	}
685
686	if ((mask & B_STAT_MODIFICATION_TIME) != 0 || updateTime
687		|| (mask & B_STAT_CHANGE_TIME) != 0) {
688		// the user or root can do that or any user with write access
689		if (!isOwnerOrRoot && !hasWriteAccess)
690			return B_NOT_ALLOWED;
691		struct timespec newTimespec = { 0, 0};
692
693		if ((mask & B_STAT_MODIFICATION_TIME) != 0)
694			newTimespec = stat->st_mtim;
695
696		if ((mask & B_STAT_CHANGE_TIME) != 0
697			&& stat->st_ctim.tv_sec > newTimespec.tv_sec)
698			newTimespec = stat->st_ctim;
699
700		if (newTimespec.tv_sec == 0)
701			Inode::_BigtimeToTimespec(real_time_clock_usecs(), &newTimespec);
702
703		inode->SetModificationTime(&newTimespec);
704	}
705	if ((mask & B_STAT_CREATION_TIME) != 0) {
706		// the user or root can do that or any user with write access
707		if (!isOwnerOrRoot && !hasWriteAccess)
708			return B_NOT_ALLOWED;
709		inode->SetCreationTime(&stat->st_crtim);
710	}
711
712	status_t status = inode->WriteBack(transaction);
713	if (status == B_OK)
714		status = transaction.Done();
715	if (status == B_OK)
716		notify_stat_changed(volume->ID(), -1, inode->ID(), mask);
717
718	return status;
719}
720
721
722static status_t
723ext2_create(fs_volume* _volume, fs_vnode* _directory, const char* name,
724	int openMode, int mode, void** _cookie, ino_t* _vnodeID)
725{
726	Volume* volume = (Volume*)_volume->private_volume;
727	Inode* directory = (Inode*)_directory->private_node;
728
729	TRACE("ext2_create()\n");
730
731	if (volume->IsReadOnly())
732		return B_READ_ONLY_DEVICE;
733
734	if (!directory->IsDirectory())
735		return B_BAD_TYPE;
736
737	TRACE("ext2_create(): Creating cookie\n");
738
739	// Allocate cookie
740	file_cookie* cookie = new(std::nothrow) file_cookie;
741	if (cookie == NULL)
742		return B_NO_MEMORY;
743	ObjectDeleter<file_cookie> cookieDeleter(cookie);
744
745	cookie->open_mode = openMode;
746	cookie->last_size = 0;
747	cookie->last_notification = system_time();
748
749	TRACE("ext2_create(): Starting transaction\n");
750
751	Transaction transaction(volume->GetJournal());
752
753	TRACE("ext2_create(): Creating inode\n");
754
755	Inode* inode;
756	bool created;
757	status_t status = Inode::Create(transaction, directory, name,
758		S_FILE | (mode & S_IUMSK), openMode, EXT2_TYPE_FILE, &created, _vnodeID,
759		&inode, &gExt2VnodeOps);
760	if (status != B_OK)
761		return status;
762
763	TRACE("ext2_create(): Created inode\n");
764
765	if ((openMode & O_NOCACHE) != 0) {
766		status = inode->DisableFileCache();
767		if (status != B_OK)
768			return status;
769	}
770
771	entry_cache_add(volume->ID(), directory->ID(), name, *_vnodeID);
772
773	status = transaction.Done();
774	if (status != B_OK) {
775		entry_cache_remove(volume->ID(), directory->ID(), name);
776		return status;
777	}
778
779	*_cookie = cookie;
780	cookieDeleter.Detach();
781
782	if (created)
783		notify_entry_created(volume->ID(), directory->ID(), name, *_vnodeID);
784
785	return B_OK;
786}
787
788
789static status_t
790ext2_create_symlink(fs_volume* _volume, fs_vnode* _directory, const char* name,
791	const char* path, int mode)
792{
793	TRACE("ext2_create_symlink()\n");
794
795	Volume* volume = (Volume*)_volume->private_volume;
796	Inode* directory = (Inode*)_directory->private_node;
797
798	if (volume->IsReadOnly())
799		return B_READ_ONLY_DEVICE;
800
801	if (!directory->IsDirectory())
802		return B_BAD_TYPE;
803
804	status_t status = directory->CheckPermissions(W_OK);
805	if (status != B_OK)
806		return status;
807
808	TRACE("ext2_create_symlink(): Starting transaction\n");
809	Transaction transaction(volume->GetJournal());
810
811	Inode* link;
812	ino_t id;
813	status = Inode::Create(transaction, directory, name, S_SYMLINK | 0777,
814		0, (uint8)EXT2_TYPE_SYMLINK, NULL, &id, &link);
815	if (status != B_OK)
816		return status;
817
818	// TODO: We have to prepare the link before publishing?
819
820	size_t length = strlen(path);
821	TRACE("ext2_create_symlink(): Path (%s) length: %d\n", path, (int)length);
822	if (length < EXT2_SHORT_SYMLINK_LENGTH) {
823		strcpy(link->Node().symlink, path);
824		link->Node().SetSize((uint32)length);
825	} else {
826		if (!link->HasFileCache()) {
827			status = link->CreateFileCache();
828			if (status != B_OK)
829				return status;
830		}
831
832		size_t written = length;
833		status = link->WriteAt(transaction, 0, (const uint8*)path, &written);
834		if (status == B_OK && written != length)
835			status = B_IO_ERROR;
836	}
837
838	if (status == B_OK)
839		status = link->WriteBack(transaction);
840
841	TRACE("ext2_create_symlink(): Publishing vnode\n");
842	publish_vnode(volume->FSVolume(), id, link, &gExt2VnodeOps,
843		link->Mode(), 0);
844	put_vnode(volume->FSVolume(), id);
845
846	if (status == B_OK) {
847		entry_cache_add(volume->ID(), directory->ID(), name, id);
848
849		status = transaction.Done();
850		if (status == B_OK)
851			notify_entry_created(volume->ID(), directory->ID(), name, id);
852		else
853			entry_cache_remove(volume->ID(), directory->ID(), name);
854	}
855	TRACE("ext2_create_symlink(): Done\n");
856
857	return status;
858}
859
860
861static status_t
862ext2_link(fs_volume* volume, fs_vnode* dir, const char* name, fs_vnode* node)
863{
864	// TODO
865
866	return B_UNSUPPORTED;
867}
868
869
870static status_t
871ext2_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name)
872{
873	TRACE("ext2_unlink()\n");
874	if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
875		return B_NOT_ALLOWED;
876
877	Volume* volume = (Volume*)_volume->private_volume;
878	Inode* directory = (Inode*)_directory->private_node;
879
880	status_t status = directory->CheckPermissions(W_OK);
881	if (status != B_OK)
882		return status;
883
884	TRACE("ext2_unlink(): Starting transaction\n");
885	Transaction transaction(volume->GetJournal());
886
887	directory->WriteLockInTransaction(transaction);
888
889	TRACE("ext2_unlink(): Looking up for directory entry\n");
890	HTree htree(volume, directory);
891	DirectoryIterator* directoryIterator;
892
893	status = htree.Lookup(name, &directoryIterator);
894	if (status != B_OK)
895		return status;
896
897	ino_t id;
898	status = directoryIterator->FindEntry(name, &id);
899	if (status != B_OK)
900		return status;
901
902	{
903		Vnode vnode(volume, id);
904		Inode* inode;
905		status = vnode.Get(&inode);
906		if (status != B_OK)
907			return status;
908
909		inode->WriteLockInTransaction(transaction);
910
911		status = inode->Unlink(transaction);
912		if (status != B_OK)
913			return status;
914
915		status = directoryIterator->RemoveEntry(transaction);
916		if (status != B_OK)
917			return status;
918	}
919	entry_cache_remove(volume->ID(), directory->ID(), name);
920
921	status = transaction.Done();
922	if (status != B_OK)
923		entry_cache_add(volume->ID(), directory->ID(), name, id);
924	else
925		notify_entry_removed(volume->ID(), directory->ID(), name, id);
926
927	return status;
928}
929
930
931static status_t
932ext2_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName,
933	fs_vnode* _newDir, const char* newName)
934{
935	TRACE("ext2_rename()\n");
936
937	Volume* volume = (Volume*)_volume->private_volume;
938	Inode* oldDirectory = (Inode*)_oldDir->private_node;
939	Inode* newDirectory = (Inode*)_newDir->private_node;
940
941	if (oldDirectory == newDirectory && strcmp(oldName, newName) == 0)
942		return B_OK;
943
944	Transaction transaction(volume->GetJournal());
945
946	oldDirectory->WriteLockInTransaction(transaction);
947	if (oldDirectory != newDirectory)
948		newDirectory->WriteLockInTransaction(transaction);
949
950	status_t status = oldDirectory->CheckPermissions(W_OK);
951	if (status == B_OK)
952		status = newDirectory->CheckPermissions(W_OK);
953	if (status != B_OK)
954		return status;
955
956	HTree oldHTree(volume, oldDirectory);
957	DirectoryIterator* oldIterator;
958
959	status = oldHTree.Lookup(oldName, &oldIterator);
960	if (status != B_OK)
961		return status;
962
963	ObjectDeleter<DirectoryIterator> oldIteratorDeleter(oldIterator);
964
965	ino_t oldID;
966	status = oldIterator->FindEntry(oldName, &oldID);
967	if (status != B_OK)
968		return status;
969
970	TRACE("ext2_rename(): found entry to rename\n");
971
972	if (oldDirectory != newDirectory) {
973		TRACE("ext2_rename(): Different parent directories\n");
974		CachedBlock cached(volume);
975
976		ino_t parentID = newDirectory->ID();
977		ino_t oldDirID = oldDirectory->ID();
978
979		do {
980			Vnode vnode(volume, parentID);
981			Inode* parent;
982
983			status = vnode.Get(&parent);
984			if (status != B_OK)
985				return B_IO_ERROR;
986
987			fsblock_t blockNum;
988			status = parent->FindBlock(0, blockNum);
989			if (status != B_OK)
990				return status;
991
992			const HTreeRoot* data = (const HTreeRoot*)cached.SetTo(blockNum);
993			parentID = data->dotdot.InodeID();
994		} while (parentID != oldID && parentID != oldDirID
995			&& parentID != EXT2_ROOT_NODE);
996
997		if (parentID == oldID)
998			return B_BAD_VALUE;
999	}
1000
1001	HTree newHTree(volume, newDirectory);
1002	DirectoryIterator* newIterator;
1003
1004	status = newHTree.Lookup(newName, &newIterator);
1005	if (status != B_OK)
1006		return status;
1007
1008	TRACE("ext2_rename(): found new directory\n");
1009
1010	ObjectDeleter<DirectoryIterator> newIteratorDeleter(newIterator);
1011
1012	Vnode vnode(volume, oldID);
1013	Inode* inode;
1014
1015	status = vnode.Get(&inode);
1016	if (status != B_OK)
1017		return status;
1018
1019	uint8 fileType;
1020
1021	// TODO: Support all file types?
1022	if (inode->IsDirectory())
1023		fileType = EXT2_TYPE_DIRECTORY;
1024	else if (inode->IsSymLink())
1025		fileType = EXT2_TYPE_SYMLINK;
1026	else
1027		fileType = EXT2_TYPE_FILE;
1028
1029	// Add entry in destination directory
1030	ino_t existentID;
1031	status = newIterator->FindEntry(newName, &existentID);
1032	if (status == B_OK) {
1033		TRACE("ext2_rename(): found existing new entry\n");
1034		if (existentID == oldID) {
1035			// Remove entry in oldID
1036			// return inode->Unlink();
1037			return B_BAD_VALUE;
1038		}
1039
1040		Vnode vnodeExistent(volume, existentID);
1041		Inode* existent;
1042
1043		if (vnodeExistent.Get(&existent) != B_OK)
1044			return B_NAME_IN_USE;
1045
1046		if (existent->IsDirectory() != inode->IsDirectory()) {
1047			return existent->IsDirectory() ? B_IS_A_DIRECTORY
1048				: B_NOT_A_DIRECTORY;
1049		}
1050
1051		// TODO: Perhaps we have to revert this in case of error?
1052		status = newIterator->ChangeEntry(transaction, oldID, fileType);
1053		if (status != B_OK)
1054			return status;
1055
1056		notify_entry_removed(volume->ID(), newDirectory->ID(), newName,
1057			existentID);
1058	} else if (status == B_ENTRY_NOT_FOUND) {
1059		newIterator->Restart();
1060
1061		status = newIterator->AddEntry(transaction, newName, strlen(newName),
1062			oldID, fileType);
1063		if (status != B_OK)
1064			return status;
1065
1066	} else
1067		return status;
1068
1069	if (oldDirectory == newDirectory) {
1070		status = oldHTree.Lookup(oldName, &oldIterator);
1071		if (status != B_OK)
1072			return status;
1073
1074		oldIteratorDeleter.SetTo(oldIterator);
1075		status = oldIterator->FindEntry(oldName, &oldID);
1076		if (status != B_OK)
1077			return status;
1078	}
1079
1080	// Remove entry from source folder
1081	status = oldIterator->RemoveEntry(transaction);
1082	if (status != B_OK)
1083		return status;
1084
1085	inode->WriteLockInTransaction(transaction);
1086
1087	if (oldDirectory != newDirectory && inode->IsDirectory()) {
1088		DirectoryIterator inodeIterator(inode);
1089
1090		status = inodeIterator.FindEntry("..");
1091		if (status == B_ENTRY_NOT_FOUND) {
1092			ERROR("Corrupt file system. Missing \"..\" in directory %"
1093				B_PRIdINO "\n", inode->ID());
1094			return B_BAD_DATA;
1095		} else if (status != B_OK)
1096			return status;
1097
1098		inodeIterator.ChangeEntry(transaction, newDirectory->ID(),
1099			(uint8)EXT2_TYPE_DIRECTORY);
1100		// Decrement hardlink count on the source folder
1101		status = oldDirectory->Unlink(transaction);
1102		if (status != B_OK)
1103			ERROR("Error while decrementing hardlink count on the source folder\n");
1104		// Increment hardlink count on the destination folder
1105		newDirectory->IncrementNumLinks(transaction);
1106		status = newDirectory->WriteBack(transaction);
1107		if (status != B_OK)
1108			ERROR("Error while writing back the destination folder inode\n");
1109	}
1110
1111	status = inode->WriteBack(transaction);
1112	if (status != B_OK)
1113		return status;
1114
1115	entry_cache_remove(volume->ID(), oldDirectory->ID(), oldName);
1116	entry_cache_add(volume->ID(), newDirectory->ID(), newName, oldID);
1117
1118	status = transaction.Done();
1119	if (status != B_OK) {
1120		entry_cache_remove(volume->ID(), oldDirectory->ID(), newName);
1121		entry_cache_add(volume->ID(), newDirectory->ID(), oldName, oldID);
1122
1123		return status;
1124	}
1125
1126	notify_entry_moved(volume->ID(), oldDirectory->ID(), oldName,
1127		newDirectory->ID(), newName, oldID);
1128
1129	return B_OK;
1130}
1131
1132
1133static status_t
1134ext2_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie)
1135{
1136	Volume* volume = (Volume*)_volume->private_volume;
1137	Inode* inode = (Inode*)_node->private_node;
1138
1139	// opening a directory read-only is allowed, although you can't read
1140	// any data from it.
1141	if (inode->IsDirectory() && (openMode & O_RWMASK) != 0)
1142		return B_IS_A_DIRECTORY;
1143
1144	status_t status =  inode->CheckPermissions(open_mode_to_access(openMode)
1145		| (openMode & O_TRUNC ? W_OK : 0));
1146	if (status != B_OK)
1147		return status;
1148
1149	// Prepare the cookie
1150	file_cookie* cookie = new(std::nothrow) file_cookie;
1151	if (cookie == NULL)
1152		return B_NO_MEMORY;
1153	ObjectDeleter<file_cookie> cookieDeleter(cookie);
1154
1155	cookie->open_mode = openMode & EXT2_OPEN_MODE_USER_MASK;
1156	cookie->last_size = inode->Size();
1157	cookie->last_notification = system_time();
1158
1159	MethodDeleter<Inode, status_t> fileCacheEnabler(&Inode::EnableFileCache);
1160	if ((openMode & O_NOCACHE) != 0) {
1161		status = inode->DisableFileCache();
1162		if (status != B_OK)
1163			return status;
1164		fileCacheEnabler.SetTo(inode);
1165	}
1166
1167	// Should we truncate the file?
1168	if ((openMode & O_TRUNC) != 0) {
1169		if ((openMode & O_RWMASK) == O_RDONLY)
1170			return B_NOT_ALLOWED;
1171
1172		Transaction transaction(volume->GetJournal());
1173		inode->WriteLockInTransaction(transaction);
1174
1175		status_t status = inode->Resize(transaction, 0);
1176		if (status == B_OK)
1177			status = inode->WriteBack(transaction);
1178		if (status == B_OK)
1179			status = transaction.Done();
1180		if (status != B_OK)
1181			return status;
1182
1183		// TODO: No need to notify file size changed?
1184	}
1185
1186	fileCacheEnabler.Detach();
1187	cookieDeleter.Detach();
1188	*_cookie = cookie;
1189
1190	return B_OK;
1191}
1192
1193
1194static status_t
1195ext2_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
1196	void* buffer, size_t* _length)
1197{
1198	Inode* inode = (Inode*)_node->private_node;
1199
1200	if (!inode->IsFile()) {
1201		*_length = 0;
1202		return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE;
1203	}
1204
1205	return inode->ReadAt(pos, (uint8*)buffer, _length);
1206}
1207
1208
1209static status_t
1210ext2_write(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
1211	const void* buffer, size_t* _length)
1212{
1213	TRACE("ext2_write()\n");
1214	Volume* volume = (Volume*)_volume->private_volume;
1215	Inode* inode = (Inode*)_node->private_node;
1216
1217	if (volume->IsReadOnly())
1218		return B_READ_ONLY_DEVICE;
1219
1220	if (inode->IsDirectory()) {
1221		*_length = 0;
1222		return B_IS_A_DIRECTORY;
1223	}
1224
1225	TRACE("ext2_write(): Preparing cookie\n");
1226
1227	file_cookie* cookie = (file_cookie*)_cookie;
1228
1229	if ((cookie->open_mode & O_APPEND) != 0)
1230		pos = inode->Size();
1231
1232	TRACE("ext2_write(): Creating transaction\n");
1233	Transaction transaction;
1234
1235	status_t status = inode->WriteAt(transaction, pos, (const uint8*)buffer,
1236		_length);
1237	if (status == B_OK)
1238		status = transaction.Done();
1239	if (status == B_OK) {
1240		TRACE("ext2_write(): Finalizing\n");
1241		ReadLocker lock(*inode->Lock());
1242
1243		if (cookie->last_size != inode->Size()
1244			&& system_time() > cookie->last_notification
1245				+ INODE_NOTIFICATION_INTERVAL) {
1246			notify_stat_changed(volume->ID(), -1, inode->ID(),
1247				B_STAT_MODIFICATION_TIME | B_STAT_SIZE | B_STAT_INTERIM_UPDATE);
1248			cookie->last_size = inode->Size();
1249			cookie->last_notification = system_time();
1250		}
1251	}
1252
1253	TRACE("ext2_write(): Done\n");
1254
1255	return status;
1256}
1257
1258
1259static status_t
1260ext2_close(fs_volume *_volume, fs_vnode *_node, void *_cookie)
1261{
1262	return B_OK;
1263}
1264
1265
1266static status_t
1267ext2_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1268{
1269	file_cookie* cookie = (file_cookie*)_cookie;
1270	Volume* volume = (Volume*)_volume->private_volume;
1271	Inode* inode = (Inode*)_node->private_node;
1272
1273	if (inode->Size() != cookie->last_size)
1274		notify_stat_changed(volume->ID(), -1, inode->ID(), B_STAT_SIZE);
1275
1276	if ((cookie->open_mode & O_NOCACHE) != 0)
1277		inode->EnableFileCache();
1278
1279	delete cookie;
1280	return B_OK;
1281}
1282
1283
1284static status_t
1285ext2_access(fs_volume* _volume, fs_vnode* _node, int accessMode)
1286{
1287	Inode* inode = (Inode*)_node->private_node;
1288	return inode->CheckPermissions(accessMode);
1289}
1290
1291
1292static status_t
1293ext2_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer,
1294	size_t *_bufferSize)
1295{
1296	Inode* inode = (Inode*)_node->private_node;
1297
1298	if (!inode->IsSymLink())
1299		return B_BAD_VALUE;
1300
1301	if (inode->Size() < (off_t)*_bufferSize)
1302		*_bufferSize = inode->Size();
1303
1304	if (inode->Size() > EXT2_SHORT_SYMLINK_LENGTH)
1305		return inode->ReadAt(0, (uint8 *)buffer, _bufferSize);
1306
1307	memcpy(buffer, inode->Node().symlink, *_bufferSize);
1308	return B_OK;
1309}
1310
1311
1312//	#pragma mark - Directory functions
1313
1314
1315static status_t
1316ext2_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name,
1317	int mode)
1318{
1319	TRACE("ext2_create_dir()\n");
1320	Volume* volume = (Volume*)_volume->private_volume;
1321	Inode* directory = (Inode*)_directory->private_node;
1322
1323	if (volume->IsReadOnly())
1324		return B_READ_ONLY_DEVICE;
1325
1326	if (!directory->IsDirectory())
1327		return B_BAD_TYPE;
1328
1329	status_t status = directory->CheckPermissions(W_OK);
1330	if (status != B_OK)
1331		return status;
1332
1333	TRACE("ext2_create_dir(): Starting transaction\n");
1334	Transaction transaction(volume->GetJournal());
1335
1336	ino_t id;
1337	status = Inode::Create(transaction, directory, name,
1338		S_DIRECTORY | (mode & S_IUMSK), 0, EXT2_TYPE_DIRECTORY, NULL, &id);
1339	if (status != B_OK)
1340		return status;
1341
1342	put_vnode(volume->FSVolume(), id);
1343
1344	entry_cache_add(volume->ID(), directory->ID(), name, id);
1345
1346	status = transaction.Done();
1347	if (status != B_OK) {
1348		entry_cache_remove(volume->ID(), directory->ID(), name);
1349		return status;
1350	}
1351
1352	notify_entry_created(volume->ID(), directory->ID(), name, id);
1353
1354	TRACE("ext2_create_dir(): Done\n");
1355
1356	return B_OK;
1357}
1358
1359
1360static status_t
1361ext2_remove_dir(fs_volume* _volume, fs_vnode* _directory, const char* name)
1362{
1363	TRACE("ext2_remove_dir()\n");
1364
1365	Volume* volume = (Volume*)_volume->private_volume;
1366	Inode* directory = (Inode*)_directory->private_node;
1367
1368	status_t status = directory->CheckPermissions(W_OK);
1369	if (status != B_OK)
1370		return status;
1371
1372	TRACE("ext2_remove_dir(): Starting transaction\n");
1373	Transaction transaction(volume->GetJournal());
1374
1375	directory->WriteLockInTransaction(transaction);
1376
1377	TRACE("ext2_remove_dir(): Looking up for directory entry\n");
1378	HTree htree(volume, directory);
1379	DirectoryIterator* directoryIterator;
1380
1381	status = htree.Lookup(name, &directoryIterator);
1382	if (status != B_OK)
1383		return status;
1384
1385	ino_t id;
1386	status = directoryIterator->FindEntry(name, &id);
1387	if (status != B_OK)
1388		return status;
1389
1390	Vnode vnode(volume, id);
1391	Inode* inode;
1392	status = vnode.Get(&inode);
1393	if (status != B_OK)
1394		return status;
1395
1396	inode->WriteLockInTransaction(transaction);
1397
1398	status = inode->Unlink(transaction);
1399	if (status != B_OK)
1400		return status;
1401
1402	status = directory->Unlink(transaction);
1403	if (status != B_OK)
1404		return status;
1405
1406	status = directoryIterator->RemoveEntry(transaction);
1407	if (status != B_OK)
1408		return status;
1409
1410	entry_cache_remove(volume->ID(), directory->ID(), name);
1411	entry_cache_remove(volume->ID(), id, "..");
1412
1413	status = transaction.Done();
1414	if (status != B_OK) {
1415		entry_cache_add(volume->ID(), directory->ID(), name, id);
1416		entry_cache_add(volume->ID(), id, "..", id);
1417	} else
1418		notify_entry_removed(volume->ID(), directory->ID(), name, id);
1419
1420	return status;
1421}
1422
1423
1424static status_t
1425ext2_open_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie)
1426{
1427	Inode* inode = (Inode*)_node->private_node;
1428	status_t status = inode->CheckPermissions(R_OK);
1429	if (status < B_OK)
1430		return status;
1431
1432	if (!inode->IsDirectory())
1433		return B_NOT_A_DIRECTORY;
1434
1435	DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode);
1436	if (iterator == NULL)
1437		return B_NO_MEMORY;
1438
1439	*_cookie = iterator;
1440	return B_OK;
1441}
1442
1443
1444static status_t
1445ext2_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie,
1446	struct dirent *dirent, size_t bufferSize, uint32 *_num)
1447{
1448	DirectoryIterator *iterator = (DirectoryIterator *)_cookie;
1449	Volume* volume = (Volume*)_volume->private_volume;
1450
1451	uint32 maxCount = *_num;
1452	uint32 count = 0;
1453
1454	while (count < maxCount && bufferSize > sizeof(struct dirent)) {
1455
1456		size_t length = bufferSize - sizeof(struct dirent) + 1;
1457		ino_t id;
1458
1459		status_t status = iterator->GetNext(dirent->d_name, &length, &id);
1460		if (status == B_ENTRY_NOT_FOUND)
1461			break;
1462
1463		if (status == B_BUFFER_OVERFLOW) {
1464			// the remaining name buffer length was too small
1465			if (count == 0)
1466				return B_BUFFER_OVERFLOW;
1467			break;
1468		}
1469
1470		if (status != B_OK)
1471			return status;
1472
1473		status = iterator->Next();
1474		if (status != B_OK && status != B_ENTRY_NOT_FOUND)
1475			return status;
1476
1477		dirent->d_dev = volume->ID();
1478		dirent->d_ino = id;
1479		dirent->d_reclen = sizeof(struct dirent) + length;
1480
1481		bufferSize -= dirent->d_reclen;
1482		dirent = (struct dirent*)((uint8*)dirent + dirent->d_reclen);
1483		count++;
1484	}
1485
1486	*_num = count;
1487	return B_OK;
1488}
1489
1490
1491static status_t
1492ext2_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie)
1493{
1494	DirectoryIterator *iterator = (DirectoryIterator *)_cookie;
1495	return iterator->Rewind();
1496}
1497
1498
1499static status_t
1500ext2_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void * /*_cookie*/)
1501{
1502	return B_OK;
1503}
1504
1505
1506static status_t
1507ext2_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
1508{
1509	delete (DirectoryIterator*)_cookie;
1510	return B_OK;
1511}
1512
1513
1514static status_t
1515ext2_open_attr_dir(fs_volume *_volume, fs_vnode *_node, void **_cookie)
1516{
1517	Inode* inode = (Inode*)_node->private_node;
1518	Volume* volume = (Volume*)_volume->private_volume;
1519	TRACE("%s()\n", __FUNCTION__);
1520
1521	if (!(volume->SuperBlock().CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR))
1522		return ENOSYS;
1523
1524	// on directories too ?
1525	if (!inode->IsFile())
1526		return EINVAL;
1527
1528	int32 *index = new(std::nothrow) int32;
1529	if (index == NULL)
1530		return B_NO_MEMORY;
1531	*index = 0;
1532	*(int32**)_cookie = index;
1533	return B_OK;
1534}
1535
1536static status_t
1537ext2_close_attr_dir(fs_volume* _volume, fs_vnode* _node, void* cookie)
1538{
1539	TRACE("%s()\n", __FUNCTION__);
1540	return B_OK;
1541}
1542
1543
1544static status_t
1545ext2_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1546{
1547	TRACE("%s()\n", __FUNCTION__);
1548	delete (int32 *)_cookie;
1549	return B_OK;
1550}
1551
1552
1553static status_t
1554ext2_read_attr_dir(fs_volume* _volume, fs_vnode* _node,
1555				void* _cookie, struct dirent* dirent, size_t bufferSize,
1556				uint32* _num)
1557{
1558	Inode* inode = (Inode*)_node->private_node;
1559	int32 index = *(int32 *)_cookie;
1560	Attribute attribute(inode);
1561	TRACE("%s()\n", __FUNCTION__);
1562
1563	size_t length = bufferSize;
1564	status_t status = attribute.Find(index);
1565	if (status == B_ENTRY_NOT_FOUND) {
1566		*_num = 0;
1567		return B_OK;
1568	} else if (status != B_OK)
1569		return status;
1570
1571	status = attribute.GetName(dirent->d_name, &length);
1572	if (status != B_OK)
1573		return B_OK;
1574
1575	Volume* volume = (Volume*)_volume->private_volume;
1576
1577	dirent->d_dev = volume->ID();
1578	dirent->d_ino = inode->ID();
1579	dirent->d_reclen = sizeof(struct dirent) + length;
1580
1581	*_num = 1;
1582	*(int32*)_cookie = index + 1;
1583	return B_OK;
1584}
1585
1586
1587static status_t
1588ext2_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1589{
1590	*(int32*)_cookie = 0;
1591	TRACE("%s()\n", __FUNCTION__);
1592	return B_OK;
1593}
1594
1595
1596	/* attribute operations */
1597static status_t
1598ext2_create_attr(fs_volume* _volume, fs_vnode* _node,
1599	const char* name, uint32 type, int openMode, void** _cookie)
1600{
1601	return EROFS;
1602}
1603
1604
1605static status_t
1606ext2_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name,
1607	int openMode, void** _cookie)
1608{
1609	TRACE("%s()\n", __FUNCTION__);
1610
1611	Volume* volume = (Volume*)_volume->private_volume;
1612	Inode* inode = (Inode*)_node->private_node;
1613	Attribute attribute(inode);
1614
1615	if (!(volume->SuperBlock().CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR))
1616		return ENOSYS;
1617
1618	return attribute.Open(name, openMode, (attr_cookie**)_cookie);
1619}
1620
1621
1622static status_t
1623ext2_close_attr(fs_volume* _volume, fs_vnode* _node,
1624	void* cookie)
1625{
1626	return B_OK;
1627}
1628
1629
1630static status_t
1631ext2_free_attr_cookie(fs_volume* _volume, fs_vnode* _node,
1632	void* cookie)
1633{
1634	delete (attr_cookie*)cookie;
1635	return B_OK;
1636}
1637
1638
1639static status_t
1640ext2_read_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie,
1641	off_t pos, void* buffer, size_t* _length)
1642{
1643	TRACE("%s()\n", __FUNCTION__);
1644
1645	attr_cookie* cookie = (attr_cookie*)_cookie;
1646	Inode* inode = (Inode*)_node->private_node;
1647
1648	Attribute attribute(inode, cookie);
1649
1650	return attribute.Read(cookie, pos, (uint8*)buffer, _length);
1651}
1652
1653
1654static status_t
1655ext2_write_attr(fs_volume* _volume, fs_vnode* _node, void* cookie,
1656	off_t pos, const void* buffer, size_t* length)
1657{
1658	return EROFS;
1659}
1660
1661
1662
1663static status_t
1664ext2_read_attr_stat(fs_volume* _volume, fs_vnode* _node,
1665	void* _cookie, struct stat* stat)
1666{
1667	attr_cookie* cookie = (attr_cookie*)_cookie;
1668	Inode* inode = (Inode*)_node->private_node;
1669
1670	Attribute attribute(inode, cookie);
1671
1672	return attribute.Stat(*stat);
1673}
1674
1675
1676static status_t
1677ext2_write_attr_stat(fs_volume* _volume, fs_vnode* _node,
1678	void* cookie, const struct stat* stat, int statMask)
1679{
1680	return EROFS;
1681}
1682
1683
1684static status_t
1685ext2_rename_attr(fs_volume* _volume, fs_vnode* fromVnode,
1686	const char* fromName, fs_vnode* toVnode, const char* toName)
1687{
1688	return ENOSYS;
1689}
1690
1691
1692static status_t
1693ext2_remove_attr(fs_volume* _volume, fs_vnode* vnode,
1694	const char* name)
1695{
1696	return ENOSYS;
1697}
1698
1699
1700fs_volume_ops gExt2VolumeOps = {
1701	&ext2_unmount,
1702	&ext2_read_fs_info,
1703	&ext2_write_fs_info,
1704	&ext2_sync,
1705	&ext2_get_vnode,
1706};
1707
1708
1709fs_vnode_ops gExt2VnodeOps = {
1710	/* vnode operations */
1711	&ext2_lookup,
1712	NULL,
1713	&ext2_put_vnode,
1714	&ext2_remove_vnode,
1715
1716	/* VM file access */
1717	&ext2_can_page,
1718	&ext2_read_pages,
1719	&ext2_write_pages,
1720
1721	NULL,	// io()
1722	NULL,	// cancel_io()
1723
1724	&ext2_get_file_map,
1725
1726	&ext2_ioctl,
1727	NULL,
1728	NULL,	// fs_select
1729	NULL,	// fs_deselect
1730	&ext2_fsync,
1731
1732	&ext2_read_link,
1733	&ext2_create_symlink,
1734
1735	&ext2_link,
1736	&ext2_unlink,
1737	&ext2_rename,
1738
1739	&ext2_access,
1740	&ext2_read_stat,
1741	&ext2_write_stat,
1742	NULL,	// fs_preallocate
1743
1744	/* file operations */
1745	&ext2_create,
1746	&ext2_open,
1747	&ext2_close,
1748	&ext2_free_cookie,
1749	&ext2_read,
1750	&ext2_write,
1751
1752	/* directory operations */
1753	&ext2_create_dir,
1754	&ext2_remove_dir,
1755	&ext2_open_dir,
1756	&ext2_close_dir,
1757	&ext2_free_dir_cookie,
1758	&ext2_read_dir,
1759	&ext2_rewind_dir,
1760
1761	/* attribute directory operations */
1762	&ext2_open_attr_dir,
1763	&ext2_close_attr_dir,
1764	&ext2_free_attr_dir_cookie,
1765	&ext2_read_attr_dir,
1766	&ext2_rewind_attr_dir,
1767
1768	/* attribute operations */
1769	NULL, //&ext2_create_attr,
1770	&ext2_open_attr,
1771	&ext2_close_attr,
1772	&ext2_free_attr_cookie,
1773	&ext2_read_attr,
1774	NULL, //&ext2_write_attr,
1775	&ext2_read_attr_stat,
1776	NULL, //&ext2_write_attr_stat,
1777	NULL, //&ext2_rename_attr,
1778	NULL, //&ext2_remove_attr,
1779};
1780
1781
1782static file_system_module_info sExt2FileSystem = {
1783	{
1784		"file_systems/ext2" B_CURRENT_FS_API_VERSION,
1785		0,
1786		NULL,
1787	},
1788
1789	"ext2",						// short_name
1790	"Ext2 File System",			// pretty_name
1791	B_DISK_SYSTEM_SUPPORTS_WRITING
1792		| B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME,	// DDM flags
1793
1794	// scanning
1795	ext2_identify_partition,
1796	ext2_scan_partition,
1797	ext2_free_identify_partition_cookie,
1798	NULL,	// free_partition_content_cookie()
1799
1800	&ext2_mount,
1801
1802	NULL,
1803};
1804
1805
1806module_info *modules[] = {
1807	(module_info *)&sExt2FileSystem,
1808	NULL,
1809};
1810