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