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