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