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