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