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