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