mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 12:26:32 +00:00
1024 lines
23 KiB
C
1024 lines
23 KiB
C
/*
|
|
* PROJECT: Mke2fs
|
|
* FILE: Disk.c
|
|
* PROGRAMMER: Matt Wu <mattwu@163.com>
|
|
* HOMEPAGE: http://ext2.yeah.net
|
|
*/
|
|
|
|
/* INCLUDES **************************************************************/
|
|
|
|
#include "Mke2fs.h"
|
|
|
|
#include <fmifs/fmifs.h>
|
|
#include <debug.h>
|
|
|
|
/* GLOBALS ***************************************************************/
|
|
|
|
int inode_ratio = 4096;
|
|
|
|
BOOLEAN bLocked = FALSE;
|
|
|
|
/* This is needed for the ext2fs driver to mount the volume */
|
|
#define ZAP_BOOTBLOCK
|
|
|
|
/* FUNCTIONS *************************************************************/
|
|
|
|
int int_log2(int arg)
|
|
{
|
|
int l = 0;
|
|
|
|
arg >>= 1;
|
|
|
|
while (arg)
|
|
{
|
|
l++;
|
|
arg >>= 1;
|
|
}
|
|
|
|
return l;
|
|
}
|
|
|
|
int int_log10(unsigned int arg)
|
|
{
|
|
int l;
|
|
|
|
for (l=0; arg ; l++)
|
|
arg = arg / 10;
|
|
|
|
return l;
|
|
}
|
|
|
|
|
|
static char default_str[] = "default";
|
|
|
|
struct mke2fs_defaults {
|
|
const char *type;
|
|
int size;
|
|
int blocksize;
|
|
int inode_ratio;
|
|
} settings[] = {
|
|
{ default_str, 0, 4096, 8192 },
|
|
{ default_str, 512, 1024, 4096 },
|
|
{ default_str, 3, 1024, 8192 },
|
|
{ "journal", 0, 4096, 8192 },
|
|
{ "news", 0, 4096, 4096 },
|
|
{ "largefile", 0, 4096, 1024 * 1024 },
|
|
{ "largefile4", 0, 4096, 4096 * 1024 },
|
|
{ 0, 0, 0, 0},
|
|
};
|
|
|
|
void set_fs_defaults(const char *fs_type,
|
|
PEXT2_SUPER_BLOCK super,
|
|
int blocksize, int *inode_ratio)
|
|
{
|
|
int megs;
|
|
int ratio = 0;
|
|
struct mke2fs_defaults *p;
|
|
|
|
megs = (super->s_blocks_count * (EXT2_BLOCK_SIZE(super) / 1024) / 1024);
|
|
|
|
if (inode_ratio)
|
|
ratio = *inode_ratio;
|
|
|
|
if (!fs_type)
|
|
fs_type = default_str;
|
|
|
|
for (p = settings; p->type; p++)
|
|
{
|
|
if ((strcmp(p->type, fs_type) != 0) &&
|
|
(strcmp(p->type, default_str) != 0))
|
|
continue;
|
|
|
|
if ((p->size != 0) &&
|
|
(megs > p->size))
|
|
continue;
|
|
|
|
if (ratio == 0)
|
|
*inode_ratio = p->inode_ratio;
|
|
|
|
if (blocksize == 0)
|
|
{
|
|
super->s_log_frag_size = super->s_log_block_size =
|
|
int_log2(p->blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
|
|
}
|
|
}
|
|
|
|
if (blocksize == 0)
|
|
{
|
|
super->s_blocks_count /= EXT2_BLOCK_SIZE(super) / 1024;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Helper function which zeros out _num_ blocks starting at _blk_. In
|
|
* case of an error, the details of the error is returned via _ret_blk_
|
|
* and _ret_count_ if they are non-NULL pointers. Returns 0 on
|
|
* success, and an error code on an error.
|
|
*
|
|
* As a special case, if the first argument is NULL, then it will
|
|
* attempt to free the static zeroizing buffer. (This is to keep
|
|
* programs that check for memory leaks happy.)
|
|
*/
|
|
bool zero_blocks(PEXT2_FILESYS fs, ULONG blk, ULONG num,
|
|
ULONG *ret_blk, ULONG *ret_count)
|
|
{
|
|
ULONG j, count;
|
|
static unsigned char *buf;
|
|
bool retval;
|
|
|
|
/* If fs is null, clean up the static buffer and return */
|
|
if (!fs)
|
|
{
|
|
if (buf)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, buf);
|
|
buf = 0;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#define STRIDE_LENGTH 8
|
|
|
|
/* Allocate the zeroizing buffer if necessary */
|
|
if (!buf)
|
|
{
|
|
buf = (unsigned char *)
|
|
RtlAllocateHeap(RtlGetProcessHeap(), 0, fs->blocksize * STRIDE_LENGTH);
|
|
if (!buf)
|
|
{
|
|
DPRINT1("Mke2fs: while allocating zeroizing buffer");
|
|
if (ret_blk)
|
|
*ret_blk = blk;
|
|
return false;
|
|
}
|
|
memset(buf, 0, fs->blocksize * STRIDE_LENGTH);
|
|
}
|
|
|
|
/* OK, do the write loop */
|
|
for (j=0; j < num; j += STRIDE_LENGTH, blk += STRIDE_LENGTH)
|
|
{
|
|
if (num-j > STRIDE_LENGTH)
|
|
count = STRIDE_LENGTH;
|
|
else
|
|
count = num - j;
|
|
|
|
retval = NT_SUCCESS(Ext2WriteDisk(
|
|
fs,
|
|
((ULONGLONG)blk * fs->blocksize),
|
|
count * fs->blocksize,
|
|
buf));
|
|
|
|
if (!retval)
|
|
{
|
|
if (ret_count)
|
|
*ret_count = count;
|
|
|
|
if (ret_blk)
|
|
*ret_blk = blk;
|
|
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool zap_sector(PEXT2_FILESYS Ext2Sys, int sect, int nsect)
|
|
{
|
|
unsigned char *buf;
|
|
ULONG *magic;
|
|
|
|
buf = (unsigned char *)
|
|
RtlAllocateHeap(RtlGetProcessHeap(), 0, SECTOR_SIZE*nsect);
|
|
if (!buf)
|
|
{
|
|
DPRINT1("Mke2fs: Out of memory erasing sectors %d-%d\n",
|
|
sect, sect + nsect - 1);
|
|
return false;
|
|
}
|
|
|
|
#define BSD_DISKMAGIC (0x82564557UL) /* The disk magic number */
|
|
#define BSD_MAGICDISK (0x57455682UL) /* The disk magic number reversed */
|
|
#define BSD_LABEL_OFFSET 64
|
|
|
|
if (sect == 0)
|
|
{
|
|
Ext2ReadDisk(
|
|
Ext2Sys,
|
|
(LONGLONG)(sect * SECTOR_SIZE),
|
|
SECTOR_SIZE, buf);
|
|
|
|
// Check for a BSD disklabel, and don't erase it if so
|
|
magic = (ULONG *) (buf + BSD_LABEL_OFFSET);
|
|
if ((*magic == BSD_DISKMAGIC) || (*magic == BSD_MAGICDISK))
|
|
goto clean_up;
|
|
}
|
|
|
|
memset(buf, 0, (ULONG)nsect * SECTOR_SIZE);
|
|
|
|
// Write buf to disk
|
|
Ext2WriteDisk( Ext2Sys,
|
|
(LONGLONG)(sect * SECTOR_SIZE),
|
|
(ULONG)nsect * SECTOR_SIZE,
|
|
buf );
|
|
|
|
clean_up:
|
|
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, buf);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ext2_mkdir( PEXT2_FILESYS fs,
|
|
ULONG parent,
|
|
ULONG inum,
|
|
char *name,
|
|
ULONG *no,
|
|
PEXT2_INODE pid )
|
|
{
|
|
bool retval;
|
|
EXT2_INODE parent_inode, inode;
|
|
ULONG ino = inum;
|
|
//ULONG scratch_ino;
|
|
ULONG blk;
|
|
char *block = 0;
|
|
int filetype = 0;
|
|
|
|
LARGE_INTEGER SysTime;
|
|
|
|
NtQuerySystemTime(&SysTime);
|
|
|
|
/*
|
|
* Allocate an inode, if necessary
|
|
*/
|
|
if (!ino)
|
|
{
|
|
retval = ext2_new_inode(fs, parent, LINUX_S_IFDIR | 0755, 0, &ino);
|
|
if (!retval)
|
|
goto cleanup;
|
|
}
|
|
|
|
if (no)
|
|
*no = ino;
|
|
|
|
/*
|
|
* Allocate a data block for the directory
|
|
*/
|
|
retval = ext2_new_block(fs, 0, 0, &blk);
|
|
if (!retval)
|
|
goto cleanup;
|
|
|
|
/*
|
|
* Create a scratch template for the directory
|
|
*/
|
|
retval = ext2_new_dir_block(fs, ino, parent, &block);
|
|
if (!retval)
|
|
goto cleanup;
|
|
|
|
/*
|
|
* Get the parent's inode, if necessary
|
|
*/
|
|
if (parent != ino)
|
|
{
|
|
retval = ext2_load_inode(fs, parent, &parent_inode);
|
|
if (!retval)
|
|
goto cleanup;
|
|
}
|
|
else
|
|
{
|
|
memset(&parent_inode, 0, sizeof(parent_inode));
|
|
}
|
|
|
|
/*
|
|
* Create the inode structure....
|
|
*/
|
|
memset(&inode, 0, sizeof(EXT2_INODE));
|
|
inode.i_mode = (USHORT)(LINUX_S_IFDIR | (0777 & ~fs->umask));
|
|
inode.i_uid = inode.i_gid = 0;
|
|
inode.i_blocks = fs->blocksize / 512;
|
|
inode.i_block[0] = blk;
|
|
inode.i_links_count = 2;
|
|
RtlTimeToSecondsSince1970(&SysTime, &inode.i_mtime);
|
|
inode.i_ctime = inode.i_atime = inode.i_mtime;
|
|
inode.i_size = fs->blocksize;
|
|
|
|
/*
|
|
* Write out the inode and inode data block
|
|
*/
|
|
retval = ext2_write_block(fs, blk, block);
|
|
if (!retval)
|
|
goto cleanup;
|
|
|
|
retval = ext2_save_inode(fs, ino, &inode);
|
|
if (!retval)
|
|
goto cleanup;
|
|
|
|
if (pid)
|
|
{
|
|
*pid = inode;
|
|
}
|
|
|
|
if (parent != ino)
|
|
{
|
|
/*
|
|
* Add entry for this inode to parent dir 's block
|
|
*/
|
|
|
|
if (fs->ext2_sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
|
|
filetype = EXT2_FT_DIR;
|
|
|
|
retval = ext2_add_entry(fs, parent, ino, filetype, name);
|
|
|
|
if (!retval)
|
|
goto cleanup;
|
|
|
|
/*
|
|
* Update parent inode's counts
|
|
*/
|
|
|
|
parent_inode.i_links_count++;
|
|
retval = ext2_save_inode(fs, parent, &parent_inode);
|
|
if (!retval)
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
/*
|
|
* Update accounting....
|
|
*/
|
|
ext2_block_alloc_stats(fs, blk, +1);
|
|
ext2_inode_alloc_stats2(fs, ino, +1, 1);
|
|
|
|
cleanup:
|
|
|
|
if (block)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, block);
|
|
block = NULL;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
bool create_root_dir(PEXT2_FILESYS fs)
|
|
{
|
|
bool retval;
|
|
EXT2_INODE inode;
|
|
|
|
retval = ext2_mkdir(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, 0, NULL, &inode);
|
|
|
|
if (!retval)
|
|
{
|
|
DPRINT1("Mke2fs: while creating root dir");
|
|
return false;
|
|
}
|
|
|
|
{
|
|
inode.i_uid = 0;
|
|
inode.i_gid = 0;
|
|
|
|
retval = ext2_save_inode(fs, EXT2_ROOT_INO, &inode);
|
|
if (!retval)
|
|
{
|
|
DPRINT1("Mke2fs: while setting root inode ownership");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool create_lost_and_found(PEXT2_FILESYS Ext2Sys)
|
|
{
|
|
bool retval;
|
|
ULONG ino;
|
|
char *name = "lost+found";
|
|
int lpf_size = 0;
|
|
EXT2_INODE inode;
|
|
ULONG dwBlk = 0;
|
|
BOOLEAN bExt= TRUE;
|
|
|
|
PEXT2_DIR_ENTRY dir;
|
|
|
|
char * buf;
|
|
|
|
buf = (char *)RtlAllocateHeap(RtlGetProcessHeap(), 0, Ext2Sys->blocksize);
|
|
if (!buf)
|
|
{
|
|
bExt = FALSE;
|
|
}
|
|
else
|
|
{
|
|
memset(buf, 0, Ext2Sys->blocksize);
|
|
|
|
dir = (PEXT2_DIR_ENTRY) buf;
|
|
dir->rec_len = Ext2Sys->blocksize;
|
|
}
|
|
|
|
Ext2Sys->umask = 077;
|
|
retval = ext2_mkdir(Ext2Sys, EXT2_ROOT_INO, 0, name, &ino, &inode);
|
|
|
|
if (!retval)
|
|
{
|
|
DPRINT1("Mke2fs: while creating /lost+found.\n");
|
|
return false;
|
|
}
|
|
|
|
if (!bExt)
|
|
goto errorout;
|
|
|
|
lpf_size = inode.i_size;
|
|
|
|
while(TRUE)
|
|
{
|
|
if (lpf_size >= 16*1024)
|
|
break;
|
|
|
|
retval = ext2_alloc_block(Ext2Sys, 0, &dwBlk);
|
|
|
|
if (! retval)
|
|
{
|
|
DPRINT1("Mke2fs: create_lost_and_found: error alloc block.\n");
|
|
break;
|
|
}
|
|
|
|
retval = ext2_expand_inode(Ext2Sys, &inode, dwBlk);
|
|
if (!retval)
|
|
{
|
|
DPRINT1("Mke2fs: errors when expanding /lost+found.\n");
|
|
break;
|
|
}
|
|
|
|
ext2_write_block(Ext2Sys, dwBlk, buf);
|
|
|
|
inode.i_blocks += (Ext2Sys->blocksize/SECTOR_SIZE);
|
|
lpf_size += Ext2Sys->blocksize;
|
|
}
|
|
|
|
{
|
|
inode.i_size = lpf_size;
|
|
|
|
ASSERT( (inode.i_size/Ext2Sys->blocksize) ==
|
|
Ext2DataBlocks(Ext2Sys, inode.i_blocks/(Ext2Sys->blocksize/SECTOR_SIZE)));
|
|
|
|
ASSERT( (inode.i_blocks/(Ext2Sys->blocksize/SECTOR_SIZE)) ==
|
|
Ext2TotalBlocks(Ext2Sys, inode.i_size/Ext2Sys->blocksize));
|
|
|
|
}
|
|
|
|
ext2_save_inode(Ext2Sys, ino, &inode);
|
|
|
|
errorout:
|
|
|
|
if (buf)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, buf);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* This function forces out the primary superblock. We need to only
|
|
* write out those fields which we have changed, since if the
|
|
* filesystem is mounted, it may have changed some of the other
|
|
* fields.
|
|
*
|
|
* It takes as input a superblock which has already been byte swapped
|
|
* (if necessary).
|
|
*/
|
|
|
|
bool write_primary_superblock(PEXT2_FILESYS Ext2Sys, PEXT2_SUPER_BLOCK super)
|
|
{
|
|
bool bRet;
|
|
|
|
bRet = NT_SUCCESS(Ext2WriteDisk(
|
|
Ext2Sys,
|
|
((LONGLONG)SUPERBLOCK_OFFSET),
|
|
SUPERBLOCK_SIZE, (PUCHAR)super));
|
|
|
|
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*
|
|
* Updates the revision to EXT2_DYNAMIC_REV
|
|
*/
|
|
void ext2_update_dynamic_rev(PEXT2_FILESYS fs)
|
|
{
|
|
PEXT2_SUPER_BLOCK sb = fs->ext2_sb;
|
|
|
|
if (sb->s_rev_level > EXT2_GOOD_OLD_REV)
|
|
return;
|
|
|
|
sb->s_rev_level = EXT2_DYNAMIC_REV;
|
|
sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
|
|
sb->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
|
|
/* s_uuid is handled by e2fsck already */
|
|
/* other fields should be left alone */
|
|
}
|
|
|
|
|
|
bool ext2_flush(PEXT2_FILESYS fs)
|
|
{
|
|
ULONG i,j,maxgroup,sgrp;
|
|
ULONG group_block;
|
|
bool retval;
|
|
char *group_ptr;
|
|
unsigned long fs_state;
|
|
PEXT2_SUPER_BLOCK super_shadow = 0;
|
|
PEXT2_GROUP_DESC group_shadow = 0;
|
|
|
|
LARGE_INTEGER SysTime;
|
|
|
|
NtQuerySystemTime(&SysTime);
|
|
|
|
fs_state = fs->ext2_sb->s_state;
|
|
|
|
RtlTimeToSecondsSince1970(&SysTime, &fs->ext2_sb->s_wtime);
|
|
fs->ext2_sb->s_block_group_nr = 0;
|
|
|
|
super_shadow = fs->ext2_sb;
|
|
group_shadow = fs->group_desc;
|
|
|
|
/*
|
|
* Write out master superblock. This has to be done
|
|
* separately, since it is located at a fixed location
|
|
* (SUPERBLOCK_OFFSET).
|
|
*/
|
|
retval = write_primary_superblock(fs, super_shadow);
|
|
if (!retval)
|
|
goto errout;
|
|
|
|
/*
|
|
* If this is an external journal device, don't write out the
|
|
* block group descriptors or any of the backup superblocks
|
|
*/
|
|
if (fs->ext2_sb->s_feature_incompat &
|
|
EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
|
|
{
|
|
retval = false;
|
|
goto errout;
|
|
}
|
|
|
|
/*
|
|
* Set the state of the FS to be non-valid. (The state has
|
|
* already been backed up earlier, and will be restored when
|
|
* we exit.)
|
|
*/
|
|
fs->ext2_sb->s_state &= ~EXT2_VALID_FS;
|
|
|
|
/*
|
|
* Write out the master group descriptors, and the backup
|
|
* superblocks and group descriptors.
|
|
*/
|
|
group_block = fs->ext2_sb->s_first_data_block;
|
|
maxgroup = fs->group_desc_count;
|
|
|
|
for (i = 0; i < maxgroup; i++)
|
|
{
|
|
if (!ext2_bg_has_super(fs->ext2_sb, i))
|
|
goto next_group;
|
|
|
|
sgrp = i;
|
|
if (sgrp > ((1 << 16) - 1))
|
|
sgrp = (1 << 16) - 1;
|
|
|
|
fs->ext2_sb->s_block_group_nr = (USHORT) sgrp;
|
|
|
|
if (i !=0 )
|
|
{
|
|
retval = NT_SUCCESS(Ext2WriteDisk(
|
|
fs,
|
|
((ULONGLONG)group_block * fs->blocksize),
|
|
SUPERBLOCK_SIZE, (PUCHAR)super_shadow));
|
|
|
|
if (!retval)
|
|
{
|
|
goto errout;
|
|
}
|
|
}
|
|
|
|
group_ptr = (char *) group_shadow;
|
|
|
|
for (j=0; j < fs->desc_blocks; j++)
|
|
{
|
|
|
|
retval = NT_SUCCESS(Ext2WriteDisk(
|
|
fs,
|
|
((ULONGLONG)(group_block+1+j) * fs->blocksize),
|
|
fs->blocksize, (PUCHAR) group_ptr));
|
|
|
|
if (!retval)
|
|
{
|
|
goto errout;
|
|
}
|
|
|
|
group_ptr += fs->blocksize;
|
|
}
|
|
|
|
next_group:
|
|
group_block += EXT2_BLOCKS_PER_GROUP(fs->ext2_sb);
|
|
|
|
}
|
|
|
|
fs->ext2_sb->s_block_group_nr = 0;
|
|
|
|
/*
|
|
* If the write_bitmaps() function is present, call it to
|
|
* flush the bitmaps. This is done this way so that a simple
|
|
* program that doesn't mess with the bitmaps doesn't need to
|
|
* drag in the bitmaps.c code.
|
|
*/
|
|
retval = ext2_write_bitmaps(fs);
|
|
if (!retval)
|
|
goto errout;
|
|
|
|
/*
|
|
* Flush the blocks out to disk
|
|
*/
|
|
|
|
// retval = io_channel_flush(fs->io);
|
|
|
|
errout:
|
|
|
|
fs->ext2_sb->s_state = (USHORT) fs_state;
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
bool create_journal_dev(PEXT2_FILESYS fs)
|
|
{
|
|
bool retval = false;
|
|
char *buf = NULL;
|
|
ULONG blk;
|
|
ULONG count;
|
|
|
|
if (!retval)
|
|
{
|
|
DPRINT1("Mke2fs: ext2_create_journal_dev: while initializing journal superblock.\n");
|
|
return false;
|
|
}
|
|
|
|
DPRINT("Mke2fs: Zeroing journal device: \n");
|
|
|
|
retval = zero_blocks(fs, 0, fs->ext2_sb->s_blocks_count,
|
|
&blk, &count);
|
|
|
|
zero_blocks(0, 0, 0, 0, 0);
|
|
|
|
if (!retval)
|
|
{
|
|
DPRINT1("Mke2fs: create_journal_dev: while zeroing journal device (block %lu, count %lu).\n",
|
|
blk, count);
|
|
return false;
|
|
}
|
|
|
|
retval = NT_SUCCESS(Ext2WriteDisk(
|
|
fs,
|
|
((ULONGLONG)blk * (fs->ext2_sb->s_first_data_block+1)),
|
|
fs->blocksize, (unsigned char *)buf));
|
|
|
|
if (!retval)
|
|
{
|
|
DPRINT1("Mke2fs: create_journal_dev: while writing journal superblock.\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#define BLOCK_BITS (Ext2Sys->ext2_sb->s_log_block_size + 10)
|
|
|
|
ULONG
|
|
Ext2DataBlocks(PEXT2_FILESYS Ext2Sys, ULONG TotalBlocks)
|
|
{
|
|
ULONG dwData[4] = {1, 1, 1, 1};
|
|
ULONG dwMeta[4] = {0, 0, 0, 0};
|
|
ULONG DataBlocks = 0;
|
|
ULONG i, j;
|
|
|
|
if (TotalBlocks <= EXT2_NDIR_BLOCKS)
|
|
{
|
|
return TotalBlocks;
|
|
}
|
|
|
|
TotalBlocks -= EXT2_NDIR_BLOCKS;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
dwData[i] = dwData[i] << ((BLOCK_BITS - 2) * i);
|
|
|
|
if (i > 0)
|
|
{
|
|
dwMeta[i] = 1 + (dwMeta[i - 1] << (BLOCK_BITS - 2));
|
|
}
|
|
}
|
|
|
|
for( i=1; (i < 4) && (TotalBlocks > 0); i++)
|
|
{
|
|
if (TotalBlocks >= (dwData[i] + dwMeta[i]))
|
|
{
|
|
TotalBlocks -= (dwData[i] + dwMeta[i]);
|
|
DataBlocks += dwData[i];
|
|
}
|
|
else
|
|
{
|
|
ULONG dwDivide = 0;
|
|
ULONG dwRemain = 0;
|
|
|
|
for (j=i; (j > 0) && (TotalBlocks > 0); j--)
|
|
{
|
|
dwDivide = (TotalBlocks - 1) / (dwData[j-1] + dwMeta[j-1]);
|
|
dwRemain = (TotalBlocks - 1) % (dwData[j-1] + dwMeta[j-1]);
|
|
|
|
DataBlocks += (dwDivide * dwData[j-1]);
|
|
TotalBlocks = dwRemain;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (DataBlocks + EXT2_NDIR_BLOCKS);
|
|
}
|
|
|
|
|
|
ULONG
|
|
Ext2TotalBlocks(PEXT2_FILESYS Ext2Sys, ULONG DataBlocks)
|
|
{
|
|
ULONG dwData[4] = {1, 1, 1, 1};
|
|
ULONG dwMeta[4] = {0, 0, 0, 0};
|
|
ULONG TotalBlocks = 0;
|
|
ULONG i, j;
|
|
|
|
if (DataBlocks <= EXT2_NDIR_BLOCKS)
|
|
{
|
|
return DataBlocks;
|
|
}
|
|
|
|
DataBlocks -= EXT2_NDIR_BLOCKS;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
dwData[i] = dwData[i] << ((BLOCK_BITS - 2) * i);
|
|
|
|
if (i > 0)
|
|
{
|
|
dwMeta[i] = 1 + (dwMeta[i - 1] << (BLOCK_BITS - 2));
|
|
}
|
|
}
|
|
|
|
for( i=1; (i < 4) && (DataBlocks > 0); i++)
|
|
{
|
|
if (DataBlocks >= dwData[i])
|
|
{
|
|
DataBlocks -= dwData[i];
|
|
TotalBlocks += (dwData[i] + dwMeta[i]);
|
|
}
|
|
else
|
|
{
|
|
ULONG dwDivide = 0;
|
|
ULONG dwRemain = 0;
|
|
|
|
for (j=i; (j > 0) && (DataBlocks > 0); j--)
|
|
{
|
|
dwDivide = (DataBlocks) / (dwData[j-1]);
|
|
dwRemain = (DataBlocks) % (dwData[j-1]);
|
|
|
|
TotalBlocks += (dwDivide * (dwData[j-1] + dwMeta[j-1]) + 1);
|
|
DataBlocks = dwRemain;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (TotalBlocks + EXT2_NDIR_BLOCKS);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
Ext2Format(
|
|
IN PUNICODE_STRING DriveRoot,
|
|
IN PFMIFSCALLBACK Callback,
|
|
IN BOOLEAN QuickFormat,
|
|
IN BOOLEAN BackwardCompatible,
|
|
IN MEDIA_TYPE MediaType,
|
|
IN PUNICODE_STRING Label,
|
|
IN ULONG ClusterSize)
|
|
{
|
|
BOOLEAN bRet;
|
|
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
|
/* Super Block: 1024 bytes long */
|
|
EXT2_SUPER_BLOCK Ext2Sb;
|
|
/* File Sys Structure */
|
|
EXT2_FILESYS FileSys;
|
|
ULONG Percent;
|
|
ULONG rsv;
|
|
ULONG blocks;
|
|
ULONG start;
|
|
ULONG ret_blk;
|
|
|
|
// FIXME:
|
|
UNREFERENCED_PARAMETER(BackwardCompatible);
|
|
UNREFERENCED_PARAMETER(MediaType);
|
|
|
|
if (Callback != NULL)
|
|
{
|
|
Callback(PROGRESS, 0, (PVOID)&Percent);
|
|
}
|
|
|
|
|
|
RtlZeroMemory(&Ext2Sb, sizeof(EXT2_SUPER_BLOCK));
|
|
RtlZeroMemory(&FileSys, sizeof(EXT2_FILESYS));
|
|
FileSys.ext2_sb = &Ext2Sb;
|
|
|
|
|
|
if (!NT_SUCCESS(Ext2OpenDevice(&FileSys, DriveRoot)))
|
|
{
|
|
DPRINT1("Mke2fs: Volume %wZ does not exist, ...\n", DriveRoot);
|
|
goto clean_up;
|
|
}
|
|
|
|
|
|
if (!NT_SUCCESS(Ext2GetMediaInfo(&FileSys)))
|
|
{
|
|
DPRINT1("Mke2fs: Can't get media information\n");
|
|
goto clean_up;
|
|
}
|
|
|
|
set_fs_defaults(NULL, &Ext2Sb, ClusterSize, &inode_ratio);
|
|
|
|
Ext2Sb.s_blocks_count = FileSys.PartInfo.PartitionLength.QuadPart /
|
|
EXT2_BLOCK_SIZE(&Ext2Sb);
|
|
|
|
|
|
/*
|
|
* Calculate number of inodes based on the inode ratio
|
|
*/
|
|
Ext2Sb.s_inodes_count =
|
|
(ULONG)(((LONGLONG) Ext2Sb.s_blocks_count * EXT2_BLOCK_SIZE(&Ext2Sb)) / inode_ratio);
|
|
|
|
/*
|
|
* Calculate number of blocks to reserve
|
|
*/
|
|
Ext2Sb.s_r_blocks_count = (Ext2Sb.s_blocks_count * 5) / 100;
|
|
|
|
|
|
Status = Ext2LockVolume(&FileSys);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
bLocked = TRUE;
|
|
}
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
|
|
// Initialize
|
|
if (!ext2_initialize_sb(&FileSys))
|
|
{
|
|
DPRINT1("Mke2fs: error...\n");
|
|
goto clean_up;
|
|
}
|
|
|
|
|
|
zap_sector(&FileSys, 2, 6);
|
|
|
|
/*
|
|
* Generate a UUID for it...
|
|
*/
|
|
{
|
|
__u8 uuid[16];
|
|
uuid_generate(&uuid[0]);
|
|
memcpy(&Ext2Sb.s_uuid[0], &uuid[0], 16);
|
|
}
|
|
|
|
/*
|
|
* Add "jitter" to the superblock's check interval so that we
|
|
* don't check all the filesystems at the same time. We use a
|
|
* kludgy hack of using the UUID to derive a random jitter value.
|
|
*/
|
|
{
|
|
ULONG i, val;
|
|
|
|
for (i = 0, val = 0 ; i < sizeof(Ext2Sb.s_uuid); i++)
|
|
val += Ext2Sb.s_uuid[i];
|
|
|
|
Ext2Sb.s_max_mnt_count += val % EXT2_DFL_MAX_MNT_COUNT;
|
|
}
|
|
|
|
/*
|
|
* Set the volume label...
|
|
*/
|
|
if (Label)
|
|
{
|
|
ANSI_STRING ansi_label;
|
|
ansi_label.MaximumLength = sizeof(Ext2Sb.s_volume_name);
|
|
ansi_label.Length = 0;
|
|
ansi_label.Buffer = Ext2Sb.s_volume_name;
|
|
RtlUnicodeStringToAnsiString(&ansi_label, Label, FALSE);
|
|
}
|
|
|
|
ext2_print_super(&Ext2Sb);
|
|
|
|
bRet = ext2_allocate_tables(&FileSys);
|
|
if (!bRet)
|
|
{
|
|
goto clean_up;
|
|
}
|
|
|
|
/* rsv must be a power of two (64kB is MD RAID sb alignment) */
|
|
rsv = 65536 / FileSys.blocksize;
|
|
blocks = Ext2Sb.s_blocks_count;
|
|
|
|
#ifdef ZAP_BOOTBLOCK
|
|
DPRINT1("Mke2fs: zeroing volume boot record\n");
|
|
zap_sector(&FileSys, 0, 2);
|
|
#endif
|
|
|
|
/*
|
|
* Wipe out any old MD RAID (or other) metadata at the end
|
|
* of the device. This will also verify that the device is
|
|
* as large as we think. Be careful with very small devices.
|
|
*/
|
|
|
|
start = (blocks & ~(rsv - 1));
|
|
if (start > rsv)
|
|
start -= rsv;
|
|
|
|
bRet = TRUE;
|
|
if (start > 0)
|
|
bRet = zero_blocks(&FileSys, start, blocks - start, &ret_blk, NULL);
|
|
|
|
if (!bRet)
|
|
{
|
|
DPRINT1("Mke2fs: zeroing block %lu at end of filesystem", ret_blk);
|
|
goto clean_up;
|
|
}
|
|
|
|
write_inode_tables(&FileSys);
|
|
|
|
create_root_dir(&FileSys);
|
|
create_lost_and_found(&FileSys);
|
|
|
|
ext2_reserve_inodes(&FileSys);
|
|
|
|
create_bad_block_inode(&FileSys, NULL);
|
|
|
|
DPRINT("Mke2fs: Writing superblocks and filesystem accounting information ... \n");
|
|
|
|
if (!QuickFormat)
|
|
{
|
|
DPRINT1("Mke2fs: Slow format not supported yet\n");
|
|
}
|
|
|
|
if (!ext2_flush(&FileSys))
|
|
{
|
|
DPRINT1("Mke2fs: Warning, had trouble writing out superblocks.\n");
|
|
goto clean_up;
|
|
}
|
|
|
|
DPRINT("Mke2fs: Writing superblocks and filesystem accounting information done!\n");
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
clean_up:
|
|
|
|
// Clean up ...
|
|
ext2_free_group_desc(&FileSys);
|
|
|
|
ext2_free_block_bitmap(&FileSys);
|
|
ext2_free_inode_bitmap(&FileSys);
|
|
|
|
if(bLocked)
|
|
{
|
|
Ext2DisMountVolume(&FileSys);
|
|
Ext2UnLockVolume(&FileSys);
|
|
}
|
|
|
|
Ext2CloseDevice(&FileSys);
|
|
|
|
return NT_SUCCESS(Status);
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
Ext2Chkdsk(
|
|
IN PUNICODE_STRING DriveRoot,
|
|
IN PFMIFSCALLBACK Callback,
|
|
IN BOOLEAN FixErrors,
|
|
IN BOOLEAN Verbose,
|
|
IN BOOLEAN CheckOnlyIfDirty,
|
|
IN BOOLEAN ScanDrive,
|
|
IN PVOID pUnknown1,
|
|
IN PVOID pUnknown2,
|
|
IN PVOID pUnknown3,
|
|
IN PVOID pUnknown4,
|
|
IN PULONG ExitStatus)
|
|
{
|
|
UNIMPLEMENTED;
|
|
*ExitStatus = (ULONG)STATUS_SUCCESS;
|
|
return TRUE;
|
|
}
|