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