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