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