vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / kernel / file_systems / ext2 / kernel_interface.cpp
blob7c16d2b4348351e2d3ff8d4bfdc02e1599bbc7e0
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 */
8 #include <dirent.h>
9 #include <util/kernel_cpp.h>
10 #include <string.h>
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>
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"
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)
38 #define EXT2_IO_SIZE 65536
41 struct identify_cookie {
42 ext2_super_block super_block;
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)
51 Inode* inode = (Inode*)cookie;
53 return file_map_translate(inode->Map(), offset, size, vecs, _count,
54 inode->GetVolume()->BlockSize());
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)
63 Inode* inode = (Inode*)cookie;
64 rw_lock_read_unlock(inode->Lock());
65 return B_OK;
69 // #pragma mark - Scanning
72 static float
73 ext2_identify_partition(int fd, partition_data *partition, void **_cookie)
75 ext2_super_block superBlock;
76 status_t status = Volume::Identify(fd, &superBlock);
77 if (status != B_OK)
78 return -1;
80 identify_cookie *cookie = new identify_cookie;
81 memcpy(&cookie->super_block, &superBlock, sizeof(ext2_super_block));
83 *_cookie = cookie;
84 return 0.8f;
88 static status_t
89 ext2_scan_partition(int fd, partition_data *partition, void *_cookie)
91 identify_cookie *cookie = (identify_cookie *)_cookie;
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;
104 return B_OK;
108 static void
109 ext2_free_identify_partition_cookie(partition_data* partition, void* _cookie)
111 delete (identify_cookie*)_cookie;
115 // #pragma mark -
118 static status_t
119 ext2_mount(fs_volume* _volume, const char* device, uint32 flags,
120 const char* args, ino_t* _rootID)
122 Volume* volume = new(std::nothrow) Volume(_volume);
123 if (volume == NULL)
124 return B_NO_MEMORY;
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;
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;
140 *_rootID = volume->RootNode()->ID();
141 return B_OK;
145 static status_t
146 ext2_unmount(fs_volume *_volume)
148 Volume* volume = (Volume *)_volume->private_volume;
150 status_t status = volume->Unmount();
151 delete volume;
153 return status;
157 static status_t
158 ext2_read_fs_info(fs_volume* _volume, struct fs_info* info)
160 Volume* volume = (Volume*)_volume->private_volume;
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();
170 // Volume name
171 strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
173 // File system name
174 strlcpy(info->fsh_name, "ext2", sizeof(info->fsh_name));
176 return B_OK;
180 static status_t
181 ext2_write_fs_info(fs_volume* _volume, const struct fs_info* info, uint32 mask)
183 Volume* volume = (Volume*)_volume->private_volume;
185 if (volume->IsReadOnly())
186 return B_READ_ONLY_DEVICE;
188 MutexLocker locker(volume->Lock());
190 status_t status = B_BAD_VALUE;
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();
198 return status;
202 static status_t
203 ext2_sync(fs_volume* _volume)
205 Volume* volume = (Volume*)_volume->private_volume;
206 return volume->Sync();
210 // #pragma mark -
213 static status_t
214 ext2_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type,
215 uint32* _flags, bool reenter)
217 Volume* volume = (Volume*)_volume->private_volume;
219 if (id < 2 || id > volume->NumInodes()) {
220 ERROR("invalid inode id %" B_PRIdINO " requested!\n", id);
221 return B_BAD_VALUE;
224 Inode* inode = new(std::nothrow) Inode(volume, id);
225 if (inode == NULL)
226 return B_NO_MEMORY;
228 status_t status = inode->InitCheck();
229 if (status != B_OK)
230 delete inode;
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));
240 return status;
244 static status_t
245 ext2_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
247 delete (Inode*)_node->private_node;
248 return B_OK;
252 static status_t
253 ext2_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
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);
260 if (!inode->IsDeleted())
261 return B_OK;
263 TRACE("ext2_remove_vnode(): Starting transaction\n");
264 Transaction transaction(volume->GetJournal());
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;
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;
278 TRACE("ext2_remove_vnode(): Setting deletion time\n");
279 inode->Node().SetDeletionTime(real_time_clock());
281 status = inode->WriteBack(transaction);
282 if (status != B_OK)
283 return status;
285 TRACE("ext2_remove_vnode(): Freeing inode\n");
286 status = volume->FreeInode(transaction, inode->ID(), inode->IsDirectory());
288 // TODO: When Transaction::Done() fails, do we have to re-add the vnode?
289 if (status == B_OK)
290 status = transaction.Done();
292 return status;
296 static bool
297 ext2_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie)
299 return true;
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)
307 Volume* volume = (Volume*)_volume->private_volume;
308 Inode* inode = (Inode*)_node->private_node;
310 if (inode->FileCache() == NULL)
311 return B_BAD_VALUE;
313 rw_lock_read_lock(inode->Lock());
315 uint32 vecIndex = 0;
316 size_t vecOffset = 0;
317 size_t bytesLeft = *_numBytes;
318 status_t status;
320 while (true) {
321 file_io_vec fileVecs[8];
322 size_t fileVecCount = 8;
324 status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
325 &fileVecCount, 0);
326 if (status != B_OK && status != B_BUFFER_OVERFLOW)
327 break;
329 bool bufferOverflow = status == B_BUFFER_OVERFLOW;
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;
337 pos += bytes;
338 bytesLeft -= bytes;
341 rw_lock_read_unlock(inode->Lock());
343 return status;
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)
351 Volume* volume = (Volume*)_volume->private_volume;
352 Inode* inode = (Inode*)_node->private_node;
354 if (volume->IsReadOnly())
355 return B_READ_ONLY_DEVICE;
357 if (inode->FileCache() == NULL)
358 return B_BAD_VALUE;
360 rw_lock_read_lock(inode->Lock());
362 uint32 vecIndex = 0;
363 size_t vecOffset = 0;
364 size_t bytesLeft = *_numBytes;
365 status_t status;
367 while (true) {
368 file_io_vec fileVecs[8];
369 size_t fileVecCount = 8;
371 status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
372 &fileVecCount, 0);
373 if (status != B_OK && status != B_BUFFER_OVERFLOW)
374 break;
376 bool bufferOverflow = status == B_BUFFER_OVERFLOW;
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;
384 pos += bytes;
385 bytesLeft -= bytes;
388 rw_lock_read_unlock(inode->Lock());
390 return status;
394 static status_t
395 ext2_io(fs_volume* _volume, fs_vnode* _node, void* _cookie, io_request* request)
397 Volume* volume = (Volume*)_volume->private_volume;
398 Inode* inode = (Inode*)_node->private_node;
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;
405 #endif
407 if (inode->FileCache() == NULL) {
408 #ifndef EXT2_SHELL
409 notify_io_request(request, B_BAD_VALUE);
410 #endif
411 return B_BAD_VALUE;
414 // We lock the node here and will unlock it in the "finished" hook.
415 rw_lock_read_lock(inode->Lock());
417 return do_iterative_fd_io(volume->Device(), request,
418 iterative_io_get_vecs_hook, iterative_io_finished_hook, inode);
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)
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;
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;
438 if (block > volume->NumBlocks()) {
439 panic("ext2_get_file_map() found block %" B_PRIu64 " for offset %"
440 B_PRIdOFF "\n", block, offset);
443 off_t blockOffset = block << volume->BlockShift();
444 uint32 blockLength = volume->BlockSize() * count;
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;
457 // 'block' is 0 for sparse blocks
458 if (block != 0)
459 vecs[index].offset = blockOffset;
460 else
461 vecs[index].offset = -1;
463 vecs[index].length = blockLength;
464 index++;
467 offset += blockLength;
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;
476 size -= blockLength;
479 // can never get here
480 return B_ERROR;
484 // #pragma mark -
487 static status_t
488 ext2_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name,
489 ino_t* _vnodeID)
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;
496 // check access permissions
497 status_t status = directory->CheckPermissions(X_OK);
498 if (status < B_OK)
499 return status;
501 HTree htree(volume, directory);
502 DirectoryIterator* iterator;
504 status = htree.Lookup(name, &iterator);
505 if (status != B_OK)
506 return status;
508 ObjectDeleter<DirectoryIterator> iteratorDeleter(iterator);
510 status = iterator->FindEntry(name, _vnodeID);
511 if (status != B_OK)
512 return status;
514 return get_vnode(volume->FSVolume(), *_vnodeID, NULL);
518 static status_t
519 ext2_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd,
520 void* buffer, size_t bufferLength)
522 TRACE("ioctl: %" B_PRIu32 "\n", cmd);
524 Volume* volume = (Volume*)_volume->private_volume;
525 switch (cmd) {
526 case 56742:
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;
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);
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;
552 for (uint32 i = 0; i < length; ++i) {
553 uint8* block = cached.SetToWritable(transaction, blockNum);
554 memset(block, 0, blockSize);
555 blockNum++;
558 TRACE("ioctl: Blocks cleared\n");
560 transaction.Done();
561 transaction.Start(volume->GetJournal());
564 TRACE("ioctl: Done\n");
566 return B_OK;
570 return B_DEV_INVALID_IOCTL;
574 static status_t
575 ext2_fsync(fs_volume* _volume, fs_vnode* _node)
577 Inode* inode = (Inode*)_node->private_node;
578 return inode->Sync();
582 static status_t
583 ext2_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat)
585 Inode* inode = (Inode*)_node->private_node;
586 const ext2_inode& node = inode->Node();
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;
593 stat->st_uid = node.UserID();
594 stat->st_gid = node.GroupID();
595 stat->st_mode = node.Mode();
596 stat->st_type = 0;
598 inode->GetAccessTime(&stat->st_atim);
599 inode->GetModificationTime(&stat->st_mtim);
600 inode->GetChangeTime(&stat->st_ctim);
601 inode->GetCreationTime(&stat->st_crtim);
603 stat->st_size = inode->Size();
604 stat->st_blocks = (inode->Size() + 511) / 512;
606 return B_OK;
610 status_t
611 ext2_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
612 uint32 mask)
614 TRACE("ext2_write_stat\n");
615 Volume* volume = (Volume*)_volume->private_volume;
617 if (volume->IsReadOnly())
618 return B_READ_ONLY_DEVICE;
620 Inode* inode = (Inode*)_node->private_node;
622 ext2_inode& node = inode->Node();
623 bool updateTime = false;
624 uid_t uid = geteuid();
626 bool isOwnerOrRoot = uid == 0 || uid == (uid_t)node.UserID();
627 bool hasWriteAccess = inode->CheckPermissions(W_OK) == B_OK;
629 TRACE("ext2_write_stat: Starting transaction\n");
630 Transaction transaction(volume->GetJournal());
631 inode->WriteLockInTransaction(transaction);
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;
641 TRACE("ext2_write_stat: Old size: %ld, new size: %ld\n",
642 (long)inode->Size(), (long)stat->st_size);
644 off_t oldSize = inode->Size();
646 status_t status = inode->Resize(transaction, stat->st_size);
647 if(status != B_OK)
648 return status;
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());
656 updateTime = true;
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;
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;
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;
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};
690 if ((mask & B_STAT_MODIFICATION_TIME) != 0)
691 newTimespec = stat->st_mtim;
693 if ((mask & B_STAT_CHANGE_TIME) != 0
694 && stat->st_ctim.tv_sec > newTimespec.tv_sec)
695 newTimespec = stat->st_ctim;
697 if (newTimespec.tv_sec == 0)
698 Inode::_BigtimeToTimespec(real_time_clock_usecs(), &newTimespec);
700 inode->SetModificationTime(&newTimespec);
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);
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);
715 return status;
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)
723 Volume* volume = (Volume*)_volume->private_volume;
724 Inode* directory = (Inode*)_directory->private_node;
726 TRACE("ext2_create()\n");
728 if (volume->IsReadOnly())
729 return B_READ_ONLY_DEVICE;
731 if (!directory->IsDirectory())
732 return B_BAD_TYPE;
734 TRACE("ext2_create(): Creating cookie\n");
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);
742 cookie->open_mode = openMode;
743 cookie->last_size = 0;
744 cookie->last_notification = system_time();
746 TRACE("ext2_create(): Starting transaction\n");
748 Transaction transaction(volume->GetJournal());
750 TRACE("ext2_create(): Creating inode\n");
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;
760 TRACE("ext2_create(): Created inode\n");
762 if ((openMode & O_NOCACHE) != 0) {
763 status = inode->DisableFileCache();
764 if (status != B_OK)
765 return status;
768 entry_cache_add(volume->ID(), directory->ID(), name, *_vnodeID);
770 status = transaction.Done();
771 if (status != B_OK) {
772 entry_cache_remove(volume->ID(), directory->ID(), name);
773 return status;
776 *_cookie = cookie;
777 cookieDeleter.Detach();
779 if (created)
780 notify_entry_created(volume->ID(), directory->ID(), name, *_vnodeID);
782 return B_OK;
786 static status_t
787 ext2_create_symlink(fs_volume* _volume, fs_vnode* _directory, const char* name,
788 const char* path, int mode)
790 TRACE("ext2_create_symlink()\n");
792 Volume* volume = (Volume*)_volume->private_volume;
793 Inode* directory = (Inode*)_directory->private_node;
795 if (volume->IsReadOnly())
796 return B_READ_ONLY_DEVICE;
798 if (!directory->IsDirectory())
799 return B_BAD_TYPE;
801 status_t status = directory->CheckPermissions(W_OK);
802 if (status != B_OK)
803 return status;
805 TRACE("ext2_create_symlink(): Starting transaction\n");
806 Transaction transaction(volume->GetJournal());
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;
815 // TODO: We have to prepare the link before publishing?
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);
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);
833 if (!link->HasFileCache()) {
834 status = link->CreateFileCache();
835 if (status != B_OK)
836 return status;
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;
845 if (status == B_OK)
846 status = link->WriteBack(transaction);
848 entry_cache_add(volume->ID(), directory->ID(), name, id);
850 status = transaction.Done();
851 if (status != B_OK) {
852 entry_cache_remove(volume->ID(), directory->ID(), name);
853 return status;
856 notify_entry_created(volume->ID(), directory->ID(), name, id);
858 TRACE("ext2_create_symlink(): Done\n");
860 return status;
864 static status_t
865 ext2_link(fs_volume* volume, fs_vnode* dir, const char* name, fs_vnode* node)
867 // TODO
869 return B_UNSUPPORTED;
873 static status_t
874 ext2_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name)
876 TRACE("ext2_unlink()\n");
877 if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
878 return B_NOT_ALLOWED;
880 Volume* volume = (Volume*)_volume->private_volume;
881 Inode* directory = (Inode*)_directory->private_node;
883 status_t status = directory->CheckPermissions(W_OK);
884 if (status != B_OK)
885 return status;
887 TRACE("ext2_unlink(): Starting transaction\n");
888 Transaction transaction(volume->GetJournal());
890 directory->WriteLockInTransaction(transaction);
892 TRACE("ext2_unlink(): Looking up for directory entry\n");
893 HTree htree(volume, directory);
894 DirectoryIterator* directoryIterator;
896 status = htree.Lookup(name, &directoryIterator);
897 if (status != B_OK)
898 return status;
900 ino_t id;
901 status = directoryIterator->FindEntry(name, &id);
902 if (status != B_OK)
903 return status;
905 Vnode vnode(volume, id);
906 Inode* inode;
907 status = vnode.Get(&inode);
908 if (status != B_OK)
909 return status;
911 inode->WriteLockInTransaction(transaction);
913 status = inode->Unlink(transaction);
914 if (status != B_OK)
915 return status;
917 status = directoryIterator->RemoveEntry(transaction);
918 if (status != B_OK)
919 return status;
921 entry_cache_remove(volume->ID(), directory->ID(), name);
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);
929 return status;
933 static status_t
934 ext2_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName,
935 fs_vnode* _newDir, const char* newName)
937 TRACE("ext2_rename()\n");
939 Volume* volume = (Volume*)_volume->private_volume;
940 Inode* oldDirectory = (Inode*)_oldDir->private_node;
941 Inode* newDirectory = (Inode*)_newDir->private_node;
943 if (oldDirectory == newDirectory && strcmp(oldName, newName) == 0)
944 return B_OK;
946 Transaction transaction(volume->GetJournal());
948 oldDirectory->WriteLockInTransaction(transaction);
949 if (oldDirectory != newDirectory)
950 newDirectory->WriteLockInTransaction(transaction);
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;
958 HTree oldHTree(volume, oldDirectory);
959 DirectoryIterator* oldIterator;
961 status = oldHTree.Lookup(oldName, &oldIterator);
962 if (status != B_OK)
963 return status;
965 ObjectDeleter<DirectoryIterator> oldIteratorDeleter(oldIterator);
967 ino_t oldID;
968 status = oldIterator->FindEntry(oldName, &oldID);
969 if (status != B_OK)
970 return status;
972 if (oldDirectory != newDirectory) {
973 TRACE("ext2_rename(): Different parent directories\n");
974 CachedBlock cached(volume);
976 ino_t parentID = newDirectory->ID();
977 ino_t oldDirID = oldDirectory->ID();
979 do {
980 Vnode vnode(volume, parentID);
981 Inode* parent;
983 status = vnode.Get(&parent);
984 if (status != B_OK)
985 return B_IO_ERROR;
987 fsblock_t blockNum;
988 status = parent->FindBlock(0, blockNum);
989 if (status != B_OK)
990 return status;
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);
997 if (parentID == oldID)
998 return B_BAD_VALUE;
1001 HTree newHTree(volume, newDirectory);
1002 DirectoryIterator* newIterator;
1004 status = newHTree.Lookup(newName, &newIterator);
1005 if (status != B_OK)
1006 return status;
1008 ObjectDeleter<DirectoryIterator> newIteratorDeleter(newIterator);
1010 Vnode vnode(volume, oldID);
1011 Inode* inode;
1013 status = vnode.Get(&inode);
1014 if (status != B_OK)
1015 return status;
1017 uint8 fileType;
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;
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;
1037 Vnode vnodeExistent(volume, existentID);
1038 Inode* existent;
1040 if (vnodeExistent.Get(&existent) != B_OK)
1041 return B_NAME_IN_USE;
1043 if (existent->IsDirectory() != inode->IsDirectory()) {
1044 return existent->IsDirectory() ? B_IS_A_DIRECTORY
1045 : B_NOT_A_DIRECTORY;
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;
1053 notify_entry_removed(volume->ID(), newDirectory->ID(), newName,
1054 existentID);
1055 } else if (status == B_ENTRY_NOT_FOUND) {
1056 newIterator->Restart();
1058 status = newIterator->AddEntry(transaction, newName, strlen(newName),
1059 oldID, fileType);
1060 if (status != B_OK)
1061 return status;
1062 } else
1063 return status;
1065 // Remove entry from source folder
1066 status = oldIterator->RemoveEntry(transaction);
1067 if (status != B_OK)
1068 return status;
1070 inode->WriteLockInTransaction(transaction);
1072 if (oldDirectory != newDirectory && inode->IsDirectory()) {
1073 DirectoryIterator inodeIterator(inode);
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;
1083 inodeIterator.ChangeEntry(transaction, newDirectory->ID(),
1084 (uint8)EXT2_TYPE_DIRECTORY);
1087 status = inode->WriteBack(transaction);
1088 if (status != B_OK)
1089 return status;
1091 entry_cache_remove(volume->ID(), oldDirectory->ID(), oldName);
1092 entry_cache_add(volume->ID(), newDirectory->ID(), newName, oldID);
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);
1099 return status;
1102 notify_entry_moved(volume->ID(), oldDirectory->ID(), oldName,
1103 newDirectory->ID(), newName, oldID);
1105 return B_OK;
1109 static status_t
1110 ext2_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie)
1112 Volume* volume = (Volume*)_volume->private_volume;
1113 Inode* inode = (Inode*)_node->private_node;
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;
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;
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);
1131 cookie->open_mode = openMode & EXT2_OPEN_MODE_USER_MASK;
1132 cookie->last_size = inode->Size();
1133 cookie->last_notification = system_time();
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);
1143 // Should we truncate the file?
1144 if ((openMode & O_TRUNC) != 0) {
1145 if ((openMode & O_RWMASK) == O_RDONLY)
1146 return B_NOT_ALLOWED;
1148 Transaction transaction(volume->GetJournal());
1149 inode->WriteLockInTransaction(transaction);
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;
1159 // TODO: No need to notify file size changed?
1162 fileCacheEnabler.Detach();
1163 cookieDeleter.Detach();
1164 *_cookie = cookie;
1166 return B_OK;
1170 static status_t
1171 ext2_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
1172 void* buffer, size_t* _length)
1174 Inode* inode = (Inode*)_node->private_node;
1176 if (!inode->IsFile()) {
1177 *_length = 0;
1178 return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE;
1181 return inode->ReadAt(pos, (uint8*)buffer, _length);
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)
1189 TRACE("ext2_write()\n");
1190 Volume* volume = (Volume*)_volume->private_volume;
1191 Inode* inode = (Inode*)_node->private_node;
1193 if (volume->IsReadOnly())
1194 return B_READ_ONLY_DEVICE;
1196 if (inode->IsDirectory()) {
1197 *_length = 0;
1198 return B_IS_A_DIRECTORY;
1201 TRACE("ext2_write(): Preparing cookie\n");
1203 file_cookie* cookie = (file_cookie*)_cookie;
1205 if ((cookie->open_mode & O_APPEND) != 0)
1206 pos = inode->Size();
1208 TRACE("ext2_write(): Creating transaction\n");
1209 Transaction transaction;
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());
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();
1229 TRACE("ext2_write(): Done\n");
1231 return status;
1235 static status_t
1236 ext2_close(fs_volume *_volume, fs_vnode *_node, void *_cookie)
1238 return B_OK;
1242 static status_t
1243 ext2_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1245 file_cookie* cookie = (file_cookie*)_cookie;
1246 Volume* volume = (Volume*)_volume->private_volume;
1247 Inode* inode = (Inode*)_node->private_node;
1249 if (inode->Size() != cookie->last_size)
1250 notify_stat_changed(volume->ID(), -1, inode->ID(), B_STAT_SIZE);
1252 if ((cookie->open_mode & O_NOCACHE) != 0)
1253 inode->EnableFileCache();
1255 delete cookie;
1256 return B_OK;
1260 static status_t
1261 ext2_access(fs_volume* _volume, fs_vnode* _node, int accessMode)
1263 Inode* inode = (Inode*)_node->private_node;
1264 return inode->CheckPermissions(accessMode);
1268 static status_t
1269 ext2_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer,
1270 size_t *_bufferSize)
1272 Inode* inode = (Inode*)_node->private_node;
1274 if (!inode->IsSymLink())
1275 return B_BAD_VALUE;
1277 if (inode->Size() < (off_t)*_bufferSize)
1278 *_bufferSize = inode->Size();
1280 if (inode->Size() > EXT2_SHORT_SYMLINK_LENGTH)
1281 return inode->ReadAt(0, (uint8 *)buffer, _bufferSize);
1283 memcpy(buffer, inode->Node().symlink, *_bufferSize);
1284 return B_OK;
1288 // #pragma mark - Directory functions
1291 static status_t
1292 ext2_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name,
1293 int mode)
1295 TRACE("ext2_create_dir()\n");
1296 Volume* volume = (Volume*)_volume->private_volume;
1297 Inode* directory = (Inode*)_directory->private_node;
1299 if (volume->IsReadOnly())
1300 return B_READ_ONLY_DEVICE;
1302 if (!directory->IsDirectory())
1303 return B_BAD_TYPE;
1305 status_t status = directory->CheckPermissions(W_OK);
1306 if (status != B_OK)
1307 return status;
1309 TRACE("ext2_create_dir(): Starting transaction\n");
1310 Transaction transaction(volume->GetJournal());
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;
1318 put_vnode(volume->FSVolume(), id);
1320 entry_cache_add(volume->ID(), directory->ID(), name, id);
1322 status = transaction.Done();
1323 if (status != B_OK) {
1324 entry_cache_remove(volume->ID(), directory->ID(), name);
1325 return status;
1328 notify_entry_created(volume->ID(), directory->ID(), name, id);
1330 TRACE("ext2_create_dir(): Done\n");
1332 return B_OK;
1336 static status_t
1337 ext2_remove_dir(fs_volume* _volume, fs_vnode* _directory, const char* name)
1339 TRACE("ext2_remove_dir()\n");
1341 Volume* volume = (Volume*)_volume->private_volume;
1342 Inode* directory = (Inode*)_directory->private_node;
1344 status_t status = directory->CheckPermissions(W_OK);
1345 if (status != B_OK)
1346 return status;
1348 TRACE("ext2_remove_dir(): Starting transaction\n");
1349 Transaction transaction(volume->GetJournal());
1351 directory->WriteLockInTransaction(transaction);
1353 TRACE("ext2_remove_dir(): Looking up for directory entry\n");
1354 HTree htree(volume, directory);
1355 DirectoryIterator* directoryIterator;
1357 status = htree.Lookup(name, &directoryIterator);
1358 if (status != B_OK)
1359 return status;
1361 ino_t id;
1362 status = directoryIterator->FindEntry(name, &id);
1363 if (status != B_OK)
1364 return status;
1366 Vnode vnode(volume, id);
1367 Inode* inode;
1368 status = vnode.Get(&inode);
1369 if (status != B_OK)
1370 return status;
1372 inode->WriteLockInTransaction(transaction);
1374 status = inode->Unlink(transaction);
1375 if (status != B_OK)
1376 return status;
1378 status = directory->Unlink(transaction);
1379 if (status != B_OK)
1380 return status;
1382 status = directoryIterator->RemoveEntry(transaction);
1383 if (status != B_OK)
1384 return status;
1386 entry_cache_remove(volume->ID(), directory->ID(), name);
1387 entry_cache_remove(volume->ID(), id, "..");
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);
1396 return status;
1400 static status_t
1401 ext2_open_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie)
1403 Inode* inode = (Inode*)_node->private_node;
1404 status_t status = inode->CheckPermissions(R_OK);
1405 if (status < B_OK)
1406 return status;
1408 if (!inode->IsDirectory())
1409 return B_NOT_A_DIRECTORY;
1411 DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode);
1412 if (iterator == NULL)
1413 return B_NO_MEMORY;
1415 *_cookie = iterator;
1416 return B_OK;
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)
1424 DirectoryIterator *iterator = (DirectoryIterator *)_cookie;
1425 Volume* volume = (Volume*)_volume->private_volume;
1427 uint32 maxCount = *_num;
1428 uint32 count = 0;
1430 while (count < maxCount && bufferSize > sizeof(struct dirent)) {
1432 size_t length = bufferSize - sizeof(struct dirent) + 1;
1433 ino_t id;
1435 status_t status = iterator->GetNext(dirent->d_name, &length, &id);
1436 if (status == B_ENTRY_NOT_FOUND)
1437 break;
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;
1446 if (status != B_OK)
1447 return status;
1449 status = iterator->Next();
1450 if (status != B_OK && status != B_ENTRY_NOT_FOUND)
1451 return status;
1453 dirent->d_dev = volume->ID();
1454 dirent->d_ino = id;
1455 dirent->d_reclen = sizeof(struct dirent) + length;
1457 bufferSize -= dirent->d_reclen;
1458 dirent = (struct dirent*)((uint8*)dirent + dirent->d_reclen);
1459 count++;
1462 *_num = count;
1463 return B_OK;
1467 static status_t
1468 ext2_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie)
1470 DirectoryIterator *iterator = (DirectoryIterator *)_cookie;
1471 return iterator->Rewind();
1475 static status_t
1476 ext2_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void * /*_cookie*/)
1478 return B_OK;
1482 static status_t
1483 ext2_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
1485 delete (DirectoryIterator*)_cookie;
1486 return B_OK;
1490 static status_t
1491 ext2_open_attr_dir(fs_volume *_volume, fs_vnode *_node, void **_cookie)
1493 Inode* inode = (Inode*)_node->private_node;
1494 Volume* volume = (Volume*)_volume->private_volume;
1495 TRACE("%s()\n", __FUNCTION__);
1497 if (!(volume->SuperBlock().CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR))
1498 return ENOSYS;
1500 // on directories too ?
1501 if (!inode->IsFile())
1502 return EINVAL;
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;
1512 static status_t
1513 ext2_close_attr_dir(fs_volume* _volume, fs_vnode* _node, void* cookie)
1515 TRACE("%s()\n", __FUNCTION__);
1516 return B_OK;
1520 static status_t
1521 ext2_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1523 TRACE("%s()\n", __FUNCTION__);
1524 delete (int32 *)_cookie;
1525 return B_OK;
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)
1534 Inode* inode = (Inode*)_node->private_node;
1535 int32 index = *(int32 *)_cookie;
1536 Attribute attribute(inode);
1537 TRACE("%s()\n", __FUNCTION__);
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;
1547 status = attribute.GetName(dirent->d_name, &length);
1548 if (status != B_OK)
1549 return B_OK;
1551 Volume* volume = (Volume*)_volume->private_volume;
1553 dirent->d_dev = volume->ID();
1554 dirent->d_ino = inode->ID();
1555 dirent->d_reclen = sizeof(struct dirent) + length;
1557 *_num = 1;
1558 *(int32*)_cookie = index + 1;
1559 return B_OK;
1563 static status_t
1564 ext2_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1566 *(int32*)_cookie = 0;
1567 TRACE("%s()\n", __FUNCTION__);
1568 return B_OK;
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)
1577 return EROFS;
1581 static status_t
1582 ext2_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name,
1583 int openMode, void** _cookie)
1585 TRACE("%s()\n", __FUNCTION__);
1587 Volume* volume = (Volume*)_volume->private_volume;
1588 Inode* inode = (Inode*)_node->private_node;
1589 Attribute attribute(inode);
1591 if (!(volume->SuperBlock().CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR))
1592 return ENOSYS;
1594 return attribute.Open(name, openMode, (attr_cookie**)_cookie);
1598 static status_t
1599 ext2_close_attr(fs_volume* _volume, fs_vnode* _node,
1600 void* cookie)
1602 return B_OK;
1606 static status_t
1607 ext2_free_attr_cookie(fs_volume* _volume, fs_vnode* _node,
1608 void* cookie)
1610 delete (attr_cookie*)cookie;
1611 return B_OK;
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)
1619 TRACE("%s()\n", __FUNCTION__);
1621 attr_cookie* cookie = (attr_cookie*)_cookie;
1622 Inode* inode = (Inode*)_node->private_node;
1624 Attribute attribute(inode, cookie);
1626 return attribute.Read(cookie, pos, (uint8*)buffer, _length);
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)
1634 return EROFS;
1639 static status_t
1640 ext2_read_attr_stat(fs_volume* _volume, fs_vnode* _node,
1641 void* _cookie, struct stat* stat)
1643 attr_cookie* cookie = (attr_cookie*)_cookie;
1644 Inode* inode = (Inode*)_node->private_node;
1646 Attribute attribute(inode, cookie);
1648 return attribute.Stat(*stat);
1652 static status_t
1653 ext2_write_attr_stat(fs_volume* _volume, fs_vnode* _node,
1654 void* cookie, const struct stat* stat, int statMask)
1656 return EROFS;
1660 static status_t
1661 ext2_rename_attr(fs_volume* _volume, fs_vnode* fromVnode,
1662 const char* fromName, fs_vnode* toVnode, const char* toName)
1664 return ENOSYS;
1668 static status_t
1669 ext2_remove_attr(fs_volume* _volume, fs_vnode* vnode,
1670 const char* name)
1672 return ENOSYS;
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,
1685 fs_vnode_ops gExt2VnodeOps = {
1686 /* vnode operations */
1687 &ext2_lookup,
1688 NULL,
1689 &ext2_put_vnode,
1690 &ext2_remove_vnode,
1692 /* VM file access */
1693 &ext2_can_page,
1694 &ext2_read_pages,
1695 &ext2_write_pages,
1697 NULL, // io()
1698 NULL, // cancel_io()
1700 &ext2_get_file_map,
1702 &ext2_ioctl,
1703 NULL,
1704 NULL, // fs_select
1705 NULL, // fs_deselect
1706 &ext2_fsync,
1708 &ext2_read_link,
1709 &ext2_create_symlink,
1711 &ext2_link,
1712 &ext2_unlink,
1713 &ext2_rename,
1715 &ext2_access,
1716 &ext2_read_stat,
1717 &ext2_write_stat,
1718 NULL, // fs_preallocate
1720 /* file operations */
1721 &ext2_create,
1722 &ext2_open,
1723 &ext2_close,
1724 &ext2_free_cookie,
1725 &ext2_read,
1726 &ext2_write,
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,
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,
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,
1758 static file_system_module_info sExt2FileSystem = {
1760 "file_systems/ext2" B_CURRENT_FS_API_VERSION,
1762 NULL,
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
1770 // scanning
1771 ext2_identify_partition,
1772 ext2_scan_partition,
1773 ext2_free_identify_partition_cookie,
1774 NULL, // free_partition_content_cookie()
1776 &ext2_mount,
1778 NULL,
1782 module_info *modules[] = {
1783 (module_info *)&sExt2FileSystem,
1784 NULL,