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