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