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