blob: 2a602154b135fc315c65448f6f57f20d9cd62b5d [file] [log] [blame]
/*
ext2.c -- generic ext2 stuff
Copyright (C) 1998, 1999, 2000, 2001, 2007 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <config.h>
#ifndef DISCOVER_ONLY
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <uuid/uuid.h>
#include "ext2.h"
/* ext2 stuff ****************************************************************/
unsigned char _bitmap[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
int ext2_copy_block(struct ext2_fs *fs, blk_t from, blk_t to)
{
unsigned char* buf = ped_malloc (fs->blocksize);
if (!ext2_bcache_flush(fs, from)) return 0;
if (!ext2_bcache_flush(fs, to)) return 0;
if (!ext2_read_blocks(fs, buf, from, 1)) return 0;
if (!ext2_write_blocks(fs, buf, to, 1)) return 0;
return 1;
}
int ext2_get_block_state(struct ext2_fs *fs, blk_t block)
{
struct ext2_buffer_head *bh;
int group;
int offset;
int state;
block -= EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);
group = block / EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
offset = block % EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
bh = ext2_bread(fs, EXT2_GROUP_BLOCK_BITMAP(fs->gd[group]));
state = bh->data[offset>>3] & _bitmap[offset&7];
ext2_brelse(bh, 0);
return state;
}
blk_t ext2_find_free_block(struct ext2_fs *fs)
{
int i;
for (i=0;i<fs->numgroups;i++)
if (EXT2_GROUP_FREE_BLOCKS_COUNT(fs->gd[i]))
{
blk_t j;
blk_t offset;
offset = i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)
+ EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);
for (j=fs->adminblocks;
j<EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
j++)
if (ext2_is_data_block(fs, offset + j) &&
!ext2_get_block_state(fs, offset + j))
return offset + j;
ped_exception_throw (PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Inconsistent group descriptors!"));
}
ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
_("File system full!"));
return 0;
}
ino_t ext2_find_free_inode(struct ext2_fs *fs)
{
int i;
for (i=0;i<fs->numgroups;i++)
if (EXT2_GROUP_FREE_INODES_COUNT(fs->gd[i]))
{
ino_t j;
ino_t offset;
offset = i * EXT2_SUPER_INODES_PER_GROUP(fs->sb) + 1;
for (j=0;j<EXT2_SUPER_INODES_PER_GROUP(fs->sb);j++)
if (!ext2_get_inode_state(fs, offset + j))
return offset + j;
ped_exception_throw (PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Inconsistent group descriptors!"));
}
ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
_("File system full!"));
return 0;
}
int ext2_move_blocks(struct ext2_fs *fs, blk_t src, blk_t num, blk_t dest)
{
unsigned char *buf;
blk_t i;
ped_exception_fetch_all();
if ((buf = ped_malloc(num << fs->logsize)) != NULL)
{
ped_exception_leave_all();
if (!ext2_bcache_flush_range(fs, src, num)) return 0;
if (!ext2_bcache_flush_range(fs, dest, num)) return 0;
if (!ext2_read_blocks(fs, buf, src, num)) return 0;
if (!ext2_write_blocks(fs, buf, dest, num)) return 0;
ped_free(buf);
return 1;
}
ped_exception_catch();
ped_exception_leave_all();
if (src > dest)
{
for (i=0;i<num;i++)
if (!ext2_copy_block(fs, src+i, dest+i))
return 0;
}
else
{
for (i=num;i>0;i--)
if (!ext2_copy_block(fs, src+i, dest+i))
return 0;
}
return 1;
}
int ext2_read_blocks(struct ext2_fs *fs, void *ptr, blk_t block, blk_t num)
{
return fs->devhandle->ops->read(fs->devhandle->cookie, ptr, block, num);
}
int ext2_set_block_state(struct ext2_fs *fs, blk_t block, int state, int updatemetadata)
{
struct ext2_buffer_head *bh;
int group;
int offset;
block -= EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);
group = block / EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
offset = block % EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
bh = ext2_bread(fs, EXT2_GROUP_BLOCK_BITMAP(fs->gd[group]));
bh->dirty = 1;
if (state)
bh->data[offset>>3] |= _bitmap[offset&7];
else
bh->data[offset>>3] &= ~_bitmap[offset&7];
ext2_brelse(bh, 0);
if (updatemetadata)
{
int diff;
diff = state ? -1 : 1;
fs->gd[group].bg_free_blocks_count = PED_CPU_TO_LE16
(EXT2_GROUP_FREE_BLOCKS_COUNT(fs->gd[group]) + diff);
fs->sb.s_free_blocks_count = PED_CPU_TO_LE32
(EXT2_SUPER_FREE_BLOCKS_COUNT(fs->sb) + diff);
fs->metadirty |= EXT2_META_SB | EXT2_META_GD;
}
return 1;
}
int ext2_write_blocks(struct ext2_fs *fs, void *ptr, blk_t block, blk_t num)
{
return fs->devhandle->ops->write(fs->devhandle->cookie, ptr, block, num);
}
int ext2_zero_blocks(struct ext2_fs *fs, blk_t block, blk_t num)
{
unsigned char *buf;
blk_t i;
ped_exception_fetch_all();
buf = ped_malloc (num << fs->logsize);
if (buf)
{
ped_exception_leave_all();
memset(buf, 0, num << fs->logsize);
if (!ext2_bcache_flush_range(fs, block, num))
goto error_free_buf;
if (!ext2_write_blocks(fs, buf, block, num))
goto error_free_buf;
ped_free(buf);
return 1;
}
ped_exception_catch();
buf = ped_malloc (fs->blocksize);
if (buf)
{
ped_exception_leave_all();
memset(buf, 0, fs->blocksize);
for (i=0;i<num;i++)
{
if (!ext2_bcache_flush(fs, block+i))
goto error_free_buf;
if (!ext2_write_blocks(fs, buf, block+i, 1))
goto error_free_buf;
}
ped_free(buf);
return 1;
}
ped_exception_catch();
ped_exception_leave_all();
for (i=0;i<num;i++)
{
struct ext2_buffer_head *bh;
bh = ext2_bcreate(fs, block+i);
if (!bh)
goto error;
bh->dirty = 1;
if (!ext2_brelse(bh, 1))
goto error;
}
return 1;
error_free_buf:
ped_free(buf);
error:
return 0;
}
off_t ext2_get_inode_offset(struct ext2_fs *fs, ino_t inode, blk_t *block)
{
int group;
int offset;
inode--;
group = inode / EXT2_SUPER_INODES_PER_GROUP(fs->sb);
offset = (inode % EXT2_SUPER_INODES_PER_GROUP(fs->sb))
* sizeof(struct ext2_inode);
*block = EXT2_GROUP_INODE_TABLE(fs->gd[group])
+ (offset >> fs->logsize);
return offset & (fs->blocksize - 1);
}
int ext2_get_inode_state(struct ext2_fs *fs, ino_t inode)
{
struct ext2_buffer_head *bh;
int group;
int offset;
int ret;
inode--;
group = inode / EXT2_SUPER_INODES_PER_GROUP(fs->sb);
offset = inode % EXT2_SUPER_INODES_PER_GROUP(fs->sb);
bh = ext2_bread(fs, EXT2_GROUP_INODE_BITMAP(fs->gd[group]));
ret = bh->data[offset>>3] & _bitmap[offset&7];
ext2_brelse(bh, 0);
return ret;
}
int ext2_read_inode(struct ext2_fs *fs, ino_t inode, struct ext2_inode *data)
{
struct ext2_buffer_head *bh;
blk_t blk;
off_t off;
off = ext2_get_inode_offset(fs, inode, &blk);
bh = ext2_bread(fs, blk);
if (!bh)
return 0;
memcpy(data, bh->data + off, sizeof(struct ext2_inode));
ext2_brelse(bh, 0);
return 1;
}
int ext2_set_inode_state(struct ext2_fs *fs, ino_t inode, int state, int updatemetadata)
{
struct ext2_buffer_head *bh;
int group;
int offset;
inode--;
group = inode / EXT2_SUPER_INODES_PER_GROUP(fs->sb);
offset = inode % EXT2_SUPER_INODES_PER_GROUP(fs->sb);
bh = ext2_bread(fs, EXT2_GROUP_INODE_BITMAP(fs->gd[group]));
if (!bh)
return 0;
bh->dirty = 1;
if (state)
bh->data[offset>>3] |= _bitmap[offset&7];
else
bh->data[offset>>3] &= ~_bitmap[offset&7];
ext2_brelse(bh, 0);
if (updatemetadata)
{
int diff;
diff = state ? -1 : 1;
fs->gd[group].bg_free_inodes_count = PED_CPU_TO_LE16
(EXT2_GROUP_FREE_INODES_COUNT(fs->gd[group]) + diff);
fs->sb.s_free_inodes_count = PED_CPU_TO_LE32
(EXT2_SUPER_FREE_INODES_COUNT(fs->sb) + diff);
fs->metadirty = EXT2_META_SB | EXT2_META_GD;
}
return 1;
}
static void
_inode_update_size(struct ext2_fs *fs, struct ext2_inode *inode, int delta)
{
int i512perblock = 1 << (fs->logsize - 9);
uint64_t size;
/* i_blocks is in 512 byte blocks */
inode->i_blocks = PED_CPU_TO_LE32(EXT2_INODE_BLOCKS(*inode)
+ delta * i512perblock);
size = EXT2_INODE_SIZE(*inode) + delta * fs->blocksize;
inode->i_size = PED_CPU_TO_LE32(size % (1LL << 32));
inode->i_size_high = PED_CPU_TO_LE32(size / (1LL << 32));
inode->i_mtime = PED_CPU_TO_LE32(time(NULL));
}
int ext2_do_inode(struct ext2_fs *fs, struct ext2_inode *inode, blk_t block,
int action)
{
struct ext2_buffer_head *bh;
uint32_t *udata;
blk_t count = 0;
int i;
int u32perblock = fs->blocksize >> 2;
int i512perblock = 1 << (fs->logsize - 9);
if (block == 0 || EXT2_INODE_MODE(*inode) == 0)
return -1;
if (fs->opt_debug)
switch (action)
{
case EXT2_ACTION_ADD:
fprintf(stderr,"adding 0x%04x to inode\n",
block);
break;
case EXT2_ACTION_DELETE:
fprintf(stderr,"deleting 0x%04x from inode\n",
block);
break;
case EXT2_ACTION_FIND:
fprintf(stderr,"finding 0x%04x in inode\n",
block);
break;
}
/* Direct blocks for first 12 blocks */
for (i = 0; i < EXT2_NDIR_BLOCKS; i++)
{
if (action == EXT2_ACTION_ADD && !EXT2_INODE_BLOCK(*inode, i))
{
inode->i_block[i] = PED_CPU_TO_LE32(block);
_inode_update_size (fs, inode, 1);
ext2_set_block_state(fs, block, 1, 1);
return i;
}
if (EXT2_INODE_BLOCK(*inode, i) == block)
{
if (action == EXT2_ACTION_DELETE)
{
inode->i_block[i] = 0;
_inode_update_size (fs, inode, -1);
ext2_set_block_state(fs, block, 0, 1);
}
return i;
}
if (EXT2_INODE_BLOCK(*inode, i))
count += i512perblock;
}
count += EXT2_INODE_BLOCK(*inode, EXT2_IND_BLOCK) ? i512perblock : 0;
count += EXT2_INODE_BLOCK(*inode, EXT2_DIND_BLOCK) ? i512perblock : 0;
count += EXT2_INODE_BLOCK(*inode, EXT2_TIND_BLOCK) ? i512perblock : 0;
if (!EXT2_INODE_BLOCK(*inode, EXT2_IND_BLOCK) ||
(count >= EXT2_INODE_BLOCKS(*inode) && action != EXT2_ACTION_ADD))
return -1;
bh = ext2_bread(fs, EXT2_INODE_BLOCK(*inode, EXT2_IND_BLOCK));
udata = (uint32_t *)bh->data;
/* Indirect blocks for next 256/512/1024 blocks (for 1k/2k/4k blocks) */
for (i = 0; i < u32perblock; i++) {
if (action == EXT2_ACTION_ADD && !udata[i]) {
bh->dirty = 1;
udata[i] = PED_CPU_TO_LE32(block);
_inode_update_size (fs, inode, 1);
ext2_set_block_state(fs, block, 1, 1);
ext2_brelse(bh, 0);
return EXT2_NDIR_BLOCKS + i;
}
if (PED_LE32_TO_CPU(udata[i]) == block) {
if (action == EXT2_ACTION_DELETE) {
bh->dirty = 1;
udata[i] = 0;
_inode_update_size (fs, inode, -1);
ext2_set_block_state(fs, block, 0, 1);
}
ext2_brelse(bh, 0);
return EXT2_NDIR_BLOCKS + i;
}
if (udata[i])
{
count += i512perblock;
if (count >= EXT2_INODE_BLOCKS(*inode) &&
action != EXT2_ACTION_ADD)
return -1;
}
}
ext2_brelse(bh, 0);
if (!EXT2_INODE_BLOCK(*inode, EXT2_DIND_BLOCK) ||
(count >= EXT2_INODE_BLOCKS(*inode) && action != EXT2_ACTION_ADD))
return -1;
bh = ext2_bread(fs, EXT2_INODE_BLOCK(*inode, EXT2_DIND_BLOCK));
udata = (uint32_t *)bh->data;
/* Double indirect blocks for next 2^16/2^18/2^20 1k/2k/4k blocks */
for (i = 0; i < u32perblock; i++) {
struct ext2_buffer_head *bh2;
uint32_t *udata2;
int j;
if (!udata[i]) {
ext2_brelse(bh, 0);
return -1;
}
bh2 = ext2_bread(fs, PED_LE32_TO_CPU(udata[i]));
udata2 = (uint32_t *)bh2->data;
count += i512perblock;
for (j = 0; j < u32perblock; j++) {
if (action == EXT2_ACTION_ADD && !udata2[j]) {
bh2->dirty = 1;
udata2[j] = PED_CPU_TO_LE32(block);
_inode_update_size (fs, inode, 1);
ext2_set_block_state(fs, block, 1, 1);
ext2_brelse(bh, 0);
ext2_brelse(bh2, 0);
return EXT2_NDIR_BLOCKS + i * u32perblock + j;
}
if (PED_LE32_TO_CPU(udata2[j]) == block) {
if (action == EXT2_ACTION_DELETE) {
bh2->dirty = 1;
udata2[j] = 0;
_inode_update_size (fs, inode, -1);
ext2_set_block_state(fs, block, 0, 1);
}
ext2_brelse(bh, 0);
ext2_brelse(bh2, 0);
return EXT2_NDIR_BLOCKS + i * u32perblock + j;
}
if (udata2[j])
{
count += i512perblock;
if (count >= EXT2_INODE_BLOCKS(*inode) &&
action != EXT2_ACTION_ADD)
return -1;
}
}
ext2_brelse(bh2, 0);
}
ext2_brelse(bh, 0);
/* FIXME: we should check for triple-indirect blocks here, but it
* would be nice to have a better routine to traverse blocks, and
* file systems that need triple-indirect blocks for the resize
* inode are too big to worry about yet.
*/
return -1;
}
int ext2_write_inode(struct ext2_fs *fs, ino_t inode, const struct ext2_inode *data)
{
struct ext2_buffer_head *bh;
blk_t blk;
off_t off;
off = ext2_get_inode_offset(fs, inode, &blk);
bh = ext2_bread(fs, blk);
if (!bh)
return 0;
bh->dirty = 1;
memcpy(bh->data + off, data, sizeof(struct ext2_inode));
ext2_brelse(bh, 0);
return 1;
}
int ext2_zero_inode(struct ext2_fs *fs, ino_t inode)
{
struct ext2_inode buf;
memset(&buf, 0, sizeof(struct ext2_inode));
return ext2_write_inode(fs, inode, &buf);
}
/* check whether y is root of x
* (formula grabbed from linux ext2 kernel source) */
static int is_root(int x, int y)
{
if (!x)
return 1;
while (1)
{
if (x == 1)
return 1;
if (x % y)
return 0;
x /= y;
}
}
/* check whether group contains a superblock copy on file systems
* where not all groups have one (sparse superblock feature) */
int ext2_is_group_sparse(struct ext2_fs *fs, int group)
{
if (!fs->sparse)
return 1;
if (is_root(group, 3) || is_root(group, 5) || is_root(group, 7))
return 1;
return 0;
}
void ext2_close(struct ext2_fs *fs)
{
ext2_commit_metadata(fs, EXT2_META_PRIMARY | EXT2_META_BACKUP);
ext2_sync(fs);
ext2_bcache_deinit(fs);
fs->devhandle->ops->close(fs->devhandle->cookie);
ped_free(fs->gd);
ped_free(fs);
}
int ext2_commit_metadata(struct ext2_fs *fs, int copies)
{
int i;
int num;
int wmeta = fs->metadirty & copies;
unsigned char* sb = ped_malloc(fs->blocksize);
struct ext2_super_block *sb_for_io;
int sb_block;
/* See if there is even anything to write... */
if (wmeta == EXT2_META_CLEAN)
return 1;
fs->sb.s_r_blocks_count = PED_CPU_TO_LE32 (
fs->r_frac * (loff_t)EXT2_SUPER_BLOCKS_COUNT(fs->sb)
/ 100);
if (!ext2_read_blocks (fs, sb, 0, 1))
return 0;
if (EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb)) {
memcpy(sb, &fs->sb, 1024);
sb_for_io = (struct ext2_super_block *) sb;
} else {
memcpy(sb+1024, &fs->sb, 1024);
sb_for_io = (struct ext2_super_block *) (sb + 1024);
}
num = copies & EXT2_META_BACKUP ? fs->numgroups : 1;
for (i = 0, sb_block = EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb); i < num;
i++, sb_block += EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb))
{
if (!ext2_is_group_sparse(fs, i))
continue;
if (fs->dynamic_version)
sb_for_io->s_block_group_nr = PED_CPU_TO_LE16 (i);
if ((i == 0 && wmeta & EXT2_META_PRIMARY_SB) ||
(i != 0 && wmeta & EXT2_META_SB))
{
if (!ext2_bcache_flush_range(fs, sb_block, 1))
return 0;
if (!ext2_write_blocks(fs, sb, sb_block, 1))
return 0;
}
if ((i == 0 && wmeta & EXT2_META_PRIMARY_GD) ||
(i != 0 && wmeta & EXT2_META_GD))
{
if (!ext2_bcache_flush_range(fs, sb_block + 1,
fs->gdblocks))
return 0;
if (!ext2_write_blocks(fs, fs->gd, sb_block + 1,
fs->gdblocks))
return 0;
}
}
sb_for_io->s_block_group_nr = 0;
/* Clear the flags of the components we just finished writing. */
fs->metadirty &= ~copies;
return 1;
}
int ext2_sync(struct ext2_fs *fs)
{
if (!ext2_commit_metadata(fs, EXT2_META_PRIMARY)) return 0;
if (!ext2_bcache_sync(fs)) return 0;
if (!fs->devhandle->ops->sync(fs->devhandle->cookie)) return 0;
return 1;
}
struct ext2_fs *ext2_open(struct ext2_dev_handle *handle, int state)
{
struct ext2_fs *fs;
if ((fs = (struct ext2_fs *) ped_malloc(sizeof(struct ext2_fs)))
== NULL)
goto error;
handle->ops->set_blocksize(handle->cookie, 10);
if (!handle->ops->read(handle->cookie, &fs->sb, 1, 1)
|| EXT2_SUPER_MAGIC(fs->sb) != EXT2_SUPER_MAGIC_CONST)
{
ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
_("Invalid superblock. Are you sure this is an ext2 "
"file system?"));
goto error_free_fs;
}
fs->opt_debug = 1;
fs->opt_safe = 1;
fs->opt_verbose = 0;
if (EXT2_SUPER_STATE(fs->sb) & EXT2_ERROR_FS & ~(state & EXT2_ERROR_FS))
{
if (ped_exception_throw (
PED_EXCEPTION_WARNING, PED_EXCEPTION_IGNORE_CANCEL,
_("File system has errors! You should run e2fsck."))
== PED_EXCEPTION_CANCEL)
goto error_free_fs;
}
if (!((EXT2_SUPER_STATE(fs->sb) | state) & EXT2_VALID_FS)
|| (EXT2_SUPER_FEATURE_INCOMPAT(fs->sb)
& EXT3_FEATURE_INCOMPAT_RECOVER))
{
if (ped_exception_throw (
PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL,
_("File system was not cleanly unmounted! "
"You should run e2fsck. Modifying an unclean "
"file system could cause severe corruption."))
!= PED_EXCEPTION_IGNORE)
goto error_free_fs;
}
fs->dynamic_version = EXT2_SUPER_REV_LEVEL (fs->sb) > 0;
if ((EXT2_SUPER_FEATURE_COMPAT(fs->sb)
& ~(EXT3_FEATURE_COMPAT_HAS_JOURNAL |
EXT2_FEATURE_COMPAT_HAS_DIR_INDEX)) ||
(EXT2_SUPER_FEATURE_INCOMPAT(fs->sb)
& ~(EXT2_FEATURE_INCOMPAT_FILETYPE |
EXT3_FEATURE_INCOMPAT_RECOVER)) ||
(EXT2_SUPER_FEATURE_RO_COMPAT(fs->sb)
& ~(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER |
EXT2_FEATURE_RO_COMPAT_LARGE_FILE)))
{
ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
_("File system has an incompatible feature enabled."));
goto error_free_fs;
}
fs->devhandle = handle;
fs->logsize = EXT2_SUPER_LOG_BLOCK_SIZE(fs->sb) + 10;
handle->ops->set_blocksize(handle->cookie, fs->logsize);
if (!ext2_bcache_init(fs))
{
ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
_("Error allocating buffer cache."));
goto error_free_fs;
}
fs->blocksize = 1 << fs->logsize;
fs->numgroups = ped_div_round_up (EXT2_SUPER_BLOCKS_COUNT(fs->sb)
- EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb),
EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb));
fs->gdblocks = ped_div_round_up (fs->numgroups
* sizeof(struct ext2_group_desc),
fs->blocksize);
fs->inodeblocks = ped_div_round_up (EXT2_SUPER_INODES_PER_GROUP(fs->sb)
* sizeof(struct ext2_inode),
fs->blocksize);
fs->r_frac = ped_div_round_up (100 * (loff_t)EXT2_SUPER_R_BLOCKS_COUNT(fs->sb),
EXT2_SUPER_BLOCKS_COUNT(fs->sb));
fs->adminblocks = 3 + fs->gdblocks + fs->inodeblocks;
fs->sparse = 0;
if (EXT2_SUPER_FEATURE_RO_COMPAT(fs->sb)
& EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
fs->sparse = 1;
fs->has_journal = 0 < (EXT2_SUPER_FEATURE_COMPAT(fs->sb)
& EXT3_FEATURE_COMPAT_HAS_JOURNAL);
fs->has_internal_journal
= fs->has_journal
&& uuid_is_null(EXT2_SUPER_JOURNAL_UUID(fs->sb))
&& EXT2_SUPER_JOURNAL_INUM(fs->sb);
fs->gd = ped_malloc (fs->numgroups * sizeof (struct ext2_group_desc)
+ fs->blocksize);
if (!fs->gd)
goto error_deinit_bcache;
ext2_read_blocks(fs, fs->gd, EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb) + 1,
fs->gdblocks);
fs->metadirty = 0;
return fs;
ped_free(fs->gd);
error_deinit_bcache:
ext2_bcache_deinit(fs);
error_free_fs:
ped_free(fs);
error:
return NULL;
}
#endif /* !DISCOVER_ONLY */