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