4 * PROGRAMMER: Matt Wu <mattwu@163.com>
5 * HOMEPAGE: http://ext2.yeah.net
8 /* INCLUDES **************************************************************/
13 /* GLOBALS ***************************************************************/
15 int inode_ratio
= 4096;
17 BOOLEAN bLocked
= FALSE
;
19 /* FUNCTIONS *************************************************************/
36 int int_log10(unsigned int arg
)
47 static char default_str
[] = "default";
49 struct mke2fs_defaults
{
55 { default_str
, 0, 4096, 8192 },
56 { default_str
, 512, 1024, 4096 },
57 { default_str
, 3, 1024, 8192 },
58 { "journal", 0, 4096, 8192 },
59 { "news", 0, 4096, 4096 },
60 { "largefile", 0, 4096, 1024 * 1024 },
61 { "largefile4", 0, 4096, 4096 * 1024 },
65 void set_fs_defaults(const char *fs_type
,
66 PEXT2_SUPER_BLOCK super
,
67 int blocksize
, int *inode_ratio
)
71 struct mke2fs_defaults
*p
;
73 megs
= (super
->s_blocks_count
* (EXT2_BLOCK_SIZE(super
) / 1024) / 1024);
79 fs_type
= default_str
;
81 for (p
= settings
; p
->type
; p
++)
83 if ((strcmp(p
->type
, fs_type
) != 0) &&
84 (strcmp(p
->type
, default_str
) != 0))
92 *inode_ratio
= p
->inode_ratio
;
96 super
->s_log_frag_size
= super
->s_log_block_size
=
97 int_log2(p
->blocksize
>> EXT2_MIN_BLOCK_LOG_SIZE
);
103 super
->s_blocks_count
/= EXT2_BLOCK_SIZE(super
) / 1024;
108 * Helper function which zeros out _num_ blocks starting at _blk_. In
109 * case of an error, the details of the error is returned via _ret_blk_
110 * and _ret_count_ if they are non-NULL pointers. Returns 0 on
111 * success, and an error code on an error.
113 * As a special case, if the first argument is NULL, then it will
114 * attempt to free the static zeroizing buffer. (This is to keep
115 * programs that check for memory leaks happy.)
117 bool zero_blocks(PEXT2_FILESYS fs
, ULONG blk
, ULONG num
,
118 ULONG
*ret_blk
, ULONG
*ret_count
)
120 ULONG j
, count
, next_update
, next_update_incr
;
121 static unsigned char *buf
;
124 /* If fs is null, clean up the static buffer and return */
129 RtlFreeHeap(RtlGetProcessHeap(), 0, buf
);
135 #define STRIDE_LENGTH 8
137 /* Allocate the zeroizing buffer if necessary */
140 buf
= (unsigned char *)
141 RtlAllocateHeap(RtlGetProcessHeap(), 0, fs
->blocksize
* STRIDE_LENGTH
);
144 DPRINT1("Mke2fs: while allocating zeroizing buffer");
149 memset(buf
, 0, fs
->blocksize
* STRIDE_LENGTH
);
152 /* OK, do the write loop */
154 next_update_incr
= num
/ 100;
155 if (next_update_incr
< 1)
156 next_update_incr
= 1;
158 for (j
=0; j
< num
; j
+= STRIDE_LENGTH
, blk
+= STRIDE_LENGTH
)
160 if (num
-j
> STRIDE_LENGTH
)
161 count
= STRIDE_LENGTH
;
165 retval
= NT_SUCCESS(Ext2WriteDisk(
167 ((ULONGLONG
)blk
* fs
->blocksize
),
168 count
* fs
->blocksize
,
187 bool zap_sector(PEXT2_FILESYS Ext2Sys
, int sect
, int nsect
)
192 buf
= (unsigned char *)
193 RtlAllocateHeap(RtlGetProcessHeap(), 0, SECTOR_SIZE
*nsect
);
196 DPRINT1("Mke2fs: Out of memory erasing sectors %d-%d\n",
197 sect
, sect
+ nsect
- 1);
201 memset(buf
, 0, (ULONG
)nsect
* SECTOR_SIZE
);
203 #define BSD_DISKMAGIC (0x82564557UL) /* The disk magic number */
204 #define BSD_MAGICDISK (0x57455682UL) /* The disk magic number reversed */
205 #define BSD_LABEL_OFFSET 64
211 (LONGLONG
)(sect
* SECTOR_SIZE
),
214 // Check for a BSD disklabel, and don't erase it if so
215 magic
= (ULONG
*) (buf
+ BSD_LABEL_OFFSET
);
216 if ((*magic
== BSD_DISKMAGIC
) || (*magic
== BSD_MAGICDISK
))
221 Ext2WriteDisk( Ext2Sys
,
222 (LONGLONG
)(sect
* SECTOR_SIZE
),
223 (ULONG
)nsect
* SECTOR_SIZE
,
228 RtlFreeHeap(RtlGetProcessHeap(), 0, buf
);
233 bool ext2_mkdir( PEXT2_FILESYS fs
,
241 EXT2_INODE parent_inode
, inode
;
248 LARGE_INTEGER SysTime
;
250 NtQuerySystemTime(&SysTime
);
253 * Allocate an inode, if necessary
257 retval
= ext2_new_inode(fs
, parent
, LINUX_S_IFDIR
| 0755, 0, &ino
);
266 * Allocate a data block for the directory
268 retval
= ext2_new_block(fs
, 0, 0, &blk
);
273 * Create a scratch template for the directory
275 retval
= ext2_new_dir_block(fs
, ino
, parent
, &block
);
280 * Get the parent's inode, if necessary
284 retval
= ext2_load_inode(fs
, parent
, &parent_inode
);
290 memset(&parent_inode
, 0, sizeof(parent_inode
));
294 * Create the inode structure....
296 memset(&inode
, 0, sizeof(EXT2_INODE
));
297 inode
.i_mode
= (USHORT
)(LINUX_S_IFDIR
| (0777 & ~fs
->umask
));
298 inode
.i_uid
= inode
.i_gid
= 0;
299 inode
.i_blocks
= fs
->blocksize
/ 512;
300 inode
.i_block
[0] = blk
;
301 inode
.i_links_count
= 2;
302 RtlTimeToSecondsSince1970(&SysTime
, &inode
.i_mtime
);
303 inode
.i_ctime
= inode
.i_atime
= inode
.i_mtime
;
304 inode
.i_size
= fs
->blocksize
;
307 * Write out the inode and inode data block
309 retval
= ext2_write_block(fs
, blk
, block
);
313 retval
= ext2_save_inode(fs
, ino
, &inode
);
325 * Add entry for this inode to parent dir 's block
328 if (fs
->ext2_sb
->s_feature_incompat
& EXT2_FEATURE_INCOMPAT_FILETYPE
)
329 filetype
= EXT2_FT_DIR
;
331 retval
= ext2_add_entry(fs
, parent
, ino
, filetype
, name
);
337 * Update parent inode's counts
340 parent_inode
.i_links_count
++;
341 retval
= ext2_save_inode(fs
, parent
, &parent_inode
);
348 * Update accounting....
350 ext2_block_alloc_stats(fs
, blk
, +1);
351 ext2_inode_alloc_stats2(fs
, ino
, +1, 1);
357 RtlFreeHeap(RtlGetProcessHeap(), 0, block
);
364 bool create_root_dir(PEXT2_FILESYS fs
)
369 retval
= ext2_mkdir(fs
, EXT2_ROOT_INO
, EXT2_ROOT_INO
, 0, NULL
, &inode
);
373 DPRINT1("Mke2fs: while creating root dir");
381 retval
= ext2_save_inode(fs
, EXT2_ROOT_INO
, &inode
);
384 DPRINT1("Mke2fs: while setting root inode ownership");
392 bool create_lost_and_found(PEXT2_FILESYS Ext2Sys
)
396 char *name
= "lost+found";
406 buf
= (char *)RtlAllocateHeap(RtlGetProcessHeap(), 0, Ext2Sys
->blocksize
);
413 memset(buf
, 0, Ext2Sys
->blocksize
);
415 dir
= (PEXT2_DIR_ENTRY
) buf
;
416 dir
->rec_len
= Ext2Sys
->blocksize
;
419 Ext2Sys
->umask
= 077;
420 retval
= ext2_mkdir(Ext2Sys
, EXT2_ROOT_INO
, 0, name
, &ino
, &inode
);
424 DPRINT1("Mke2fs: while creating /lost+found.\n");
431 lpf_size
= inode
.i_size
;
435 if (lpf_size
>= 16*1024)
438 retval
= ext2_alloc_block(Ext2Sys
, 0, &dwBlk
);
442 DPRINT1("Mke2fs: create_lost_and_found: error alloc block.\n");
446 retval
= ext2_expand_inode(Ext2Sys
, &inode
, dwBlk
);
449 DPRINT1("Mke2fs: errors when expanding /lost+found.\n");
453 ext2_write_block(Ext2Sys
, dwBlk
, buf
);
455 inode
.i_blocks
+= (Ext2Sys
->blocksize
/SECTOR_SIZE
);
456 lpf_size
+= Ext2Sys
->blocksize
;
460 inode
.i_size
= lpf_size
;
462 ASSERT( (inode
.i_size
/Ext2Sys
->blocksize
) ==
463 Ext2DataBlocks(Ext2Sys
, inode
.i_blocks
/(Ext2Sys
->blocksize
/SECTOR_SIZE
)));
465 ASSERT( (inode
.i_blocks
/(Ext2Sys
->blocksize
/SECTOR_SIZE
)) ==
466 Ext2TotalBlocks(Ext2Sys
, inode
.i_size
/Ext2Sys
->blocksize
));
470 ext2_save_inode(Ext2Sys
, ino
, &inode
);
476 RtlFreeHeap(RtlGetProcessHeap(), 0, buf
);
483 * This function forces out the primary superblock. We need to only
484 * write out those fields which we have changed, since if the
485 * filesystem is mounted, it may have changed some of the other
488 * It takes as input a superblock which has already been byte swapped
492 bool write_primary_superblock(PEXT2_FILESYS Ext2Sys
, PEXT2_SUPER_BLOCK super
)
496 bRet
= NT_SUCCESS(Ext2WriteDisk(
498 ((LONGLONG
)SUPERBLOCK_OFFSET
),
499 SUPERBLOCK_SIZE
, (PUCHAR
)super
));
508 * Updates the revision to EXT2_DYNAMIC_REV
510 void ext2_update_dynamic_rev(PEXT2_FILESYS fs
)
512 PEXT2_SUPER_BLOCK sb
= fs
->ext2_sb
;
514 if (sb
->s_rev_level
> EXT2_GOOD_OLD_REV
)
517 sb
->s_rev_level
= EXT2_DYNAMIC_REV
;
518 sb
->s_first_ino
= EXT2_GOOD_OLD_FIRST_INO
;
519 sb
->s_inode_size
= EXT2_GOOD_OLD_INODE_SIZE
;
520 /* s_uuid is handled by e2fsck already */
521 /* other fields should be left alone */
525 bool ext2_flush(PEXT2_FILESYS fs
)
527 ULONG i
,j
,maxgroup
,sgrp
;
531 unsigned long fs_state
;
532 PEXT2_SUPER_BLOCK super_shadow
= 0;
533 PEXT2_GROUP_DESC group_shadow
= 0;
535 LARGE_INTEGER SysTime
;
537 NtQuerySystemTime(&SysTime
);
539 fs_state
= fs
->ext2_sb
->s_state
;
541 RtlTimeToSecondsSince1970(&SysTime
, &fs
->ext2_sb
->s_wtime
);
542 fs
->ext2_sb
->s_block_group_nr
= 0;
544 super_shadow
= fs
->ext2_sb
;
545 group_shadow
= fs
->group_desc
;
548 * Write out master superblock. This has to be done
549 * separately, since it is located at a fixed location
550 * (SUPERBLOCK_OFFSET).
552 retval
= write_primary_superblock(fs
, super_shadow
);
557 * If this is an external journal device, don't write out the
558 * block group descriptors or any of the backup superblocks
560 if (fs
->ext2_sb
->s_feature_incompat
&
561 EXT3_FEATURE_INCOMPAT_JOURNAL_DEV
)
568 * Set the state of the FS to be non-valid. (The state has
569 * already been backed up earlier, and will be restored when
572 fs
->ext2_sb
->s_state
&= ~EXT2_VALID_FS
;
575 * Write out the master group descriptors, and the backup
576 * superblocks and group descriptors.
578 group_block
= fs
->ext2_sb
->s_first_data_block
;
579 maxgroup
= fs
->group_desc_count
;
581 for (i
= 0; i
< maxgroup
; i
++)
583 if (!ext2_bg_has_super(fs
->ext2_sb
, i
))
587 if (sgrp
> ((1 << 16) - 1))
588 sgrp
= (1 << 16) - 1;
590 fs
->ext2_sb
->s_block_group_nr
= (USHORT
) sgrp
;
594 retval
= NT_SUCCESS(Ext2WriteDisk(
596 ((ULONGLONG
)group_block
* fs
->blocksize
),
597 SUPERBLOCK_SIZE
, (PUCHAR
)super_shadow
));
605 group_ptr
= (char *) group_shadow
;
607 for (j
=0; j
< fs
->desc_blocks
; j
++)
610 retval
= NT_SUCCESS(Ext2WriteDisk(
612 ((ULONGLONG
)(group_block
+1+j
) * fs
->blocksize
),
613 fs
->blocksize
, (PUCHAR
) group_ptr
));
620 group_ptr
+= fs
->blocksize
;
624 group_block
+= EXT2_BLOCKS_PER_GROUP(fs
->ext2_sb
);
628 fs
->ext2_sb
->s_block_group_nr
= 0;
631 * If the write_bitmaps() function is present, call it to
632 * flush the bitmaps. This is done this way so that a simple
633 * program that doesn't mess with the bitmaps doesn't need to
634 * drag in the bitmaps.c code.
636 retval
= ext2_write_bitmaps(fs
);
641 * Flush the blocks out to disk
644 // retval = io_channel_flush(fs->io);
648 fs
->ext2_sb
->s_state
= (USHORT
) fs_state
;
654 bool create_journal_dev(PEXT2_FILESYS fs
)
663 DPRINT1("Mke2fs: ext2_create_journal_dev: while initializing journal superblock.\n");
667 DPRINT("Mke2fs: Zeroing journal device: \n");
669 retval
= zero_blocks(fs
, 0, fs
->ext2_sb
->s_blocks_count
,
672 zero_blocks(0, 0, 0, 0, 0);
676 DPRINT1("Mke2fs: create_journal_dev: while zeroing journal device (block %lu, count %lu).\n",
681 retval
= NT_SUCCESS(Ext2WriteDisk(
683 ((ULONGLONG
)blk
* (fs
->ext2_sb
->s_first_data_block
+1)),
684 fs
->blocksize
, (unsigned char *)buf
));
688 DPRINT1("Mke2fs: create_journal_dev: while writing journal superblock.\n");
695 #define BLOCK_BITS (Ext2Sys->ext2_sb->s_log_block_size + 10)
698 Ext2DataBlocks(PEXT2_FILESYS Ext2Sys
, ULONG TotalBlocks
)
700 ULONG dwData
[4] = {1, 1, 1, 1};
701 ULONG dwMeta
[4] = {0, 0, 0, 0};
702 ULONG DataBlocks
= 0;
705 if (TotalBlocks
<= EXT2_NDIR_BLOCKS
)
710 TotalBlocks
-= EXT2_NDIR_BLOCKS
;
712 for (i
= 0; i
< 4; i
++)
714 dwData
[i
] = dwData
[i
] << ((BLOCK_BITS
- 2) * i
);
718 dwMeta
[i
] = 1 + (dwMeta
[i
- 1] << (BLOCK_BITS
- 2));
722 for( i
=1; (i
< 4) && (TotalBlocks
> 0); i
++)
724 if (TotalBlocks
>= (dwData
[i
] + dwMeta
[i
]))
726 TotalBlocks
-= (dwData
[i
] + dwMeta
[i
]);
727 DataBlocks
+= dwData
[i
];
734 for (j
=i
; (j
> 0) && (TotalBlocks
> 0); j
--)
736 dwDivide
= (TotalBlocks
- 1) / (dwData
[j
-1] + dwMeta
[j
-1]);
737 dwRemain
= (TotalBlocks
- 1) % (dwData
[j
-1] + dwMeta
[j
-1]);
739 DataBlocks
+= (dwDivide
* dwData
[j
-1]);
740 TotalBlocks
= dwRemain
;
745 return (DataBlocks
+ EXT2_NDIR_BLOCKS
);
750 Ext2TotalBlocks(PEXT2_FILESYS Ext2Sys
, ULONG DataBlocks
)
752 ULONG dwData
[4] = {1, 1, 1, 1};
753 ULONG dwMeta
[4] = {0, 0, 0, 0};
754 ULONG TotalBlocks
= 0;
757 if (DataBlocks
<= EXT2_NDIR_BLOCKS
)
762 DataBlocks
-= EXT2_NDIR_BLOCKS
;
764 for (i
= 0; i
< 4; i
++)
766 dwData
[i
] = dwData
[i
] << ((BLOCK_BITS
- 2) * i
);
770 dwMeta
[i
] = 1 + (dwMeta
[i
- 1] << (BLOCK_BITS
- 2));
774 for( i
=1; (i
< 4) && (DataBlocks
> 0); i
++)
776 if (DataBlocks
>= dwData
[i
])
778 DataBlocks
-= dwData
[i
];
779 TotalBlocks
+= (dwData
[i
] + dwMeta
[i
]);
786 for (j
=i
; (j
> 0) && (DataBlocks
> 0); j
--)
788 dwDivide
= (DataBlocks
) / (dwData
[j
-1]);
789 dwRemain
= (DataBlocks
) % (dwData
[j
-1]);
791 TotalBlocks
+= (dwDivide
* (dwData
[j
-1] + dwMeta
[j
-1]) + 1);
792 DataBlocks
= dwRemain
;
797 return (TotalBlocks
+ EXT2_NDIR_BLOCKS
);
803 Ext2Format(IN PUNICODE_STRING DriveRoot
,
804 IN FMIFS_MEDIA_FLAG MediaFlag
,
805 IN PUNICODE_STRING Label
,
806 IN BOOLEAN QuickFormat
,
807 IN ULONG ClusterSize
,
808 IN PFMIFSCALLBACK Callback
)
810 BOOLEAN bRet
= FALSE
;
811 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
812 /* Super Block: 1024 bytes long */
813 EXT2_SUPER_BLOCK Ext2Sb
;
814 /* File Sys Structure */
815 EXT2_FILESYS FileSys
;
823 Callback(PROGRESS
, 0, (PVOID
)&Percent
);
826 RtlZeroMemory(&Ext2Sb
, sizeof(EXT2_SUPER_BLOCK
));
827 RtlZeroMemory(&FileSys
, sizeof(EXT2_FILESYS
));
828 FileSys
.ext2_sb
= &Ext2Sb
;
831 if (!NT_SUCCESS(Ext2OpenDevice(&FileSys
, DriveRoot
)))
833 DPRINT1("Mke2fs: Volume %wZ does not exist, ...\n", DriveRoot
);
838 if (!NT_SUCCESS(Ext2GetMediaInfo(&FileSys
)))
840 DPRINT1("Mke2fs: Can't get media information\n");
844 set_fs_defaults(NULL
, &Ext2Sb
, ClusterSize
, &inode_ratio
);
846 Ext2Sb
.s_blocks_count
= FileSys
.PartInfo
.PartitionLength
.QuadPart
/
847 EXT2_BLOCK_SIZE(&Ext2Sb
);
851 * Calculate number of inodes based on the inode ratio
853 Ext2Sb
.s_inodes_count
=
854 (ULONG
)(((LONGLONG
) Ext2Sb
.s_blocks_count
* EXT2_BLOCK_SIZE(&Ext2Sb
)) / inode_ratio
);
857 * Calculate number of blocks to reserve
859 Ext2Sb
.s_r_blocks_count
= (Ext2Sb
.s_blocks_count
* 5) / 100;
862 Status
= Ext2LockVolume(&FileSys
);
863 if (NT_SUCCESS(Status
))
870 if (!ext2_initialize_sb(&FileSys
))
872 DPRINT1("Mke2fs: error...\n");
877 zap_sector(&FileSys
, 2, 6);
880 * Generate a UUID for it...
884 uuid_generate(&uuid
[0]);
885 memcpy(&Ext2Sb
.s_uuid
[0], &uuid
[0], 16);
889 * Add "jitter" to the superblock's check interval so that we
890 * don't check all the filesystems at the same time. We use a
891 * kludgy hack of using the UUID to derive a random jitter value.
896 for (i
= 0, val
= 0 ; i
< sizeof(Ext2Sb
.s_uuid
); i
++)
897 val
+= Ext2Sb
.s_uuid
[i
];
899 Ext2Sb
.s_max_mnt_count
+= val
% EXT2_DFL_MAX_MNT_COUNT
;
903 * Set the volume label...
907 ANSI_STRING ansi_label
;
908 ansi_label
.MaximumLength
= sizeof(Ext2Sb
.s_volume_name
);
909 ansi_label
.Length
= 0;
910 ansi_label
.Buffer
= Ext2Sb
.s_volume_name
;
911 RtlUnicodeStringToAnsiString(&ansi_label
, Label
, FALSE
);
914 ext2_print_super(&Ext2Sb
);
916 bRet
= ext2_allocate_tables(&FileSys
);
923 /* rsv must be a power of two (64kB is MD RAID sb alignment) */
924 rsv
= 65536 / FileSys
.blocksize
;
925 blocks
= Ext2Sb
.s_blocks_count
;
928 zap_sector(&FileSys
, 0, 2);
932 * Wipe out any old MD RAID (or other) metadata at the end
933 * of the device. This will also verify that the device is
934 * as large as we think. Be careful with very small devices.
937 start
= (blocks
& ~(rsv
- 1));
942 bRet
= zero_blocks(&FileSys
, start
, blocks
- start
, &ret_blk
, NULL
);
946 DPRINT1("Mke2fs: zeroing block %lu at end of filesystem", ret_blk
);
950 write_inode_tables(&FileSys
);
952 create_root_dir(&FileSys
);
953 create_lost_and_found(&FileSys
);
955 ext2_reserve_inodes(&FileSys
);
957 create_bad_block_inode(&FileSys
, NULL
);
959 DPRINT("Mke2fs: Writing superblocks and filesystem accounting information ... \n");
963 DPRINT1("Mke2fs: Slow format not supported yet\n");
966 if (!ext2_flush(&FileSys
))
969 DPRINT1("Mke2fs: Warning, had trouble writing out superblocks.\n");
973 DPRINT("Mke2fs: Writing superblocks and filesystem accounting information done!\n");
976 Status
= STATUS_SUCCESS
;
981 ext2_free_group_desc(&FileSys
);
983 ext2_free_block_bitmap(&FileSys
);
984 ext2_free_inode_bitmap(&FileSys
);
988 Ext2DisMountVolume(&FileSys
);
994 Ext2UnLockVolume(&FileSys
);
998 Ext2CloseDevice(&FileSys
);
1000 Callback(DONE
, 0, (PVOID
)&bRet
);
1007 Ext2Chkdsk(IN PUNICODE_STRING DriveRoot
,
1008 IN BOOLEAN FixErrors
,
1010 IN BOOLEAN CheckOnlyIfDirty
,
1011 IN BOOLEAN ScanDrive
,
1012 IN PFMIFSCALLBACK Callback
)
1015 return STATUS_SUCCESS
;