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