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.
9 #include <util/kernel_cpp.h>
12 #include <AutoDeleter.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"
31 # define TRACE(x...) dprintf("\33[34mext2:\33[0m " x)
33 # define TRACE(x...) ;
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
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
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());
69 // #pragma mark - Scanning
73 ext2_identify_partition(int fd
, partition_data
*partition
, void **_cookie
)
75 ext2_super_block superBlock
;
76 status_t status
= Volume::Identify(fd
, &superBlock
);
80 identify_cookie
*cookie
= new identify_cookie
;
81 memcpy(&cookie
->super_block
, &superBlock
, sizeof(ext2_super_block
));
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
)
109 ext2_free_identify_partition_cookie(partition_data
* partition
, void* _cookie
)
111 delete (identify_cookie
*)_cookie
;
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
);
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
));
140 *_rootID
= volume
->RootNode()->ID();
146 ext2_unmount(fs_volume
*_volume
)
148 Volume
* volume
= (Volume
*)_volume
->private_volume
;
150 status_t status
= volume
->Unmount();
158 ext2_read_fs_info(fs_volume
* _volume
, struct fs_info
* info
)
160 Volume
* volume
= (Volume
*)_volume
->private_volume
;
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();
171 strlcpy(info
->volume_name
, volume
->Name(), sizeof(info
->volume_name
));
174 strlcpy(info
->fsh_name
, "ext2", sizeof(info
->fsh_name
));
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
);
203 ext2_sync(fs_volume
* _volume
)
205 Volume
* volume
= (Volume
*)_volume
->private_volume
;
206 return volume
->Sync();
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
);
224 Inode
* inode
= new(std::nothrow
) Inode(volume
, id
);
228 status_t status
= inode
->InitCheck();
232 if (status
== B_OK
) {
233 _node
->private_node
= inode
;
234 _node
->ops
= &gExt2VnodeOps
;
235 *_type
= inode
->Mode();
238 ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status
));
245 ext2_put_vnode(fs_volume
* _volume
, fs_vnode
* _node
, bool reenter
)
247 delete (Inode
*)_node
->private_node
;
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())
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);
273 TRACE("ext2_remove_vnode(): Removing from orphan list\n");
274 status_t status
= volume
->RemoveOrphan(transaction
, inode
->ID());
278 TRACE("ext2_remove_vnode(): Setting deletion time\n");
279 inode
->Node().SetDeletionTime(real_time_clock());
281 status
= inode
->WriteBack(transaction
);
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?
290 status
= transaction
.Done();
297 ext2_can_page(fs_volume
* _volume
, fs_vnode
* _node
, void* _cookie
)
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
)
313 rw_lock_read_lock(inode
->Lock());
316 size_t vecOffset
= 0;
317 size_t bytesLeft
= *_numBytes
;
321 file_io_vec fileVecs
[8];
322 size_t fileVecCount
= 8;
324 status
= file_map_translate(inode
->Map(), pos
, bytesLeft
, fileVecs
,
326 if (status
!= B_OK
&& status
!= B_BUFFER_OVERFLOW
)
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
)
341 rw_lock_read_unlock(inode
->Lock());
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
)
360 rw_lock_read_lock(inode
->Lock());
363 size_t vecOffset
= 0;
364 size_t bytesLeft
= *_numBytes
;
368 file_io_vec fileVecs
[8];
369 size_t fileVecCount
= 8;
371 status
= file_map_translate(inode
->Map(), pos
, bytesLeft
, fileVecs
,
373 if (status
!= B_OK
&& status
!= B_BUFFER_OVERFLOW
)
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
)
388 rw_lock_read_unlock(inode
->Lock());
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
;
401 if (io_request_is_write(request
) && volume
->IsReadOnly()) {
402 notify_io_request(request
, B_READ_ONLY_DEVICE
);
403 return B_READ_ONLY_DEVICE
;
407 if (inode
->FileCache() == NULL
) {
409 notify_io_request(request
, 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
);
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
;
434 status_t status
= inode
->FindBlock(offset
, block
, &count
);
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
;
452 // we're out of file_io_vecs; let's bail out
454 return B_BUFFER_OVERFLOW
;
457 // 'block' is 0 for sparse blocks
459 vecs
[index
].offset
= blockOffset
;
461 vecs
[index
].offset
= -1;
463 vecs
[index
].length
= blockLength
;
467 offset
+= blockLength
;
469 if (offset
>= inode
->Size() || size
<= blockLength
) {
472 TRACE("ext2_get_file_map for inode %" B_PRIdINO
"\n", inode
->ID());
479 // can never get here
488 ext2_lookup(fs_volume
* _volume
, fs_vnode
* _directory
, const char* name
,
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
);
501 HTree
htree(volume
, directory
);
502 DirectoryIterator
* iterator
;
504 status
= htree
.Lookup(name
, &iterator
);
508 ObjectDeleter
<DirectoryIterator
> iteratorDeleter(iterator
);
510 status
= iterator
->FindEntry(name
, _vnodeID
);
514 return get_vnode(volume
->FSVolume(), *_vnodeID
, NULL
);
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
;
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();
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
,
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
);
558 TRACE("ioctl: Blocks cleared\n");
561 transaction
.Start(volume
->GetJournal());
564 TRACE("ioctl: Done\n");
570 return B_DEV_INVALID_IOCTL
;
575 ext2_fsync(fs_volume
* _volume
, fs_vnode
* _node
)
577 Inode
* inode
= (Inode
*)_node
->private_node
;
578 return inode
->Sync();
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();
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;
611 ext2_write_stat(fs_volume
* _volume
, fs_vnode
* _node
, const struct stat
* stat
,
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())
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
);
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());
659 if ((mask
& B_STAT_MODE
) != 0) {
660 // only the user or root can do that
662 return B_NOT_ALLOWED
;
663 node
.UpdateMode(stat
->st_mode
, S_IUMSK
);
667 if ((mask
& B_STAT_UID
) != 0) {
668 // only root should be allowed
670 return B_NOT_ALLOWED
;
671 node
.SetUserID(stat
->st_uid
);
675 if ((mask
& B_STAT_GID
) != 0) {
676 // only the user or root can do that
678 return B_NOT_ALLOWED
;
679 node
.SetGroupID(stat
->st_gid
);
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
);
711 status
= transaction
.Done();
713 notify_stat_changed(volume
->ID(), -1, inode
->ID(), mask
);
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())
734 TRACE("ext2_create(): Creating cookie\n");
737 file_cookie
* cookie
= new(std::nothrow
) file_cookie
;
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");
754 status_t status
= Inode::Create(transaction
, directory
, name
,
755 S_FILE
| (mode
& S_IUMSK
), openMode
, EXT2_TYPE_FILE
, &created
, _vnodeID
,
756 &inode
, &gExt2VnodeOps
);
760 TRACE("ext2_create(): Created inode\n");
762 if ((openMode
& O_NOCACHE
) != 0) {
763 status
= inode
->DisableFileCache();
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
);
777 cookieDeleter
.Detach();
780 notify_entry_created(volume
->ID(), directory
->ID(), name
, *_vnodeID
);
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())
801 status_t status
= directory
->CheckPermissions(W_OK
);
805 TRACE("ext2_create_symlink(): Starting transaction\n");
806 Transaction
transaction(volume
->GetJournal());
810 status
= Inode::Create(transaction
, directory
, name
, S_SYMLINK
| 0777,
811 0, (uint8
)EXT2_TYPE_SYMLINK
, NULL
, &id
, &link
);
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
,
826 put_vnode(volume
->FSVolume(), id
);
828 TRACE("ext2_create_symlink(): Publishing vnode\n");
829 publish_vnode(volume
->FSVolume(), id
, link
, &gExt2VnodeOps
,
831 put_vnode(volume
->FSVolume(), id
);
833 if (!link
->HasFileCache()) {
834 status
= link
->CreateFileCache();
839 size_t written
= length
;
840 status
= link
->WriteAt(transaction
, 0, (const uint8
*)path
, &written
);
841 if (status
== B_OK
&& written
!= length
)
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
);
856 notify_entry_created(volume
->ID(), directory
->ID(), name
, id
);
858 TRACE("ext2_create_symlink(): Done\n");
865 ext2_link(fs_volume
* volume
, fs_vnode
* dir
, const char* name
, fs_vnode
* node
)
869 return B_UNSUPPORTED
;
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
);
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
);
901 status
= directoryIterator
->FindEntry(name
, &id
);
905 Vnode
vnode(volume
, id
);
907 status
= vnode
.Get(&inode
);
911 inode
->WriteLockInTransaction(transaction
);
913 status
= inode
->Unlink(transaction
);
917 status
= directoryIterator
->RemoveEntry(transaction
);
921 entry_cache_remove(volume
->ID(), directory
->ID(), name
);
923 status
= transaction
.Done();
925 entry_cache_add(volume
->ID(), directory
->ID(), name
, id
);
927 notify_entry_removed(volume
->ID(), directory
->ID(), name
, id
);
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)
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
);
954 status
= newDirectory
->CheckPermissions(W_OK
);
958 HTree
oldHTree(volume
, oldDirectory
);
959 DirectoryIterator
* oldIterator
;
961 status
= oldHTree
.Lookup(oldName
, &oldIterator
);
965 ObjectDeleter
<DirectoryIterator
> oldIteratorDeleter(oldIterator
);
968 status
= oldIterator
->FindEntry(oldName
, &oldID
);
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();
980 Vnode
vnode(volume
, parentID
);
983 status
= vnode
.Get(&parent
);
988 status
= parent
->FindBlock(0, blockNum
);
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
)
1001 HTree
newHTree(volume
, newDirectory
);
1002 DirectoryIterator
* newIterator
;
1004 status
= newHTree
.Lookup(newName
, &newIterator
);
1008 ObjectDeleter
<DirectoryIterator
> newIteratorDeleter(newIterator
);
1010 Vnode
vnode(volume
, oldID
);
1013 status
= vnode
.Get(&inode
);
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
;
1025 fileType
= EXT2_TYPE_FILE
;
1027 // Add entry in destination directory
1029 status
= newIterator
->FindEntry(newName
, &existentID
);
1030 if (status
== B_OK
) {
1031 if (existentID
== oldID
) {
1032 // Remove entry in oldID
1033 // return inode->Unlink();
1037 Vnode
vnodeExistent(volume
, existentID
);
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
);
1053 notify_entry_removed(volume
->ID(), newDirectory
->ID(), newName
,
1055 } else if (status
== B_ENTRY_NOT_FOUND
) {
1056 newIterator
->Restart();
1058 status
= newIterator
->AddEntry(transaction
, newName
, strlen(newName
),
1065 // Remove entry from source folder
1066 status
= oldIterator
->RemoveEntry(transaction
);
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());
1080 } else if (status
!= B_OK
)
1083 inodeIterator
.ChangeEntry(transaction
, newDirectory
->ID(),
1084 (uint8
)EXT2_TYPE_DIRECTORY
);
1087 status
= inode
->WriteBack(transaction
);
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
);
1102 notify_entry_moved(volume
->ID(), oldDirectory
->ID(), oldName
,
1103 newDirectory
->ID(), newName
, oldID
);
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));
1125 // Prepare the cookie
1126 file_cookie
* cookie
= new(std::nothrow
) file_cookie
;
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();
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);
1153 status
= inode
->WriteBack(transaction
);
1155 status
= transaction
.Done();
1159 // TODO: No need to notify file size changed?
1162 fileCacheEnabler
.Detach();
1163 cookieDeleter
.Detach();
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()) {
1178 return inode
->IsDirectory() ? B_IS_A_DIRECTORY
: B_BAD_VALUE
;
1181 return inode
->ReadAt(pos
, (uint8
*)buffer
, _length
);
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()) {
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
,
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");
1236 ext2_close(fs_volume
*_volume
, fs_vnode
*_node
, void *_cookie
)
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();
1261 ext2_access(fs_volume
* _volume
, fs_vnode
* _node
, int accessMode
)
1263 Inode
* inode
= (Inode
*)_node
->private_node
;
1264 return inode
->CheckPermissions(accessMode
);
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())
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
);
1288 // #pragma mark - Directory functions
1292 ext2_create_dir(fs_volume
* _volume
, fs_vnode
* _directory
, const char* name
,
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())
1305 status_t status
= directory
->CheckPermissions(W_OK
);
1309 TRACE("ext2_create_dir(): Starting transaction\n");
1310 Transaction
transaction(volume
->GetJournal());
1313 status
= Inode::Create(transaction
, directory
, name
,
1314 S_DIRECTORY
| (mode
& S_IUMSK
), 0, EXT2_TYPE_DIRECTORY
, NULL
, &id
);
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
);
1328 notify_entry_created(volume
->ID(), directory
->ID(), name
, id
);
1330 TRACE("ext2_create_dir(): Done\n");
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
);
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
);
1362 status
= directoryIterator
->FindEntry(name
, &id
);
1366 Vnode
vnode(volume
, id
);
1368 status
= vnode
.Get(&inode
);
1372 inode
->WriteLockInTransaction(transaction
);
1374 status
= inode
->Unlink(transaction
);
1378 status
= directory
->Unlink(transaction
);
1382 status
= directoryIterator
->RemoveEntry(transaction
);
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
);
1394 notify_entry_removed(volume
->ID(), directory
->ID(), name
, id
);
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
);
1408 if (!inode
->IsDirectory())
1409 return B_NOT_A_DIRECTORY
;
1411 DirectoryIterator
* iterator
= new(std::nothrow
) DirectoryIterator(inode
);
1412 if (iterator
== NULL
)
1415 *_cookie
= iterator
;
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
;
1430 while (count
< maxCount
&& bufferSize
> sizeof(struct dirent
)) {
1432 size_t length
= bufferSize
- sizeof(struct dirent
) + 1;
1435 status_t status
= iterator
->GetNext(dirent
->d_name
, &length
, &id
);
1436 if (status
== B_ENTRY_NOT_FOUND
)
1439 if (status
== B_BUFFER_OVERFLOW
) {
1440 // the remaining name buffer length was too small
1442 return B_BUFFER_OVERFLOW
;
1449 status
= iterator
->Next();
1450 if (status
!= B_OK
&& status
!= B_ENTRY_NOT_FOUND
)
1453 dirent
->d_dev
= volume
->ID();
1455 dirent
->d_reclen
= sizeof(struct dirent
) + length
;
1457 bufferSize
-= dirent
->d_reclen
;
1458 dirent
= (struct dirent
*)((uint8
*)dirent
+ dirent
->d_reclen
);
1468 ext2_rewind_dir(fs_volume
* /*_volume*/, fs_vnode
* /*node*/, void *_cookie
)
1470 DirectoryIterator
*iterator
= (DirectoryIterator
*)_cookie
;
1471 return iterator
->Rewind();
1476 ext2_close_dir(fs_volume
* /*_volume*/, fs_vnode
* /*node*/, void * /*_cookie*/)
1483 ext2_free_dir_cookie(fs_volume
*_volume
, fs_vnode
*_node
, void *_cookie
)
1485 delete (DirectoryIterator
*)_cookie
;
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
))
1500 // on directories too ?
1501 if (!inode
->IsFile())
1504 int32
*index
= new(std::nothrow
) int32
;
1508 *(int32
**)_cookie
= index
;
1513 ext2_close_attr_dir(fs_volume
* _volume
, fs_vnode
* _node
, void* cookie
)
1515 TRACE("%s()\n", __FUNCTION__
);
1521 ext2_free_attr_dir_cookie(fs_volume
* _volume
, fs_vnode
* _node
, void* _cookie
)
1523 TRACE("%s()\n", __FUNCTION__
);
1524 delete (int32
*)_cookie
;
1530 ext2_read_attr_dir(fs_volume
* _volume
, fs_vnode
* _node
,
1531 void* _cookie
, struct dirent
* dirent
, size_t bufferSize
,
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
) {
1544 } else if (status
!= B_OK
)
1547 status
= attribute
.GetName(dirent
->d_name
, &length
);
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
;
1558 *(int32
*)_cookie
= index
+ 1;
1564 ext2_rewind_attr_dir(fs_volume
* _volume
, fs_vnode
* _node
, void* _cookie
)
1566 *(int32
*)_cookie
= 0;
1567 TRACE("%s()\n", __FUNCTION__
);
1572 /* attribute operations */
1574 ext2_create_attr(fs_volume
* _volume
, fs_vnode
* _node
,
1575 const char* name
, uint32 type
, int openMode
, void** _cookie
)
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
))
1594 return attribute
.Open(name
, openMode
, (attr_cookie
**)_cookie
);
1599 ext2_close_attr(fs_volume
* _volume
, fs_vnode
* _node
,
1607 ext2_free_attr_cookie(fs_volume
* _volume
, fs_vnode
* _node
,
1610 delete (attr_cookie
*)cookie
;
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
);
1631 ext2_write_attr(fs_volume
* _volume
, fs_vnode
* _node
, void* cookie
,
1632 off_t pos
, const void* buffer
, size_t* length
)
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
);
1653 ext2_write_attr_stat(fs_volume
* _volume
, fs_vnode
* _node
,
1654 void* cookie
, const struct stat
* stat
, int statMask
)
1661 ext2_rename_attr(fs_volume
* _volume
, fs_vnode
* fromVnode
,
1662 const char* fromName
, fs_vnode
* toVnode
, const char* toName
)
1669 ext2_remove_attr(fs_volume
* _volume
, fs_vnode
* vnode
,
1676 fs_volume_ops gExt2VolumeOps
= {
1679 &ext2_write_fs_info
,
1685 fs_vnode_ops gExt2VnodeOps
= {
1686 /* vnode operations */
1692 /* VM file access */
1698 NULL
, // cancel_io()
1705 NULL
, // fs_deselect
1709 &ext2_create_symlink
,
1718 NULL
, // fs_preallocate
1720 /* file operations */
1728 /* directory operations */
1733 &ext2_free_dir_cookie
,
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,
1748 &ext2_free_attr_cookie
,
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
,
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
1771 ext2_identify_partition
,
1772 ext2_scan_partition
,
1773 ext2_free_identify_partition_cookie
,
1774 NULL
, // free_partition_content_cookie()
1782 module_info
*modules
[] = {
1783 (module_info
*)&sExt2FileSystem
,