import ext2fs lib to prep for new e2fsprogs
No binary (build failed)
—
(log yok)
| @@ -0,0 +1,173 @@ | |
| + | /* |
| + | * alloc.c --- allocate new inodes, blocks for ext2fs |
| + | * |
| + | * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | * |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <time.h> |
| + | #include <string.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | /* |
| + | * Right now, just search forward from the parent directory's block |
| + | * group to find the next free inode. |
| + | * |
| + | * Should have a special policy for directories. |
| + | */ |
| + | errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, |
| + | int mode EXT2FS_ATTR((unused)), |
| + | ext2fs_inode_bitmap map, ext2_ino_t *ret) |
| + | { |
| + | ext2_ino_t dir_group = 0; |
| + | ext2_ino_t i; |
| + | ext2_ino_t start_inode; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | if (!map) |
| + | map = fs->inode_map; |
| + | if (!map) |
| + | return EXT2_ET_NO_INODE_BITMAP; |
| + | |
| + | if (dir > 0) |
| + | dir_group = (dir - 1) / EXT2_INODES_PER_GROUP(fs->super); |
| + | |
| + | start_inode = (dir_group * EXT2_INODES_PER_GROUP(fs->super)) + 1; |
| + | if (start_inode < EXT2_FIRST_INODE(fs->super)) |
| + | start_inode = EXT2_FIRST_INODE(fs->super); |
| + | i = start_inode; |
| + | |
| + | do { |
| + | if (!ext2fs_fast_test_inode_bitmap(map, i)) |
| + | break; |
| + | i++; |
| + | if (i > fs->super->s_inodes_count) |
| + | i = EXT2_FIRST_INODE(fs->super); |
| + | } while (i != start_inode); |
| + | |
| + | if (ext2fs_test_inode_bitmap(map, i)) |
| + | return EXT2_ET_INODE_ALLOC_FAIL; |
| + | *ret = i; |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * Stupid algorithm --- we now just search forward starting from the |
| + | * goal. Should put in a smarter one someday.... |
| + | */ |
| + | errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal, |
| + | ext2fs_block_bitmap map, blk_t *ret) |
| + | { |
| + | blk_t i; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | if (!map) |
| + | map = fs->block_map; |
| + | if (!map) |
| + | return EXT2_ET_NO_BLOCK_BITMAP; |
| + | if (!goal || (goal >= fs->super->s_blocks_count)) |
| + | goal = fs->super->s_first_data_block; |
| + | i = goal; |
| + | do { |
| + | if (!ext2fs_fast_test_block_bitmap(map, i)) { |
| + | *ret = i; |
| + | return 0; |
| + | } |
| + | i++; |
| + | if (i >= fs->super->s_blocks_count) |
| + | i = fs->super->s_first_data_block; |
| + | } while (i != goal); |
| + | return EXT2_ET_BLOCK_ALLOC_FAIL; |
| + | } |
| + | |
| + | /* |
| + | * This function zeros out the allocated block, and updates all of the |
| + | * appropriate filesystem records. |
| + | */ |
| + | errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal, |
| + | char *block_buf, blk_t *ret) |
| + | { |
| + | errcode_t retval; |
| + | blk_t block; |
| + | char *buf = 0; |
| + | |
| + | if (!block_buf) { |
| + | retval = ext2fs_get_mem(fs->blocksize, &buf); |
| + | if (retval) |
| + | return retval; |
| + | block_buf = buf; |
| + | } |
| + | memset(block_buf, 0, fs->blocksize); |
| + | |
| + | if (!fs->block_map) { |
| + | retval = ext2fs_read_block_bitmap(fs); |
| + | if (retval) |
| + | goto fail; |
| + | } |
| + | |
| + | retval = ext2fs_new_block(fs, goal, 0, &block); |
| + | if (retval) |
| + | goto fail; |
| + | |
| + | retval = io_channel_write_blk(fs->io, block, 1, block_buf); |
| + | if (retval) |
| + | goto fail; |
| + | |
| + | ext2fs_block_alloc_stats(fs, block, +1); |
| + | *ret = block; |
| + | return 0; |
| + | |
| + | fail: |
| + | if (buf) |
| + | ext2fs_free_mem(&buf); |
| + | return retval; |
| + | } |
| + | |
| + | errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, blk_t finish, |
| + | int num, ext2fs_block_bitmap map, blk_t *ret) |
| + | { |
| + | blk_t b = start; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | if (!map) |
| + | map = fs->block_map; |
| + | if (!map) |
| + | return EXT2_ET_NO_BLOCK_BITMAP; |
| + | if (!b) |
| + | b = fs->super->s_first_data_block; |
| + | if (!finish) |
| + | finish = start; |
| + | if (!num) |
| + | num = 1; |
| + | do { |
| + | if (b+num-1 > fs->super->s_blocks_count) |
| + | b = fs->super->s_first_data_block; |
| + | if (ext2fs_fast_test_block_bitmap_range(map, b, num)) { |
| + | *ret = b; |
| + | return 0; |
| + | } |
| + | b++; |
| + | } while (b != finish); |
| + | return EXT2_ET_BLOCK_ALLOC_FAIL; |
| + | } |
| + | |
| @@ -0,0 +1,57 @@ | |
| + | /* |
| + | * alloc_sb.c --- Allocate the superblock and block group descriptors for a |
| + | * newly initialized filesystem. Used by mke2fs when initializing a filesystem |
| + | * |
| + | * Copyright (C) 1994, 1995, 1996, 2003 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | int ext2fs_reserve_super_and_bgd(ext2_filsys fs, |
| + | dgrp_t group, |
| + | ext2fs_block_bitmap bmap) |
| + | { |
| + | blk_t super_blk, old_desc_blk, new_desc_blk; |
| + | int j, old_desc_blocks, num_blocks; |
| + | |
| + | num_blocks = ext2fs_super_and_bgd_loc(fs, group, &super_blk, |
| + | &old_desc_blk, &new_desc_blk, 0); |
| + | |
| + | if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) |
| + | old_desc_blocks = fs->super->s_first_meta_bg; |
| + | else |
| + | old_desc_blocks = |
| + | fs->desc_blocks + fs->super->s_reserved_gdt_blocks; |
| + | |
| + | if (super_blk || (group == 0)) |
| + | ext2fs_mark_block_bitmap(bmap, super_blk); |
| + | |
| + | if (old_desc_blk) { |
| + | for (j=0; j < old_desc_blocks; j++) |
| + | ext2fs_mark_block_bitmap(bmap, old_desc_blk + j); |
| + | } |
| + | if (new_desc_blk) |
| + | ext2fs_mark_block_bitmap(bmap, new_desc_blk); |
| + | |
| + | return num_blocks; |
| + | } |
| @@ -0,0 +1,52 @@ | |
| + | /* |
| + | * alloc_stats.c --- Update allocation statistics for ext2fs |
| + | * |
| + | * Copyright (C) 2001 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | * |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino, |
| + | int inuse, int isdir) |
| + | { |
| + | int group = ext2fs_group_of_ino(fs, ino); |
| + | |
| + | if (inuse > 0) |
| + | ext2fs_mark_inode_bitmap(fs->inode_map, ino); |
| + | else |
| + | ext2fs_unmark_inode_bitmap(fs->inode_map, ino); |
| + | fs->group_desc[group].bg_free_inodes_count -= inuse; |
| + | if (isdir) |
| + | fs->group_desc[group].bg_used_dirs_count += inuse; |
| + | fs->super->s_free_inodes_count -= inuse; |
| + | ext2fs_mark_super_dirty(fs); |
| + | ext2fs_mark_ib_dirty(fs); |
| + | } |
| + | |
| + | void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse) |
| + | { |
| + | ext2fs_inode_alloc_stats2(fs, ino, inuse, 0); |
| + | } |
| + | |
| + | void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse) |
| + | { |
| + | int group = ext2fs_group_of_blk(fs, blk); |
| + | |
| + | if (inuse > 0) |
| + | ext2fs_mark_block_bitmap(fs->block_map, blk); |
| + | else |
| + | ext2fs_unmark_block_bitmap(fs->block_map, blk); |
| + | fs->group_desc[group].bg_free_blocks_count -= inuse; |
| + | fs->super->s_free_blocks_count -= inuse; |
| + | ext2fs_mark_super_dirty(fs); |
| + | ext2fs_mark_bb_dirty(fs); |
| + | } |
| @@ -0,0 +1,117 @@ | |
| + | /* |
| + | * alloc_tables.c --- Allocate tables for a newly initialized |
| + | * filesystem. Used by mke2fs when initializing a filesystem |
| + | * |
| + | * Copyright (C) 1996 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group, |
| + | ext2fs_block_bitmap bmap) |
| + | { |
| + | errcode_t retval; |
| + | blk_t group_blk, start_blk, last_blk, new_blk, blk; |
| + | int j; |
| + | |
| + | group_blk = fs->super->s_first_data_block + |
| + | (group * fs->super->s_blocks_per_group); |
| + | |
| + | last_blk = group_blk + fs->super->s_blocks_per_group; |
| + | if (last_blk >= fs->super->s_blocks_count) |
| + | last_blk = fs->super->s_blocks_count - 1; |
| + | |
| + | if (!bmap) |
| + | bmap = fs->block_map; |
| + | |
| + | /* |
| + | * Allocate the block and inode bitmaps, if necessary |
| + | */ |
| + | if (fs->stride) { |
| + | start_blk = group_blk + fs->inode_blocks_per_group; |
| + | start_blk += ((fs->stride * group) % |
| + | (last_blk - start_blk)); |
| + | if (start_blk > last_blk) |
| + | start_blk = group_blk; |
| + | } else |
| + | start_blk = group_blk; |
| + | |
| + | if (!fs->group_desc[group].bg_block_bitmap) { |
| + | retval = ext2fs_get_free_blocks(fs, start_blk, last_blk, |
| + | 1, bmap, &new_blk); |
| + | if (retval == EXT2_ET_BLOCK_ALLOC_FAIL) |
| + | retval = ext2fs_get_free_blocks(fs, group_blk, |
| + | last_blk, 1, bmap, &new_blk); |
| + | if (retval) |
| + | return retval; |
| + | ext2fs_mark_block_bitmap(bmap, new_blk); |
| + | fs->group_desc[group].bg_block_bitmap = new_blk; |
| + | } |
| + | |
| + | if (!fs->group_desc[group].bg_inode_bitmap) { |
| + | retval = ext2fs_get_free_blocks(fs, start_blk, last_blk, |
| + | 1, bmap, &new_blk); |
| + | if (retval == EXT2_ET_BLOCK_ALLOC_FAIL) |
| + | retval = ext2fs_get_free_blocks(fs, group_blk, |
| + | last_blk, 1, bmap, &new_blk); |
| + | if (retval) |
| + | return retval; |
| + | ext2fs_mark_block_bitmap(bmap, new_blk); |
| + | fs->group_desc[group].bg_inode_bitmap = new_blk; |
| + | } |
| + | |
| + | /* |
| + | * Allocate the inode table |
| + | */ |
| + | if (!fs->group_desc[group].bg_inode_table) { |
| + | retval = ext2fs_get_free_blocks(fs, group_blk, last_blk, |
| + | fs->inode_blocks_per_group, |
| + | bmap, &new_blk); |
| + | if (retval) |
| + | return retval; |
| + | for (j=0, blk = new_blk; |
| + | j < fs->inode_blocks_per_group; |
| + | j++, blk++) |
| + | ext2fs_mark_block_bitmap(bmap, blk); |
| + | fs->group_desc[group].bg_inode_table = new_blk; |
| + | } |
| + | |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | |
| + | errcode_t ext2fs_allocate_tables(ext2_filsys fs) |
| + | { |
| + | errcode_t retval; |
| + | dgrp_t i; |
| + | |
| + | for (i = 0; i < fs->group_desc_count; i++) { |
| + | retval = ext2fs_allocate_group_table(fs, i, fs->block_map); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | return 0; |
| + | } |
| + | |
| @@ -0,0 +1,327 @@ | |
| + | /* |
| + | * badblocks.c --- routines to manipulate the bad block structure |
| + | * |
| + | * Copyright (C) 1994, 1995, 1996 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fsP.h" |
| + | |
| + | /* |
| + | * Helper function for making a badblocks list |
| + | */ |
| + | static errcode_t make_u32_list(int size, int num, __u32 *list, |
| + | ext2_u32_list *ret) |
| + | { |
| + | ext2_u32_list bb; |
| + | errcode_t retval; |
| + | |
| + | retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_list), &bb); |
| + | if (retval) |
| + | return retval; |
| + | memset(bb, 0, sizeof(struct ext2_struct_u32_list)); |
| + | bb->magic = EXT2_ET_MAGIC_BADBLOCKS_LIST; |
| + | bb->size = size ? size : 10; |
| + | bb->num = num; |
| + | retval = ext2fs_get_mem(bb->size * sizeof(blk_t), &bb->list); |
| + | if (!bb->list) { |
| + | ext2fs_free_mem(&bb); |
| + | return retval; |
| + | } |
| + | if (list) |
| + | memcpy(bb->list, list, bb->size * sizeof(blk_t)); |
| + | else |
| + | memset(bb->list, 0, bb->size * sizeof(blk_t)); |
| + | *ret = bb; |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | /* |
| + | * This procedure creates an empty u32 list. |
| + | */ |
| + | errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size) |
| + | { |
| + | return make_u32_list(size, 0, 0, ret); |
| + | } |
| + | |
| + | /* |
| + | * This procedure creates an empty badblocks list. |
| + | */ |
| + | errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret, int size) |
| + | { |
| + | return make_u32_list(size, 0, 0, (ext2_badblocks_list *) ret); |
| + | } |
| + | |
| + | |
| + | /* |
| + | * This procedure copies a badblocks list |
| + | */ |
| + | errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest) |
| + | { |
| + | errcode_t retval; |
| + | |
| + | retval = make_u32_list(src->size, src->num, src->list, dest); |
| + | if (retval) |
| + | return retval; |
| + | (*dest)->badblocks_flags = src->badblocks_flags; |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src, |
| + | ext2_badblocks_list *dest) |
| + | { |
| + | return ext2fs_u32_copy((ext2_u32_list) src, |
| + | (ext2_u32_list *) dest); |
| + | } |
| + | |
| + | /* |
| + | * This procedure frees a badblocks list. |
| + | * |
| + | * (note: moved to closefs.c) |
| + | */ |
| + | |
| + | |
| + | /* |
| + | * This procedure adds a block to a badblocks list. |
| + | */ |
| + | errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk) |
| + | { |
| + | errcode_t retval; |
| + | int i, j; |
| + | unsigned long old_size; |
| + | |
| + | EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST); |
| + | |
| + | if (bb->num >= bb->size) { |
| + | old_size = bb->size * sizeof(__u32); |
| + | bb->size += 100; |
| + | retval = ext2fs_resize_mem(old_size, bb->size * sizeof(__u32), |
| + | &bb->list); |
| + | if (retval) { |
| + | bb->size -= 100; |
| + | return retval; |
| + | } |
| + | } |
| + | |
| + | /* |
| + | * Add special case code for appending to the end of the list |
| + | */ |
| + | i = bb->num-1; |
| + | if ((bb->num != 0) && (bb->list[i] == blk)) |
| + | return 0; |
| + | if ((bb->num == 0) || (bb->list[i] < blk)) { |
| + | bb->list[bb->num++] = blk; |
| + | return 0; |
| + | } |
| + | |
| + | j = bb->num; |
| + | for (i=0; i < bb->num; i++) { |
| + | if (bb->list[i] == blk) |
| + | return 0; |
| + | if (bb->list[i] > blk) { |
| + | j = i; |
| + | break; |
| + | } |
| + | } |
| + | for (i=bb->num; i > j; i--) |
| + | bb->list[i] = bb->list[i-1]; |
| + | bb->list[j] = blk; |
| + | bb->num++; |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb, blk_t blk) |
| + | { |
| + | return ext2fs_u32_list_add((ext2_u32_list) bb, (__u32) blk); |
| + | } |
| + | |
| + | /* |
| + | * This procedure finds a particular block is on a badblocks |
| + | * list. |
| + | */ |
| + | int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk) |
| + | { |
| + | int low, high, mid; |
| + | |
| + | if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) |
| + | return -1; |
| + | |
| + | if (bb->num == 0) |
| + | return -1; |
| + | |
| + | low = 0; |
| + | high = bb->num-1; |
| + | if (blk == bb->list[low]) |
| + | return low; |
| + | if (blk == bb->list[high]) |
| + | return high; |
| + | |
| + | while (low < high) { |
| + | mid = (low+high)/2; |
| + | if (mid == low || mid == high) |
| + | break; |
| + | if (blk == bb->list[mid]) |
| + | return mid; |
| + | if (blk < bb->list[mid]) |
| + | high = mid; |
| + | else |
| + | low = mid; |
| + | } |
| + | return -1; |
| + | } |
| + | |
| + | /* |
| + | * This procedure tests to see if a particular block is on a badblocks |
| + | * list. |
| + | */ |
| + | int ext2fs_u32_list_test(ext2_u32_list bb, __u32 blk) |
| + | { |
| + | if (ext2fs_u32_list_find(bb, blk) < 0) |
| + | return 0; |
| + | else |
| + | return 1; |
| + | } |
| + | |
| + | int ext2fs_badblocks_list_test(ext2_badblocks_list bb, blk_t blk) |
| + | { |
| + | return ext2fs_u32_list_test((ext2_u32_list) bb, (__u32) blk); |
| + | } |
| + | |
| + | |
| + | /* |
| + | * Remove a block from the badblock list |
| + | */ |
| + | int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk) |
| + | { |
| + | int remloc, i; |
| + | |
| + | if (bb->num == 0) |
| + | return -1; |
| + | |
| + | remloc = ext2fs_u32_list_find(bb, blk); |
| + | if (remloc < 0) |
| + | return -1; |
| + | |
| + | for (i = remloc ; i < bb->num-1; i++) |
| + | bb->list[i] = bb->list[i+1]; |
| + | bb->num--; |
| + | return 0; |
| + | } |
| + | |
| + | void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk) |
| + | { |
| + | ext2fs_u32_list_del(bb, blk); |
| + | } |
| + | |
| + | errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb, |
| + | ext2_u32_iterate *ret) |
| + | { |
| + | ext2_u32_iterate iter; |
| + | errcode_t retval; |
| + | |
| + | EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST); |
| + | |
| + | retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_iterate), &iter); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | iter->magic = EXT2_ET_MAGIC_BADBLOCKS_ITERATE; |
| + | iter->bb = bb; |
| + | iter->ptr = 0; |
| + | *ret = iter; |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb, |
| + | ext2_badblocks_iterate *ret) |
| + | { |
| + | return ext2fs_u32_list_iterate_begin((ext2_u32_list) bb, |
| + | (ext2_u32_iterate *) ret); |
| + | } |
| + | |
| + | |
| + | int ext2fs_u32_list_iterate(ext2_u32_iterate iter, __u32 *blk) |
| + | { |
| + | ext2_u32_list bb; |
| + | |
| + | if (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE) |
| + | return 0; |
| + | |
| + | bb = iter->bb; |
| + | |
| + | if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) |
| + | return 0; |
| + | |
| + | if (iter->ptr < bb->num) { |
| + | *blk = bb->list[iter->ptr++]; |
| + | return 1; |
| + | } |
| + | *blk = 0; |
| + | return 0; |
| + | } |
| + | |
| + | int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter, blk_t *blk) |
| + | { |
| + | return ext2fs_u32_list_iterate((ext2_u32_iterate) iter, |
| + | (__u32 *) blk); |
| + | } |
| + | |
| + | |
| + | void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter) |
| + | { |
| + | if (!iter || (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE)) |
| + | return; |
| + | |
| + | iter->bb = 0; |
| + | ext2fs_free_mem(&iter); |
| + | } |
| + | |
| + | void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter) |
| + | { |
| + | ext2fs_u32_list_iterate_end((ext2_u32_iterate) iter); |
| + | } |
| + | |
| + | |
| + | int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2) |
| + | { |
| + | EXT2_CHECK_MAGIC(bb1, EXT2_ET_MAGIC_BADBLOCKS_LIST); |
| + | EXT2_CHECK_MAGIC(bb2, EXT2_ET_MAGIC_BADBLOCKS_LIST); |
| + | |
| + | if (bb1->num != bb2->num) |
| + | return 0; |
| + | |
| + | if (memcmp(bb1->list, bb2->list, bb1->num * sizeof(blk_t)) != 0) |
| + | return 0; |
| + | return 1; |
| + | } |
| + | |
| + | int ext2fs_badblocks_equal(ext2_badblocks_list bb1, ext2_badblocks_list bb2) |
| + | { |
| + | return ext2fs_u32_list_equal((ext2_u32_list) bb1, |
| + | (ext2_u32_list) bb2); |
| + | } |
| + | |
| + | int ext2fs_u32_list_count(ext2_u32_list bb) |
| + | { |
| + | return bb->num; |
| + | } |
| @@ -0,0 +1,63 @@ | |
| + | /* |
| + | * bb_compat.c --- compatibility badblocks routines |
| + | * |
| + | * Copyright (C) 1997 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fsP.h" |
| + | |
| + | errcode_t badblocks_list_create(badblocks_list *ret, int size) |
| + | { |
| + | return ext2fs_badblocks_list_create(ret, size); |
| + | } |
| + | |
| + | void badblocks_list_free(badblocks_list bb) |
| + | { |
| + | ext2fs_badblocks_list_free(bb); |
| + | } |
| + | |
| + | errcode_t badblocks_list_add(badblocks_list bb, blk_t blk) |
| + | { |
| + | return ext2fs_badblocks_list_add(bb, blk); |
| + | } |
| + | |
| + | int badblocks_list_test(badblocks_list bb, blk_t blk) |
| + | { |
| + | return ext2fs_badblocks_list_test(bb, blk); |
| + | } |
| + | |
| + | errcode_t badblocks_list_iterate_begin(badblocks_list bb, |
| + | badblocks_iterate *ret) |
| + | { |
| + | return ext2fs_badblocks_list_iterate_begin(bb, ret); |
| + | } |
| + | |
| + | int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk) |
| + | { |
| + | return ext2fs_badblocks_list_iterate(iter, blk); |
| + | } |
| + | |
| + | void badblocks_list_iterate_end(badblocks_iterate iter) |
| + | { |
| + | ext2fs_badblocks_list_iterate_end(iter); |
| + | } |
| @@ -0,0 +1,267 @@ | |
| + | /* |
| + | * bb_inode.c --- routines to update the bad block inode. |
| + | * |
| + | * WARNING: This routine modifies a lot of state in the filesystem; if |
| + | * this routine returns an error, the bad block inode may be in an |
| + | * inconsistent state. |
| + | * |
| + | * Copyright (C) 1994, 1995 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | struct set_badblock_record { |
| + | ext2_badblocks_iterate bb_iter; |
| + | int bad_block_count; |
| + | blk_t *ind_blocks; |
| + | int max_ind_blocks; |
| + | int ind_blocks_size; |
| + | int ind_blocks_ptr; |
| + | char *block_buf; |
| + | errcode_t err; |
| + | }; |
| + | |
| + | static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, |
| + | e2_blkcnt_t blockcnt, |
| + | blk_t ref_block, int ref_offset, |
| + | void *priv_data); |
| + | static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, |
| + | e2_blkcnt_t blockcnt, |
| + | blk_t ref_block, int ref_offset, |
| + | void *priv_data); |
| + | |
| + | /* |
| + | * Given a bad blocks bitmap, update the bad blocks inode to reflect |
| + | * the map. |
| + | */ |
| + | errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list) |
| + | { |
| + | errcode_t retval; |
| + | struct set_badblock_record rec; |
| + | struct ext2_inode inode; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | if (!fs->block_map) |
| + | return EXT2_ET_NO_BLOCK_BITMAP; |
| + | |
| + | rec.bad_block_count = 0; |
| + | rec.ind_blocks_size = rec.ind_blocks_ptr = 0; |
| + | rec.max_ind_blocks = 10; |
| + | retval = ext2fs_get_mem(rec.max_ind_blocks * sizeof(blk_t), |
| + | &rec.ind_blocks); |
| + | if (retval) |
| + | return retval; |
| + | memset(rec.ind_blocks, 0, rec.max_ind_blocks * sizeof(blk_t)); |
| + | retval = ext2fs_get_mem(fs->blocksize, &rec.block_buf); |
| + | if (retval) |
| + | goto cleanup; |
| + | memset(rec.block_buf, 0, fs->blocksize); |
| + | rec.err = 0; |
| + | |
| + | /* |
| + | * First clear the old bad blocks (while saving the indirect blocks) |
| + | */ |
| + | retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, |
| + | BLOCK_FLAG_DEPTH_TRAVERSE, 0, |
| + | clear_bad_block_proc, &rec); |
| + | if (retval) |
| + | goto cleanup; |
| + | if (rec.err) { |
| + | retval = rec.err; |
| + | goto cleanup; |
| + | } |
| + | |
| + | /* |
| + | * Now set the bad blocks! |
| + | * |
| + | * First, mark the bad blocks as used. This prevents a bad |
| + | * block from being used as an indirecto block for the bad |
| + | * block inode (!). |
| + | */ |
| + | if (bb_list) { |
| + | retval = ext2fs_badblocks_list_iterate_begin(bb_list, |
| + | &rec.bb_iter); |
| + | if (retval) |
| + | goto cleanup; |
| + | retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, |
| + | BLOCK_FLAG_APPEND, 0, |
| + | set_bad_block_proc, &rec); |
| + | ext2fs_badblocks_list_iterate_end(rec.bb_iter); |
| + | if (retval) |
| + | goto cleanup; |
| + | if (rec.err) { |
| + | retval = rec.err; |
| + | goto cleanup; |
| + | } |
| + | } |
| + | |
| + | /* |
| + | * Update the bad block inode's mod time and block count |
| + | * field. |
| + | */ |
| + | retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode); |
| + | if (retval) |
| + | goto cleanup; |
| + | |
| + | inode.i_atime = inode.i_mtime = time(0); |
| + | if (!inode.i_ctime) |
| + | inode.i_ctime = time(0); |
| + | inode.i_blocks = rec.bad_block_count * (fs->blocksize / 512); |
| + | inode.i_size = rec.bad_block_count * fs->blocksize; |
| + | |
| + | retval = ext2fs_write_inode(fs, EXT2_BAD_INO, &inode); |
| + | if (retval) |
| + | goto cleanup; |
| + | |
| + | cleanup: |
| + | ext2fs_free_mem(&rec.ind_blocks); |
| + | ext2fs_free_mem(&rec.block_buf); |
| + | return retval; |
| + | } |
| + | |
| + | /* |
| + | * Helper function for update_bb_inode() |
| + | * |
| + | * Clear the bad blocks in the bad block inode, while saving the |
| + | * indirect blocks. |
| + | */ |
| + | #ifdef __TURBOC__ |
| + | #pragma argsused |
| + | #endif |
| + | static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, |
| + | e2_blkcnt_t blockcnt, |
| + | blk_t ref_block EXT2FS_ATTR((unused)), |
| + | int ref_offset EXT2FS_ATTR((unused)), |
| + | void *priv_data) |
| + | { |
| + | struct set_badblock_record *rec = (struct set_badblock_record *) |
| + | priv_data; |
| + | errcode_t retval; |
| + | unsigned long old_size; |
| + | |
| + | if (!*block_nr) |
| + | return 0; |
| + | |
| + | /* |
| + | * If the block number is outrageous, clear it and ignore it. |
| + | */ |
| + | if (*block_nr >= fs->super->s_blocks_count || |
| + | *block_nr < fs->super->s_first_data_block) { |
| + | *block_nr = 0; |
| + | return BLOCK_CHANGED; |
| + | } |
| + | |
| + | if (blockcnt < 0) { |
| + | if (rec->ind_blocks_size >= rec->max_ind_blocks) { |
| + | old_size = rec->max_ind_blocks * sizeof(blk_t); |
| + | rec->max_ind_blocks += 10; |
| + | retval = ext2fs_resize_mem(old_size, |
| + | rec->max_ind_blocks * sizeof(blk_t), |
| + | &rec->ind_blocks); |
| + | if (retval) { |
| + | rec->max_ind_blocks -= 10; |
| + | rec->err = retval; |
| + | return BLOCK_ABORT; |
| + | } |
| + | } |
| + | rec->ind_blocks[rec->ind_blocks_size++] = *block_nr; |
| + | } |
| + | |
| + | /* |
| + | * Mark the block as unused, and update accounting information |
| + | */ |
| + | ext2fs_block_alloc_stats(fs, *block_nr, -1); |
| + | |
| + | *block_nr = 0; |
| + | return BLOCK_CHANGED; |
| + | } |
| + | |
| + | |
| + | /* |
| + | * Helper function for update_bb_inode() |
| + | * |
| + | * Set the block list in the bad block inode, using the supplied bitmap. |
| + | */ |
| + | #ifdef __TURBOC__ |
| + | #pragma argsused |
| + | #endif |
| + | static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, |
| + | e2_blkcnt_t blockcnt, |
| + | blk_t ref_block EXT2FS_ATTR((unused)), |
| + | int ref_offset EXT2FS_ATTR((unused)), |
| + | void *priv_data) |
| + | { |
| + | struct set_badblock_record *rec = (struct set_badblock_record *) |
| + | priv_data; |
| + | errcode_t retval; |
| + | blk_t blk; |
| + | |
| + | if (blockcnt >= 0) { |
| + | /* |
| + | * Get the next bad block. |
| + | */ |
| + | if (!ext2fs_badblocks_list_iterate(rec->bb_iter, &blk)) |
| + | return BLOCK_ABORT; |
| + | rec->bad_block_count++; |
| + | } else { |
| + | /* |
| + | * An indirect block; fetch a block from the |
| + | * previously used indirect block list. The block |
| + | * most be not marked as used; if so, get another one. |
| + | * If we run out of reserved indirect blocks, allocate |
| + | * a new one. |
| + | */ |
| + | retry: |
| + | if (rec->ind_blocks_ptr < rec->ind_blocks_size) { |
| + | blk = rec->ind_blocks[rec->ind_blocks_ptr++]; |
| + | if (ext2fs_test_block_bitmap(fs->block_map, blk)) |
| + | goto retry; |
| + | } else { |
| + | retval = ext2fs_new_block(fs, 0, 0, &blk); |
| + | if (retval) { |
| + | rec->err = retval; |
| + | return BLOCK_ABORT; |
| + | } |
| + | } |
| + | retval = io_channel_write_blk(fs->io, blk, 1, rec->block_buf); |
| + | if (retval) { |
| + | rec->err = retval; |
| + | return BLOCK_ABORT; |
| + | } |
| + | } |
| + | |
| + | /* |
| + | * Update block counts |
| + | */ |
| + | ext2fs_block_alloc_stats(fs, blk, +1); |
| + | |
| + | *block_nr = blk; |
| + | return BLOCK_CHANGED; |
| + | } |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| @@ -0,0 +1,212 @@ | |
| + | /* |
| + | * bitmaps.c --- routines to read, write, and manipulate the inode and |
| + | * block bitmaps. |
| + | * |
| + | * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | static errcode_t make_bitmap(__u32 start, __u32 end, __u32 real_end, |
| + | const char *descr, char *init_map, |
| + | ext2fs_generic_bitmap *ret) |
| + | { |
| + | ext2fs_generic_bitmap bitmap; |
| + | errcode_t retval; |
| + | size_t size; |
| + | |
| + | retval = ext2fs_get_mem(sizeof(struct ext2fs_struct_generic_bitmap), |
| + | &bitmap); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP; |
| + | bitmap->fs = NULL; |
| + | bitmap->start = start; |
| + | bitmap->end = end; |
| + | bitmap->real_end = real_end; |
| + | bitmap->base_error_code = EXT2_ET_BAD_GENERIC_MARK; |
| + | if (descr) { |
| + | retval = ext2fs_get_mem(strlen(descr)+1, &bitmap->description); |
| + | if (retval) { |
| + | ext2fs_free_mem(&bitmap); |
| + | return retval; |
| + | } |
| + | strcpy(bitmap->description, descr); |
| + | } else |
| + | bitmap->description = 0; |
| + | |
| + | size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1); |
| + | retval = ext2fs_get_mem(size, &bitmap->bitmap); |
| + | if (retval) { |
| + | ext2fs_free_mem(&bitmap->description); |
| + | ext2fs_free_mem(&bitmap); |
| + | return retval; |
| + | } |
| + | |
| + | if (init_map) |
| + | memcpy(bitmap->bitmap, init_map, size); |
| + | else |
| + | memset(bitmap->bitmap, 0, size); |
| + | *ret = bitmap; |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_allocate_generic_bitmap(__u32 start, |
| + | __u32 end, |
| + | __u32 real_end, |
| + | const char *descr, |
| + | ext2fs_generic_bitmap *ret) |
| + | { |
| + | return make_bitmap(start, end, real_end, descr, 0, ret); |
| + | } |
| + | |
| + | errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src, |
| + | ext2fs_generic_bitmap *dest) |
| + | { |
| + | errcode_t retval; |
| + | ext2fs_generic_bitmap new_map; |
| + | |
| + | retval = make_bitmap(src->start, src->end, src->real_end, |
| + | src->description, src->bitmap, &new_map); |
| + | if (retval) |
| + | return retval; |
| + | new_map->magic = src->magic; |
| + | new_map->fs = src->fs; |
| + | new_map->base_error_code = src->base_error_code; |
| + | *dest = new_map; |
| + | return 0; |
| + | } |
| + | |
| + | void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map) |
| + | { |
| + | __u32 i, j; |
| + | |
| + | for (i=map->end+1, j = i - map->start; i <= map->real_end; i++, j++) |
| + | ext2fs_set_bit(j, map->bitmap); |
| + | |
| + | return; |
| + | } |
| + | |
| + | errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs, |
| + | const char *descr, |
| + | ext2fs_inode_bitmap *ret) |
| + | { |
| + | ext2fs_inode_bitmap bitmap; |
| + | errcode_t retval; |
| + | __u32 start, end, real_end; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | fs->write_bitmaps = ext2fs_write_bitmaps; |
| + | |
| + | start = 1; |
| + | end = fs->super->s_inodes_count; |
| + | real_end = (EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count); |
| + | |
| + | retval = ext2fs_allocate_generic_bitmap(start, end, real_end, |
| + | descr, &bitmap); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | bitmap->magic = EXT2_ET_MAGIC_INODE_BITMAP; |
| + | bitmap->fs = fs; |
| + | bitmap->base_error_code = EXT2_ET_BAD_INODE_MARK; |
| + | |
| + | *ret = bitmap; |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs, |
| + | const char *descr, |
| + | ext2fs_block_bitmap *ret) |
| + | { |
| + | ext2fs_block_bitmap bitmap; |
| + | errcode_t retval; |
| + | __u32 start, end, real_end; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | fs->write_bitmaps = ext2fs_write_bitmaps; |
| + | |
| + | start = fs->super->s_first_data_block; |
| + | end = fs->super->s_blocks_count-1; |
| + | real_end = (EXT2_BLOCKS_PER_GROUP(fs->super) |
| + | * fs->group_desc_count)-1 + start; |
| + | |
| + | retval = ext2fs_allocate_generic_bitmap(start, end, real_end, |
| + | descr, &bitmap); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | bitmap->magic = EXT2_ET_MAGIC_BLOCK_BITMAP; |
| + | bitmap->fs = fs; |
| + | bitmap->base_error_code = EXT2_ET_BAD_BLOCK_MARK; |
| + | |
| + | *ret = bitmap; |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap, |
| + | ext2_ino_t end, ext2_ino_t *oend) |
| + | { |
| + | EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_INODE_BITMAP); |
| + | |
| + | if (end > bitmap->real_end) |
| + | return EXT2_ET_FUDGE_INODE_BITMAP_END; |
| + | if (oend) |
| + | *oend = bitmap->end; |
| + | bitmap->end = end; |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap, |
| + | blk_t end, blk_t *oend) |
| + | { |
| + | EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_BLOCK_BITMAP); |
| + | |
| + | if (end > bitmap->real_end) |
| + | return EXT2_ET_FUDGE_BLOCK_BITMAP_END; |
| + | if (oend) |
| + | *oend = bitmap->end; |
| + | bitmap->end = end; |
| + | return 0; |
| + | } |
| + | |
| + | void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap) |
| + | { |
| + | if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_INODE_BITMAP)) |
| + | return; |
| + | |
| + | memset(bitmap->bitmap, 0, |
| + | (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1)); |
| + | } |
| + | |
| + | void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap) |
| + | { |
| + | if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_BLOCK_BITMAP)) |
| + | return; |
| + | |
| + | memset(bitmap->bitmap, 0, |
| + | (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1)); |
| + | } |
| @@ -0,0 +1,91 @@ | |
| + | /* |
| + | * bitops.c --- Bitmap frobbing code. See bitops.h for the inlined |
| + | * routines. |
| + | * |
| + | * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | #ifndef _EXT2_HAVE_ASM_BITOPS_ |
| + | |
| + | /* |
| + | * For the benefit of those who are trying to port Linux to another |
| + | * architecture, here are some C-language equivalents. You should |
| + | * recode these in the native assmebly language, if at all possible. |
| + | * |
| + | * C language equivalents written by Theodore Ts'o, 9/26/92. |
| + | * Modified by Pete A. Zaitcev 7/14/95 to be portable to big endian |
| + | * systems, as well as non-32 bit systems. |
| + | */ |
| + | |
| + | int ext2fs_set_bit(int nr,void * addr) |
| + | { |
| + | int mask, retval; |
| + | unsigned char *ADDR = (unsigned char *) addr; |
| + | |
| + | ADDR += nr >> 3; |
| + | mask = 1 << (nr & 0x07); |
| + | retval = mask & *ADDR; |
| + | *ADDR |= mask; |
| + | return retval; |
| + | } |
| + | |
| + | int ext2fs_clear_bit(int nr, void * addr) |
| + | { |
| + | int mask, retval; |
| + | unsigned char *ADDR = (unsigned char *) addr; |
| + | |
| + | ADDR += nr >> 3; |
| + | mask = 1 << (nr & 0x07); |
| + | retval = mask & *ADDR; |
| + | *ADDR &= ~mask; |
| + | return retval; |
| + | } |
| + | |
| + | int ext2fs_test_bit(int nr, const void * addr) |
| + | { |
| + | int mask; |
| + | const unsigned char *ADDR = (const unsigned char *) addr; |
| + | |
| + | ADDR += nr >> 3; |
| + | mask = 1 << (nr & 0x07); |
| + | return (mask & *ADDR); |
| + | } |
| + | |
| + | #endif /* !_EXT2_HAVE_ASM_BITOPS_ */ |
| + | |
| + | void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg, |
| + | const char *description) |
| + | { |
| + | #ifndef OMIT_COM_ERR |
| + | if (description) |
| + | com_err(0, errcode, "#%lu for %s", arg, description); |
| + | else |
| + | com_err(0, errcode, "#%lu", arg); |
| + | #endif |
| + | } |
| + | |
| + | void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap, |
| + | int code, unsigned long arg) |
| + | { |
| + | #ifndef OMIT_COM_ERR |
| + | if (bitmap->description) |
| + | com_err(0, bitmap->base_error_code+code, |
| + | "#%lu for %s", arg, bitmap->description); |
| + | else |
| + | com_err(0, bitmap->base_error_code + code, "#%lu", arg); |
| + | #endif |
| + | } |
| + | |
| @@ -0,0 +1,617 @@ | |
| + | /* |
| + | * bitops.h --- Bitmap frobbing code. The byte swapping routines are |
| + | * also included here. |
| + | * |
| + | * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | * |
| + | * i386 bitops operations taken from <asm/bitops.h>, Copyright 1992, |
| + | * Linus Torvalds. |
| + | */ |
| + | |
| + | |
| + | extern int ext2fs_set_bit(int nr,void * addr); |
| + | extern int ext2fs_clear_bit(int nr, void * addr); |
| + | extern int ext2fs_test_bit(int nr, const void * addr); |
| + | extern __u16 ext2fs_swab16(__u16 val); |
| + | extern __u32 ext2fs_swab32(__u32 val); |
| + | |
| + | #ifdef WORDS_BIGENDIAN |
| + | #define ext2fs_cpu_to_le32(x) ext2fs_swab32((x)) |
| + | #define ext2fs_le32_to_cpu(x) ext2fs_swab32((x)) |
| + | #define ext2fs_cpu_to_le16(x) ext2fs_swab16((x)) |
| + | #define ext2fs_le16_to_cpu(x) ext2fs_swab16((x)) |
| + | #define ext2fs_cpu_to_be32(x) ((__u32)(x)) |
| + | #define ext2fs_be32_to_cpu(x) ((__u32)(x)) |
| + | #define ext2fs_cpu_to_be16(x) ((__u16)(x)) |
| + | #define ext2fs_be16_to_cpu(x) ((__u16)(x)) |
| + | #else |
| + | #define ext2fs_cpu_to_le32(x) ((__u32)(x)) |
| + | #define ext2fs_le32_to_cpu(x) ((__u32)(x)) |
| + | #define ext2fs_cpu_to_le16(x) ((__u16)(x)) |
| + | #define ext2fs_le16_to_cpu(x) ((__u16)(x)) |
| + | #define ext2fs_cpu_to_be32(x) ext2fs_swab32((x)) |
| + | #define ext2fs_be32_to_cpu(x) ext2fs_swab32((x)) |
| + | #define ext2fs_cpu_to_be16(x) ext2fs_swab16((x)) |
| + | #define ext2fs_be16_to_cpu(x) ext2fs_swab16((x)) |
| + | #endif |
| + | |
| + | /* |
| + | * EXT2FS bitmap manipulation routines. |
| + | */ |
| + | |
| + | /* Support for sending warning messages from the inline subroutines */ |
| + | extern const char *ext2fs_block_string; |
| + | extern const char *ext2fs_inode_string; |
| + | extern const char *ext2fs_mark_string; |
| + | extern const char *ext2fs_unmark_string; |
| + | extern const char *ext2fs_test_string; |
| + | extern void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg, |
| + | const char *description); |
| + | extern void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap, |
| + | int code, unsigned long arg); |
| + | |
| + | extern int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); |
| + | extern int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap, |
| + | blk_t block); |
| + | extern int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); |
| + | |
| + | extern int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); |
| + | extern int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, |
| + | ext2_ino_t inode); |
| + | extern int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); |
| + | |
| + | extern void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap, |
| + | blk_t block); |
| + | extern void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap, |
| + | blk_t block); |
| + | extern int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap, |
| + | blk_t block); |
| + | |
| + | extern void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, |
| + | ext2_ino_t inode); |
| + | extern void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, |
| + | ext2_ino_t inode); |
| + | extern int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap, |
| + | ext2_ino_t inode); |
| + | extern blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap); |
| + | extern ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap); |
| + | extern blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap); |
| + | extern ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap); |
| + | |
| + | extern void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, |
| + | blk_t block, int num); |
| + | extern void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, |
| + | blk_t block, int num); |
| + | extern int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap, |
| + | blk_t block, int num); |
| + | extern void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, |
| + | blk_t block, int num); |
| + | extern void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, |
| + | blk_t block, int num); |
| + | extern int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap, |
| + | blk_t block, int num); |
| + | extern void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map); |
| + | |
| + | /* These two routines moved to gen_bitmap.c */ |
| + | extern int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap, |
| + | __u32 bitno); |
| + | extern int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap, |
| + | blk_t bitno); |
| + | /* |
| + | * The inline routines themselves... |
| + | * |
| + | * If NO_INLINE_FUNCS is defined, then we won't try to do inline |
| + | * functions at all; they will be included as normal functions in |
| + | * inline.c |
| + | */ |
| + | #ifdef NO_INLINE_FUNCS |
| + | #if (defined(__GNUC__) && (defined(__i386__) || defined(__i486__) || \ |
| + | defined(__i586__) || defined(__mc68000__) || \ |
| + | defined(__sparc__))) |
| + | /* This prevents bitops.c from trying to include the C */ |
| + | /* function version of these functions */ |
| + | #define _EXT2_HAVE_ASM_BITOPS_ |
| + | #endif |
| + | #endif /* NO_INLINE_FUNCS */ |
| + | |
| + | #if (defined(INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS)) |
| + | #ifdef INCLUDE_INLINE_FUNCS |
| + | #define _INLINE_ extern |
| + | #else |
| + | #ifdef __GNUC__ |
| + | #define _INLINE_ extern __inline__ |
| + | #else /* For Watcom C */ |
| + | #define _INLINE_ extern inline |
| + | #endif |
| + | #endif |
| + | |
| + | #if ((defined __GNUC__) && !defined(_EXT2_USE_C_VERSIONS_) && \ |
| + | (defined(__i386__) || defined(__i486__) || defined(__i586__))) |
| + | |
| + | #define _EXT2_HAVE_ASM_BITOPS_ |
| + | #define _EXT2_HAVE_ASM_SWAB_ |
| + | #define _EXT2_HAVE_ASM_FINDBIT_ |
| + | |
| + | /* |
| + | * These are done by inline assembly for speed reasons..... |
| + | * |
| + | * All bitoperations return 0 if the bit was cleared before the |
| + | * operation and != 0 if it was not. Bit 0 is the LSB of addr; bit 32 |
| + | * is the LSB of (addr+1). |
| + | */ |
| + | |
| + | /* |
| + | * Some hacks to defeat gcc over-optimizations.. |
| + | */ |
| + | struct __dummy_h { unsigned long a[100]; }; |
| + | #define EXT2FS_ADDR (*(struct __dummy_h *) addr) |
| + | #define EXT2FS_CONST_ADDR (*(const struct __dummy_h *) addr) |
| + | |
| + | _INLINE_ int ext2fs_set_bit(int nr, void * addr) |
| + | { |
| + | int oldbit; |
| + | |
| + | __asm__ __volatile__("btsl %2,%1\n\tsbbl %0,%0" |
| + | :"=r" (oldbit),"=m" (EXT2FS_ADDR) |
| + | :"r" (nr)); |
| + | return oldbit; |
| + | } |
| + | |
| + | _INLINE_ int ext2fs_clear_bit(int nr, void * addr) |
| + | { |
| + | int oldbit; |
| + | |
| + | __asm__ __volatile__("btrl %2,%1\n\tsbbl %0,%0" |
| + | :"=r" (oldbit),"=m" (EXT2FS_ADDR) |
| + | :"r" (nr)); |
| + | return oldbit; |
| + | } |
| + | |
| + | _INLINE_ int ext2fs_test_bit(int nr, const void * addr) |
| + | { |
| + | int oldbit; |
| + | |
| + | __asm__ __volatile__("btl %2,%1\n\tsbbl %0,%0" |
| + | :"=r" (oldbit) |
| + | :"m" (EXT2FS_CONST_ADDR),"r" (nr)); |
| + | return oldbit; |
| + | } |
| + | |
| + | #if 0 |
| + | _INLINE_ int ext2fs_find_first_bit_set(void * addr, unsigned size) |
| + | { |
| + | int d0, d1, d2; |
| + | int res; |
| + | |
| + | if (!size) |
| + | return 0; |
| + | /* This looks at memory. Mark it volatile to tell gcc not to move it around */ |
| + | __asm__ __volatile__( |
| + | "cld\n\t" |
| + | "xorl %%eax,%%eax\n\t" |
| + | "xorl %%edx,%%edx\n\t" |
| + | "repe; scasl\n\t" |
| + | "je 1f\n\t" |
| + | "movl -4(%%edi),%%eax\n\t" |
| + | "subl $4,%%edi\n\t" |
| + | "bsfl %%eax,%%edx\n" |
| + | "1:\tsubl %%esi,%%edi\n\t" |
| + | "shll $3,%%edi\n\t" |
| + | "addl %%edi,%%edx" |
| + | :"=d" (res), "=&c" (d0), "=&D" (d1), "=&a" (d2) |
| + | :"1" ((size + 31) >> 5), "2" (addr), "S" (addr)); |
| + | return res; |
| + | } |
| + | |
| + | _INLINE_ int ext2fs_find_next_bit_set (void * addr, int size, int offset) |
| + | { |
| + | unsigned long * p = ((unsigned long *) addr) + (offset >> 5); |
| + | int set = 0, bit = offset & 31, res; |
| + | |
| + | if (bit) { |
| + | /* |
| + | * Look for zero in first byte |
| + | */ |
| + | __asm__("bsfl %1,%0\n\t" |
| + | "jne 1f\n\t" |
| + | "movl $32, %0\n" |
| + | "1:" |
| + | : "=r" (set) |
| + | : "r" (*p >> bit)); |
| + | if (set < (32 - bit)) |
| + | return set + offset; |
| + | set = 32 - bit; |
| + | p++; |
| + | } |
| + | /* |
| + | * No bit found yet, search remaining full bytes for a bit |
| + | */ |
| + | res = ext2fs_find_first_bit_set(p, size - 32 * (p - (unsigned long *) addr)); |
| + | return (offset + set + res); |
| + | } |
| + | #endif |
| + | |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | _INLINE_ __u32 ext2fs_swab32(__u32 val) |
| + | { |
| + | #ifdef EXT2FS_REQUIRE_486 |
| + | __asm__("bswap %0" : "=r" (val) : "0" (val)); |
| + | #else |
| + | __asm__("xchgb %b0,%h0\n\t" /* swap lower bytes */ |
| + | "rorl $16,%0\n\t" /* swap words */ |
| + | "xchgb %b0,%h0" /* swap higher bytes */ |
| + | :"=q" (val) |
| + | : "0" (val)); |
| + | #endif |
| + | return val; |
| + | } |
| + | |
| + | _INLINE_ __u16 ext2fs_swab16(__u16 val) |
| + | { |
| + | __asm__("xchgb %b0,%h0" /* swap bytes */ \ |
| + | : "=q" (val) \ |
| + | : "0" (val)); \ |
| + | return val; |
| + | } |
| + | #endif |
| + | |
| + | #undef EXT2FS_ADDR |
| + | |
| + | #endif /* i386 */ |
| + | |
| + | #ifdef __mc68000__ |
| + | |
| + | #define _EXT2_HAVE_ASM_BITOPS_ |
| + | |
| + | _INLINE_ int ext2fs_set_bit(int nr,void * addr) |
| + | { |
| + | char retval; |
| + | |
| + | __asm__ __volatile__ ("bfset %2@{%1:#1}; sne %0" |
| + | : "=d" (retval) : "d" (nr^7), "a" (addr)); |
| + | |
| + | return retval; |
| + | } |
| + | |
| + | _INLINE_ int ext2fs_clear_bit(int nr, void * addr) |
| + | { |
| + | char retval; |
| + | |
| + | __asm__ __volatile__ ("bfclr %2@{%1:#1}; sne %0" |
| + | : "=d" (retval) : "d" (nr^7), "a" (addr)); |
| + | |
| + | return retval; |
| + | } |
| + | |
| + | _INLINE_ int ext2fs_test_bit(int nr, const void * addr) |
| + | { |
| + | char retval; |
| + | |
| + | __asm__ __volatile__ ("bftst %2@{%1:#1}; sne %0" |
| + | : "=d" (retval) : "d" (nr^7), "a" (addr)); |
| + | |
| + | return retval; |
| + | } |
| + | |
| + | #endif /* __mc68000__ */ |
| + | |
| + | |
| + | #if !defined(_EXT2_HAVE_ASM_SWAB_) && defined(EXT2FS_ENABLE_SWAPFS) |
| + | |
| + | _INLINE_ __u16 ext2fs_swab16(__u16 val) |
| + | { |
| + | return (val >> 8) | (val << 8); |
| + | } |
| + | |
| + | _INLINE_ __u32 ext2fs_swab32(__u32 val) |
| + | { |
| + | return ((val>>24) | ((val>>8)&0xFF00) | |
| + | ((val<<8)&0xFF0000) | (val<<24)); |
| + | } |
| + | |
| + | #endif /* !_EXT2_HAVE_ASM_SWAB */ |
| + | |
| + | #if !defined(_EXT2_HAVE_ASM_FINDBIT_) |
| + | _INLINE_ int ext2fs_find_first_bit_set(void * addr, unsigned size) |
| + | { |
| + | char *cp = (unsigned char *) addr; |
| + | int res = 0, d0; |
| + | |
| + | if (!size) |
| + | return 0; |
| + | |
| + | while ((size > res) && (*cp == 0)) { |
| + | cp++; |
| + | res += 8; |
| + | } |
| + | d0 = ffs(*cp); |
| + | if (d0 == 0) |
| + | return size; |
| + | |
| + | return res + d0 - 1; |
| + | } |
| + | |
| + | _INLINE_ int ext2fs_find_next_bit_set (void * addr, int size, int offset) |
| + | { |
| + | unsigned char * p; |
| + | int set = 0, bit = offset & 7, res = 0, d0; |
| + | |
| + | res = offset >> 3; |
| + | p = ((unsigned char *) addr) + res; |
| + | |
| + | if (bit) { |
| + | set = ffs(*p & ~((1 << bit) - 1)); |
| + | if (set) |
| + | return (offset & ~7) + set - 1; |
| + | p++; |
| + | res += 8; |
| + | } |
| + | while ((size > res) && (*p == 0)) { |
| + | p++; |
| + | res += 8; |
| + | } |
| + | d0 = ffs(*p); |
| + | if (d0 == 0) |
| + | return size; |
| + | |
| + | return (res + d0 - 1); |
| + | } |
| + | #endif |
| + | |
| + | _INLINE_ int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap, |
| + | blk_t bitno); |
| + | |
| + | _INLINE_ int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap, |
| + | blk_t bitno) |
| + | { |
| + | if ((bitno < bitmap->start) || (bitno > bitmap->end)) { |
| + | ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, bitno); |
| + | return 0; |
| + | } |
| + | return ext2fs_test_bit(bitno - bitmap->start, bitmap->bitmap); |
| + | } |
| + | |
| + | _INLINE_ int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap, |
| + | blk_t block) |
| + | { |
| + | return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) |
| + | bitmap, |
| + | block); |
| + | } |
| + | |
| + | _INLINE_ int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap, |
| + | blk_t block) |
| + | { |
| + | return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, |
| + | block); |
| + | } |
| + | |
| + | _INLINE_ int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap, |
| + | blk_t block) |
| + | { |
| + | return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, |
| + | block); |
| + | } |
| + | |
| + | _INLINE_ int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, |
| + | ext2_ino_t inode) |
| + | { |
| + | return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap, |
| + | inode); |
| + | } |
| + | |
| + | _INLINE_ int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, |
| + | ext2_ino_t inode) |
| + | { |
| + | return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, |
| + | inode); |
| + | } |
| + | |
| + | _INLINE_ int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap, |
| + | ext2_ino_t inode) |
| + | { |
| + | return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, |
| + | inode); |
| + | } |
| + | |
| + | _INLINE_ void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap, |
| + | blk_t block) |
| + | { |
| + | #ifdef EXT2FS_DEBUG_FAST_OPS |
| + | if ((block < bitmap->start) || (block > bitmap->end)) { |
| + | ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block, |
| + | bitmap->description); |
| + | return; |
| + | } |
| + | #endif |
| + | ext2fs_set_bit(block - bitmap->start, bitmap->bitmap); |
| + | } |
| + | |
| + | _INLINE_ void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap, |
| + | blk_t block) |
| + | { |
| + | #ifdef EXT2FS_DEBUG_FAST_OPS |
| + | if ((block < bitmap->start) || (block > bitmap->end)) { |
| + | ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, |
| + | block, bitmap->description); |
| + | return; |
| + | } |
| + | #endif |
| + | ext2fs_clear_bit(block - bitmap->start, bitmap->bitmap); |
| + | } |
| + | |
| + | _INLINE_ int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap, |
| + | blk_t block) |
| + | { |
| + | #ifdef EXT2FS_DEBUG_FAST_OPS |
| + | if ((block < bitmap->start) || (block > bitmap->end)) { |
| + | ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST, |
| + | block, bitmap->description); |
| + | return 0; |
| + | } |
| + | #endif |
| + | return ext2fs_test_bit(block - bitmap->start, bitmap->bitmap); |
| + | } |
| + | |
| + | _INLINE_ void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, |
| + | ext2_ino_t inode) |
| + | { |
| + | #ifdef EXT2FS_DEBUG_FAST_OPS |
| + | if ((inode < bitmap->start) || (inode > bitmap->end)) { |
| + | ext2fs_warn_bitmap(EXT2_ET_BAD_INODE_MARK, |
| + | inode, bitmap->description); |
| + | return; |
| + | } |
| + | #endif |
| + | ext2fs_set_bit(inode - bitmap->start, bitmap->bitmap); |
| + | } |
| + | |
| + | _INLINE_ void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, |
| + | ext2_ino_t inode) |
| + | { |
| + | #ifdef EXT2FS_DEBUG_FAST_OPS |
| + | if ((inode < bitmap->start) || (inode > bitmap->end)) { |
| + | ext2fs_warn_bitmap(EXT2_ET_BAD_INODE_UNMARK, |
| + | inode, bitmap->description); |
| + | return; |
| + | } |
| + | #endif |
| + | ext2fs_clear_bit(inode - bitmap->start, bitmap->bitmap); |
| + | } |
| + | |
| + | _INLINE_ int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap, |
| + | ext2_ino_t inode) |
| + | { |
| + | #ifdef EXT2FS_DEBUG_FAST_OPS |
| + | if ((inode < bitmap->start) || (inode > bitmap->end)) { |
| + | ext2fs_warn_bitmap(EXT2_ET_BAD_INODE_TEST, |
| + | inode, bitmap->description); |
| + | return 0; |
| + | } |
| + | #endif |
| + | return ext2fs_test_bit(inode - bitmap->start, bitmap->bitmap); |
| + | } |
| + | |
| + | _INLINE_ blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap) |
| + | { |
| + | return bitmap->start; |
| + | } |
| + | |
| + | _INLINE_ ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap) |
| + | { |
| + | return bitmap->start; |
| + | } |
| + | |
| + | _INLINE_ blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap) |
| + | { |
| + | return bitmap->end; |
| + | } |
| + | |
| + | _INLINE_ ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap) |
| + | { |
| + | return bitmap->end; |
| + | } |
| + | |
| + | _INLINE_ int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap, |
| + | blk_t block, int num) |
| + | { |
| + | int i; |
| + | |
| + | if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { |
| + | ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST, |
| + | block, bitmap->description); |
| + | return 0; |
| + | } |
| + | for (i=0; i < num; i++) { |
| + | if (ext2fs_fast_test_block_bitmap(bitmap, block+i)) |
| + | return 0; |
| + | } |
| + | return 1; |
| + | } |
| + | |
| + | _INLINE_ int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap, |
| + | blk_t block, int num) |
| + | { |
| + | int i; |
| + | |
| + | #ifdef EXT2FS_DEBUG_FAST_OPS |
| + | if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { |
| + | ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST, |
| + | block, bitmap->description); |
| + | return 0; |
| + | } |
| + | #endif |
| + | for (i=0; i < num; i++) { |
| + | if (ext2fs_fast_test_block_bitmap(bitmap, block+i)) |
| + | return 0; |
| + | } |
| + | return 1; |
| + | } |
| + | |
| + | _INLINE_ void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, |
| + | blk_t block, int num) |
| + | { |
| + | int i; |
| + | |
| + | if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { |
| + | ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block, |
| + | bitmap->description); |
| + | return; |
| + | } |
| + | for (i=0; i < num; i++) |
| + | ext2fs_set_bit(block + i - bitmap->start, bitmap->bitmap); |
| + | } |
| + | |
| + | _INLINE_ void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, |
| + | blk_t block, int num) |
| + | { |
| + | int i; |
| + | |
| + | #ifdef EXT2FS_DEBUG_FAST_OPS |
| + | if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { |
| + | ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block, |
| + | bitmap->description); |
| + | return; |
| + | } |
| + | #endif |
| + | for (i=0; i < num; i++) |
| + | ext2fs_set_bit(block + i - bitmap->start, bitmap->bitmap); |
| + | } |
| + | |
| + | _INLINE_ void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, |
| + | blk_t block, int num) |
| + | { |
| + | int i; |
| + | |
| + | if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { |
| + | ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block, |
| + | bitmap->description); |
| + | return; |
| + | } |
| + | for (i=0; i < num; i++) |
| + | ext2fs_clear_bit(block + i - bitmap->start, bitmap->bitmap); |
| + | } |
| + | |
| + | _INLINE_ void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, |
| + | blk_t block, int num) |
| + | { |
| + | int i; |
| + | |
| + | #ifdef EXT2FS_DEBUG_FAST_OPS |
| + | if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { |
| + | ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block, |
| + | bitmap->description); |
| + | return; |
| + | } |
| + | #endif |
| + | for (i=0; i < num; i++) |
| + | ext2fs_clear_bit(block + i - bitmap->start, bitmap->bitmap); |
| + | } |
| + | #undef _INLINE_ |
| + | #endif |
| + | |
| @@ -0,0 +1,437 @@ | |
| + | /* |
| + | * block.c --- iterate over all blocks in an inode |
| + | * |
| + | * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | struct block_context { |
| + | ext2_filsys fs; |
| + | int (*func)(ext2_filsys fs, |
| + | blk_t *blocknr, |
| + | e2_blkcnt_t bcount, |
| + | blk_t ref_blk, |
| + | int ref_offset, |
| + | void *priv_data); |
| + | e2_blkcnt_t bcount; |
| + | int bsize; |
| + | int flags; |
| + | errcode_t errcode; |
| + | char *ind_buf; |
| + | char *dind_buf; |
| + | char *tind_buf; |
| + | void *priv_data; |
| + | }; |
| + | |
| + | static int block_iterate_ind(blk_t *ind_block, blk_t ref_block, |
| + | int ref_offset, struct block_context *ctx) |
| + | { |
| + | int ret = 0, changed = 0; |
| + | int i, flags, limit, offset; |
| + | blk_t *block_nr; |
| + | |
| + | limit = ctx->fs->blocksize >> 2; |
| + | if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && |
| + | !(ctx->flags & BLOCK_FLAG_DATA_ONLY)) |
| + | ret = (*ctx->func)(ctx->fs, ind_block, |
| + | BLOCK_COUNT_IND, ref_block, |
| + | ref_offset, ctx->priv_data); |
| + | if (!*ind_block || (ret & BLOCK_ABORT)) { |
| + | ctx->bcount += limit; |
| + | return ret; |
| + | } |
| + | if (*ind_block >= ctx->fs->super->s_blocks_count || |
| + | *ind_block < ctx->fs->super->s_first_data_block) { |
| + | ctx->errcode = EXT2_ET_BAD_IND_BLOCK; |
| + | ret |= BLOCK_ERROR; |
| + | return ret; |
| + | } |
| + | ctx->errcode = ext2fs_read_ind_block(ctx->fs, *ind_block, |
| + | ctx->ind_buf); |
| + | if (ctx->errcode) { |
| + | ret |= BLOCK_ERROR; |
| + | return ret; |
| + | } |
| + | |
| + | block_nr = (blk_t *) ctx->ind_buf; |
| + | offset = 0; |
| + | if (ctx->flags & BLOCK_FLAG_APPEND) { |
| + | for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { |
| + | flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount, |
| + | *ind_block, offset, |
| + | ctx->priv_data); |
| + | changed |= flags; |
| + | if (flags & BLOCK_ABORT) { |
| + | ret |= BLOCK_ABORT; |
| + | break; |
| + | } |
| + | offset += sizeof(blk_t); |
| + | } |
| + | } else { |
| + | for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { |
| + | if (*block_nr == 0) |
| + | continue; |
| + | flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount, |
| + | *ind_block, offset, |
| + | ctx->priv_data); |
| + | changed |= flags; |
| + | if (flags & BLOCK_ABORT) { |
| + | ret |= BLOCK_ABORT; |
| + | break; |
| + | } |
| + | offset += sizeof(blk_t); |
| + | } |
| + | } |
| + | if (changed & BLOCK_CHANGED) { |
| + | ctx->errcode = ext2fs_write_ind_block(ctx->fs, *ind_block, |
| + | ctx->ind_buf); |
| + | if (ctx->errcode) |
| + | ret |= BLOCK_ERROR | BLOCK_ABORT; |
| + | } |
| + | if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && |
| + | !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && |
| + | !(ret & BLOCK_ABORT)) |
| + | ret |= (*ctx->func)(ctx->fs, ind_block, |
| + | BLOCK_COUNT_IND, ref_block, |
| + | ref_offset, ctx->priv_data); |
| + | return ret; |
| + | } |
| + | |
| + | static int block_iterate_dind(blk_t *dind_block, blk_t ref_block, |
| + | int ref_offset, struct block_context *ctx) |
| + | { |
| + | int ret = 0, changed = 0; |
| + | int i, flags, limit, offset; |
| + | blk_t *block_nr; |
| + | |
| + | limit = ctx->fs->blocksize >> 2; |
| + | if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE | |
| + | BLOCK_FLAG_DATA_ONLY))) |
| + | ret = (*ctx->func)(ctx->fs, dind_block, |
| + | BLOCK_COUNT_DIND, ref_block, |
| + | ref_offset, ctx->priv_data); |
| + | if (!*dind_block || (ret & BLOCK_ABORT)) { |
| + | ctx->bcount += limit*limit; |
| + | return ret; |
| + | } |
| + | if (*dind_block >= ctx->fs->super->s_blocks_count || |
| + | *dind_block < ctx->fs->super->s_first_data_block) { |
| + | ctx->errcode = EXT2_ET_BAD_DIND_BLOCK; |
| + | ret |= BLOCK_ERROR; |
| + | return ret; |
| + | } |
| + | ctx->errcode = ext2fs_read_ind_block(ctx->fs, *dind_block, |
| + | ctx->dind_buf); |
| + | if (ctx->errcode) { |
| + | ret |= BLOCK_ERROR; |
| + | return ret; |
| + | } |
| + | |
| + | block_nr = (blk_t *) ctx->dind_buf; |
| + | offset = 0; |
| + | if (ctx->flags & BLOCK_FLAG_APPEND) { |
| + | for (i = 0; i < limit; i++, block_nr++) { |
| + | flags = block_iterate_ind(block_nr, |
| + | *dind_block, offset, |
| + | ctx); |
| + | changed |= flags; |
| + | if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { |
| + | ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); |
| + | break; |
| + | } |
| + | offset += sizeof(blk_t); |
| + | } |
| + | } else { |
| + | for (i = 0; i < limit; i++, block_nr++) { |
| + | if (*block_nr == 0) { |
| + | ctx->bcount += limit; |
| + | continue; |
| + | } |
| + | flags = block_iterate_ind(block_nr, |
| + | *dind_block, offset, |
| + | ctx); |
| + | changed |= flags; |
| + | if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { |
| + | ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); |
| + | break; |
| + | } |
| + | offset += sizeof(blk_t); |
| + | } |
| + | } |
| + | if (changed & BLOCK_CHANGED) { |
| + | ctx->errcode = ext2fs_write_ind_block(ctx->fs, *dind_block, |
| + | ctx->dind_buf); |
| + | if (ctx->errcode) |
| + | ret |= BLOCK_ERROR | BLOCK_ABORT; |
| + | } |
| + | if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && |
| + | !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && |
| + | !(ret & BLOCK_ABORT)) |
| + | ret |= (*ctx->func)(ctx->fs, dind_block, |
| + | BLOCK_COUNT_DIND, ref_block, |
| + | ref_offset, ctx->priv_data); |
| + | return ret; |
| + | } |
| + | |
| + | static int block_iterate_tind(blk_t *tind_block, blk_t ref_block, |
| + | int ref_offset, struct block_context *ctx) |
| + | { |
| + | int ret = 0, changed = 0; |
| + | int i, flags, limit, offset; |
| + | blk_t *block_nr; |
| + | |
| + | limit = ctx->fs->blocksize >> 2; |
| + | if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE | |
| + | BLOCK_FLAG_DATA_ONLY))) |
| + | ret = (*ctx->func)(ctx->fs, tind_block, |
| + | BLOCK_COUNT_TIND, ref_block, |
| + | ref_offset, ctx->priv_data); |
| + | if (!*tind_block || (ret & BLOCK_ABORT)) { |
| + | ctx->bcount += limit*limit*limit; |
| + | return ret; |
| + | } |
| + | if (*tind_block >= ctx->fs->super->s_blocks_count || |
| + | *tind_block < ctx->fs->super->s_first_data_block) { |
| + | ctx->errcode = EXT2_ET_BAD_TIND_BLOCK; |
| + | ret |= BLOCK_ERROR; |
| + | return ret; |
| + | } |
| + | ctx->errcode = ext2fs_read_ind_block(ctx->fs, *tind_block, |
| + | ctx->tind_buf); |
| + | if (ctx->errcode) { |
| + | ret |= BLOCK_ERROR; |
| + | return ret; |
| + | } |
| + | |
| + | block_nr = (blk_t *) ctx->tind_buf; |
| + | offset = 0; |
| + | if (ctx->flags & BLOCK_FLAG_APPEND) { |
| + | for (i = 0; i < limit; i++, block_nr++) { |
| + | flags = block_iterate_dind(block_nr, |
| + | *tind_block, |
| + | offset, ctx); |
| + | changed |= flags; |
| + | if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { |
| + | ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); |
| + | break; |
| + | } |
| + | offset += sizeof(blk_t); |
| + | } |
| + | } else { |
| + | for (i = 0; i < limit; i++, block_nr++) { |
| + | if (*block_nr == 0) { |
| + | ctx->bcount += limit*limit; |
| + | continue; |
| + | } |
| + | flags = block_iterate_dind(block_nr, |
| + | *tind_block, |
| + | offset, ctx); |
| + | changed |= flags; |
| + | if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { |
| + | ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); |
| + | break; |
| + | } |
| + | offset += sizeof(blk_t); |
| + | } |
| + | } |
| + | if (changed & BLOCK_CHANGED) { |
| + | ctx->errcode = ext2fs_write_ind_block(ctx->fs, *tind_block, |
| + | ctx->tind_buf); |
| + | if (ctx->errcode) |
| + | ret |= BLOCK_ERROR | BLOCK_ABORT; |
| + | } |
| + | if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && |
| + | !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && |
| + | !(ret & BLOCK_ABORT)) |
| + | ret |= (*ctx->func)(ctx->fs, tind_block, |
| + | BLOCK_COUNT_TIND, ref_block, |
| + | ref_offset, ctx->priv_data); |
| + | |
| + | return ret; |
| + | } |
| + | |
| + | errcode_t ext2fs_block_iterate2(ext2_filsys fs, |
| + | ext2_ino_t ino, |
| + | int flags, |
| + | char *block_buf, |
| + | int (*func)(ext2_filsys fs, |
| + | blk_t *blocknr, |
| + | e2_blkcnt_t blockcnt, |
| + | blk_t ref_blk, |
| + | int ref_offset, |
| + | void *priv_data), |
| + | void *priv_data) |
| + | { |
| + | int i; |
| + | int got_inode = 0; |
| + | int ret = 0; |
| + | blk_t blocks[EXT2_N_BLOCKS]; /* directory data blocks */ |
| + | struct ext2_inode inode; |
| + | errcode_t retval; |
| + | struct block_context ctx; |
| + | int limit; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | /* |
| + | * Check to see if we need to limit large files |
| + | */ |
| + | if (flags & BLOCK_FLAG_NO_LARGE) { |
| + | ctx.errcode = ext2fs_read_inode(fs, ino, &inode); |
| + | if (ctx.errcode) |
| + | return ctx.errcode; |
| + | got_inode = 1; |
| + | if (!LINUX_S_ISDIR(inode.i_mode) && |
| + | (inode.i_size_high != 0)) |
| + | return EXT2_ET_FILE_TOO_BIG; |
| + | } |
| + | |
| + | retval = ext2fs_get_blocks(fs, ino, blocks); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | limit = fs->blocksize >> 2; |
| + | |
| + | ctx.fs = fs; |
| + | ctx.func = func; |
| + | ctx.priv_data = priv_data; |
| + | ctx.flags = flags; |
| + | ctx.bcount = 0; |
| + | if (block_buf) { |
| + | ctx.ind_buf = block_buf; |
| + | } else { |
| + | retval = ext2fs_get_mem(fs->blocksize * 3, &ctx.ind_buf); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | ctx.dind_buf = ctx.ind_buf + fs->blocksize; |
| + | ctx.tind_buf = ctx.dind_buf + fs->blocksize; |
| + | |
| + | /* |
| + | * Iterate over the HURD translator block (if present) |
| + | */ |
| + | if ((fs->super->s_creator_os == EXT2_OS_HURD) && |
| + | !(flags & BLOCK_FLAG_DATA_ONLY)) { |
| + | ctx.errcode = ext2fs_read_inode(fs, ino, &inode); |
| + | if (ctx.errcode) |
| + | goto abort_exit; |
| + | got_inode = 1; |
| + | if (inode.osd1.hurd1.h_i_translator) { |
| + | ret |= (*ctx.func)(fs, |
| + | &inode.osd1.hurd1.h_i_translator, |
| + | BLOCK_COUNT_TRANSLATOR, |
| + | 0, 0, priv_data); |
| + | if (ret & BLOCK_ABORT) |
| + | goto abort_exit; |
| + | } |
| + | } |
| + | |
| + | /* |
| + | * Iterate over normal data blocks |
| + | */ |
| + | for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) { |
| + | if (blocks[i] || (flags & BLOCK_FLAG_APPEND)) { |
| + | ret |= (*ctx.func)(fs, &blocks[i], |
| + | ctx.bcount, 0, i, priv_data); |
| + | if (ret & BLOCK_ABORT) |
| + | goto abort_exit; |
| + | } |
| + | } |
| + | if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) { |
| + | ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK, |
| + | 0, EXT2_IND_BLOCK, &ctx); |
| + | if (ret & BLOCK_ABORT) |
| + | goto abort_exit; |
| + | } else |
| + | ctx.bcount += limit; |
| + | if (*(blocks + EXT2_DIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) { |
| + | ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK, |
| + | 0, EXT2_DIND_BLOCK, &ctx); |
| + | if (ret & BLOCK_ABORT) |
| + | goto abort_exit; |
| + | } else |
| + | ctx.bcount += limit * limit; |
| + | if (*(blocks + EXT2_TIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) { |
| + | ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK, |
| + | 0, EXT2_TIND_BLOCK, &ctx); |
| + | if (ret & BLOCK_ABORT) |
| + | goto abort_exit; |
| + | } |
| + | |
| + | abort_exit: |
| + | if (ret & BLOCK_CHANGED) { |
| + | if (!got_inode) { |
| + | retval = ext2fs_read_inode(fs, ino, &inode); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | for (i=0; i < EXT2_N_BLOCKS; i++) |
| + | inode.i_block[i] = blocks[i]; |
| + | retval = ext2fs_write_inode(fs, ino, &inode); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | |
| + | if (!block_buf) |
| + | ext2fs_free_mem(&ctx.ind_buf); |
| + | |
| + | return (ret & BLOCK_ERROR) ? ctx.errcode : 0; |
| + | } |
| + | |
| + | /* |
| + | * Emulate the old ext2fs_block_iterate function! |
| + | */ |
| + | |
| + | struct xlate { |
| + | int (*func)(ext2_filsys fs, |
| + | blk_t *blocknr, |
| + | int bcount, |
| + | void *priv_data); |
| + | void *real_private; |
| + | }; |
| + | |
| + | #ifdef __TURBOC__ |
| + | #pragma argsused |
| + | #endif |
| + | static int xlate_func(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, |
| + | blk_t ref_block EXT2FS_ATTR((unused)), |
| + | int ref_offset EXT2FS_ATTR((unused)), |
| + | void *priv_data) |
| + | { |
| + | struct xlate *xl = (struct xlate *) priv_data; |
| + | |
| + | return (*xl->func)(fs, blocknr, (int) blockcnt, xl->real_private); |
| + | } |
| + | |
| + | errcode_t ext2fs_block_iterate(ext2_filsys fs, |
| + | ext2_ino_t ino, |
| + | int flags, |
| + | char *block_buf, |
| + | int (*func)(ext2_filsys fs, |
| + | blk_t *blocknr, |
| + | int blockcnt, |
| + | void *priv_data), |
| + | void *priv_data) |
| + | { |
| + | struct xlate xl; |
| + | |
| + | xl.real_private = priv_data; |
| + | xl.func = func; |
| + | |
| + | return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags, |
| + | block_buf, xlate_func, &xl); |
| + | } |
| + | |
| @@ -0,0 +1,270 @@ | |
| + | /* |
| + | * bmap.c --- logical to physical block mapping |
| + | * |
| + | * Copyright (C) 1997 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | #if defined(__GNUC__) && !defined(NO_INLINE_FUNCS) |
| + | #define _BMAP_INLINE_ __inline__ |
| + | #else |
| + | #define _BMAP_INLINE_ |
| + | #endif |
| + | |
| + | extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, |
| + | struct ext2_inode *inode, |
| + | char *block_buf, int bmap_flags, |
| + | blk_t block, blk_t *phys_blk); |
| + | |
| + | #define inode_bmap(inode, nr) ((inode)->i_block[(nr)]) |
| + | |
| + | static _BMAP_INLINE_ errcode_t block_ind_bmap(ext2_filsys fs, int flags, |
| + | blk_t ind, char *block_buf, |
| + | int *blocks_alloc, |
| + | blk_t nr, blk_t *ret_blk) |
| + | { |
| + | errcode_t retval; |
| + | blk_t b; |
| + | |
| + | if (!ind) { |
| + | if (flags & BMAP_SET) |
| + | return EXT2_ET_SET_BMAP_NO_IND; |
| + | *ret_blk = 0; |
| + | return 0; |
| + | } |
| + | retval = io_channel_read_blk(fs->io, ind, 1, block_buf); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | if (flags & BMAP_SET) { |
| + | b = *ret_blk; |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || |
| + | (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) |
| + | b = ext2fs_swab32(b); |
| + | #endif |
| + | ((blk_t *) block_buf)[nr] = b; |
| + | return io_channel_write_blk(fs->io, ind, 1, block_buf); |
| + | } |
| + | |
| + | b = ((blk_t *) block_buf)[nr]; |
| + | |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || |
| + | (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) |
| + | b = ext2fs_swab32(b); |
| + | #endif |
| + | |
| + | if (!b && (flags & BMAP_ALLOC)) { |
| + | b = nr ? ((blk_t *) block_buf)[nr-1] : 0; |
| + | retval = ext2fs_alloc_block(fs, b, |
| + | block_buf + fs->blocksize, &b); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || |
| + | (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) |
| + | ((blk_t *) block_buf)[nr] = ext2fs_swab32(b); |
| + | else |
| + | #endif |
| + | ((blk_t *) block_buf)[nr] = b; |
| + | |
| + | retval = io_channel_write_blk(fs->io, ind, 1, block_buf); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | (*blocks_alloc)++; |
| + | } |
| + | |
| + | *ret_blk = b; |
| + | return 0; |
| + | } |
| + | |
| + | static _BMAP_INLINE_ errcode_t block_dind_bmap(ext2_filsys fs, int flags, |
| + | blk_t dind, char *block_buf, |
| + | int *blocks_alloc, |
| + | blk_t nr, blk_t *ret_blk) |
| + | { |
| + | blk_t b; |
| + | errcode_t retval; |
| + | blk_t addr_per_block; |
| + | |
| + | addr_per_block = (blk_t) fs->blocksize >> 2; |
| + | |
| + | retval = block_ind_bmap(fs, flags & ~BMAP_SET, dind, block_buf, |
| + | blocks_alloc, nr / addr_per_block, &b); |
| + | if (retval) |
| + | return retval; |
| + | retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc, |
| + | nr % addr_per_block, ret_blk); |
| + | return retval; |
| + | } |
| + | |
| + | static _BMAP_INLINE_ errcode_t block_tind_bmap(ext2_filsys fs, int flags, |
| + | blk_t tind, char *block_buf, |
| + | int *blocks_alloc, |
| + | blk_t nr, blk_t *ret_blk) |
| + | { |
| + | blk_t b; |
| + | errcode_t retval; |
| + | blk_t addr_per_block; |
| + | |
| + | addr_per_block = (blk_t) fs->blocksize >> 2; |
| + | |
| + | retval = block_dind_bmap(fs, flags & ~BMAP_SET, tind, block_buf, |
| + | blocks_alloc, nr / addr_per_block, &b); |
| + | if (retval) |
| + | return retval; |
| + | retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc, |
| + | nr % addr_per_block, ret_blk); |
| + | return retval; |
| + | } |
| + | |
| + | errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, |
| + | char *block_buf, int bmap_flags, blk_t block, |
| + | blk_t *phys_blk) |
| + | { |
| + | struct ext2_inode inode_buf; |
| + | blk_t addr_per_block; |
| + | blk_t b; |
| + | char *buf = 0; |
| + | errcode_t retval = 0; |
| + | int blocks_alloc = 0, inode_dirty = 0; |
| + | |
| + | if (!(bmap_flags & BMAP_SET)) |
| + | *phys_blk = 0; |
| + | |
| + | /* Read inode structure if necessary */ |
| + | if (!inode) { |
| + | retval = ext2fs_read_inode(fs, ino, &inode_buf); |
| + | if (retval) |
| + | return retval; |
| + | inode = &inode_buf; |
| + | } |
| + | addr_per_block = (blk_t) fs->blocksize >> 2; |
| + | |
| + | if (!block_buf) { |
| + | retval = ext2fs_get_mem(fs->blocksize * 2, &buf); |
| + | if (retval) |
| + | return retval; |
| + | block_buf = buf; |
| + | } |
| + | |
| + | if (block < EXT2_NDIR_BLOCKS) { |
| + | if (bmap_flags & BMAP_SET) { |
| + | b = *phys_blk; |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || |
| + | (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) |
| + | b = ext2fs_swab32(b); |
| + | #endif |
| + | inode_bmap(inode, block) = b; |
| + | inode_dirty++; |
| + | goto done; |
| + | } |
| + | |
| + | *phys_blk = inode_bmap(inode, block); |
| + | b = block ? inode_bmap(inode, block-1) : 0; |
| + | |
| + | if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) { |
| + | retval = ext2fs_alloc_block(fs, b, block_buf, &b); |
| + | if (retval) |
| + | goto done; |
| + | inode_bmap(inode, block) = b; |
| + | blocks_alloc++; |
| + | *phys_blk = b; |
| + | } |
| + | goto done; |
| + | } |
| + | |
| + | /* Indirect block */ |
| + | block -= EXT2_NDIR_BLOCKS; |
| + | if (block < addr_per_block) { |
| + | b = inode_bmap(inode, EXT2_IND_BLOCK); |
| + | if (!b) { |
| + | if (!(bmap_flags & BMAP_ALLOC)) { |
| + | if (bmap_flags & BMAP_SET) |
| + | retval = EXT2_ET_SET_BMAP_NO_IND; |
| + | goto done; |
| + | } |
| + | |
| + | b = inode_bmap(inode, EXT2_IND_BLOCK-1); |
| + | retval = ext2fs_alloc_block(fs, b, block_buf, &b); |
| + | if (retval) |
| + | goto done; |
| + | inode_bmap(inode, EXT2_IND_BLOCK) = b; |
| + | blocks_alloc++; |
| + | } |
| + | retval = block_ind_bmap(fs, bmap_flags, b, block_buf, |
| + | &blocks_alloc, block, phys_blk); |
| + | goto done; |
| + | } |
| + | |
| + | /* Doubly indirect block */ |
| + | block -= addr_per_block; |
| + | if (block < addr_per_block * addr_per_block) { |
| + | b = inode_bmap(inode, EXT2_DIND_BLOCK); |
| + | if (!b) { |
| + | if (!(bmap_flags & BMAP_ALLOC)) { |
| + | if (bmap_flags & BMAP_SET) |
| + | retval = EXT2_ET_SET_BMAP_NO_IND; |
| + | goto done; |
| + | } |
| + | |
| + | b = inode_bmap(inode, EXT2_IND_BLOCK); |
| + | retval = ext2fs_alloc_block(fs, b, block_buf, &b); |
| + | if (retval) |
| + | goto done; |
| + | inode_bmap(inode, EXT2_DIND_BLOCK) = b; |
| + | blocks_alloc++; |
| + | } |
| + | retval = block_dind_bmap(fs, bmap_flags, b, block_buf, |
| + | &blocks_alloc, block, phys_blk); |
| + | goto done; |
| + | } |
| + | |
| + | /* Triply indirect block */ |
| + | block -= addr_per_block * addr_per_block; |
| + | b = inode_bmap(inode, EXT2_TIND_BLOCK); |
| + | if (!b) { |
| + | if (!(bmap_flags & BMAP_ALLOC)) { |
| + | if (bmap_flags & BMAP_SET) |
| + | retval = EXT2_ET_SET_BMAP_NO_IND; |
| + | goto done; |
| + | } |
| + | |
| + | b = inode_bmap(inode, EXT2_DIND_BLOCK); |
| + | retval = ext2fs_alloc_block(fs, b, block_buf, &b); |
| + | if (retval) |
| + | goto done; |
| + | inode_bmap(inode, EXT2_TIND_BLOCK) = b; |
| + | blocks_alloc++; |
| + | } |
| + | retval = block_tind_bmap(fs, bmap_flags, b, block_buf, |
| + | &blocks_alloc, block, phys_blk); |
| + | done: |
| + | if (buf) |
| + | ext2fs_free_mem(&buf); |
| + | if ((retval == 0) && (blocks_alloc || inode_dirty)) { |
| + | inode->i_blocks += (blocks_alloc * fs->blocksize) / 512; |
| + | retval = ext2fs_write_inode(fs, ino, inode); |
| + | } |
| + | return retval; |
| + | } |
| + | |
| + | |
| + | |
| @@ -0,0 +1,160 @@ | |
| + | /* |
| + | * bmove.c --- Move blocks around to make way for a particular |
| + | * filesystem structure. |
| + | * |
| + | * Copyright (C) 1997 Theodore Ts'o. This file may be redistributed |
| + | * under the terms of the GNU Public License. |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | #if HAVE_SYS_TIME_H |
| + | #include <sys/time.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fsP.h" |
| + | |
| + | struct process_block_struct { |
| + | ext2_ino_t ino; |
| + | struct ext2_inode * inode; |
| + | ext2fs_block_bitmap reserve; |
| + | ext2fs_block_bitmap alloc_map; |
| + | errcode_t error; |
| + | char *buf; |
| + | int add_dir; |
| + | int flags; |
| + | }; |
| + | |
| + | static int process_block(ext2_filsys fs, blk_t *block_nr, |
| + | e2_blkcnt_t blockcnt, blk_t ref_block, |
| + | int ref_offset, void *priv_data) |
| + | { |
| + | struct process_block_struct *pb; |
| + | errcode_t retval; |
| + | int ret; |
| + | blk_t block, orig; |
| + | |
| + | pb = (struct process_block_struct *) priv_data; |
| + | block = orig = *block_nr; |
| + | ret = 0; |
| + | |
| + | /* |
| + | * Let's see if this is one which we need to relocate |
| + | */ |
| + | if (ext2fs_test_block_bitmap(pb->reserve, block)) { |
| + | do { |
| + | if (++block >= fs->super->s_blocks_count) |
| + | block = fs->super->s_first_data_block; |
| + | if (block == orig) { |
| + | pb->error = EXT2_ET_BLOCK_ALLOC_FAIL; |
| + | return BLOCK_ABORT; |
| + | } |
| + | } while (ext2fs_test_block_bitmap(pb->reserve, block) || |
| + | ext2fs_test_block_bitmap(pb->alloc_map, block)); |
| + | |
| + | retval = io_channel_read_blk(fs->io, orig, 1, pb->buf); |
| + | if (retval) { |
| + | pb->error = retval; |
| + | return BLOCK_ABORT; |
| + | } |
| + | retval = io_channel_write_blk(fs->io, block, 1, pb->buf); |
| + | if (retval) { |
| + | pb->error = retval; |
| + | return BLOCK_ABORT; |
| + | } |
| + | *block_nr = block; |
| + | ext2fs_mark_block_bitmap(pb->alloc_map, block); |
| + | ret = BLOCK_CHANGED; |
| + | if (pb->flags & EXT2_BMOVE_DEBUG) |
| + | printf("ino=%ld, blockcnt=%lld, %d->%d\n", pb->ino, |
| + | blockcnt, orig, block); |
| + | } |
| + | if (pb->add_dir) { |
| + | retval = ext2fs_add_dir_block(fs->dblist, pb->ino, |
| + | block, (int) blockcnt); |
| + | if (retval) { |
| + | pb->error = retval; |
| + | ret |= BLOCK_ABORT; |
| + | } |
| + | } |
| + | return ret; |
| + | } |
| + | |
| + | errcode_t ext2fs_move_blocks(ext2_filsys fs, |
| + | ext2fs_block_bitmap reserve, |
| + | ext2fs_block_bitmap alloc_map, |
| + | int flags) |
| + | { |
| + | ext2_ino_t ino; |
| + | struct ext2_inode inode; |
| + | errcode_t retval; |
| + | struct process_block_struct pb; |
| + | ext2_inode_scan scan; |
| + | char *block_buf; |
| + | |
| + | retval = ext2fs_open_inode_scan(fs, 0, &scan); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | pb.reserve = reserve; |
| + | pb.error = 0; |
| + | pb.alloc_map = alloc_map ? alloc_map : fs->block_map; |
| + | pb.flags = flags; |
| + | |
| + | retval = ext2fs_get_mem(fs->blocksize * 4, &block_buf); |
| + | if (retval) |
| + | return retval; |
| + | pb.buf = block_buf + fs->blocksize * 3; |
| + | |
| + | /* |
| + | * If GET_DBLIST is set in the flags field, then we should |
| + | * gather directory block information while we're doing the |
| + | * block move. |
| + | */ |
| + | if (flags & EXT2_BMOVE_GET_DBLIST) { |
| + | if (fs->dblist) { |
| + | ext2fs_free_dblist(fs->dblist); |
| + | fs->dblist = NULL; |
| + | } |
| + | retval = ext2fs_init_dblist(fs, 0); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | |
| + | retval = ext2fs_get_next_inode(scan, &ino, &inode); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | while (ino) { |
| + | if ((inode.i_links_count == 0) || |
| + | !ext2fs_inode_has_valid_blocks(&inode)) |
| + | goto next; |
| + | |
| + | pb.ino = ino; |
| + | pb.inode = &inode; |
| + | |
| + | pb.add_dir = (LINUX_S_ISDIR(inode.i_mode) && |
| + | flags & EXT2_BMOVE_GET_DBLIST); |
| + | |
| + | retval = ext2fs_block_iterate2(fs, ino, 0, block_buf, |
| + | process_block, &pb); |
| + | if (retval) |
| + | return retval; |
| + | if (pb.error) |
| + | return pb.error; |
| + | |
| + | next: |
| + | retval = ext2fs_get_next_inode(scan, &ino, &inode); |
| + | if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) |
| + | goto next; |
| + | } |
| + | return 0; |
| + | } |
| + | |
| @@ -0,0 +1,86 @@ | |
| + | /* |
| + | * brel.h |
| + | * |
| + | * Copyright (C) 1996, 1997 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | struct ext2_block_relocate_entry { |
| + | blk_t new; |
| + | __s16 offset; |
| + | __u16 flags; |
| + | union { |
| + | blk_t block_ref; |
| + | ext2_ino_t inode_ref; |
| + | } owner; |
| + | }; |
| + | |
| + | #define RELOCATE_TYPE_REF 0x0007 |
| + | #define RELOCATE_BLOCK_REF 0x0001 |
| + | #define RELOCATE_INODE_REF 0x0002 |
| + | |
| + | typedef struct ext2_block_relocation_table *ext2_brel; |
| + | |
| + | struct ext2_block_relocation_table { |
| + | __u32 magic; |
| + | char *name; |
| + | blk_t current; |
| + | void *priv_data; |
| + | |
| + | /* |
| + | * Add a block relocation entry. |
| + | */ |
| + | errcode_t (*put)(ext2_brel brel, blk_t old, |
| + | struct ext2_block_relocate_entry *ent); |
| + | |
| + | /* |
| + | * Get a block relocation entry. |
| + | */ |
| + | errcode_t (*get)(ext2_brel brel, blk_t old, |
| + | struct ext2_block_relocate_entry *ent); |
| + | |
| + | /* |
| + | * Initialize for iterating over the block relocation entries. |
| + | */ |
| + | errcode_t (*start_iter)(ext2_brel brel); |
| + | |
| + | /* |
| + | * The iterator function for the inode relocation entries. |
| + | * Returns an inode number of 0 when out of entries. |
| + | */ |
| + | errcode_t (*next)(ext2_brel brel, blk_t *old, |
| + | struct ext2_block_relocate_entry *ent); |
| + | |
| + | /* |
| + | * Move the inode relocation table from one block number to |
| + | * another. |
| + | */ |
| + | errcode_t (*move)(ext2_brel brel, blk_t old, blk_t new); |
| + | |
| + | /* |
| + | * Remove a block relocation entry. |
| + | */ |
| + | errcode_t (*delete)(ext2_brel brel, blk_t old); |
| + | |
| + | |
| + | /* |
| + | * Free the block relocation table. |
| + | */ |
| + | errcode_t (*free)(ext2_brel brel); |
| + | }; |
| + | |
| + | errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block, |
| + | ext2_brel *brel); |
| + | |
| + | #define ext2fs_brel_put(brel, old, ent) ((brel)->put((brel), old, ent)) |
| + | #define ext2fs_brel_get(brel, old, ent) ((brel)->get((brel), old, ent)) |
| + | #define ext2fs_brel_start_iter(brel) ((brel)->start_iter((brel))) |
| + | #define ext2fs_brel_next(brel, old, ent) ((brel)->next((brel), old, ent)) |
| + | #define ext2fs_brel_move(brel, old, new) ((brel)->move((brel), old, new)) |
| + | #define ext2fs_brel_delete(brel, old) ((brel)->delete((brel), old)) |
| + | #define ext2fs_brel_free(brel) ((brel)->free((brel))) |
| + | |
| @@ -0,0 +1,197 @@ | |
| + | /* |
| + | * brel_ma.c |
| + | * |
| + | * Copyright (C) 1996, 1997 Theodore Ts'o. |
| + | * |
| + | * TODO: rewrite to not use a direct array!!! (Fortunately this |
| + | * module isn't really used yet.) |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <fcntl.h> |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #if HAVE_ERRNO_H |
| + | #include <errno.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | #include "brel.h" |
| + | |
| + | static errcode_t bma_put(ext2_brel brel, blk_t old, |
| + | struct ext2_block_relocate_entry *ent); |
| + | static errcode_t bma_get(ext2_brel brel, blk_t old, |
| + | struct ext2_block_relocate_entry *ent); |
| + | static errcode_t bma_start_iter(ext2_brel brel); |
| + | static errcode_t bma_next(ext2_brel brel, blk_t *old, |
| + | struct ext2_block_relocate_entry *ent); |
| + | static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new); |
| + | static errcode_t bma_delete(ext2_brel brel, blk_t old); |
| + | static errcode_t bma_free(ext2_brel brel); |
| + | |
| + | struct brel_ma { |
| + | __u32 magic; |
| + | blk_t max_block; |
| + | struct ext2_block_relocate_entry *entries; |
| + | }; |
| + | |
| + | errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block, |
| + | ext2_brel *new_brel) |
| + | { |
| + | ext2_brel brel = 0; |
| + | errcode_t retval; |
| + | struct brel_ma *ma = 0; |
| + | size_t size; |
| + | |
| + | *new_brel = 0; |
| + | |
| + | /* |
| + | * Allocate memory structures |
| + | */ |
| + | retval = ext2fs_get_mem(sizeof(struct ext2_block_relocation_table), |
| + | &brel); |
| + | if (retval) |
| + | goto errout; |
| + | memset(brel, 0, sizeof(struct ext2_block_relocation_table)); |
| + | |
| + | retval = ext2fs_get_mem(strlen(name)+1, &brel->name); |
| + | if (retval) |
| + | goto errout; |
| + | strcpy(brel->name, name); |
| + | |
| + | retval = ext2fs_get_mem(sizeof(struct brel_ma), &ma); |
| + | if (retval) |
| + | goto errout; |
| + | memset(ma, 0, sizeof(struct brel_ma)); |
| + | brel->priv_data = ma; |
| + | |
| + | size = (size_t) (sizeof(struct ext2_block_relocate_entry) * |
| + | (max_block+1)); |
| + | retval = ext2fs_get_mem(size, &ma->entries); |
| + | if (retval) |
| + | goto errout; |
| + | memset(ma->entries, 0, size); |
| + | ma->max_block = max_block; |
| + | |
| + | /* |
| + | * Fill in the brel data structure |
| + | */ |
| + | brel->put = bma_put; |
| + | brel->get = bma_get; |
| + | brel->start_iter = bma_start_iter; |
| + | brel->next = bma_next; |
| + | brel->move = bma_move; |
| + | brel->delete = bma_delete; |
| + | brel->free = bma_free; |
| + | |
| + | *new_brel = brel; |
| + | return 0; |
| + | |
| + | errout: |
| + | bma_free(brel); |
| + | return retval; |
| + | } |
| + | |
| + | static errcode_t bma_put(ext2_brel brel, blk_t old, |
| + | struct ext2_block_relocate_entry *ent) |
| + | { |
| + | struct brel_ma *ma; |
| + | |
| + | ma = brel->priv_data; |
| + | if (old > ma->max_block) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | ma->entries[(unsigned)old] = *ent; |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t bma_get(ext2_brel brel, blk_t old, |
| + | struct ext2_block_relocate_entry *ent) |
| + | { |
| + | struct brel_ma *ma; |
| + | |
| + | ma = brel->priv_data; |
| + | if (old > ma->max_block) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | if (ma->entries[(unsigned)old].new == 0) |
| + | return ENOENT; |
| + | *ent = ma->entries[old]; |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t bma_start_iter(ext2_brel brel) |
| + | { |
| + | brel->current = 0; |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t bma_next(ext2_brel brel, blk_t *old, |
| + | struct ext2_block_relocate_entry *ent) |
| + | { |
| + | struct brel_ma *ma; |
| + | |
| + | ma = brel->priv_data; |
| + | while (++brel->current < ma->max_block) { |
| + | if (ma->entries[(unsigned)brel->current].new == 0) |
| + | continue; |
| + | *old = brel->current; |
| + | *ent = ma->entries[(unsigned)brel->current]; |
| + | return 0; |
| + | } |
| + | *old = 0; |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new) |
| + | { |
| + | struct brel_ma *ma; |
| + | |
| + | ma = brel->priv_data; |
| + | if ((old > ma->max_block) || (new > ma->max_block)) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | if (ma->entries[(unsigned)old].new == 0) |
| + | return ENOENT; |
| + | ma->entries[(unsigned)new] = ma->entries[old]; |
| + | ma->entries[(unsigned)old].new = 0; |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t bma_delete(ext2_brel brel, blk_t old) |
| + | { |
| + | struct brel_ma *ma; |
| + | |
| + | ma = brel->priv_data; |
| + | if (old > ma->max_block) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | if (ma->entries[(unsigned)old].new == 0) |
| + | return ENOENT; |
| + | ma->entries[(unsigned)old].new = 0; |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t bma_free(ext2_brel brel) |
| + | { |
| + | struct brel_ma *ma; |
| + | |
| + | if (!brel) |
| + | return 0; |
| + | |
| + | ma = brel->priv_data; |
| + | |
| + | if (ma) { |
| + | if (ma->entries) |
| + | ext2fs_free_mem(&ma->entries); |
| + | ext2fs_free_mem(&ma); |
| + | } |
| + | if (brel->name) |
| + | ext2fs_free_mem(&brel->name); |
| + | ext2fs_free_mem(&brel); |
| + | return 0; |
| + | } |
| @@ -0,0 +1,68 @@ | |
| + | /* |
| + | * check_desc.c --- Check the group descriptors of an ext2 filesystem |
| + | * |
| + | * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | /* |
| + | * This routine sanity checks the group descriptors |
| + | */ |
| + | errcode_t ext2fs_check_desc(ext2_filsys fs) |
| + | { |
| + | dgrp_t i; |
| + | blk_t block = fs->super->s_first_data_block; |
| + | blk_t next; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | for (i = 0; i < fs->group_desc_count; i++) { |
| + | next = block + fs->super->s_blocks_per_group; |
| + | /* |
| + | * Check to make sure block bitmap for group is |
| + | * located within the group. |
| + | */ |
| + | if (fs->group_desc[i].bg_block_bitmap < block || |
| + | fs->group_desc[i].bg_block_bitmap >= next) |
| + | return EXT2_ET_GDESC_BAD_BLOCK_MAP; |
| + | /* |
| + | * Check to make sure inode bitmap for group is |
| + | * located within the group |
| + | */ |
| + | if (fs->group_desc[i].bg_inode_bitmap < block || |
| + | fs->group_desc[i].bg_inode_bitmap >= next) |
| + | return EXT2_ET_GDESC_BAD_INODE_MAP; |
| + | /* |
| + | * Check to make sure inode table for group is located |
| + | * within the group |
| + | */ |
| + | if (fs->group_desc[i].bg_inode_table < block || |
| + | ((fs->group_desc[i].bg_inode_table + |
| + | fs->inode_blocks_per_group) >= next)) |
| + | return EXT2_ET_GDESC_BAD_INODE_TABLE; |
| + | |
| + | block = next; |
| + | } |
| + | return 0; |
| + | } |
| @@ -0,0 +1,381 @@ | |
| + | /* |
| + | * closefs.c --- close an ext2 filesystem |
| + | * |
| + | * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <time.h> |
| + | #include <string.h> |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fsP.h" |
| + | |
| + | static int test_root(int a, int b) |
| + | { |
| + | if (a == 0) |
| + | return 1; |
| + | while (1) { |
| + | if (a == 1) |
| + | return 1; |
| + | if (a % b) |
| + | return 0; |
| + | a = a / b; |
| + | } |
| + | } |
| + | |
| + | int ext2fs_bg_has_super(ext2_filsys fs, int group_block) |
| + | { |
| + | if (!(fs->super->s_feature_ro_compat & |
| + | EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) |
| + | return 1; |
| + | |
| + | if (test_root(group_block, 3) || (test_root(group_block, 5)) || |
| + | test_root(group_block, 7)) |
| + | return 1; |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | int ext2fs_super_and_bgd_loc(ext2_filsys fs, |
| + | dgrp_t group, |
| + | blk_t *ret_super_blk, |
| + | blk_t *ret_old_desc_blk, |
| + | blk_t *ret_new_desc_blk, |
| + | int *ret_meta_bg) |
| + | { |
| + | blk_t group_block, super_blk = 0, old_desc_blk = 0, new_desc_blk = 0; |
| + | unsigned int meta_bg, meta_bg_size; |
| + | int numblocks, has_super; |
| + | int old_desc_blocks; |
| + | |
| + | group_block = fs->super->s_first_data_block + |
| + | (group * fs->super->s_blocks_per_group); |
| + | |
| + | if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) |
| + | old_desc_blocks = fs->super->s_first_meta_bg; |
| + | else |
| + | old_desc_blocks = |
| + | fs->desc_blocks + fs->super->s_reserved_gdt_blocks; |
| + | |
| + | if (group == fs->group_desc_count-1) { |
| + | numblocks = (fs->super->s_blocks_count - |
| + | fs->super->s_first_data_block) % |
| + | fs->super->s_blocks_per_group; |
| + | if (!numblocks) |
| + | numblocks = fs->super->s_blocks_per_group; |
| + | } else |
| + | numblocks = fs->super->s_blocks_per_group; |
| + | |
| + | has_super = ext2fs_bg_has_super(fs, group); |
| + | |
| + | if (has_super) { |
| + | super_blk = group_block; |
| + | numblocks--; |
| + | } |
| + | meta_bg_size = (fs->blocksize / sizeof (struct ext2_group_desc)); |
| + | meta_bg = group / meta_bg_size; |
| + | |
| + | if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) || |
| + | (meta_bg < fs->super->s_first_meta_bg)) { |
| + | if (has_super) { |
| + | old_desc_blk = group_block + 1; |
| + | numblocks -= old_desc_blocks; |
| + | } |
| + | } else { |
| + | if (((group % meta_bg_size) == 0) || |
| + | ((group % meta_bg_size) == 1) || |
| + | ((group % meta_bg_size) == (meta_bg_size-1))) { |
| + | if (has_super) |
| + | has_super = 1; |
| + | new_desc_blk = group_block + has_super; |
| + | numblocks--; |
| + | } |
| + | } |
| + | |
| + | numblocks -= 2 + fs->inode_blocks_per_group; |
| + | |
| + | if (ret_super_blk) |
| + | *ret_super_blk = super_blk; |
| + | if (ret_old_desc_blk) |
| + | *ret_old_desc_blk = old_desc_blk; |
| + | if (ret_new_desc_blk) |
| + | *ret_new_desc_blk = new_desc_blk; |
| + | if (ret_meta_bg) |
| + | *ret_meta_bg = meta_bg; |
| + | return (numblocks); |
| + | } |
| + | |
| + | |
| + | /* |
| + | * 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). |
| + | * |
| + | */ |
| + | static errcode_t write_primary_superblock(ext2_filsys fs, |
| + | struct ext2_super_block *super) |
| + | { |
| + | __u16 *old_super, *new_super; |
| + | int check_idx, write_idx, size; |
| + | errcode_t retval; |
| + | |
| + | if (!fs->io->manager->write_byte || !fs->orig_super) { |
| + | io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); |
| + | retval = io_channel_write_blk(fs->io, 1, -SUPERBLOCK_SIZE, |
| + | super); |
| + | io_channel_set_blksize(fs->io, fs->blocksize); |
| + | return retval; |
| + | } |
| + | |
| + | old_super = (__u16 *) fs->orig_super; |
| + | new_super = (__u16 *) super; |
| + | |
| + | for (check_idx = 0; check_idx < SUPERBLOCK_SIZE/2; check_idx++) { |
| + | if (old_super[check_idx] == new_super[check_idx]) |
| + | continue; |
| + | write_idx = check_idx; |
| + | for (check_idx++; check_idx < SUPERBLOCK_SIZE/2; check_idx++) |
| + | if (old_super[check_idx] == new_super[check_idx]) |
| + | break; |
| + | size = 2 * (check_idx - write_idx); |
| + | #if 0 |
| + | printf("Writing %d bytes starting at %d\n", |
| + | size, write_idx*2); |
| + | #endif |
| + | retval = io_channel_write_byte(fs->io, |
| + | SUPERBLOCK_OFFSET + (2 * write_idx), size, |
| + | new_super + write_idx); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | memcpy(fs->orig_super, super, SUPERBLOCK_SIZE); |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | /* |
| + | * Updates the revision to EXT2_DYNAMIC_REV |
| + | */ |
| + | void ext2fs_update_dynamic_rev(ext2_filsys fs) |
| + | { |
| + | struct ext2_super_block *sb = fs->super; |
| + | |
| + | 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 */ |
| + | } |
| + | |
| + | static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group, |
| + | blk_t group_block, |
| + | struct ext2_super_block *super_shadow) |
| + | { |
| + | dgrp_t sgrp = group; |
| + | |
| + | if (sgrp > ((1 << 16) - 1)) |
| + | sgrp = (1 << 16) - 1; |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | if (fs->flags & EXT2_FLAG_SWAP_BYTES) |
| + | super_shadow->s_block_group_nr = ext2fs_swab16(sgrp); |
| + | else |
| + | #endif |
| + | fs->super->s_block_group_nr = sgrp; |
| + | |
| + | return io_channel_write_blk(fs->io, group_block, -SUPERBLOCK_SIZE, |
| + | super_shadow); |
| + | } |
| + | |
| + | |
| + | errcode_t ext2fs_flush(ext2_filsys fs) |
| + | { |
| + | dgrp_t i,j; |
| + | blk_t group_block; |
| + | errcode_t retval; |
| + | unsigned long fs_state; |
| + | struct ext2_super_block *super_shadow = 0; |
| + | struct ext2_group_desc *group_shadow = 0; |
| + | struct ext2_group_desc *s, *t; |
| + | char *group_ptr; |
| + | int old_desc_blocks; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | fs_state = fs->super->s_state; |
| + | |
| + | fs->super->s_wtime = time(NULL); |
| + | fs->super->s_block_group_nr = 0; |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | if (fs->flags & EXT2_FLAG_SWAP_BYTES) { |
| + | retval = EXT2_ET_NO_MEMORY; |
| + | retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow); |
| + | if (retval) |
| + | goto errout; |
| + | retval = ext2fs_get_mem((size_t)(fs->blocksize * |
| + | fs->desc_blocks), |
| + | &group_shadow); |
| + | if (retval) |
| + | goto errout; |
| + | memset(group_shadow, 0, (size_t) fs->blocksize * |
| + | fs->desc_blocks); |
| + | |
| + | /* swap the group descriptors */ |
| + | for (j=0, s=fs->group_desc, t=group_shadow; |
| + | j < fs->group_desc_count; j++, t++, s++) { |
| + | *t = *s; |
| + | ext2fs_swap_group_desc(t); |
| + | } |
| + | } else { |
| + | super_shadow = fs->super; |
| + | group_shadow = fs->group_desc; |
| + | } |
| + | #else |
| + | super_shadow = fs->super; |
| + | group_shadow = fs->group_desc; |
| + | #endif |
| + | |
| + | /* |
| + | * If this is an external journal device, don't write out the |
| + | * block group descriptors or any of the backup superblocks |
| + | */ |
| + | if (fs->super->s_feature_incompat & |
| + | EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) |
| + | goto write_primary_superblock_only; |
| + | |
| + | /* |
| + | * Set the state of the FS to be non-valid. (The state has |
| + | * already been backed up earlier, and will be restored after |
| + | * we write out the backup superblocks.) |
| + | */ |
| + | fs->super->s_state &= ~EXT2_VALID_FS; |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | if (fs->flags & EXT2_FLAG_SWAP_BYTES) { |
| + | *super_shadow = *fs->super; |
| + | ext2fs_swap_super(super_shadow); |
| + | } |
| + | #endif |
| + | |
| + | /* |
| + | * Write out the master group descriptors, and the backup |
| + | * superblocks and group descriptors. |
| + | */ |
| + | group_block = fs->super->s_first_data_block; |
| + | group_ptr = (char *) group_shadow; |
| + | if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) |
| + | old_desc_blocks = fs->super->s_first_meta_bg; |
| + | else |
| + | old_desc_blocks = fs->desc_blocks; |
| + | |
| + | for (i = 0; i < fs->group_desc_count; i++) { |
| + | blk_t super_blk, old_desc_blk, new_desc_blk; |
| + | int meta_bg; |
| + | |
| + | ext2fs_super_and_bgd_loc(fs, i, &super_blk, &old_desc_blk, |
| + | &new_desc_blk, &meta_bg); |
| + | |
| + | if (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) &&i && super_blk) { |
| + | retval = write_backup_super(fs, i, super_blk, |
| + | super_shadow); |
| + | if (retval) |
| + | goto errout; |
| + | } |
| + | if (fs->flags & EXT2_FLAG_SUPER_ONLY) |
| + | continue; |
| + | if ((old_desc_blk) && |
| + | (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) || (i == 0))) { |
| + | retval = io_channel_write_blk(fs->io, |
| + | old_desc_blk, old_desc_blocks, group_ptr); |
| + | if (retval) |
| + | goto errout; |
| + | } |
| + | if (new_desc_blk) { |
| + | retval = io_channel_write_blk(fs->io, new_desc_blk, |
| + | 1, group_ptr + (meta_bg*fs->blocksize)); |
| + | if (retval) |
| + | goto errout; |
| + | } |
| + | } |
| + | fs->super->s_block_group_nr = 0; |
| + | fs->super->s_state = fs_state; |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | if (fs->flags & EXT2_FLAG_SWAP_BYTES) { |
| + | *super_shadow = *fs->super; |
| + | ext2fs_swap_super(super_shadow); |
| + | } |
| + | #endif |
| + | |
| + | /* |
| + | * 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. |
| + | */ |
| + | if (fs->write_bitmaps) { |
| + | retval = fs->write_bitmaps(fs); |
| + | if (retval) |
| + | goto errout; |
| + | } |
| + | |
| + | write_primary_superblock_only: |
| + | /* |
| + | * Write out master superblock. This has to be done |
| + | * separately, since it is located at a fixed location |
| + | * (SUPERBLOCK_OFFSET). We flush all other pending changes |
| + | * out to disk first, just to avoid a race condition with an |
| + | * insy-tinsy window.... |
| + | */ |
| + | retval = io_channel_flush(fs->io); |
| + | retval = write_primary_superblock(fs, super_shadow); |
| + | if (retval) |
| + | goto errout; |
| + | |
| + | fs->flags &= ~EXT2_FLAG_DIRTY; |
| + | |
| + | retval = io_channel_flush(fs->io); |
| + | errout: |
| + | fs->super->s_state = fs_state; |
| + | if (fs->flags & EXT2_FLAG_SWAP_BYTES) { |
| + | if (super_shadow) |
| + | ext2fs_free_mem(&super_shadow); |
| + | if (group_shadow) |
| + | ext2fs_free_mem(&group_shadow); |
| + | } |
| + | return retval; |
| + | } |
| + | |
| + | errcode_t ext2fs_close(ext2_filsys fs) |
| + | { |
| + | errcode_t retval; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | if (fs->flags & EXT2_FLAG_DIRTY) { |
| + | retval = ext2fs_flush(fs); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | if (fs->write_bitmaps) { |
| + | retval = fs->write_bitmaps(fs); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | ext2fs_free(fs); |
| + | return 0; |
| + | } |
| + | |
| @@ -0,0 +1,72 @@ | |
| + | /* |
| + | * cmp_bitmaps.c --- routines to compare inode and block bitmaps. |
| + | * |
| + | * Copyright (C) 1995 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1, |
| + | ext2fs_block_bitmap bm2) |
| + | { |
| + | blk_t i; |
| + | |
| + | EXT2_CHECK_MAGIC(bm1, EXT2_ET_MAGIC_BLOCK_BITMAP); |
| + | EXT2_CHECK_MAGIC(bm2, EXT2_ET_MAGIC_BLOCK_BITMAP); |
| + | |
| + | if ((bm1->start != bm2->start) || |
| + | (bm1->end != bm2->end) || |
| + | (memcmp(bm1->bitmap, bm2->bitmap, |
| + | (size_t) (bm1->end - bm1->start)/8))) |
| + | return EXT2_ET_NEQ_BLOCK_BITMAP; |
| + | |
| + | for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++) |
| + | if (ext2fs_fast_test_block_bitmap(bm1, i) != |
| + | ext2fs_fast_test_block_bitmap(bm2, i)) |
| + | return EXT2_ET_NEQ_BLOCK_BITMAP; |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1, |
| + | ext2fs_inode_bitmap bm2) |
| + | { |
| + | ext2_ino_t i; |
| + | |
| + | EXT2_CHECK_MAGIC(bm1, EXT2_ET_MAGIC_INODE_BITMAP); |
| + | EXT2_CHECK_MAGIC(bm2, EXT2_ET_MAGIC_INODE_BITMAP); |
| + | |
| + | if ((bm1->start != bm2->start) || |
| + | (bm1->end != bm2->end) || |
| + | (memcmp(bm1->bitmap, bm2->bitmap, |
| + | (size_t) (bm1->end - bm1->start)/8))) |
| + | return EXT2_ET_NEQ_INODE_BITMAP; |
| + | |
| + | for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++) |
| + | if (ext2fs_fast_test_inode_bitmap(bm1, i) != |
| + | ext2fs_fast_test_inode_bitmap(bm2, i)) |
| + | return EXT2_ET_NEQ_INODE_BITMAP; |
| + | |
| + | return 0; |
| + | } |
| + | |
| @@ -0,0 +1,260 @@ | |
| + | /* |
| + | * dblist.c -- directory block list functions |
| + | * |
| + | * Copyright 1997 by Theodore Ts'o |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | * |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <string.h> |
| + | #include <time.h> |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fsP.h" |
| + | |
| + | static EXT2_QSORT_TYPE dir_block_cmp(const void *a, const void *b); |
| + | |
| + | /* |
| + | * Returns the number of directories in the filesystem as reported by |
| + | * the group descriptors. Of course, the group descriptors could be |
| + | * wrong! |
| + | */ |
| + | errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs) |
| + | { |
| + | dgrp_t i; |
| + | ext2_ino_t num_dirs, max_dirs; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | num_dirs = 0; |
| + | max_dirs = fs->super->s_inodes_per_group; |
| + | for (i = 0; i < fs->group_desc_count; i++) { |
| + | if (fs->group_desc[i].bg_used_dirs_count > max_dirs) |
| + | num_dirs += max_dirs / 8; |
| + | else |
| + | num_dirs += fs->group_desc[i].bg_used_dirs_count; |
| + | } |
| + | if (num_dirs > fs->super->s_inodes_count) |
| + | num_dirs = fs->super->s_inodes_count; |
| + | |
| + | *ret_num_dirs = num_dirs; |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * helper function for making a new directory block list (for |
| + | * initialize and copy). |
| + | */ |
| + | static errcode_t make_dblist(ext2_filsys fs, ext2_ino_t size, ext2_ino_t count, |
| + | struct ext2_db_entry *list, |
| + | ext2_dblist *ret_dblist) |
| + | { |
| + | ext2_dblist dblist; |
| + | errcode_t retval; |
| + | size_t len; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | if ((ret_dblist == 0) && fs->dblist && |
| + | (fs->dblist->magic == EXT2_ET_MAGIC_DBLIST)) |
| + | return 0; |
| + | |
| + | retval = ext2fs_get_mem(sizeof(struct ext2_struct_dblist), &dblist); |
| + | if (retval) |
| + | return retval; |
| + | memset(dblist, 0, sizeof(struct ext2_struct_dblist)); |
| + | |
| + | dblist->magic = EXT2_ET_MAGIC_DBLIST; |
| + | dblist->fs = fs; |
| + | if (size) |
| + | dblist->size = size; |
| + | else { |
| + | retval = ext2fs_get_num_dirs(fs, &dblist->size); |
| + | if (retval) |
| + | goto cleanup; |
| + | dblist->size = (dblist->size * 2) + 12; |
| + | } |
| + | len = (size_t) sizeof(struct ext2_db_entry) * dblist->size; |
| + | dblist->count = count; |
| + | retval = ext2fs_get_mem(len, &dblist->list); |
| + | if (retval) |
| + | goto cleanup; |
| + | |
| + | if (list) |
| + | memcpy(dblist->list, list, len); |
| + | else |
| + | memset(dblist->list, 0, len); |
| + | if (ret_dblist) |
| + | *ret_dblist = dblist; |
| + | else |
| + | fs->dblist = dblist; |
| + | return 0; |
| + | cleanup: |
| + | if (dblist) |
| + | ext2fs_free_mem(&dblist); |
| + | return retval; |
| + | } |
| + | |
| + | /* |
| + | * Initialize a directory block list |
| + | */ |
| + | errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist) |
| + | { |
| + | ext2_dblist dblist; |
| + | errcode_t retval; |
| + | |
| + | retval = make_dblist(fs, 0, 0, 0, &dblist); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | dblist->sorted = 1; |
| + | if (ret_dblist) |
| + | *ret_dblist = dblist; |
| + | else |
| + | fs->dblist = dblist; |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * Copy a directory block list |
| + | */ |
| + | errcode_t ext2fs_copy_dblist(ext2_dblist src, ext2_dblist *dest) |
| + | { |
| + | ext2_dblist dblist; |
| + | errcode_t retval; |
| + | |
| + | retval = make_dblist(src->fs, src->size, src->count, src->list, |
| + | &dblist); |
| + | if (retval) |
| + | return retval; |
| + | dblist->sorted = src->sorted; |
| + | *dest = dblist; |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * Close a directory block list |
| + | * |
| + | * (moved to closefs.c) |
| + | */ |
| + | |
| + | |
| + | /* |
| + | * Add a directory block to the directory block list |
| + | */ |
| + | errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk, |
| + | int blockcnt) |
| + | { |
| + | struct ext2_db_entry *new_entry; |
| + | errcode_t retval; |
| + | unsigned long old_size; |
| + | |
| + | EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); |
| + | |
| + | if (dblist->count >= dblist->size) { |
| + | old_size = dblist->size * sizeof(struct ext2_db_entry); |
| + | dblist->size += 100; |
| + | retval = ext2fs_resize_mem(old_size, (size_t) dblist->size * |
| + | sizeof(struct ext2_db_entry), |
| + | &dblist->list); |
| + | if (retval) { |
| + | dblist->size -= 100; |
| + | return retval; |
| + | } |
| + | } |
| + | new_entry = dblist->list + ( (int) dblist->count++); |
| + | new_entry->blk = blk; |
| + | new_entry->ino = ino; |
| + | new_entry->blockcnt = blockcnt; |
| + | |
| + | dblist->sorted = 0; |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * Change the directory block to the directory block list |
| + | */ |
| + | errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk, |
| + | int blockcnt) |
| + | { |
| + | dgrp_t i; |
| + | |
| + | EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); |
| + | |
| + | for (i=0; i < dblist->count; i++) { |
| + | if ((dblist->list[i].ino != ino) || |
| + | (dblist->list[i].blockcnt != blockcnt)) |
| + | continue; |
| + | dblist->list[i].blk = blk; |
| + | dblist->sorted = 0; |
| + | return 0; |
| + | } |
| + | return EXT2_ET_DB_NOT_FOUND; |
| + | } |
| + | |
| + | void ext2fs_dblist_sort(ext2_dblist dblist, |
| + | EXT2_QSORT_TYPE (*sortfunc)(const void *, |
| + | const void *)) |
| + | { |
| + | if (!sortfunc) |
| + | sortfunc = dir_block_cmp; |
| + | qsort(dblist->list, (size_t) dblist->count, |
| + | sizeof(struct ext2_db_entry), sortfunc); |
| + | dblist->sorted = 1; |
| + | } |
| + | |
| + | /* |
| + | * This function iterates over the directory block list |
| + | */ |
| + | errcode_t ext2fs_dblist_iterate(ext2_dblist dblist, |
| + | int (*func)(ext2_filsys fs, |
| + | struct ext2_db_entry *db_info, |
| + | void *priv_data), |
| + | void *priv_data) |
| + | { |
| + | ext2_ino_t i; |
| + | int ret; |
| + | |
| + | EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); |
| + | |
| + | if (!dblist->sorted) |
| + | ext2fs_dblist_sort(dblist, 0); |
| + | for (i=0; i < dblist->count; i++) { |
| + | ret = (*func)(dblist->fs, &dblist->list[(int)i], priv_data); |
| + | if (ret & DBLIST_ABORT) |
| + | return 0; |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | static EXT2_QSORT_TYPE dir_block_cmp(const void *a, const void *b) |
| + | { |
| + | const struct ext2_db_entry *db_a = |
| + | (const struct ext2_db_entry *) a; |
| + | const struct ext2_db_entry *db_b = |
| + | (const struct ext2_db_entry *) b; |
| + | |
| + | if (db_a->blk != db_b->blk) |
| + | return (int) (db_a->blk - db_b->blk); |
| + | |
| + | if (db_a->ino != db_b->ino) |
| + | return (int) (db_a->ino - db_b->ino); |
| + | |
| + | return (int) (db_a->blockcnt - db_b->blockcnt); |
| + | } |
| + | |
| + | int ext2fs_dblist_count(ext2_dblist dblist) |
| + | { |
| + | return (int) dblist->count; |
| + | } |
| @@ -0,0 +1,75 @@ | |
| + | /* |
| + | * dblist_dir.c --- iterate by directory entry |
| + | * |
| + | * Copyright 1997 by Theodore Ts'o |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | * |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <string.h> |
| + | #include <time.h> |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fsP.h" |
| + | |
| + | static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry *db_info, |
| + | void *priv_data); |
| + | |
| + | errcode_t ext2fs_dblist_dir_iterate(ext2_dblist dblist, |
| + | int flags, |
| + | char *block_buf, |
| + | int (*func)(ext2_ino_t dir, |
| + | int entry, |
| + | struct ext2_dir_entry *dirent, |
| + | int offset, |
| + | int blocksize, |
| + | char *buf, |
| + | void *priv_data), |
| + | void *priv_data) |
| + | { |
| + | errcode_t retval; |
| + | struct dir_context ctx; |
| + | |
| + | EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); |
| + | |
| + | ctx.dir = 0; |
| + | ctx.flags = flags; |
| + | if (block_buf) |
| + | ctx.buf = block_buf; |
| + | else { |
| + | retval = ext2fs_get_mem(dblist->fs->blocksize, &ctx.buf); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | ctx.func = func; |
| + | ctx.priv_data = priv_data; |
| + | ctx.errcode = 0; |
| + | |
| + | retval = ext2fs_dblist_iterate(dblist, db_dir_proc, &ctx); |
| + | |
| + | if (!block_buf) |
| + | ext2fs_free_mem(&ctx.buf); |
| + | if (retval) |
| + | return retval; |
| + | return ctx.errcode; |
| + | } |
| + | |
| + | static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry *db_info, |
| + | void *priv_data) |
| + | { |
| + | struct dir_context *ctx; |
| + | |
| + | ctx = (struct dir_context *) priv_data; |
| + | ctx->dir = db_info->ino; |
| + | |
| + | return ext2fs_process_dir_block(fs, &db_info->blk, |
| + | db_info->blockcnt, 0, 0, priv_data); |
| + | } |
| @@ -0,0 +1,219 @@ | |
| + | /* |
| + | * dir_iterate.c --- ext2fs directory iteration operations |
| + | * |
| + | * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #if HAVE_ERRNO_H |
| + | #include <errno.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fsP.h" |
| + | |
| + | /* |
| + | * This function checks to see whether or not a potential deleted |
| + | * directory entry looks valid. What we do is check the deleted entry |
| + | * and each successive entry to make sure that they all look valid and |
| + | * that the last deleted entry ends at the beginning of the next |
| + | * undeleted entry. Returns 1 if the deleted entry looks valid, zero |
| + | * if not valid. |
| + | */ |
| + | static int ext2fs_validate_entry(char *buf, int offset, int final_offset) |
| + | { |
| + | struct ext2_dir_entry *dirent; |
| + | |
| + | while (offset < final_offset) { |
| + | dirent = (struct ext2_dir_entry *)(buf + offset); |
| + | offset += dirent->rec_len; |
| + | if ((dirent->rec_len < 8) || |
| + | ((dirent->rec_len % 4) != 0) || |
| + | (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) |
| + | return 0; |
| + | } |
| + | return (offset == final_offset); |
| + | } |
| + | |
| + | errcode_t ext2fs_dir_iterate2(ext2_filsys fs, |
| + | ext2_ino_t dir, |
| + | int flags, |
| + | char *block_buf, |
| + | int (*func)(ext2_ino_t dir, |
| + | int entry, |
| + | struct ext2_dir_entry *dirent, |
| + | int offset, |
| + | int blocksize, |
| + | char *buf, |
| + | void *priv_data), |
| + | void *priv_data) |
| + | { |
| + | struct dir_context ctx; |
| + | errcode_t retval; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | retval = ext2fs_check_directory(fs, dir); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | ctx.dir = dir; |
| + | ctx.flags = flags; |
| + | if (block_buf) |
| + | ctx.buf = block_buf; |
| + | else { |
| + | retval = ext2fs_get_mem(fs->blocksize, &ctx.buf); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | ctx.func = func; |
| + | ctx.priv_data = priv_data; |
| + | ctx.errcode = 0; |
| + | retval = ext2fs_block_iterate2(fs, dir, 0, 0, |
| + | ext2fs_process_dir_block, &ctx); |
| + | if (!block_buf) |
| + | ext2fs_free_mem(&ctx.buf); |
| + | if (retval) |
| + | return retval; |
| + | return ctx.errcode; |
| + | } |
| + | |
| + | struct xlate { |
| + | int (*func)(struct ext2_dir_entry *dirent, |
| + | int offset, |
| + | int blocksize, |
| + | char *buf, |
| + | void *priv_data); |
| + | void *real_private; |
| + | }; |
| + | |
| + | static int xlate_func(ext2_ino_t dir EXT2FS_ATTR((unused)), |
| + | int entry EXT2FS_ATTR((unused)), |
| + | struct ext2_dir_entry *dirent, int offset, |
| + | int blocksize, char *buf, void *priv_data) |
| + | { |
| + | struct xlate *xl = (struct xlate *) priv_data; |
| + | |
| + | return (*xl->func)(dirent, offset, blocksize, buf, xl->real_private); |
| + | } |
| + | |
| + | extern errcode_t ext2fs_dir_iterate(ext2_filsys fs, |
| + | ext2_ino_t dir, |
| + | int flags, |
| + | char *block_buf, |
| + | int (*func)(struct ext2_dir_entry *dirent, |
| + | int offset, |
| + | int blocksize, |
| + | char *buf, |
| + | void *priv_data), |
| + | void *priv_data) |
| + | { |
| + | struct xlate xl; |
| + | |
| + | xl.real_private = priv_data; |
| + | xl.func = func; |
| + | |
| + | return ext2fs_dir_iterate2(fs, dir, flags, block_buf, |
| + | xlate_func, &xl); |
| + | } |
| + | |
| + | |
| + | /* |
| + | * Helper function which is private to this module. Used by |
| + | * ext2fs_dir_iterate() and ext2fs_dblist_dir_iterate() |
| + | */ |
| + | int ext2fs_process_dir_block(ext2_filsys fs, |
| + | blk_t *blocknr, |
| + | e2_blkcnt_t blockcnt, |
| + | blk_t ref_block EXT2FS_ATTR((unused)), |
| + | int ref_offset EXT2FS_ATTR((unused)), |
| + | void *priv_data) |
| + | { |
| + | struct dir_context *ctx = (struct dir_context *) priv_data; |
| + | unsigned int offset = 0; |
| + | unsigned int next_real_entry = 0; |
| + | int ret = 0; |
| + | int changed = 0; |
| + | int do_abort = 0; |
| + | int entry, size; |
| + | struct ext2_dir_entry *dirent; |
| + | |
| + | if (blockcnt < 0) |
| + | return 0; |
| + | |
| + | entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE; |
| + | |
| + | ctx->errcode = ext2fs_read_dir_block(fs, *blocknr, ctx->buf); |
| + | if (ctx->errcode) |
| + | return BLOCK_ABORT; |
| + | |
| + | while (offset < fs->blocksize) { |
| + | dirent = (struct ext2_dir_entry *) (ctx->buf + offset); |
| + | if (((offset + dirent->rec_len) > fs->blocksize) || |
| + | (dirent->rec_len < 8) || |
| + | ((dirent->rec_len % 4) != 0) || |
| + | (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) { |
| + | ctx->errcode = EXT2_ET_DIR_CORRUPTED; |
| + | return BLOCK_ABORT; |
| + | } |
| + | if (!dirent->inode && |
| + | !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY)) |
| + | goto next; |
| + | |
| + | ret = (ctx->func)(ctx->dir, |
| + | (next_real_entry > offset) ? |
| + | DIRENT_DELETED_FILE : entry, |
| + | dirent, offset, |
| + | fs->blocksize, ctx->buf, |
| + | ctx->priv_data); |
| + | if (entry < DIRENT_OTHER_FILE) |
| + | entry++; |
| + | |
| + | if (ret & DIRENT_CHANGED) |
| + | changed++; |
| + | if (ret & DIRENT_ABORT) { |
| + | do_abort++; |
| + | break; |
| + | } |
| + | next: |
| + | if (next_real_entry == offset) |
| + | next_real_entry += dirent->rec_len; |
| + | |
| + | if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) { |
| + | size = ((dirent->name_len & 0xFF) + 11) & ~3; |
| + | |
| + | if (dirent->rec_len != size) { |
| + | unsigned int final_offset; |
| + | |
| + | final_offset = offset + dirent->rec_len; |
| + | offset += size; |
| + | while (offset < final_offset && |
| + | !ext2fs_validate_entry(ctx->buf, |
| + | offset, |
| + | final_offset)) |
| + | offset += 4; |
| + | continue; |
| + | } |
| + | } |
| + | offset += dirent->rec_len; |
| + | } |
| + | |
| + | if (changed) { |
| + | ctx->errcode = ext2fs_write_dir_block(fs, *blocknr, ctx->buf); |
| + | if (ctx->errcode) |
| + | return BLOCK_ABORT; |
| + | } |
| + | if (do_abort) |
| + | return BLOCK_ABORT; |
| + | return 0; |
| + | } |
| + | |
| @@ -0,0 +1,130 @@ | |
| + | /* |
| + | * dirblock.c --- directory block routines. |
| + | * |
| + | * Copyright (C) 1995, 1996 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <string.h> |
| + | #include <time.h> |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block, |
| + | void *buf, int flags EXT2FS_ATTR((unused))) |
| + | { |
| + | errcode_t retval; |
| + | char *p, *end; |
| + | struct ext2_dir_entry *dirent; |
| + | unsigned int name_len, rec_len, do_swap; |
| + | |
| + | |
| + | retval = io_channel_read_blk(fs->io, block, 1, buf); |
| + | if (retval) |
| + | return retval; |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | do_swap = (fs->flags & (EXT2_FLAG_SWAP_BYTES| |
| + | EXT2_FLAG_SWAP_BYTES_READ)) != 0; |
| + | #endif |
| + | p = (char *) buf; |
| + | end = (char *) buf + fs->blocksize; |
| + | while (p < end-8) { |
| + | dirent = (struct ext2_dir_entry *) p; |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | if (do_swap) { |
| + | dirent->inode = ext2fs_swab32(dirent->inode); |
| + | dirent->rec_len = ext2fs_swab16(dirent->rec_len); |
| + | dirent->name_len = ext2fs_swab16(dirent->name_len); |
| + | } |
| + | #endif |
| + | name_len = dirent->name_len; |
| + | #ifdef WORDS_BIGENDIAN |
| + | if (flags & EXT2_DIRBLOCK_V2_STRUCT) |
| + | dirent->name_len = ext2fs_swab16(dirent->name_len); |
| + | #endif |
| + | rec_len = dirent->rec_len; |
| + | if ((rec_len < 8) || (rec_len % 4)) { |
| + | rec_len = 8; |
| + | retval = EXT2_ET_DIR_CORRUPTED; |
| + | } |
| + | if (((name_len & 0xFF) + 8) > dirent->rec_len) |
| + | retval = EXT2_ET_DIR_CORRUPTED; |
| + | p += rec_len; |
| + | } |
| + | return retval; |
| + | } |
| + | |
| + | errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block, |
| + | void *buf) |
| + | { |
| + | return ext2fs_read_dir_block2(fs, block, buf, 0); |
| + | } |
| + | |
| + | |
| + | errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block, |
| + | void *inbuf, int flags EXT2FS_ATTR((unused))) |
| + | { |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | int do_swap = 0; |
| + | errcode_t retval; |
| + | char *p, *end; |
| + | char *buf = 0; |
| + | struct ext2_dir_entry *dirent; |
| + | |
| + | if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || |
| + | (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) |
| + | do_swap = 1; |
| + | |
| + | #ifndef WORDS_BIGENDIAN |
| + | if (!do_swap) |
| + | return io_channel_write_blk(fs->io, block, 1, (char *) inbuf); |
| + | #endif |
| + | |
| + | retval = ext2fs_get_mem(fs->blocksize, &buf); |
| + | if (retval) |
| + | return retval; |
| + | memcpy(buf, inbuf, fs->blocksize); |
| + | p = buf; |
| + | end = buf + fs->blocksize; |
| + | while (p < end) { |
| + | dirent = (struct ext2_dir_entry *) p; |
| + | if ((dirent->rec_len < 8) || |
| + | (dirent->rec_len % 4)) { |
| + | ext2fs_free_mem(&buf); |
| + | return (EXT2_ET_DIR_CORRUPTED); |
| + | } |
| + | p += dirent->rec_len; |
| + | if (do_swap) { |
| + | dirent->inode = ext2fs_swab32(dirent->inode); |
| + | dirent->rec_len = ext2fs_swab16(dirent->rec_len); |
| + | dirent->name_len = ext2fs_swab16(dirent->name_len); |
| + | } |
| + | #ifdef WORDS_BIGENDIAN |
| + | if (flags & EXT2_DIRBLOCK_V2_STRUCT) |
| + | dirent->name_len = ext2fs_swab16(dirent->name_len); |
| + | #endif |
| + | } |
| + | retval = io_channel_write_blk(fs->io, block, 1, buf); |
| + | ext2fs_free_mem(&buf); |
| + | return retval; |
| + | #else |
| + | return io_channel_write_blk(fs->io, block, 1, (char *) inbuf); |
| + | #endif |
| + | } |
| + | |
| + | |
| + | errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block, |
| + | void *inbuf) |
| + | { |
| + | return ext2fs_write_dir_block2(fs, block, inbuf, 0); |
| + | } |
| + | |
| @@ -0,0 +1,233 @@ | |
| + | /* |
| + | * dirhash.c -- Calculate the hash of a directory entry |
| + | * |
| + | * Copyright (c) 2001 Daniel Phillips |
| + | * |
| + | * Copyright (c) 2002 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | /* |
| + | * Keyed 32-bit hash function using TEA in a Davis-Meyer function |
| + | * H0 = Key |
| + | * Hi = E Mi(Hi-1) + Hi-1 |
| + | * |
| + | * (see Applied Cryptography, 2nd edition, p448). |
| + | * |
| + | * Jeremy Fitzhardinge <[email protected]> 1998 |
| + | * |
| + | * This code is made available under the terms of the GPL |
| + | */ |
| + | #define DELTA 0x9E3779B9 |
| + | |
| + | static void TEA_transform(__u32 buf[4], __u32 const in[]) |
| + | { |
| + | __u32 sum = 0; |
| + | __u32 b0 = buf[0], b1 = buf[1]; |
| + | __u32 a = in[0], b = in[1], c = in[2], d = in[3]; |
| + | int n = 16; |
| + | |
| + | do { |
| + | sum += DELTA; |
| + | b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b); |
| + | b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d); |
| + | } while(--n); |
| + | |
| + | buf[0] += b0; |
| + | buf[1] += b1; |
| + | } |
| + | |
| + | /* F, G and H are basic MD4 functions: selection, majority, parity */ |
| + | #define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) |
| + | #define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z))) |
| + | #define H(x, y, z) ((x) ^ (y) ^ (z)) |
| + | |
| + | /* |
| + | * The generic round function. The application is so specific that |
| + | * we don't bother protecting all the arguments with parens, as is generally |
| + | * good macro practice, in favor of extra legibility. |
| + | * Rotation is separate from addition to prevent recomputation |
| + | */ |
| + | #define ROUND(f, a, b, c, d, x, s) \ |
| + | (a += f(b, c, d) + x, a = (a << s) | (a >> (32-s))) |
| + | #define K1 0 |
| + | #define K2 013240474631UL |
| + | #define K3 015666365641UL |
| + | |
| + | /* |
| + | * Basic cut-down MD4 transform. Returns only 32 bits of result. |
| + | */ |
| + | static void halfMD4Transform (__u32 buf[4], __u32 const in[]) |
| + | { |
| + | __u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; |
| + | |
| + | /* Round 1 */ |
| + | ROUND(F, a, b, c, d, in[0] + K1, 3); |
| + | ROUND(F, d, a, b, c, in[1] + K1, 7); |
| + | ROUND(F, c, d, a, b, in[2] + K1, 11); |
| + | ROUND(F, b, c, d, a, in[3] + K1, 19); |
| + | ROUND(F, a, b, c, d, in[4] + K1, 3); |
| + | ROUND(F, d, a, b, c, in[5] + K1, 7); |
| + | ROUND(F, c, d, a, b, in[6] + K1, 11); |
| + | ROUND(F, b, c, d, a, in[7] + K1, 19); |
| + | |
| + | /* Round 2 */ |
| + | ROUND(G, a, b, c, d, in[1] + K2, 3); |
| + | ROUND(G, d, a, b, c, in[3] + K2, 5); |
| + | ROUND(G, c, d, a, b, in[5] + K2, 9); |
| + | ROUND(G, b, c, d, a, in[7] + K2, 13); |
| + | ROUND(G, a, b, c, d, in[0] + K2, 3); |
| + | ROUND(G, d, a, b, c, in[2] + K2, 5); |
| + | ROUND(G, c, d, a, b, in[4] + K2, 9); |
| + | ROUND(G, b, c, d, a, in[6] + K2, 13); |
| + | |
| + | /* Round 3 */ |
| + | ROUND(H, a, b, c, d, in[3] + K3, 3); |
| + | ROUND(H, d, a, b, c, in[7] + K3, 9); |
| + | ROUND(H, c, d, a, b, in[2] + K3, 11); |
| + | ROUND(H, b, c, d, a, in[6] + K3, 15); |
| + | ROUND(H, a, b, c, d, in[1] + K3, 3); |
| + | ROUND(H, d, a, b, c, in[5] + K3, 9); |
| + | ROUND(H, c, d, a, b, in[0] + K3, 11); |
| + | ROUND(H, b, c, d, a, in[4] + K3, 15); |
| + | |
| + | buf[0] += a; |
| + | buf[1] += b; |
| + | buf[2] += c; |
| + | buf[3] += d; |
| + | } |
| + | |
| + | #undef ROUND |
| + | #undef F |
| + | #undef G |
| + | #undef H |
| + | #undef K1 |
| + | #undef K2 |
| + | #undef K3 |
| + | |
| + | /* The old legacy hash */ |
| + | static ext2_dirhash_t dx_hack_hash (const char *name, int len) |
| + | { |
| + | __u32 hash0 = 0x12a3fe2d, hash1 = 0x37abe8f9; |
| + | while (len--) { |
| + | __u32 hash = hash1 + (hash0 ^ (*name++ * 7152373)); |
| + | |
| + | if (hash & 0x80000000) hash -= 0x7fffffff; |
| + | hash1 = hash0; |
| + | hash0 = hash; |
| + | } |
| + | return (hash0 << 1); |
| + | } |
| + | |
| + | static void str2hashbuf(const char *msg, int len, __u32 *buf, int num) |
| + | { |
| + | __u32 pad, val; |
| + | int i; |
| + | |
| + | pad = (__u32)len | ((__u32)len << 8); |
| + | pad |= pad << 16; |
| + | |
| + | val = pad; |
| + | if (len > num*4) |
| + | len = num * 4; |
| + | for (i=0; i < len; i++) { |
| + | if ((i % 4) == 0) |
| + | val = pad; |
| + | val = msg[i] + (val << 8); |
| + | if ((i % 4) == 3) { |
| + | *buf++ = val; |
| + | val = pad; |
| + | num--; |
| + | } |
| + | } |
| + | if (--num >= 0) |
| + | *buf++ = val; |
| + | while (--num >= 0) |
| + | *buf++ = pad; |
| + | } |
| + | |
| + | /* |
| + | * Returns the hash of a filename. If len is 0 and name is NULL, then |
| + | * this function can be used to test whether or not a hash version is |
| + | * supported. |
| + | * |
| + | * The seed is an 4 longword (32 bits) "secret" which can be used to |
| + | * uniquify a hash. If the seed is all zero's, then some default seed |
| + | * may be used. |
| + | * |
| + | * A particular hash version specifies whether or not the seed is |
| + | * represented, and whether or not the returned hash is 32 bits or 64 |
| + | * bits. 32 bit hashes will return 0 for the minor hash. |
| + | */ |
| + | errcode_t ext2fs_dirhash(int version, const char *name, int len, |
| + | const __u32 *seed, |
| + | ext2_dirhash_t *ret_hash, |
| + | ext2_dirhash_t *ret_minor_hash) |
| + | { |
| + | __u32 hash; |
| + | __u32 minor_hash = 0; |
| + | const char *p; |
| + | int i; |
| + | __u32 in[8], buf[4]; |
| + | |
| + | /* Initialize the default seed for the hash checksum functions */ |
| + | buf[0] = 0x67452301; |
| + | buf[1] = 0xefcdab89; |
| + | buf[2] = 0x98badcfe; |
| + | buf[3] = 0x10325476; |
| + | |
| + | /* Check to see if the seed is all zero's */ |
| + | if (seed) { |
| + | for (i=0; i < 4; i++) { |
| + | if (seed[i]) |
| + | break; |
| + | } |
| + | if (i < 4) |
| + | memcpy(buf, seed, sizeof(buf)); |
| + | } |
| + | |
| + | switch (version) { |
| + | case EXT2_HASH_LEGACY: |
| + | hash = dx_hack_hash(name, len); |
| + | break; |
| + | case EXT2_HASH_HALF_MD4: |
| + | p = name; |
| + | while (len > 0) { |
| + | str2hashbuf(p, len, in, 8); |
| + | halfMD4Transform(buf, in); |
| + | len -= 32; |
| + | p += 32; |
| + | } |
| + | minor_hash = buf[2]; |
| + | hash = buf[1]; |
| + | break; |
| + | case EXT2_HASH_TEA: |
| + | p = name; |
| + | while (len > 0) { |
| + | str2hashbuf(p, len, in, 4); |
| + | TEA_transform(buf, in); |
| + | len -= 16; |
| + | p += 16; |
| + | } |
| + | hash = buf[0]; |
| + | minor_hash = buf[1]; |
| + | break; |
| + | default: |
| + | *ret_hash = 0; |
| + | return EXT2_ET_DIRHASH_UNSUPP; |
| + | } |
| + | *ret_hash = hash & ~1; |
| + | if (ret_minor_hash) |
| + | *ret_minor_hash = minor_hash; |
| + | return 0; |
| + | } |
| @@ -0,0 +1,456 @@ | |
| + | /* |
| + | * dosio.c -- Disk I/O module for the ext2fs/DOS library. |
| + | * |
| + | * Copyright (c) 1997 by Theodore Ts'o. |
| + | * |
| + | * Copyright (c) 1997 Mark Habersack |
| + | * This file may be distributed under the terms of the GNU Public License. |
| + | * |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <bios.h> |
| + | #include <string.h> |
| + | #include <ctype.h> |
| + | #include <io.h> |
| + | #ifdef HAVE_ERRNO_H |
| + | #include <errno.h> |
| + | #endif |
| + | |
| + | #include <ext2fs/ext2_types.h> |
| + | #include "utils.h" |
| + | #include "dosio.h" |
| + | #include "et/com_err.h" |
| + | #include "ext2_err.h" |
| + | #include "ext2fs/io.h" |
| + | |
| + | /* |
| + | * Some helper macros |
| + | */ |
| + | #define LINUX_EXT2FS 0x83 |
| + | #define LINUX_SWAP 0x82 |
| + | #define WRITE_ERR(_msg_) write(2, _msg_, strlen(_msg_)) |
| + | #define WRITE_ERR_S(_msg_) write(2, _msg_, sizeof(_msg_)) |
| + | |
| + | /* |
| + | * Exported variables |
| + | */ |
| + | unsigned long _dio_error; |
| + | unsigned long _dio_hw_error; |
| + | |
| + | /* |
| + | * Array of all opened partitions |
| + | */ |
| + | static PARTITION **partitions = NULL; |
| + | static unsigned short npart = 0; /* Number of mapped partitions */ |
| + | static PARTITION *active = NULL; |
| + | |
| + | /* |
| + | * I/O Manager routine prototypes |
| + | */ |
| + | static errcode_t dos_open(const char *dev, int flags, io_channel *channel); |
| + | static errcode_t dos_close(io_channel channel); |
| + | static errcode_t dos_set_blksize(io_channel channel, int blksize); |
| + | static errcode_t dos_read_blk(io_channel channel, unsigned long block, |
| + | int count, void *buf); |
| + | static errcode_t dos_write_blk(io_channel channel, unsigned long block, |
| + | int count, const void *buf); |
| + | static errcode_t dos_flush(io_channel channel); |
| + | |
| + | static struct struct_io_manager struct_dos_manager = { |
| + | EXT2_ET_MAGIC_IO_MANAGER, |
| + | "DOS I/O Manager", |
| + | dos_open, |
| + | dos_close, |
| + | dos_set_blksize, |
| + | dos_read_blk, |
| + | dos_write_blk, |
| + | dos_flush |
| + | }; |
| + | io_manager dos_io_manager = &struct_dos_manager; |
| + | |
| + | /* |
| + | * Macro taken from unix_io.c |
| + | */ |
| + | /* |
| + | * For checking structure magic numbers... |
| + | */ |
| + | |
| + | #define EXT2_CHECK_MAGIC(struct, code) \ |
| + | if ((struct)->magic != (code)) return (code) |
| + | |
| + | /* |
| + | * Calculates a CHS address of a sector from its LBA |
| + | * offset for the given partition. |
| + | */ |
| + | static void lba2chs(unsigned long lba_addr, CHS *chs, PARTITION *part) |
| + | { |
| + | unsigned long abss; |
| + | |
| + | chs->offset = lba_addr & 0x000001FF; |
| + | abss = (lba_addr >> 9) + part->start; |
| + | chs->cyl = abss / (part->sects * part->heads); |
| + | chs->head = (abss / part->sects) % part->heads; |
| + | chs->sector = (abss % part->sects) + 1; |
| + | } |
| + | |
| + | #ifdef __TURBOC__ |
| + | #pragma argsused |
| + | #endif |
| + | /* |
| + | * Scans the passed partition table looking for *pno partition |
| + | * that has LINUX_EXT2FS type. |
| + | * |
| + | * TODO: |
| + | * For partition numbers >5 Linux uses DOS extended partitions - |
| + | * dive into them an return an appropriate entry. Also dive into |
| + | * extended partitions when scanning for a first Linux/ext2fs. |
| + | */ |
| + | static PTABLE_ENTRY *scan_partition_table(PTABLE_ENTRY *pentry, |
| + | unsigned short phys, |
| + | unsigned char *pno) |
| + | { |
| + | unsigned i; |
| + | |
| + | if(*pno != 0xFF && *pno >= 5) |
| + | return NULL; /* We don't support extended partitions for now */ |
| + | |
| + | if(*pno != 0xFF) |
| + | { |
| + | if(pentry[*pno].type == LINUX_EXT2FS) |
| + | return &pentry[*pno]; |
| + | else |
| + | { |
| + | if(!pentry[*pno].type) |
| + | *pno = 0xFE; |
| + | else if(pentry[*pno].type == LINUX_SWAP) |
| + | *pno = 0xFD; |
| + | return NULL; |
| + | } |
| + | } |
| + | |
| + | for(i = 0; i < 4; i++) |
| + | if(pentry[i].type == LINUX_EXT2FS) |
| + | { |
| + | *pno = i; |
| + | return &pentry[i]; |
| + | } |
| + | |
| + | return NULL; |
| + | } |
| + | |
| + | /* |
| + | * Allocate libext2fs structures associated with I/O manager |
| + | */ |
| + | static io_channel alloc_io_channel(PARTITION *part) |
| + | { |
| + | io_channel ioch; |
| + | |
| + | ioch = (io_channel)malloc(sizeof(struct struct_io_channel)); |
| + | if (!ioch) |
| + | return NULL; |
| + | memset(ioch, 0, sizeof(struct struct_io_channel)); |
| + | ioch->magic = EXT2_ET_MAGIC_IO_CHANNEL; |
| + | ioch->manager = dos_io_manager; |
| + | ioch->name = (char *)malloc(strlen(part->dev)+1); |
| + | if (!ioch->name) { |
| + | free(ioch); |
| + | return NULL; |
| + | } |
| + | strcpy(ioch->name, part->dev); |
| + | ioch->private_data = part; |
| + | ioch->block_size = 1024; /* The smallest ext2fs block size */ |
| + | ioch->read_error = 0; |
| + | ioch->write_error = 0; |
| + | |
| + | return ioch; |
| + | } |
| + | |
| + | #ifdef __TURBOC__ |
| + | #pragma argsused |
| + | #endif |
| + | /* |
| + | * Open the 'name' partition, initialize all information structures |
| + | * we need to keep and create libext2fs I/O manager. |
| + | */ |
| + | static errcode_t dos_open(const char *dev, int flags, io_channel *channel) |
| + | { |
| + | unsigned char *tmp, sec[512]; |
| + | PARTITION *part; |
| + | PTABLE_ENTRY *pent; |
| + | PARTITION **newparts; |
| + | |
| + | if(!dev) |
| + | { |
| + | _dio_error = ERR_BADDEV; |
| + | return EXT2_ET_BAD_DEVICE_NAME; |
| + | } |
| + | |
| + | /* |
| + | * First check whether the dev name is OK |
| + | */ |
| + | tmp = (unsigned char*)strrchr(dev, '/'); |
| + | if(!tmp) |
| + | { |
| + | _dio_error = ERR_BADDEV; |
| + | return EXT2_ET_BAD_DEVICE_NAME; |
| + | } |
| + | *tmp = 0; |
| + | if(strcmp(dev, "/dev")) |
| + | { |
| + | _dio_error = ERR_BADDEV; |
| + | return EXT2_ET_BAD_DEVICE_NAME; |
| + | } |
| + | *tmp++ = '/'; |
| + | |
| + | /* |
| + | * Check whether the partition data is already in cache |
| + | */ |
| + | |
| + | part = (PARTITION*)malloc(sizeof(PARTITION)); |
| + | if (!part) |
| + | return ENOMEM; |
| + | { |
| + | int i = 0; |
| + | |
| + | for(;i < npart; i++) |
| + | if(!strcmp(partitions[i]->dev, dev)) |
| + | { |
| + | /* Found it! Make it the active one */ |
| + | active = partitions[i]; |
| + | *channel = alloc_io_channel(active); |
| + | if (!*channel) |
| + | return ENOMEM; |
| + | return 0; |
| + | } |
| + | } |
| + | |
| + | /* |
| + | * Drive number & optionally partn number |
| + | */ |
| + | switch(tmp[0]) |
| + | { |
| + | case 'h': |
| + | case 's': |
| + | part->phys = 0x80; |
| + | part->phys += toupper(tmp[2]) - 'A'; |
| + | /* |
| + | * Do we have the partition number? |
| + | */ |
| + | if(tmp[3]) |
| + | part->pno = isdigit((int)tmp[3]) ? tmp[3] - '0' - 1: 0; |
| + | else |
| + | part->pno = 0xFF; |
| + | break; |
| + | |
| + | case 'f': |
| + | if(tmp[2]) |
| + | part->phys = isdigit((int)tmp[2]) ? tmp[2] - '0' : 0; |
| + | else |
| + | part->phys = 0x00; /* We'll assume /dev/fd0 */ |
| + | break; |
| + | |
| + | default: |
| + | _dio_error = ERR_BADDEV; |
| + | return ENODEV; |
| + | } |
| + | |
| + | if(part->phys < 0x80) |
| + | { |
| + | /* We don't support floppies for now */ |
| + | _dio_error = ERR_NOTSUPP; |
| + | return EINVAL; |
| + | } |
| + | |
| + | part->dev = strdup(dev); |
| + | |
| + | /* |
| + | * Get drive's geometry |
| + | */ |
| + | _dio_hw_error = biosdisk(DISK_GET_GEOMETRY, |
| + | part->phys, |
| + | 0, /* head */ |
| + | 0, /* cylinder */ |
| + | 1, /* sector */ |
| + | 1, /* just one sector */ |
| + | sec); |
| + | |
| + | if(!HW_OK()) |
| + | { |
| + | _dio_error = ERR_HARDWARE; |
| + | if (part) |
| + | free(part); |
| + | return EFAULT; |
| + | } |
| + | |
| + | /* |
| + | * Calculate the geometry |
| + | */ |
| + | part->cyls = (unsigned short)(((sec[0] >> 6) << 8) + sec[1] + 1); |
| + | part->heads = sec[3] + 1; |
| + | part->sects = sec[0] & 0x3F; |
| + | |
| + | /* |
| + | * Now that we know all we need, let's look for the partition |
| + | */ |
| + | _dio_hw_error = biosdisk(DISK_READ, part->phys, 0, 0, 1, 1, sec); |
| + | |
| + | if(!HW_OK()) |
| + | { |
| + | _dio_error = ERR_HARDWARE; |
| + | if (part) |
| + | free(part); |
| + | return EFAULT; |
| + | } |
| + | |
| + | pent = (PTABLE_ENTRY*)&sec[0x1BE]; |
| + | pent = scan_partition_table(pent, part->phys, &part->pno); |
| + | |
| + | if(!pent) |
| + | { |
| + | _dio_error = part->pno == 0xFE ? ERR_EMPTYPART : |
| + | part->pno == 0xFD ? ERR_LINUXSWAP : ERR_NOTEXT2FS; |
| + | if (part) |
| + | free(part); |
| + | return ENODEV; |
| + | } |
| + | |
| + | /* |
| + | * Calculate the remaining figures |
| + | */ |
| + | { |
| + | unsigned long fsec, fhead, fcyl; |
| + | |
| + | fsec = (unsigned long)(pent->start_sec & 0x3F); |
| + | fhead = (unsigned long)pent->start_head; |
| + | fcyl = ((pent->start_sec >> 6) << 8) + pent->start_cyl; |
| + | part->start = fsec + fhead * part->sects + fcyl * |
| + | (part->heads * part->sects) - 1; |
| + | part->len = pent->size; |
| + | } |
| + | |
| + | /* |
| + | * Add the partition to the table |
| + | */ |
| + | newparts = (PARTITION**)realloc(partitions, sizeof(PARTITION) * npart); |
| + | if (!newparts) { |
| + | free(part); |
| + | return ENOMEM; |
| + | } |
| + | partitions = newparts; |
| + | partitions[npart++] = active = part; |
| + | |
| + | /* |
| + | * Now alloc all libe2fs structures |
| + | */ |
| + | *channel = alloc_io_channel(active); |
| + | if (!*channel) |
| + | return ENOMEM; |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t dos_close(io_channel channel) |
| + | { |
| + | if (channel->name) |
| + | free(channel->name); |
| + | if (channel) |
| + | free(channel); |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t dos_set_blksize(io_channel channel, int blksize) |
| + | { |
| + | channel->block_size = blksize; |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t dos_read_blk(io_channel channel, unsigned long block, |
| + | int count, void *buf) |
| + | { |
| + | PARTITION *part; |
| + | size_t size; |
| + | ext2_loff_t loc; |
| + | CHS chs; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | part = (PARTITION*)channel->private_data; |
| + | |
| + | size = (size_t)((count < 0) ? -count : count * channel->block_size); |
| + | loc = (ext2_loff_t) block * channel->block_size; |
| + | |
| + | lba2chs(loc, &chs, part); |
| + | /* |
| + | * Potential bug here: |
| + | * If DJGPP is used then reads of >18 sectors will fail! |
| + | * Have to rewrite biosdisk. |
| + | */ |
| + | _dio_hw_error = biosdisk(DISK_READ, |
| + | part->phys, |
| + | chs.head, |
| + | chs.cyl, |
| + | chs.sector, |
| + | size < 512 ? 1 : size/512, |
| + | buf); |
| + | |
| + | if(!HW_OK()) |
| + | { |
| + | _dio_error = ERR_HARDWARE; |
| + | return EFAULT; |
| + | } |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t dos_write_blk(io_channel channel, unsigned long block, |
| + | int count, const void *buf) |
| + | { |
| + | PARTITION *part; |
| + | size_t size; |
| + | ext2_loff_t loc; |
| + | CHS chs; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | part = (PARTITION*)channel->private_data; |
| + | |
| + | if(count == 1) |
| + | size = (size_t)channel->block_size; |
| + | else |
| + | { |
| + | if (count < 0) |
| + | size = (size_t)-count; |
| + | else |
| + | size = (size_t)(count * channel->block_size); |
| + | } |
| + | |
| + | loc = (ext2_loff_t)block * channel->block_size; |
| + | lba2chs(loc, &chs, part); |
| + | _dio_hw_error = biosdisk(DISK_WRITE, |
| + | part->phys, |
| + | chs.head, |
| + | chs.cyl, |
| + | chs.sector, |
| + | size < 512 ? 1 : size/512, |
| + | (void*)buf); |
| + | |
| + | if(!HW_OK()) |
| + | { |
| + | _dio_error = ERR_HARDWARE; |
| + | return EFAULT; |
| + | } |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | #ifdef __TURBOC__ |
| + | #pragma argsused |
| + | #endif |
| + | static errcode_t dos_flush(io_channel channel) |
| + | { |
| + | /* |
| + | * No buffers, no flush... |
| + | */ |
| + | return 0; |
| + | } |
| @@ -0,0 +1,153 @@ | |
| + | /* |
| + | * v1.0 |
| + | * |
| + | * Disk I/O include file for the ext2fs/DOS library. |
| + | * |
| + | * Copyright (c) 1997 Mark Habersack |
| + | * This file may be distributed under the terms of the GNU Public License. |
| + | * |
| + | */ |
| + | #ifndef __diskio_h |
| + | #define __diskio_h |
| + | #ifdef __TURBOC__ |
| + | #ifndef __LARGE__ |
| + | # error "ext2fs/DOS library requires LARGE model!" |
| + | #endif |
| + | #endif |
| + | |
| + | #ifdef __TURBOC__ |
| + | #include "msdos.h" |
| + | #endif |
| + | |
| + | /* |
| + | * A helper structure used in LBA => CHS conversion |
| + | */ |
| + | typedef struct |
| + | { |
| + | unsigned short cyl; /* Cylinder (or track) */ |
| + | unsigned short head; |
| + | unsigned short sector; |
| + | unsigned short offset; /* Offset of byte within the sector */ |
| + | } CHS; |
| + | |
| + | /* |
| + | * All partition data we need is here |
| + | */ |
| + | typedef struct |
| + | { |
| + | char *dev; /* _Linux_ device name (like "/dev/hda1") */ |
| + | unsigned char phys; /* Physical DOS drive number */ |
| + | unsigned long start; /* LBA address of partition start */ |
| + | unsigned long len; /* length of partition in sectors */ |
| + | unsigned char pno; /* Partition number (read from *dev) */ |
| + | |
| + | /* This partition's drive geometry */ |
| + | unsigned short cyls; |
| + | unsigned short heads; |
| + | unsigned short sects; |
| + | } PARTITION; |
| + | |
| + | /* |
| + | * PC partition table entry format |
| + | */ |
| + | #ifdef __DJGPP__ |
| + | #pragma pack(1) |
| + | #endif |
| + | typedef struct |
| + | { |
| + | unsigned char active; |
| + | unsigned char start_head; |
| + | unsigned char start_sec; |
| + | unsigned char start_cyl; |
| + | unsigned char type; |
| + | unsigned char end_head; |
| + | unsigned char end_sec; |
| + | unsigned char end_cyl; |
| + | unsigned long first_sec_rel; |
| + | unsigned long size; |
| + | } PTABLE_ENTRY; |
| + | #ifdef __DJGPP__ |
| + | #pragma pack() |
| + | #endif |
| + | |
| + | /* |
| + | * INT 0x13 operation codes |
| + | */ |
| + | #define DISK_READ 0x02 |
| + | #define DISK_WRITE 0x03 |
| + | #define DISK_GET_GEOMETRY 0x08 |
| + | #define DISK_READY 0x10 |
| + | |
| + | /* |
| + | * Errors to put in _dio_error |
| + | */ |
| + | #define ERR_BADDEV 0x00000001L |
| + | #define ERR_HARDWARE 0x00000002L |
| + | #define ERR_NOTSUPP 0x00000003L |
| + | #define ERR_NOTEXT2FS 0x00000004L |
| + | #define ERR_EMPTYPART 0x00000005L |
| + | #define ERR_LINUXSWAP 0x00000006L |
| + | |
| + | /* |
| + | * Functions in diskio.c |
| + | */ |
| + | |
| + | /* |
| + | * Variable contains last module's error |
| + | */ |
| + | extern unsigned long _dio_error; |
| + | |
| + | /* |
| + | * This one contains last hardware error (if _dio_error == ERR_HARDWARE) |
| + | */ |
| + | extern unsigned long _dio_hw_error; |
| + | |
| + | /* |
| + | * Macros to check for disk hardware errors |
| + | */ |
| + | #define HW_OK() ((unsigned char)_dio_hw_error == 0x00) |
| + | #define HW_BAD_CMD() ((unsigned char)_dio_hw_error == 0x01) |
| + | #define HW_NO_ADDR_MARK() ((unsigned char)_dio_hw_error == 0x02) |
| + | #define HW_WRITE_PROT() ((unsigned char)_dio_hw_error == 0x03) |
| + | #define HW_NO_SECTOR() ((unsigned char)_dio_hw_error == 0x04) |
| + | #define HW_RESET_FAIL() ((unsigned char)_dio_hw_error == 0x05) |
| + | #define HW_DISK_CHANGED() ((unsigned char)_dio_hw_error == 0x06) |
| + | #define HW_DRIVE_FAIL() ((unsigned char)_dio_hw_error == 0x07) |
| + | #define HW_DMA_OVERRUN() ((unsigned char)_dio_hw_error == 0x08) |
| + | #define HW_DMA_BOUNDARY() ((unsigned char)_dio_hw_error == 0x09) |
| + | #define HW_BAD_SECTOR() ((unsigned char)_dio_hw_error == 0x0A) |
| + | #define HW_BAD_TRACK() ((unsigned char)_dio_hw_error == 0x0B) |
| + | #define HW_UNSUPP_TRACK() ((unsigned char)_dio_hw_error == 0x0C) |
| + | #define HW_BAD_CRC_ECC() ((unsigned char)_dio_hw_error == 0x10) |
| + | #define HW_CRC_ECC_CORR() ((unsigned char)_dio_hw_error == 0x11) |
| + | #define HW_CONTR_FAIL() ((unsigned char)_dio_hw_error == 0x20) |
| + | #define HW_SEEK_FAIL() ((unsigned char)_dio_hw_error == 0x40) |
| + | #define HW_ATTACH_FAIL() ((unsigned char)_dio_hw_error == 0x80) |
| + | #define HW_DRIVE_NREADY() ((unsigned char)_dio_hw_error == 0xAA) |
| + | #define HW_UNDEF_ERROR() ((unsigned char)_dio_hw_error == 0xBB) |
| + | #define HW_WRITE_FAULT() ((unsigned char)_dio_hw_error == 0xCC) |
| + | #define HW_STATUS_ERROR() ((unsigned char)_dio_hw_error == 0xE0) |
| + | #define HW_SENSE_FAIL() ((unsigned char)_dio_hw_error == 0xFF) |
| + | |
| + | |
| + | /* |
| + | * Open the specified partition. |
| + | * String 'dev' must have a format: |
| + | * |
| + | * /dev/{sd|hd|fd}[X] |
| + | * |
| + | * where, |
| + | * |
| + | * only one of the option in curly braces can be used and X is an optional |
| + | * partition number for the given device. If X is not specified, function |
| + | * scans the drive's partition table in search for the first Linux ext2fs |
| + | * partition (signature 0x83). Along the way it dives into every extended |
| + | * partition encountered. |
| + | * Scan ends if either (a) there are no more used partition entries, or |
| + | * (b) there is no Xth partition. |
| + | * |
| + | * Routine returns 0 on success and !=0 otherwise. |
| + | */ |
| + | int open_partition(char *dev); |
| + | |
| + | #endif /* __diskio_h */ |
| @@ -0,0 +1,96 @@ | |
| + | /* |
| + | * dupfs.c --- duplicate a ext2 filesystem handle |
| + | * |
| + | * Copyright (C) 1997, 1998, 2001, 2003, 2005 by Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <time.h> |
| + | #include <string.h> |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fsP.h" |
| + | |
| + | errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest) |
| + | { |
| + | ext2_filsys fs; |
| + | errcode_t retval; |
| + | |
| + | EXT2_CHECK_MAGIC(src, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | *fs = *src; |
| + | fs->device_name = 0; |
| + | fs->super = 0; |
| + | fs->orig_super = 0; |
| + | fs->group_desc = 0; |
| + | fs->inode_map = 0; |
| + | fs->block_map = 0; |
| + | fs->badblocks = 0; |
| + | fs->dblist = 0; |
| + | |
| + | io_channel_bumpcount(fs->io); |
| + | if (fs->icache) |
| + | fs->icache->refcount++; |
| + | |
| + | retval = ext2fs_get_mem(strlen(src->device_name)+1, &fs->device_name); |
| + | if (retval) |
| + | goto errout; |
| + | strcpy(fs->device_name, src->device_name); |
| + | |
| + | retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super); |
| + | if (retval) |
| + | goto errout; |
| + | memcpy(fs->super, src->super, SUPERBLOCK_SIZE); |
| + | |
| + | retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super); |
| + | if (retval) |
| + | goto errout; |
| + | memcpy(fs->orig_super, src->orig_super, SUPERBLOCK_SIZE); |
| + | |
| + | retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize, |
| + | &fs->group_desc); |
| + | if (retval) |
| + | goto errout; |
| + | memcpy(fs->group_desc, src->group_desc, |
| + | (size_t) fs->desc_blocks * fs->blocksize); |
| + | |
| + | if (src->inode_map) { |
| + | retval = ext2fs_copy_bitmap(src->inode_map, &fs->inode_map); |
| + | if (retval) |
| + | goto errout; |
| + | } |
| + | if (src->block_map) { |
| + | retval = ext2fs_copy_bitmap(src->block_map, &fs->block_map); |
| + | if (retval) |
| + | goto errout; |
| + | } |
| + | if (src->badblocks) { |
| + | retval = ext2fs_badblocks_copy(src->badblocks, &fs->badblocks); |
| + | if (retval) |
| + | goto errout; |
| + | } |
| + | if (src->dblist) { |
| + | retval = ext2fs_copy_dblist(src->dblist, &fs->dblist); |
| + | if (retval) |
| + | goto errout; |
| + | } |
| + | *dest = fs; |
| + | return 0; |
| + | errout: |
| + | ext2fs_free(fs); |
| + | return retval; |
| + | |
| + | } |
| + | |
| @@ -0,0 +1,51 @@ | |
| + | /* |
| + | * e2image.h --- header file describing the ext2 image format |
| + | * |
| + | * Copyright (C) 2000 Theodore Ts'o. |
| + | * |
| + | * Note: this uses the POSIX IO interfaces, unlike most of the other |
| + | * functions in this library. So sue me. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | |
| + | struct ext2_image_hdr { |
| + | __u32 magic_number; /* This must be EXT2_ET_MAGIC_E2IMAGE */ |
| + | char magic_descriptor[16]; /* "Ext2 Image 1.0", w/ null padding */ |
| + | char fs_hostname[64];/* Hostname of machine of image */ |
| + | char fs_netaddr[32]; /* Network address */ |
| + | __u32 fs_netaddr_type;/* 0 = IPV4, 1 = IPV6, etc. */ |
| + | __u32 fs_device; /* Device number of image */ |
| + | char fs_device_name[64]; /* Device name */ |
| + | char fs_uuid[16]; /* UUID of filesystem */ |
| + | __u32 fs_blocksize; /* Block size of the filesystem */ |
| + | __u32 fs_reserved[8]; |
| + | |
| + | __u32 image_device; /* Device number of image file */ |
| + | __u32 image_inode; /* Inode number of image file */ |
| + | __u32 image_time; /* Time of image creation */ |
| + | __u32 image_reserved[8]; |
| + | |
| + | __u32 offset_super; /* Byte offset of the sb and descriptors */ |
| + | __u32 offset_inode; /* Byte offset of the inode table */ |
| + | __u32 offset_inodemap; /* Byte offset of the inode bitmaps */ |
| + | __u32 offset_blockmap; /* Byte offset of the inode bitmaps */ |
| + | __u32 offset_reserved[8]; |
| + | }; |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| @@ -0,0 +1,126 @@ | |
| + | /* |
| + | * expand.c --- expand an ext2fs directory |
| + | * |
| + | * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | struct expand_dir_struct { |
| + | int done; |
| + | int newblocks; |
| + | errcode_t err; |
| + | }; |
| + | |
| + | static int expand_dir_proc(ext2_filsys fs, |
| + | blk_t *blocknr, |
| + | e2_blkcnt_t blockcnt, |
| + | blk_t ref_block EXT2FS_ATTR((unused)), |
| + | int ref_offset EXT2FS_ATTR((unused)), |
| + | void *priv_data) |
| + | { |
| + | struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data; |
| + | blk_t new_blk; |
| + | static blk_t last_blk = 0; |
| + | char *block; |
| + | errcode_t retval; |
| + | |
| + | if (*blocknr) { |
| + | last_blk = *blocknr; |
| + | return 0; |
| + | } |
| + | retval = ext2fs_new_block(fs, last_blk, 0, &new_blk); |
| + | if (retval) { |
| + | es->err = retval; |
| + | return BLOCK_ABORT; |
| + | } |
| + | if (blockcnt > 0) { |
| + | retval = ext2fs_new_dir_block(fs, 0, 0, &block); |
| + | if (retval) { |
| + | es->err = retval; |
| + | return BLOCK_ABORT; |
| + | } |
| + | es->done = 1; |
| + | retval = ext2fs_write_dir_block(fs, new_blk, block); |
| + | } else { |
| + | retval = ext2fs_get_mem(fs->blocksize, &block); |
| + | if (retval) { |
| + | es->err = retval; |
| + | return BLOCK_ABORT; |
| + | } |
| + | memset(block, 0, fs->blocksize); |
| + | retval = io_channel_write_blk(fs->io, new_blk, 1, block); |
| + | } |
| + | if (retval) { |
| + | es->err = retval; |
| + | return BLOCK_ABORT; |
| + | } |
| + | ext2fs_free_mem(&block); |
| + | *blocknr = new_blk; |
| + | ext2fs_block_alloc_stats(fs, new_blk, +1); |
| + | es->newblocks++; |
| + | |
| + | if (es->done) |
| + | return (BLOCK_CHANGED | BLOCK_ABORT); |
| + | else |
| + | return BLOCK_CHANGED; |
| + | } |
| + | |
| + | errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir) |
| + | { |
| + | errcode_t retval; |
| + | struct expand_dir_struct es; |
| + | struct ext2_inode inode; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | if (!(fs->flags & EXT2_FLAG_RW)) |
| + | return EXT2_ET_RO_FILSYS; |
| + | |
| + | if (!fs->block_map) |
| + | return EXT2_ET_NO_BLOCK_BITMAP; |
| + | |
| + | retval = ext2fs_check_directory(fs, dir); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | es.done = 0; |
| + | es.err = 0; |
| + | es.newblocks = 0; |
| + | |
| + | retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND, |
| + | 0, expand_dir_proc, &es); |
| + | |
| + | if (es.err) |
| + | return es.err; |
| + | if (!es.done) |
| + | return EXT2_ET_EXPAND_DIR_ERR; |
| + | |
| + | /* |
| + | * Update the size and block count fields in the inode. |
| + | */ |
| + | retval = ext2fs_read_inode(fs, dir, &inode); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | inode.i_size += fs->blocksize; |
| + | inode.i_blocks += (fs->blocksize / 512) * es.newblocks; |
| + | |
| + | retval = ext2fs_write_inode(fs, dir, &inode); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | return 0; |
| + | } |
| @@ -0,0 +1,117 @@ | |
| + | /* |
| + | * ext2_err.h: |
| + | * This file is automatically generated; please do not edit it. |
| + | */ |
| + | |
| + | #include <et/com_err.h> |
| + | |
| + | #define EXT2_ET_BASE (2133571328L) |
| + | #define EXT2_ET_MAGIC_EXT2FS_FILSYS (2133571329L) |
| + | #define EXT2_ET_MAGIC_BADBLOCKS_LIST (2133571330L) |
| + | #define EXT2_ET_MAGIC_BADBLOCKS_ITERATE (2133571331L) |
| + | #define EXT2_ET_MAGIC_INODE_SCAN (2133571332L) |
| + | #define EXT2_ET_MAGIC_IO_CHANNEL (2133571333L) |
| + | #define EXT2_ET_MAGIC_UNIX_IO_CHANNEL (2133571334L) |
| + | #define EXT2_ET_MAGIC_IO_MANAGER (2133571335L) |
| + | #define EXT2_ET_MAGIC_BLOCK_BITMAP (2133571336L) |
| + | #define EXT2_ET_MAGIC_INODE_BITMAP (2133571337L) |
| + | #define EXT2_ET_MAGIC_GENERIC_BITMAP (2133571338L) |
| + | #define EXT2_ET_MAGIC_TEST_IO_CHANNEL (2133571339L) |
| + | #define EXT2_ET_MAGIC_DBLIST (2133571340L) |
| + | #define EXT2_ET_MAGIC_ICOUNT (2133571341L) |
| + | #define EXT2_ET_MAGIC_PQ_IO_CHANNEL (2133571342L) |
| + | #define EXT2_ET_MAGIC_EXT2_FILE (2133571343L) |
| + | #define EXT2_ET_MAGIC_E2IMAGE (2133571344L) |
| + | #define EXT2_ET_MAGIC_INODE_IO_CHANNEL (2133571345L) |
| + | #define EXT2_ET_MAGIC_RESERVED_9 (2133571346L) |
| + | #define EXT2_ET_BAD_MAGIC (2133571347L) |
| + | #define EXT2_ET_REV_TOO_HIGH (2133571348L) |
| + | #define EXT2_ET_RO_FILSYS (2133571349L) |
| + | #define EXT2_ET_GDESC_READ (2133571350L) |
| + | #define EXT2_ET_GDESC_WRITE (2133571351L) |
| + | #define EXT2_ET_GDESC_BAD_BLOCK_MAP (2133571352L) |
| + | #define EXT2_ET_GDESC_BAD_INODE_MAP (2133571353L) |
| + | #define EXT2_ET_GDESC_BAD_INODE_TABLE (2133571354L) |
| + | #define EXT2_ET_INODE_BITMAP_WRITE (2133571355L) |
| + | #define EXT2_ET_INODE_BITMAP_READ (2133571356L) |
| + | #define EXT2_ET_BLOCK_BITMAP_WRITE (2133571357L) |
| + | #define EXT2_ET_BLOCK_BITMAP_READ (2133571358L) |
| + | #define EXT2_ET_INODE_TABLE_WRITE (2133571359L) |
| + | #define EXT2_ET_INODE_TABLE_READ (2133571360L) |
| + | #define EXT2_ET_NEXT_INODE_READ (2133571361L) |
| + | #define EXT2_ET_UNEXPECTED_BLOCK_SIZE (2133571362L) |
| + | #define EXT2_ET_DIR_CORRUPTED (2133571363L) |
| + | #define EXT2_ET_SHORT_READ (2133571364L) |
| + | #define EXT2_ET_SHORT_WRITE (2133571365L) |
| + | #define EXT2_ET_DIR_NO_SPACE (2133571366L) |
| + | #define EXT2_ET_NO_INODE_BITMAP (2133571367L) |
| + | #define EXT2_ET_NO_BLOCK_BITMAP (2133571368L) |
| + | #define EXT2_ET_BAD_INODE_NUM (2133571369L) |
| + | #define EXT2_ET_BAD_BLOCK_NUM (2133571370L) |
| + | #define EXT2_ET_EXPAND_DIR_ERR (2133571371L) |
| + | #define EXT2_ET_TOOSMALL (2133571372L) |
| + | #define EXT2_ET_BAD_BLOCK_MARK (2133571373L) |
| + | #define EXT2_ET_BAD_BLOCK_UNMARK (2133571374L) |
| + | #define EXT2_ET_BAD_BLOCK_TEST (2133571375L) |
| + | #define EXT2_ET_BAD_INODE_MARK (2133571376L) |
| + | #define EXT2_ET_BAD_INODE_UNMARK (2133571377L) |
| + | #define EXT2_ET_BAD_INODE_TEST (2133571378L) |
| + | #define EXT2_ET_FUDGE_BLOCK_BITMAP_END (2133571379L) |
| + | #define EXT2_ET_FUDGE_INODE_BITMAP_END (2133571380L) |
| + | #define EXT2_ET_BAD_IND_BLOCK (2133571381L) |
| + | #define EXT2_ET_BAD_DIND_BLOCK (2133571382L) |
| + | #define EXT2_ET_BAD_TIND_BLOCK (2133571383L) |
| + | #define EXT2_ET_NEQ_BLOCK_BITMAP (2133571384L) |
| + | #define EXT2_ET_NEQ_INODE_BITMAP (2133571385L) |
| + | #define EXT2_ET_BAD_DEVICE_NAME (2133571386L) |
| + | #define EXT2_ET_MISSING_INODE_TABLE (2133571387L) |
| + | #define EXT2_ET_CORRUPT_SUPERBLOCK (2133571388L) |
| + | #define EXT2_ET_BAD_GENERIC_MARK (2133571389L) |
| + | #define EXT2_ET_BAD_GENERIC_UNMARK (2133571390L) |
| + | #define EXT2_ET_BAD_GENERIC_TEST (2133571391L) |
| + | #define EXT2_ET_SYMLINK_LOOP (2133571392L) |
| + | #define EXT2_ET_CALLBACK_NOTHANDLED (2133571393L) |
| + | #define EXT2_ET_BAD_BLOCK_IN_INODE_TABLE (2133571394L) |
| + | #define EXT2_ET_UNSUPP_FEATURE (2133571395L) |
| + | #define EXT2_ET_RO_UNSUPP_FEATURE (2133571396L) |
| + | #define EXT2_ET_LLSEEK_FAILED (2133571397L) |
| + | #define EXT2_ET_NO_MEMORY (2133571398L) |
| + | #define EXT2_ET_INVALID_ARGUMENT (2133571399L) |
| + | #define EXT2_ET_BLOCK_ALLOC_FAIL (2133571400L) |
| + | #define EXT2_ET_INODE_ALLOC_FAIL (2133571401L) |
| + | #define EXT2_ET_NO_DIRECTORY (2133571402L) |
| + | #define EXT2_ET_TOO_MANY_REFS (2133571403L) |
| + | #define EXT2_ET_FILE_NOT_FOUND (2133571404L) |
| + | #define EXT2_ET_FILE_RO (2133571405L) |
| + | #define EXT2_ET_DB_NOT_FOUND (2133571406L) |
| + | #define EXT2_ET_DIR_EXISTS (2133571407L) |
| + | #define EXT2_ET_UNIMPLEMENTED (2133571408L) |
| + | #define EXT2_ET_CANCEL_REQUESTED (2133571409L) |
| + | #define EXT2_ET_FILE_TOO_BIG (2133571410L) |
| + | #define EXT2_ET_JOURNAL_NOT_BLOCK (2133571411L) |
| + | #define EXT2_ET_NO_JOURNAL_SB (2133571412L) |
| + | #define EXT2_ET_JOURNAL_TOO_SMALL (2133571413L) |
| + | #define EXT2_ET_JOURNAL_UNSUPP_VERSION (2133571414L) |
| + | #define EXT2_ET_LOAD_EXT_JOURNAL (2133571415L) |
| + | #define EXT2_ET_NO_JOURNAL (2133571416L) |
| + | #define EXT2_ET_DIRHASH_UNSUPP (2133571417L) |
| + | #define EXT2_ET_BAD_EA_BLOCK_NUM (2133571418L) |
| + | #define EXT2_ET_TOO_MANY_INODES (2133571419L) |
| + | #define EXT2_ET_NOT_IMAGE_FILE (2133571420L) |
| + | #define EXT2_ET_RES_GDT_BLOCKS (2133571421L) |
| + | #define EXT2_ET_RESIZE_INODE_CORRUPT (2133571422L) |
| + | #define EXT2_ET_SET_BMAP_NO_IND (2133571423L) |
| + | |
| + | #if 0 |
| + | extern const struct error_table et_ext2_error_table; |
| + | extern void initialize_ext2_error_table(void); |
| + | |
| + | /* For compatibility with Heimdal */ |
| + | extern void initialize_ext2_error_table_r(struct et_list **list); |
| + | |
| + | #define ERROR_TABLE_BASE_ext2 (2133571328L) |
| + | |
| + | /* for compatibility with older versions... */ |
| + | #define init_ext2_err_tbl initialize_ext2_error_table |
| + | #define ext2_err_base ERROR_TABLE_BASE_ext2 |
| + | #endif |
| @@ -0,0 +1,69 @@ | |
| + | /* |
| + | File: linux/ext2_ext_attr.h |
| + | |
| + | On-disk format of extended attributes for the ext2 filesystem. |
| + | |
| + | (C) 2000 Andreas Gruenbacher, <[email protected]> |
| + | */ |
| + | |
| + | /* Magic value in attribute blocks */ |
| + | #define EXT2_EXT_ATTR_MAGIC_v1 0xEA010000 |
| + | #define EXT2_EXT_ATTR_MAGIC 0xEA020000 |
| + | |
| + | /* Maximum number of references to one attribute block */ |
| + | #define EXT2_EXT_ATTR_REFCOUNT_MAX 1024 |
| + | |
| + | struct ext2_ext_attr_header { |
| + | __u32 h_magic; /* magic number for identification */ |
| + | __u32 h_refcount; /* reference count */ |
| + | __u32 h_blocks; /* number of disk blocks used */ |
| + | __u32 h_hash; /* hash value of all attributes */ |
| + | __u32 h_reserved[4]; /* zero right now */ |
| + | }; |
| + | |
| + | struct ext2_ext_attr_entry { |
| + | __u8 e_name_len; /* length of name */ |
| + | __u8 e_name_index; /* attribute name index */ |
| + | __u16 e_value_offs; /* offset in disk block of value */ |
| + | __u32 e_value_block; /* disk block attribute is stored on (n/i) */ |
| + | __u32 e_value_size; /* size of attribute value */ |
| + | __u32 e_hash; /* hash value of name and value */ |
| + | #if 0 |
| + | char e_name[0]; /* attribute name */ |
| + | #endif |
| + | }; |
| + | |
| + | #define EXT2_EXT_ATTR_PAD_BITS 2 |
| + | #define EXT2_EXT_ATTR_PAD (1<<EXT2_EXT_ATTR_PAD_BITS) |
| + | #define EXT2_EXT_ATTR_ROUND (EXT2_EXT_ATTR_PAD-1) |
| + | #define EXT2_EXT_ATTR_LEN(name_len) \ |
| + | (((name_len) + EXT2_EXT_ATTR_ROUND + \ |
| + | sizeof(struct ext2_ext_attr_entry)) & ~EXT2_EXT_ATTR_ROUND) |
| + | #define EXT2_EXT_ATTR_NEXT(entry) \ |
| + | ( (struct ext2_ext_attr_entry *)( \ |
| + | (char *)(entry) + EXT2_EXT_ATTR_LEN((entry)->e_name_len)) ) |
| + | #define EXT2_EXT_ATTR_SIZE(size) \ |
| + | (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND) |
| + | #define EXT2_EXT_IS_LAST_ENTRY(entry) (*((__u32 *)(entry)) == 0UL) |
| + | #define EXT2_EXT_ATTR_NAME(entry) \ |
| + | (((char *) (entry)) + sizeof(struct ext2_ext_attr_entry)) |
| + | #define EXT2_XATTR_LEN(name_len) \ |
| + | (((name_len) + EXT2_EXT_ATTR_ROUND + \ |
| + | sizeof(struct ext2_xattr_entry)) & ~EXT2_EXT_ATTR_ROUND) |
| + | #define EXT2_XATTR_SIZE(size) \ |
| + | (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND) |
| + | |
| + | #ifdef __KERNEL__ |
| + | # ifdef CONFIG_EXT2_FS_EXT_ATTR |
| + | extern int ext2_get_ext_attr(struct inode *, const char *, char *, size_t, int); |
| + | extern int ext2_set_ext_attr(struct inode *, const char *, char *, size_t, int); |
| + | extern void ext2_ext_attr_free_inode(struct inode *inode); |
| + | extern void ext2_ext_attr_put_super(struct super_block *sb); |
| + | extern int ext2_ext_attr_init(void); |
| + | extern void ext2_ext_attr_done(void); |
| + | # else |
| + | # define ext2_get_ext_attr NULL |
| + | # define ext2_set_ext_attr NULL |
| + | # endif |
| + | #endif /* __KERNEL__ */ |
| + | |
| @@ -0,0 +1,644 @@ | |
| + | /* |
| + | * linux/include/linux/ext2_fs.h |
| + | * |
| + | * Copyright (C) 1992, 1993, 1994, 1995 |
| + | * Remy Card ([email protected]) |
| + | * Laboratoire MASI - Institut Blaise Pascal |
| + | * Universite Pierre et Marie Curie (Paris VI) |
| + | * |
| + | * from |
| + | * |
| + | * linux/include/linux/minix_fs.h |
| + | * |
| + | * Copyright (C) 1991, 1992 Linus Torvalds |
| + | */ |
| + | |
| + | #ifndef _LINUX_EXT2_FS_H |
| + | #define _LINUX_EXT2_FS_H |
| + | |
| + | #include <ext2fs/ext2_types.h> /* Changed from linux/types.h */ |
| + | |
| + | /* |
| + | * The second extended filesystem constants/structures |
| + | */ |
| + | |
| + | /* |
| + | * Define EXT2FS_DEBUG to produce debug messages |
| + | */ |
| + | #undef EXT2FS_DEBUG |
| + | |
| + | /* |
| + | * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files |
| + | */ |
| + | #define EXT2_PREALLOCATE |
| + | #define EXT2_DEFAULT_PREALLOC_BLOCKS 8 |
| + | |
| + | /* |
| + | * The second extended file system version |
| + | */ |
| + | #define EXT2FS_DATE "95/08/09" |
| + | #define EXT2FS_VERSION "0.5b" |
| + | |
| + | /* |
| + | * Special inode numbers |
| + | */ |
| + | #define EXT2_BAD_INO 1 /* Bad blocks inode */ |
| + | #define EXT2_ROOT_INO 2 /* Root inode */ |
| + | #define EXT2_ACL_IDX_INO 3 /* ACL inode */ |
| + | #define EXT2_ACL_DATA_INO 4 /* ACL inode */ |
| + | #define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ |
| + | #define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ |
| + | #define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */ |
| + | #define EXT2_JOURNAL_INO 8 /* Journal inode */ |
| + | |
| + | /* First non-reserved inode for old ext2 filesystems */ |
| + | #define EXT2_GOOD_OLD_FIRST_INO 11 |
| + | |
| + | /* |
| + | * The second extended file system magic number |
| + | */ |
| + | #define EXT2_SUPER_MAGIC 0xEF53 |
| + | |
| + | #ifdef __KERNEL__ |
| + | #define EXT2_SB(sb) (&((sb)->u.ext2_sb)) |
| + | #else |
| + | /* Assume that user mode programs are passing in an ext2fs superblock, not |
| + | * a kernel struct super_block. This will allow us to call the feature-test |
| + | * macros from user land. */ |
| + | #define EXT2_SB(sb) (sb) |
| + | #endif |
| + | |
| + | /* |
| + | * Maximal count of links to a file |
| + | */ |
| + | #define EXT2_LINK_MAX 32000 |
| + | |
| + | /* |
| + | * Macro-instructions used to manage several block sizes |
| + | */ |
| + | #define EXT2_MIN_BLOCK_LOG_SIZE 10 /* 1024 */ |
| + | #define EXT2_MAX_BLOCK_LOG_SIZE 16 /* 65536 */ |
| + | #define EXT2_MIN_BLOCK_SIZE (1 << EXT2_MIN_BLOCK_LOG_SIZE) |
| + | #define EXT2_MAX_BLOCK_SIZE (1 << EXT2_MAX_BLOCK_LOG_SIZE) |
| + | #ifdef __KERNEL__ |
| + | #define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize) |
| + | #define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits) |
| + | #define EXT2_ADDR_PER_BLOCK_BITS(s) (EXT2_SB(s)->addr_per_block_bits) |
| + | #define EXT2_INODE_SIZE(s) (EXT2_SB(s)->s_inode_size) |
| + | #define EXT2_FIRST_INO(s) (EXT2_SB(s)->s_first_ino) |
| + | #else |
| + | #define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size) |
| + | #define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) |
| + | #define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ |
| + | EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size) |
| + | #define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ |
| + | EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino) |
| + | #endif |
| + | #define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(__u32)) |
| + | |
| + | /* |
| + | * Macro-instructions used to manage fragments |
| + | */ |
| + | #define EXT2_MIN_FRAG_SIZE EXT2_MIN_BLOCK_SIZE |
| + | #define EXT2_MAX_FRAG_SIZE EXT2_MAX_BLOCK_SIZE |
| + | #define EXT2_MIN_FRAG_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE |
| + | #ifdef __KERNEL__ |
| + | # define EXT2_FRAG_SIZE(s) (EXT2_SB(s)->s_frag_size) |
| + | # define EXT2_FRAGS_PER_BLOCK(s) (EXT2_SB(s)->s_frags_per_block) |
| + | #else |
| + | # define EXT2_FRAG_SIZE(s) (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size) |
| + | # define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s)) |
| + | #endif |
| + | |
| + | /* |
| + | * ACL structures |
| + | */ |
| + | struct ext2_acl_header /* Header of Access Control Lists */ |
| + | { |
| + | __u32 aclh_size; |
| + | __u32 aclh_file_count; |
| + | __u32 aclh_acle_count; |
| + | __u32 aclh_first_acle; |
| + | }; |
| + | |
| + | struct ext2_acl_entry /* Access Control List Entry */ |
| + | { |
| + | __u32 acle_size; |
| + | __u16 acle_perms; /* Access permissions */ |
| + | __u16 acle_type; /* Type of entry */ |
| + | __u16 acle_tag; /* User or group identity */ |
| + | __u16 acle_pad1; |
| + | __u32 acle_next; /* Pointer on next entry for the */ |
| + | /* same inode or on next free entry */ |
| + | }; |
| + | |
| + | /* |
| + | * Structure of a blocks group descriptor |
| + | */ |
| + | struct ext2_group_desc |
| + | { |
| + | __u32 bg_block_bitmap; /* Blocks bitmap block */ |
| + | __u32 bg_inode_bitmap; /* Inodes bitmap block */ |
| + | __u32 bg_inode_table; /* Inodes table block */ |
| + | __u16 bg_free_blocks_count; /* Free blocks count */ |
| + | __u16 bg_free_inodes_count; /* Free inodes count */ |
| + | __u16 bg_used_dirs_count; /* Directories count */ |
| + | __u16 bg_pad; |
| + | __u32 bg_reserved[3]; |
| + | }; |
| + | |
| + | /* |
| + | * Data structures used by the directory indexing feature |
| + | * |
| + | * Note: all of the multibyte integer fields are little endian. |
| + | */ |
| + | |
| + | /* |
| + | * Note: dx_root_info is laid out so that if it should somehow get |
| + | * overlaid by a dirent the two low bits of the hash version will be |
| + | * zero. Therefore, the hash version mod 4 should never be 0. |
| + | * Sincerely, the paranoia department. |
| + | */ |
| + | struct ext2_dx_root_info { |
| + | __u32 reserved_zero; |
| + | __u8 hash_version; /* 0 now, 1 at release */ |
| + | __u8 info_length; /* 8 */ |
| + | __u8 indirect_levels; |
| + | __u8 unused_flags; |
| + | }; |
| + | |
| + | #define EXT2_HASH_LEGACY 0 |
| + | #define EXT2_HASH_HALF_MD4 1 |
| + | #define EXT2_HASH_TEA 2 |
| + | |
| + | #define EXT2_HASH_FLAG_INCOMPAT 0x1 |
| + | |
| + | struct ext2_dx_entry { |
| + | __u32 hash; |
| + | __u32 block; |
| + | }; |
| + | |
| + | struct ext2_dx_countlimit { |
| + | __u16 limit; |
| + | __u16 count; |
| + | }; |
| + | |
| + | |
| + | /* |
| + | * Macro-instructions used to manage group descriptors |
| + | */ |
| + | #define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group) |
| + | #define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group) |
| + | #define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s)) |
| + | /* limits imposed by 16-bit value gd_free_{blocks,inode}_count */ |
| + | #define EXT2_MAX_BLOCKS_PER_GROUP(s) ((1 << 16) - 8) |
| + | #define EXT2_MAX_INODES_PER_GROUP(s) ((1 << 16) - EXT2_INODES_PER_BLOCK(s)) |
| + | #ifdef __KERNEL__ |
| + | #define EXT2_DESC_PER_BLOCK(s) (EXT2_SB(s)->s_desc_per_block) |
| + | #define EXT2_DESC_PER_BLOCK_BITS(s) (EXT2_SB(s)->s_desc_per_block_bits) |
| + | #else |
| + | #define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc)) |
| + | #endif |
| + | |
| + | /* |
| + | * Constants relative to the data blocks |
| + | */ |
| + | #define EXT2_NDIR_BLOCKS 12 |
| + | #define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS |
| + | #define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) |
| + | #define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) |
| + | #define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) |
| + | |
| + | /* |
| + | * Inode flags |
| + | */ |
| + | #define EXT2_SECRM_FL 0x00000001 /* Secure deletion */ |
| + | #define EXT2_UNRM_FL 0x00000002 /* Undelete */ |
| + | #define EXT2_COMPR_FL 0x00000004 /* Compress file */ |
| + | #define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */ |
| + | #define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */ |
| + | #define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */ |
| + | #define EXT2_NODUMP_FL 0x00000040 /* do not dump file */ |
| + | #define EXT2_NOATIME_FL 0x00000080 /* do not update atime */ |
| + | /* Reserved for compression usage... */ |
| + | #define EXT2_DIRTY_FL 0x00000100 |
| + | #define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */ |
| + | #define EXT2_NOCOMPR_FL 0x00000400 /* Access raw compressed data */ |
| + | #define EXT2_ECOMPR_FL 0x00000800 /* Compression error */ |
| + | /* End compression flags --- maybe not all used */ |
| + | #define EXT2_BTREE_FL 0x00001000 /* btree format dir */ |
| + | #define EXT2_INDEX_FL 0x00001000 /* hash-indexed directory */ |
| + | #define EXT2_IMAGIC_FL 0x00002000 |
| + | #define EXT3_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */ |
| + | #define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */ |
| + | #define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */ |
| + | #define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ |
| + | #define EXT3_EXTENTS_FL 0x00080000 /* Inode uses extents */ |
| + | #define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ |
| + | |
| + | #define EXT2_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */ |
| + | #define EXT2_FL_USER_MODIFIABLE 0x000080FF /* User modifiable flags */ |
| + | |
| + | /* |
| + | * ioctl commands |
| + | */ |
| + | #define EXT2_IOC_GETFLAGS _IOR('f', 1, long) |
| + | #define EXT2_IOC_SETFLAGS _IOW('f', 2, long) |
| + | #define EXT2_IOC_GETVERSION _IOR('v', 1, long) |
| + | #define EXT2_IOC_SETVERSION _IOW('v', 2, long) |
| + | |
| + | /* |
| + | * Structure of an inode on the disk |
| + | */ |
| + | struct ext2_inode { |
| + | __u16 i_mode; /* File mode */ |
| + | __u16 i_uid; /* Low 16 bits of Owner Uid */ |
| + | __u32 i_size; /* Size in bytes */ |
| + | __u32 i_atime; /* Access time */ |
| + | __u32 i_ctime; /* Creation time */ |
| + | __u32 i_mtime; /* Modification time */ |
| + | __u32 i_dtime; /* Deletion Time */ |
| + | __u16 i_gid; /* Low 16 bits of Group Id */ |
| + | __u16 i_links_count; /* Links count */ |
| + | __u32 i_blocks; /* Blocks count */ |
| + | __u32 i_flags; /* File flags */ |
| + | union { |
| + | struct { |
| + | __u32 l_i_reserved1; |
| + | } linux1; |
| + | struct { |
| + | __u32 h_i_translator; |
| + | } hurd1; |
| + | struct { |
| + | __u32 m_i_reserved1; |
| + | } masix1; |
| + | } osd1; /* OS dependent 1 */ |
| + | __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ |
| + | __u32 i_generation; /* File version (for NFS) */ |
| + | __u32 i_file_acl; /* File ACL */ |
| + | __u32 i_dir_acl; /* Directory ACL */ |
| + | __u32 i_faddr; /* Fragment address */ |
| + | union { |
| + | struct { |
| + | __u8 l_i_frag; /* Fragment number */ |
| + | __u8 l_i_fsize; /* Fragment size */ |
| + | __u16 i_pad1; |
| + | __u16 l_i_uid_high; /* these 2 fields */ |
| + | __u16 l_i_gid_high; /* were reserved2[0] */ |
| + | __u32 l_i_reserved2; |
| + | } linux2; |
| + | struct { |
| + | __u8 h_i_frag; /* Fragment number */ |
| + | __u8 h_i_fsize; /* Fragment size */ |
| + | __u16 h_i_mode_high; |
| + | __u16 h_i_uid_high; |
| + | __u16 h_i_gid_high; |
| + | __u32 h_i_author; |
| + | } hurd2; |
| + | struct { |
| + | __u8 m_i_frag; /* Fragment number */ |
| + | __u8 m_i_fsize; /* Fragment size */ |
| + | __u16 m_pad1; |
| + | __u32 m_i_reserved2[2]; |
| + | } masix2; |
| + | } osd2; /* OS dependent 2 */ |
| + | }; |
| + | |
| + | /* |
| + | * Permanent part of an large inode on the disk |
| + | */ |
| + | struct ext2_inode_large { |
| + | __u16 i_mode; /* File mode */ |
| + | __u16 i_uid; /* Low 16 bits of Owner Uid */ |
| + | __u32 i_size; /* Size in bytes */ |
| + | __u32 i_atime; /* Access time */ |
| + | __u32 i_ctime; /* Creation time */ |
| + | __u32 i_mtime; /* Modification time */ |
| + | __u32 i_dtime; /* Deletion Time */ |
| + | __u16 i_gid; /* Low 16 bits of Group Id */ |
| + | __u16 i_links_count; /* Links count */ |
| + | __u32 i_blocks; /* Blocks count */ |
| + | __u32 i_flags; /* File flags */ |
| + | union { |
| + | struct { |
| + | __u32 l_i_reserved1; |
| + | } linux1; |
| + | struct { |
| + | __u32 h_i_translator; |
| + | } hurd1; |
| + | struct { |
| + | __u32 m_i_reserved1; |
| + | } masix1; |
| + | } osd1; /* OS dependent 1 */ |
| + | __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ |
| + | __u32 i_generation; /* File version (for NFS) */ |
| + | __u32 i_file_acl; /* File ACL */ |
| + | __u32 i_dir_acl; /* Directory ACL */ |
| + | __u32 i_faddr; /* Fragment address */ |
| + | union { |
| + | struct { |
| + | __u8 l_i_frag; /* Fragment number */ |
| + | __u8 l_i_fsize; /* Fragment size */ |
| + | __u16 i_pad1; |
| + | __u16 l_i_uid_high; /* these 2 fields */ |
| + | __u16 l_i_gid_high; /* were reserved2[0] */ |
| + | __u32 l_i_reserved2; |
| + | } linux2; |
| + | struct { |
| + | __u8 h_i_frag; /* Fragment number */ |
| + | __u8 h_i_fsize; /* Fragment size */ |
| + | __u16 h_i_mode_high; |
| + | __u16 h_i_uid_high; |
| + | __u16 h_i_gid_high; |
| + | __u32 h_i_author; |
| + | } hurd2; |
| + | struct { |
| + | __u8 m_i_frag; /* Fragment number */ |
| + | __u8 m_i_fsize; /* Fragment size */ |
| + | __u16 m_pad1; |
| + | __u32 m_i_reserved2[2]; |
| + | } masix2; |
| + | } osd2; /* OS dependent 2 */ |
| + | __u16 i_extra_isize; |
| + | __u16 i_pad1; |
| + | }; |
| + | |
| + | #define i_size_high i_dir_acl |
| + | |
| + | #if defined(__KERNEL__) || defined(__linux__) |
| + | #define i_reserved1 osd1.linux1.l_i_reserved1 |
| + | #define i_frag osd2.linux2.l_i_frag |
| + | #define i_fsize osd2.linux2.l_i_fsize |
| + | #define i_uid_low i_uid |
| + | #define i_gid_low i_gid |
| + | #define i_uid_high osd2.linux2.l_i_uid_high |
| + | #define i_gid_high osd2.linux2.l_i_gid_high |
| + | #define i_reserved2 osd2.linux2.l_i_reserved2 |
| + | |
| + | #else |
| + | #if defined(__GNU__) |
| + | |
| + | #define i_translator osd1.hurd1.h_i_translator |
| + | #define i_frag osd2.hurd2.h_i_frag; |
| + | #define i_fsize osd2.hurd2.h_i_fsize; |
| + | #define i_uid_high osd2.hurd2.h_i_uid_high |
| + | #define i_gid_high osd2.hurd2.h_i_gid_high |
| + | #define i_author osd2.hurd2.h_i_author |
| + | |
| + | #else |
| + | #if defined(__masix__) |
| + | |
| + | #define i_reserved1 osd1.masix1.m_i_reserved1 |
| + | #define i_frag osd2.masix2.m_i_frag |
| + | #define i_fsize osd2.masix2.m_i_fsize |
| + | #define i_reserved2 osd2.masix2.m_i_reserved2 |
| + | |
| + | #endif /* __masix__ */ |
| + | #endif /* __GNU__ */ |
| + | #endif /* defined(__KERNEL__) || defined(__linux__) */ |
| + | |
| + | /* |
| + | * File system states |
| + | */ |
| + | #define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */ |
| + | #define EXT2_ERROR_FS 0x0002 /* Errors detected */ |
| + | |
| + | /* |
| + | * Mount flags |
| + | */ |
| + | #define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */ |
| + | #define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */ |
| + | #define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */ |
| + | #define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */ |
| + | #define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ |
| + | #define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ |
| + | #define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ |
| + | #define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */ |
| + | |
| + | #define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt |
| + | #define set_opt(o, opt) o |= EXT2_MOUNT_##opt |
| + | #define test_opt(sb, opt) (EXT2_SB(sb)->s_mount_opt & \ |
| + | EXT2_MOUNT_##opt) |
| + | /* |
| + | * Maximal mount counts between two filesystem checks |
| + | */ |
| + | #define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */ |
| + | #define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */ |
| + | |
| + | /* |
| + | * Behaviour when detecting errors |
| + | */ |
| + | #define EXT2_ERRORS_CONTINUE 1 /* Continue execution */ |
| + | #define EXT2_ERRORS_RO 2 /* Remount fs read-only */ |
| + | #define EXT2_ERRORS_PANIC 3 /* Panic */ |
| + | #define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE |
| + | |
| + | /* |
| + | * Structure of the super block |
| + | */ |
| + | struct ext2_super_block { |
| + | __u32 s_inodes_count; /* Inodes count */ |
| + | __u32 s_blocks_count; /* Blocks count */ |
| + | __u32 s_r_blocks_count; /* Reserved blocks count */ |
| + | __u32 s_free_blocks_count; /* Free blocks count */ |
| + | __u32 s_free_inodes_count; /* Free inodes count */ |
| + | __u32 s_first_data_block; /* First Data Block */ |
| + | __u32 s_log_block_size; /* Block size */ |
| + | __s32 s_log_frag_size; /* Fragment size */ |
| + | __u32 s_blocks_per_group; /* # Blocks per group */ |
| + | __u32 s_frags_per_group; /* # Fragments per group */ |
| + | __u32 s_inodes_per_group; /* # Inodes per group */ |
| + | __u32 s_mtime; /* Mount time */ |
| + | __u32 s_wtime; /* Write time */ |
| + | __u16 s_mnt_count; /* Mount count */ |
| + | __s16 s_max_mnt_count; /* Maximal mount count */ |
| + | __u16 s_magic; /* Magic signature */ |
| + | __u16 s_state; /* File system state */ |
| + | __u16 s_errors; /* Behaviour when detecting errors */ |
| + | __u16 s_minor_rev_level; /* minor revision level */ |
| + | __u32 s_lastcheck; /* time of last check */ |
| + | __u32 s_checkinterval; /* max. time between checks */ |
| + | __u32 s_creator_os; /* OS */ |
| + | __u32 s_rev_level; /* Revision level */ |
| + | __u16 s_def_resuid; /* Default uid for reserved blocks */ |
| + | __u16 s_def_resgid; /* Default gid for reserved blocks */ |
| + | /* |
| + | * These fields are for EXT2_DYNAMIC_REV superblocks only. |
| + | * |
| + | * Note: the difference between the compatible feature set and |
| + | * the incompatible feature set is that if there is a bit set |
| + | * in the incompatible feature set that the kernel doesn't |
| + | * know about, it should refuse to mount the filesystem. |
| + | * |
| + | * e2fsck's requirements are more strict; if it doesn't know |
| + | * about a feature in either the compatible or incompatible |
| + | * feature set, it must abort and not try to meddle with |
| + | * things it doesn't understand... |
| + | */ |
| + | __u32 s_first_ino; /* First non-reserved inode */ |
| + | __u16 s_inode_size; /* size of inode structure */ |
| + | __u16 s_block_group_nr; /* block group # of this superblock */ |
| + | __u32 s_feature_compat; /* compatible feature set */ |
| + | __u32 s_feature_incompat; /* incompatible feature set */ |
| + | __u32 s_feature_ro_compat; /* readonly-compatible feature set */ |
| + | __u8 s_uuid[16]; /* 128-bit uuid for volume */ |
| + | char s_volume_name[16]; /* volume name */ |
| + | char s_last_mounted[64]; /* directory where last mounted */ |
| + | __u32 s_algorithm_usage_bitmap; /* For compression */ |
| + | /* |
| + | * Performance hints. Directory preallocation should only |
| + | * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on. |
| + | */ |
| + | __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ |
| + | __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ |
| + | __u16 s_reserved_gdt_blocks; /* Per group table for online growth */ |
| + | /* |
| + | * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set. |
| + | */ |
| + | __u8 s_journal_uuid[16]; /* uuid of journal superblock */ |
| + | __u32 s_journal_inum; /* inode number of journal file */ |
| + | __u32 s_journal_dev; /* device number of journal file */ |
| + | __u32 s_last_orphan; /* start of list of inodes to delete */ |
| + | __u32 s_hash_seed[4]; /* HTREE hash seed */ |
| + | __u8 s_def_hash_version; /* Default hash version to use */ |
| + | __u8 s_jnl_backup_type; /* Default type of journal backup */ |
| + | __u16 s_reserved_word_pad; |
| + | __u32 s_default_mount_opts; |
| + | __u32 s_first_meta_bg; /* First metablock group */ |
| + | __u32 s_mkfs_time; /* When the filesystem was created */ |
| + | __u32 s_jnl_blocks[17]; /* Backup of the journal inode */ |
| + | __u32 s_reserved[172]; /* Padding to the end of the block */ |
| + | }; |
| + | |
| + | /* |
| + | * Codes for operating systems |
| + | */ |
| + | #define EXT2_OS_LINUX 0 |
| + | #define EXT2_OS_HURD 1 |
| + | #define EXT2_OS_MASIX 2 |
| + | #define EXT2_OS_FREEBSD 3 |
| + | #define EXT2_OS_LITES 4 |
| + | |
| + | /* |
| + | * Revision levels |
| + | */ |
| + | #define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */ |
| + | #define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */ |
| + | |
| + | #define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV |
| + | #define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV |
| + | |
| + | #define EXT2_GOOD_OLD_INODE_SIZE 128 |
| + | |
| + | /* |
| + | * Journal inode backup types |
| + | */ |
| + | #define EXT3_JNL_BACKUP_BLOCKS 1 |
| + | |
| + | /* |
| + | * Feature set definitions |
| + | */ |
| + | |
| + | #define EXT2_HAS_COMPAT_FEATURE(sb,mask) \ |
| + | ( EXT2_SB(sb)->s_feature_compat & (mask) ) |
| + | #define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \ |
| + | ( EXT2_SB(sb)->s_feature_ro_compat & (mask) ) |
| + | #define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \ |
| + | ( EXT2_SB(sb)->s_feature_incompat & (mask) ) |
| + | |
| + | #define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001 |
| + | #define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002 |
| + | #define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 |
| + | #define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008 |
| + | #define EXT2_FEATURE_COMPAT_RESIZE_INODE 0x0010 |
| + | #define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020 |
| + | |
| + | #define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 |
| + | #define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 |
| + | /* #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 not used */ |
| + | |
| + | #define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 |
| + | #define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 |
| + | #define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */ |
| + | #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */ |
| + | #define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 |
| + | #define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040 |
| + | |
| + | |
| + | #define EXT2_FEATURE_COMPAT_SUPP 0 |
| + | #define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE) |
| + | #define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ |
| + | EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ |
| + | EXT2_FEATURE_RO_COMPAT_BTREE_DIR) |
| + | |
| + | /* |
| + | * Default values for user and/or group using reserved blocks |
| + | */ |
| + | #define EXT2_DEF_RESUID 0 |
| + | #define EXT2_DEF_RESGID 0 |
| + | |
| + | /* |
| + | * Default mount options |
| + | */ |
| + | #define EXT2_DEFM_DEBUG 0x0001 |
| + | #define EXT2_DEFM_BSDGROUPS 0x0002 |
| + | #define EXT2_DEFM_XATTR_USER 0x0004 |
| + | #define EXT2_DEFM_ACL 0x0008 |
| + | #define EXT2_DEFM_UID16 0x0010 |
| + | #define EXT3_DEFM_JMODE 0x0060 |
| + | #define EXT3_DEFM_JMODE_DATA 0x0020 |
| + | #define EXT3_DEFM_JMODE_ORDERED 0x0040 |
| + | #define EXT3_DEFM_JMODE_WBACK 0x0060 |
| + | |
| + | /* |
| + | * Structure of a directory entry |
| + | */ |
| + | #define EXT2_NAME_LEN 255 |
| + | |
| + | struct ext2_dir_entry { |
| + | __u32 inode; /* Inode number */ |
| + | __u16 rec_len; /* Directory entry length */ |
| + | __u16 name_len; /* Name length */ |
| + | char name[EXT2_NAME_LEN]; /* File name */ |
| + | }; |
| + | |
| + | /* |
| + | * The new version of the directory entry. Since EXT2 structures are |
| + | * stored in intel byte order, and the name_len field could never be |
| + | * bigger than 255 chars, it's safe to reclaim the extra byte for the |
| + | * file_type field. |
| + | */ |
| + | struct ext2_dir_entry_2 { |
| + | __u32 inode; /* Inode number */ |
| + | __u16 rec_len; /* Directory entry length */ |
| + | __u8 name_len; /* Name length */ |
| + | __u8 file_type; |
| + | char name[EXT2_NAME_LEN]; /* File name */ |
| + | }; |
| + | |
| + | /* |
| + | * Ext2 directory file types. Only the low 3 bits are used. The |
| + | * other bits are reserved for now. |
| + | */ |
| + | #define EXT2_FT_UNKNOWN 0 |
| + | #define EXT2_FT_REG_FILE 1 |
| + | #define EXT2_FT_DIR 2 |
| + | #define EXT2_FT_CHRDEV 3 |
| + | #define EXT2_FT_BLKDEV 4 |
| + | #define EXT2_FT_FIFO 5 |
| + | #define EXT2_FT_SOCK 6 |
| + | #define EXT2_FT_SYMLINK 7 |
| + | |
| + | #define EXT2_FT_MAX 8 |
| + | |
| + | /* |
| + | * EXT2_DIR_PAD defines the directory entries boundaries |
| + | * |
| + | * NOTE: It must be a multiple of 4 |
| + | */ |
| + | #define EXT2_DIR_PAD 4 |
| + | #define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) |
| + | #define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ |
| + | ~EXT2_DIR_ROUND) |
| + | |
| + | #endif /* _LINUX_EXT2_FS_H */ |
| @@ -0,0 +1,108 @@ | |
| + | /* |
| + | * io.h --- the I/O manager abstraction |
| + | * |
| + | * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #ifndef _EXT2FS_EXT2_IO_H |
| + | #define _EXT2FS_EXT2_IO_H |
| + | |
| + | /* |
| + | * ext2_loff_t is defined here since unix_io.c needs it. |
| + | */ |
| + | #if defined(__GNUC__) || defined(HAS_LONG_LONG) |
| + | typedef long long ext2_loff_t; |
| + | #else |
| + | typedef long ext2_loff_t; |
| + | #endif |
| + | |
| + | /* llseek.c */ |
| + | ext2_loff_t ext2fs_llseek (int, ext2_loff_t, int); |
| + | |
| + | typedef struct struct_io_manager *io_manager; |
| + | typedef struct struct_io_channel *io_channel; |
| + | |
| + | #define CHANNEL_FLAGS_WRITETHROUGH 0x01 |
| + | |
| + | struct struct_io_channel { |
| + | errcode_t magic; |
| + | io_manager manager; |
| + | char *name; |
| + | int block_size; |
| + | errcode_t (*read_error)(io_channel channel, |
| + | unsigned long block, |
| + | int count, |
| + | void *data, |
| + | size_t size, |
| + | int actual_bytes_read, |
| + | errcode_t error); |
| + | errcode_t (*write_error)(io_channel channel, |
| + | unsigned long block, |
| + | int count, |
| + | const void *data, |
| + | size_t size, |
| + | int actual_bytes_written, |
| + | errcode_t error); |
| + | int refcount; |
| + | int flags; |
| + | int reserved[14]; |
| + | void *private_data; |
| + | void *app_data; |
| + | }; |
| + | |
| + | struct struct_io_manager { |
| + | errcode_t magic; |
| + | const char *name; |
| + | errcode_t (*open)(const char *name, int flags, io_channel *channel); |
| + | errcode_t (*close)(io_channel channel); |
| + | errcode_t (*set_blksize)(io_channel channel, int blksize); |
| + | errcode_t (*read_blk)(io_channel channel, unsigned long block, |
| + | int count, void *data); |
| + | errcode_t (*write_blk)(io_channel channel, unsigned long block, |
| + | int count, const void *data); |
| + | errcode_t (*flush)(io_channel channel); |
| + | errcode_t (*write_byte)(io_channel channel, unsigned long offset, |
| + | int count, const void *data); |
| + | errcode_t (*set_option)(io_channel channel, const char *option, |
| + | const char *arg); |
| + | int reserved[14]; |
| + | }; |
| + | |
| + | #define IO_FLAG_RW 1 |
| + | |
| + | /* |
| + | * Convenience functions.... |
| + | */ |
| + | #define io_channel_close(c) ((c)->manager->close((c))) |
| + | #define io_channel_set_blksize(c,s) ((c)->manager->set_blksize((c),s)) |
| + | #define io_channel_read_blk(c,b,n,d) ((c)->manager->read_blk((c),b,n,d)) |
| + | #define io_channel_write_blk(c,b,n,d) ((c)->manager->write_blk((c),b,n,d)) |
| + | #define io_channel_flush(c) ((c)->manager->flush((c))) |
| + | #define io_channel_bumpcount(c) ((c)->refcount++) |
| + | |
| + | /* io_manager.c */ |
| + | extern errcode_t io_channel_set_options(io_channel channel, |
| + | const char *options); |
| + | extern errcode_t io_channel_write_byte(io_channel channel, |
| + | unsigned long offset, |
| + | int count, const void *data); |
| + | |
| + | /* unix_io.c */ |
| + | extern io_manager unix_io_manager; |
| + | |
| + | /* test_io.c */ |
| + | extern io_manager test_io_manager, test_io_backing_manager; |
| + | extern void (*test_io_cb_read_blk) |
| + | (unsigned long block, int count, errcode_t err); |
| + | extern void (*test_io_cb_write_blk) |
| + | (unsigned long block, int count, errcode_t err); |
| + | extern void (*test_io_cb_set_blksize) |
| + | (int blksize, errcode_t err); |
| + | |
| + | #endif /* _EXT2FS_EXT2_IO_H */ |
| + | |
| @@ -0,0 +1 @@ | |
| + | #include <linux/types.h> |
| @@ -0,0 +1,1137 @@ | |
| + | /* |
| + | * ext2fs.h --- ext2fs |
| + | * |
| + | * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #ifndef _EXT2FS_EXT2FS_H |
| + | #define _EXT2FS_EXT2FS_H |
| + | |
| + | #ifdef __GNUC__ |
| + | #define EXT2FS_ATTR(x) __attribute__(x) |
| + | #else |
| + | #define EXT2FS_ATTR(x) |
| + | #endif |
| + | |
| + | #ifdef __cplusplus |
| + | extern "C" { |
| + | #endif |
| + | |
| + | /* |
| + | * Non-GNU C compilers won't necessarily understand inline |
| + | */ |
| + | #if (!defined(__GNUC__) && !defined(__WATCOMC__)) |
| + | #define NO_INLINE_FUNCS |
| + | #endif |
| + | |
| + | /* |
| + | * Build in support for byte-swapping filesystems if we the feature |
| + | * has been configured or if we're being built on a CPU architecture |
| + | * with a non-native byte order. |
| + | */ |
| + | #if defined(ENABLE_SWAPFS) || defined(WORDS_BIGENDIAN) |
| + | #define EXT2FS_ENABLE_SWAPFS |
| + | #endif |
| + | |
| + | /* |
| + | * Where the master copy of the superblock is located, and how big |
| + | * superblocks are supposed to be. We define SUPERBLOCK_SIZE because |
| + | * the size of the superblock structure is not necessarily trustworthy |
| + | * (some versions have the padding set up so that the superblock is |
| + | * 1032 bytes long). |
| + | */ |
| + | #define SUPERBLOCK_OFFSET 1024 |
| + | #define SUPERBLOCK_SIZE 1024 |
| + | |
| + | /* |
| + | * The last ext2fs revision level that this version of the library is |
| + | * able to support. |
| + | */ |
| + | #define EXT2_LIB_CURRENT_REV EXT2_DYNAMIC_REV |
| + | |
| + | #ifdef HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include <stdio.h> |
| + | #include <stdlib.h> |
| + | |
| + | #if EXT2_FLAT_INCLUDES |
| + | #include "e2_types.h" |
| + | #include "ext2_fs.h" |
| + | #else |
| + | #include <ext2fs/ext2_types.h> |
| + | #include <ext2fs/ext2_fs.h> |
| + | #endif /* EXT2_FLAT_INCLUDES */ |
| + | |
| + | typedef __u32 ext2_ino_t; |
| + | typedef __u32 blk_t; |
| + | typedef __u32 dgrp_t; |
| + | typedef __u32 ext2_off_t; |
| + | typedef __s64 e2_blkcnt_t; |
| + | typedef __u32 ext2_dirhash_t; |
| + | |
| + | #if EXT2_FLAT_INCLUDES |
| + | #include "com_err.h" |
| + | #include "ext2_io.h" |
| + | #include "ext2_err.h" |
| + | #else |
| + | #include <et/com_err.h> |
| + | #include <ext2fs/ext2_io.h> |
| + | #include <ext2fs/ext2_err.h> |
| + | #endif |
| + | |
| + | /* |
| + | * Portability help for Microsoft Visual C++ |
| + | */ |
| + | #ifdef _MSC_VER |
| + | #define EXT2_QSORT_TYPE int __cdecl |
| + | #else |
| + | #define EXT2_QSORT_TYPE int |
| + | #endif |
| + | |
| + | typedef struct struct_ext2_filsys *ext2_filsys; |
| + | |
| + | struct ext2fs_struct_generic_bitmap { |
| + | errcode_t magic; |
| + | ext2_filsys fs; |
| + | __u32 start, end; |
| + | __u32 real_end; |
| + | char * description; |
| + | char * bitmap; |
| + | errcode_t base_error_code; |
| + | __u32 reserved[7]; |
| + | }; |
| + | |
| + | #define EXT2FS_MARK_ERROR 0 |
| + | #define EXT2FS_UNMARK_ERROR 1 |
| + | #define EXT2FS_TEST_ERROR 2 |
| + | |
| + | typedef struct ext2fs_struct_generic_bitmap *ext2fs_generic_bitmap; |
| + | typedef struct ext2fs_struct_generic_bitmap *ext2fs_inode_bitmap; |
| + | typedef struct ext2fs_struct_generic_bitmap *ext2fs_block_bitmap; |
| + | |
| + | #ifdef EXT2_DYNAMIC_REV |
| + | #define EXT2_FIRST_INODE(s) EXT2_FIRST_INO(s) |
| + | #else |
| + | #define EXT2_FIRST_INODE(s) EXT2_FIRST_INO |
| + | #define EXT2_INODE_SIZE(s) sizeof(struct ext2_inode) |
| + | #endif |
| + | |
| + | /* |
| + | * badblocks list definitions |
| + | */ |
| + | |
| + | typedef struct ext2_struct_u32_list *ext2_badblocks_list; |
| + | typedef struct ext2_struct_u32_iterate *ext2_badblocks_iterate; |
| + | |
| + | typedef struct ext2_struct_u32_list *ext2_u32_list; |
| + | typedef struct ext2_struct_u32_iterate *ext2_u32_iterate; |
| + | |
| + | /* old */ |
| + | typedef struct ext2_struct_u32_list *badblocks_list; |
| + | typedef struct ext2_struct_u32_iterate *badblocks_iterate; |
| + | |
| + | #define BADBLOCKS_FLAG_DIRTY 1 |
| + | |
| + | /* |
| + | * ext2_dblist structure and abstractions (see dblist.c) |
| + | */ |
| + | struct ext2_db_entry { |
| + | ext2_ino_t ino; |
| + | blk_t blk; |
| + | int blockcnt; |
| + | }; |
| + | |
| + | typedef struct ext2_struct_dblist *ext2_dblist; |
| + | |
| + | #define DBLIST_ABORT 1 |
| + | |
| + | /* |
| + | * ext2_fileio definitions |
| + | */ |
| + | |
| + | #define EXT2_FILE_WRITE 0x0001 |
| + | #define EXT2_FILE_CREATE 0x0002 |
| + | |
| + | #define EXT2_FILE_MASK 0x00FF |
| + | |
| + | #define EXT2_FILE_BUF_DIRTY 0x4000 |
| + | #define EXT2_FILE_BUF_VALID 0x2000 |
| + | |
| + | typedef struct ext2_file *ext2_file_t; |
| + | |
| + | #define EXT2_SEEK_SET 0 |
| + | #define EXT2_SEEK_CUR 1 |
| + | #define EXT2_SEEK_END 2 |
| + | |
| + | /* |
| + | * Flags for the ext2_filsys structure and for ext2fs_open() |
| + | */ |
| + | #define EXT2_FLAG_RW 0x01 |
| + | #define EXT2_FLAG_CHANGED 0x02 |
| + | #define EXT2_FLAG_DIRTY 0x04 |
| + | #define EXT2_FLAG_VALID 0x08 |
| + | #define EXT2_FLAG_IB_DIRTY 0x10 |
| + | #define EXT2_FLAG_BB_DIRTY 0x20 |
| + | #define EXT2_FLAG_SWAP_BYTES 0x40 |
| + | #define EXT2_FLAG_SWAP_BYTES_READ 0x80 |
| + | #define EXT2_FLAG_SWAP_BYTES_WRITE 0x100 |
| + | #define EXT2_FLAG_MASTER_SB_ONLY 0x200 |
| + | #define EXT2_FLAG_FORCE 0x400 |
| + | #define EXT2_FLAG_SUPER_ONLY 0x800 |
| + | #define EXT2_FLAG_JOURNAL_DEV_OK 0x1000 |
| + | #define EXT2_FLAG_IMAGE_FILE 0x2000 |
| + | |
| + | /* |
| + | * Special flag in the ext2 inode i_flag field that means that this is |
| + | * a new inode. (So that ext2_write_inode() can clear extra fields.) |
| + | */ |
| + | #define EXT2_NEW_INODE_FL 0x80000000 |
| + | |
| + | /* |
| + | * Flags for mkjournal |
| + | * |
| + | * EXT2_MKJOURNAL_V1_SUPER Make a (deprecated) V1 journal superblock |
| + | */ |
| + | #define EXT2_MKJOURNAL_V1_SUPER 0x0000001 |
| + | |
| + | struct struct_ext2_filsys { |
| + | errcode_t magic; |
| + | io_channel io; |
| + | int flags; |
| + | char * device_name; |
| + | struct ext2_super_block * super; |
| + | unsigned int blocksize; |
| + | int fragsize; |
| + | dgrp_t group_desc_count; |
| + | unsigned long desc_blocks; |
| + | struct ext2_group_desc * group_desc; |
| + | int inode_blocks_per_group; |
| + | ext2fs_inode_bitmap inode_map; |
| + | ext2fs_block_bitmap block_map; |
| + | errcode_t (*get_blocks)(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks); |
| + | errcode_t (*check_directory)(ext2_filsys fs, ext2_ino_t ino); |
| + | errcode_t (*write_bitmaps)(ext2_filsys fs); |
| + | errcode_t (*read_inode)(ext2_filsys fs, ext2_ino_t ino, |
| + | struct ext2_inode *inode); |
| + | errcode_t (*write_inode)(ext2_filsys fs, ext2_ino_t ino, |
| + | struct ext2_inode *inode); |
| + | ext2_badblocks_list badblocks; |
| + | ext2_dblist dblist; |
| + | __u32 stride; /* for mke2fs */ |
| + | struct ext2_super_block * orig_super; |
| + | struct ext2_image_hdr * image_header; |
| + | __u32 umask; |
| + | /* |
| + | * Reserved for future expansion |
| + | */ |
| + | __u32 reserved[8]; |
| + | |
| + | /* |
| + | * Reserved for the use of the calling application. |
| + | */ |
| + | void * priv_data; |
| + | |
| + | /* |
| + | * Inode cache |
| + | */ |
| + | struct ext2_inode_cache *icache; |
| + | io_channel image_io; |
| + | }; |
| + | |
| + | #if EXT2_FLAT_INCLUDES |
| + | #include "e2_bitops.h" |
| + | #else |
| + | #include <ext2fs/bitops.h> |
| + | #endif |
| + | |
| + | /* |
| + | * Return flags for the block iterator functions |
| + | */ |
| + | #define BLOCK_CHANGED 1 |
| + | #define BLOCK_ABORT 2 |
| + | #define BLOCK_ERROR 4 |
| + | |
| + | /* |
| + | * Block interate flags |
| + | * |
| + | * BLOCK_FLAG_APPEND, or BLOCK_FLAG_HOLE, indicates that the interator |
| + | * function should be called on blocks where the block number is zero. |
| + | * This is used by ext2fs_expand_dir() to be able to add a new block |
| + | * to an inode. It can also be used for programs that want to be able |
| + | * to deal with files that contain "holes". |
| + | * |
| + | * BLOCK_FLAG_TRAVERSE indicates that the iterator function for the |
| + | * indirect, doubly indirect, etc. blocks should be called after all |
| + | * of the blocks containined in the indirect blocks are processed. |
| + | * This is useful if you are going to be deallocating blocks from an |
| + | * inode. |
| + | * |
| + | * BLOCK_FLAG_DATA_ONLY indicates that the iterator function should be |
| + | * called for data blocks only. |
| + | * |
| + | * BLOCK_FLAG_NO_LARGE is for internal use only. It informs |
| + | * ext2fs_block_iterate2 that large files won't be accepted. |
| + | */ |
| + | #define BLOCK_FLAG_APPEND 1 |
| + | #define BLOCK_FLAG_HOLE 1 |
| + | #define BLOCK_FLAG_DEPTH_TRAVERSE 2 |
| + | #define BLOCK_FLAG_DATA_ONLY 4 |
| + | |
| + | #define BLOCK_FLAG_NO_LARGE 0x1000 |
| + | |
| + | /* |
| + | * Magic "block count" return values for the block iterator function. |
| + | */ |
| + | #define BLOCK_COUNT_IND (-1) |
| + | #define BLOCK_COUNT_DIND (-2) |
| + | #define BLOCK_COUNT_TIND (-3) |
| + | #define BLOCK_COUNT_TRANSLATOR (-4) |
| + | |
| + | #if 0 |
| + | /* |
| + | * Flags for ext2fs_move_blocks |
| + | */ |
| + | #define EXT2_BMOVE_GET_DBLIST 0x0001 |
| + | #define EXT2_BMOVE_DEBUG 0x0002 |
| + | #endif |
| + | |
| + | /* |
| + | * Flags for directory block reading and writing functions |
| + | */ |
| + | #define EXT2_DIRBLOCK_V2_STRUCT 0x0001 |
| + | |
| + | /* |
| + | * Return flags for the directory iterator functions |
| + | */ |
| + | #define DIRENT_CHANGED 1 |
| + | #define DIRENT_ABORT 2 |
| + | #define DIRENT_ERROR 3 |
| + | |
| + | /* |
| + | * Directory iterator flags |
| + | */ |
| + | |
| + | #define DIRENT_FLAG_INCLUDE_EMPTY 1 |
| + | #define DIRENT_FLAG_INCLUDE_REMOVED 2 |
| + | |
| + | #define DIRENT_DOT_FILE 1 |
| + | #define DIRENT_DOT_DOT_FILE 2 |
| + | #define DIRENT_OTHER_FILE 3 |
| + | #define DIRENT_DELETED_FILE 4 |
| + | |
| + | /* |
| + | * Inode scan definitions |
| + | */ |
| + | typedef struct ext2_struct_inode_scan *ext2_inode_scan; |
| + | |
| + | /* |
| + | * ext2fs_scan flags |
| + | */ |
| + | #define EXT2_SF_CHK_BADBLOCKS 0x0001 |
| + | #define EXT2_SF_BAD_INODE_BLK 0x0002 |
| + | #define EXT2_SF_BAD_EXTRA_BYTES 0x0004 |
| + | #define EXT2_SF_SKIP_MISSING_ITABLE 0x0008 |
| + | |
| + | /* |
| + | * ext2fs_check_if_mounted flags |
| + | */ |
| + | #define EXT2_MF_MOUNTED 1 |
| + | #define EXT2_MF_ISROOT 2 |
| + | #define EXT2_MF_READONLY 4 |
| + | #define EXT2_MF_SWAP 8 |
| + | |
| + | /* |
| + | * Ext2/linux mode flags. We define them here so that we don't need |
| + | * to depend on the OS's sys/stat.h, since we may be compiling on a |
| + | * non-Linux system. |
| + | */ |
| + | #define LINUX_S_IFMT 00170000 |
| + | #define LINUX_S_IFSOCK 0140000 |
| + | #define LINUX_S_IFLNK 0120000 |
| + | #define LINUX_S_IFREG 0100000 |
| + | #define LINUX_S_IFBLK 0060000 |
| + | #define LINUX_S_IFDIR 0040000 |
| + | #define LINUX_S_IFCHR 0020000 |
| + | #define LINUX_S_IFIFO 0010000 |
| + | #define LINUX_S_ISUID 0004000 |
| + | #define LINUX_S_ISGID 0002000 |
| + | #define LINUX_S_ISVTX 0001000 |
| + | |
| + | #define LINUX_S_IRWXU 00700 |
| + | #define LINUX_S_IRUSR 00400 |
| + | #define LINUX_S_IWUSR 00200 |
| + | #define LINUX_S_IXUSR 00100 |
| + | |
| + | #define LINUX_S_IRWXG 00070 |
| + | #define LINUX_S_IRGRP 00040 |
| + | #define LINUX_S_IWGRP 00020 |
| + | #define LINUX_S_IXGRP 00010 |
| + | |
| + | #define LINUX_S_IRWXO 00007 |
| + | #define LINUX_S_IROTH 00004 |
| + | #define LINUX_S_IWOTH 00002 |
| + | #define LINUX_S_IXOTH 00001 |
| + | |
| + | #define LINUX_S_ISLNK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFLNK) |
| + | #define LINUX_S_ISREG(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFREG) |
| + | #define LINUX_S_ISDIR(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFDIR) |
| + | #define LINUX_S_ISCHR(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFCHR) |
| + | #define LINUX_S_ISBLK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFBLK) |
| + | #define LINUX_S_ISFIFO(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFIFO) |
| + | #define LINUX_S_ISSOCK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFSOCK) |
| + | |
| + | /* |
| + | * ext2 size of an inode |
| + | */ |
| + | #define EXT2_I_SIZE(i) ((i)->i_size | ((__u64) (i)->i_size_high << 32)) |
| + | |
| + | /* |
| + | * ext2_icount_t abstraction |
| + | */ |
| + | #define EXT2_ICOUNT_OPT_INCREMENT 0x01 |
| + | |
| + | typedef struct ext2_icount *ext2_icount_t; |
| + | |
| + | /* |
| + | * Flags for ext2fs_bmap |
| + | */ |
| + | #define BMAP_ALLOC 0x0001 |
| + | #define BMAP_SET 0x0002 |
| + | |
| + | /* |
| + | * Flags for imager.c functions |
| + | */ |
| + | #define IMAGER_FLAG_INODEMAP 1 |
| + | #define IMAGER_FLAG_SPARSEWRITE 2 |
| + | |
| + | /* |
| + | * For checking structure magic numbers... |
| + | */ |
| + | |
| + | #define EXT2_CHECK_MAGIC(struct, code) \ |
| + | if ((struct)->magic != (code)) return (code) |
| + | |
| + | |
| + | /* |
| + | * For ext2 compression support |
| + | */ |
| + | #define EXT2FS_COMPRESSED_BLKADDR ((blk_t) 0xffffffff) |
| + | #define HOLE_BLKADDR(_b) ((_b) == 0 || (_b) == EXT2FS_COMPRESSED_BLKADDR) |
| + | |
| + | /* |
| + | * Features supported by this version of the library |
| + | */ |
| + | #define EXT2_LIB_FEATURE_COMPAT_SUPP (EXT2_FEATURE_COMPAT_DIR_PREALLOC|\ |
| + | EXT2_FEATURE_COMPAT_IMAGIC_INODES|\ |
| + | EXT3_FEATURE_COMPAT_HAS_JOURNAL|\ |
| + | EXT2_FEATURE_COMPAT_RESIZE_INODE|\ |
| + | EXT2_FEATURE_COMPAT_DIR_INDEX|\ |
| + | EXT2_FEATURE_COMPAT_EXT_ATTR) |
| + | |
| + | /* This #ifdef is temporary until compression is fully supported */ |
| + | #ifdef ENABLE_COMPRESSION |
| + | #ifndef I_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL |
| + | /* If the below warning bugs you, then have |
| + | `CPPFLAGS=-DI_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL' in your |
| + | environment at configure time. */ |
| + | #warning "Compression support is experimental" |
| + | #endif |
| + | #define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\ |
| + | EXT2_FEATURE_INCOMPAT_COMPRESSION|\ |
| + | EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\ |
| + | EXT2_FEATURE_INCOMPAT_META_BG|\ |
| + | EXT3_FEATURE_INCOMPAT_RECOVER) |
| + | #else |
| + | #define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\ |
| + | EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\ |
| + | EXT2_FEATURE_INCOMPAT_META_BG|\ |
| + | EXT3_FEATURE_INCOMPAT_RECOVER) |
| + | #endif |
| + | #define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\ |
| + | EXT2_FEATURE_RO_COMPAT_LARGE_FILE) |
| + | /* |
| + | * function prototypes |
| + | */ |
| + | |
| + | /* alloc.c */ |
| + | extern errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, int mode, |
| + | ext2fs_inode_bitmap map, ext2_ino_t *ret); |
| + | extern errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal, |
| + | ext2fs_block_bitmap map, blk_t *ret); |
| + | extern errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, |
| + | blk_t finish, int num, |
| + | ext2fs_block_bitmap map, |
| + | blk_t *ret); |
| + | extern errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal, |
| + | char *block_buf, blk_t *ret); |
| + | |
| + | /* alloc_sb.c */ |
| + | extern int ext2fs_reserve_super_and_bgd(ext2_filsys fs, |
| + | dgrp_t group, |
| + | ext2fs_block_bitmap bmap); |
| + | |
| + | /* alloc_stats.c */ |
| + | void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse); |
| + | void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino, |
| + | int inuse, int isdir); |
| + | void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse); |
| + | |
| + | /* alloc_tables.c */ |
| + | extern errcode_t ext2fs_allocate_tables(ext2_filsys fs); |
| + | extern errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group, |
| + | ext2fs_block_bitmap bmap); |
| + | |
| + | /* badblocks.c */ |
| + | extern errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size); |
| + | extern errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk); |
| + | extern int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk); |
| + | extern int ext2fs_u32_list_test(ext2_u32_list bb, blk_t blk); |
| + | extern errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb, |
| + | ext2_u32_iterate *ret); |
| + | extern int ext2fs_u32_list_iterate(ext2_u32_iterate iter, blk_t *blk); |
| + | extern void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter); |
| + | extern errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest); |
| + | extern int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2); |
| + | |
| + | extern errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret, |
| + | int size); |
| + | extern errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb, |
| + | blk_t blk); |
| + | extern int ext2fs_badblocks_list_test(ext2_badblocks_list bb, |
| + | blk_t blk); |
| + | extern int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk); |
| + | extern void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk); |
| + | extern errcode_t |
| + | ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb, |
| + | ext2_badblocks_iterate *ret); |
| + | extern int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter, |
| + | blk_t *blk); |
| + | extern void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter); |
| + | extern errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src, |
| + | ext2_badblocks_list *dest); |
| + | extern int ext2fs_badblocks_equal(ext2_badblocks_list bb1, |
| + | ext2_badblocks_list bb2); |
| + | extern int ext2fs_u32_list_count(ext2_u32_list bb); |
| + | |
| + | /* bb_compat */ |
| + | extern errcode_t badblocks_list_create(badblocks_list *ret, int size); |
| + | extern errcode_t badblocks_list_add(badblocks_list bb, blk_t blk); |
| + | extern int badblocks_list_test(badblocks_list bb, blk_t blk); |
| + | extern errcode_t badblocks_list_iterate_begin(badblocks_list bb, |
| + | badblocks_iterate *ret); |
| + | extern int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk); |
| + | extern void badblocks_list_iterate_end(badblocks_iterate iter); |
| + | extern void badblocks_list_free(badblocks_list bb); |
| + | |
| + | /* bb_inode.c */ |
| + | extern errcode_t ext2fs_update_bb_inode(ext2_filsys fs, |
| + | ext2_badblocks_list bb_list); |
| + | |
| + | /* bitmaps.c */ |
| + | extern errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs); |
| + | extern errcode_t ext2fs_write_block_bitmap (ext2_filsys fs); |
| + | extern errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs); |
| + | extern errcode_t ext2fs_read_block_bitmap(ext2_filsys fs); |
| + | extern errcode_t ext2fs_allocate_generic_bitmap(__u32 start, |
| + | __u32 end, |
| + | __u32 real_end, |
| + | const char *descr, |
| + | ext2fs_generic_bitmap *ret); |
| + | extern errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs, |
| + | const char *descr, |
| + | ext2fs_block_bitmap *ret); |
| + | extern errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs, |
| + | const char *descr, |
| + | ext2fs_inode_bitmap *ret); |
| + | extern errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap, |
| + | ext2_ino_t end, ext2_ino_t *oend); |
| + | extern errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap, |
| + | blk_t end, blk_t *oend); |
| + | extern void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap); |
| + | extern void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap); |
| + | extern errcode_t ext2fs_read_bitmaps(ext2_filsys fs); |
| + | extern errcode_t ext2fs_write_bitmaps(ext2_filsys fs); |
| + | |
| + | /* block.c */ |
| + | extern errcode_t ext2fs_block_iterate(ext2_filsys fs, |
| + | ext2_ino_t ino, |
| + | int flags, |
| + | char *block_buf, |
| + | int (*func)(ext2_filsys fs, |
| + | blk_t *blocknr, |
| + | int blockcnt, |
| + | void *priv_data), |
| + | void *priv_data); |
| + | errcode_t ext2fs_block_iterate2(ext2_filsys fs, |
| + | ext2_ino_t ino, |
| + | int flags, |
| + | char *block_buf, |
| + | int (*func)(ext2_filsys fs, |
| + | blk_t *blocknr, |
| + | e2_blkcnt_t blockcnt, |
| + | blk_t ref_blk, |
| + | int ref_offset, |
| + | void *priv_data), |
| + | void *priv_data); |
| + | |
| + | /* bmap.c */ |
| + | extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, |
| + | struct ext2_inode *inode, |
| + | char *block_buf, int bmap_flags, |
| + | blk_t block, blk_t *phys_blk); |
| + | |
| + | |
| + | #if 0 |
| + | /* bmove.c */ |
| + | extern errcode_t ext2fs_move_blocks(ext2_filsys fs, |
| + | ext2fs_block_bitmap reserve, |
| + | ext2fs_block_bitmap alloc_map, |
| + | int flags); |
| + | #endif |
| + | |
| + | /* check_desc.c */ |
| + | extern errcode_t ext2fs_check_desc(ext2_filsys fs); |
| + | |
| + | /* closefs.c */ |
| + | extern errcode_t ext2fs_close(ext2_filsys fs); |
| + | extern errcode_t ext2fs_flush(ext2_filsys fs); |
| + | extern int ext2fs_bg_has_super(ext2_filsys fs, int group_block); |
| + | extern int ext2fs_super_and_bgd_loc(ext2_filsys fs, |
| + | dgrp_t group, |
| + | blk_t *ret_super_blk, |
| + | blk_t *ret_old_desc_blk, |
| + | blk_t *ret_new_desc_blk, |
| + | int *ret_meta_bg); |
| + | extern void ext2fs_update_dynamic_rev(ext2_filsys fs); |
| + | |
| + | /* cmp_bitmaps.c */ |
| + | extern errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1, |
| + | ext2fs_block_bitmap bm2); |
| + | extern errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1, |
| + | ext2fs_inode_bitmap bm2); |
| + | |
| + | /* dblist.c */ |
| + | |
| + | extern errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs); |
| + | extern errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist); |
| + | extern errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino, |
| + | blk_t blk, int blockcnt); |
| + | extern void ext2fs_dblist_sort(ext2_dblist dblist, |
| + | EXT2_QSORT_TYPE (*sortfunc)(const void *, |
| + | const void *)); |
| + | extern errcode_t ext2fs_dblist_iterate(ext2_dblist dblist, |
| + | int (*func)(ext2_filsys fs, struct ext2_db_entry *db_info, |
| + | void *priv_data), |
| + | void *priv_data); |
| + | extern errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino, |
| + | blk_t blk, int blockcnt); |
| + | extern errcode_t ext2fs_copy_dblist(ext2_dblist src, |
| + | ext2_dblist *dest); |
| + | extern int ext2fs_dblist_count(ext2_dblist dblist); |
| + | |
| + | /* dblist_dir.c */ |
| + | extern errcode_t |
| + | ext2fs_dblist_dir_iterate(ext2_dblist dblist, |
| + | int flags, |
| + | char *block_buf, |
| + | int (*func)(ext2_ino_t dir, |
| + | int entry, |
| + | struct ext2_dir_entry *dirent, |
| + | int offset, |
| + | int blocksize, |
| + | char *buf, |
| + | void *priv_data), |
| + | void *priv_data); |
| + | |
| + | /* dirblock.c */ |
| + | extern errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block, |
| + | void *buf); |
| + | extern errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block, |
| + | void *buf, int flags); |
| + | extern errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block, |
| + | void *buf); |
| + | extern errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block, |
| + | void *buf, int flags); |
| + | |
| + | /* dirhash.c */ |
| + | extern errcode_t ext2fs_dirhash(int version, const char *name, int len, |
| + | const __u32 *seed, |
| + | ext2_dirhash_t *ret_hash, |
| + | ext2_dirhash_t *ret_minor_hash); |
| + | |
| + | |
| + | /* dir_iterate.c */ |
| + | extern errcode_t ext2fs_dir_iterate(ext2_filsys fs, |
| + | ext2_ino_t dir, |
| + | int flags, |
| + | char *block_buf, |
| + | int (*func)(struct ext2_dir_entry *dirent, |
| + | int offset, |
| + | int blocksize, |
| + | char *buf, |
| + | void *priv_data), |
| + | void *priv_data); |
| + | extern errcode_t ext2fs_dir_iterate2(ext2_filsys fs, |
| + | ext2_ino_t dir, |
| + | int flags, |
| + | char *block_buf, |
| + | int (*func)(ext2_ino_t dir, |
| + | int entry, |
| + | struct ext2_dir_entry *dirent, |
| + | int offset, |
| + | int blocksize, |
| + | char *buf, |
| + | void *priv_data), |
| + | void *priv_data); |
| + | |
| + | /* dupfs.c */ |
| + | extern errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest); |
| + | |
| + | /* expanddir.c */ |
| + | extern errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir); |
| + | |
| + | /* ext_attr.c */ |
| + | extern errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf); |
| + | extern errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, |
| + | void *buf); |
| + | extern errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk, |
| + | char *block_buf, |
| + | int adjust, __u32 *newcount); |
| + | |
| + | /* fileio.c */ |
| + | extern errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino, |
| + | struct ext2_inode *inode, |
| + | int flags, ext2_file_t *ret); |
| + | extern errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino, |
| + | int flags, ext2_file_t *ret); |
| + | extern ext2_filsys ext2fs_file_get_fs(ext2_file_t file); |
| + | extern errcode_t ext2fs_file_close(ext2_file_t file); |
| + | extern errcode_t ext2fs_file_flush(ext2_file_t file); |
| + | extern errcode_t ext2fs_file_read(ext2_file_t file, void *buf, |
| + | unsigned int wanted, unsigned int *got); |
| + | extern errcode_t ext2fs_file_write(ext2_file_t file, const void *buf, |
| + | unsigned int nbytes, unsigned int *written); |
| + | extern errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset, |
| + | int whence, __u64 *ret_pos); |
| + | extern errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset, |
| + | int whence, ext2_off_t *ret_pos); |
| + | errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size); |
| + | extern ext2_off_t ext2fs_file_get_size(ext2_file_t file); |
| + | extern errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size); |
| + | |
| + | /* finddev.c */ |
| + | extern char *ext2fs_find_block_device(dev_t device); |
| + | |
| + | /* flushb.c */ |
| + | extern errcode_t ext2fs_sync_device(int fd, int flushb); |
| + | |
| + | /* freefs.c */ |
| + | extern void ext2fs_free(ext2_filsys fs); |
| + | extern void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap); |
| + | extern void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap); |
| + | extern void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap); |
| + | extern void ext2fs_free_dblist(ext2_dblist dblist); |
| + | extern void ext2fs_badblocks_list_free(ext2_badblocks_list bb); |
| + | extern void ext2fs_u32_list_free(ext2_u32_list bb); |
| + | |
| + | /* getsize.c */ |
| + | extern errcode_t ext2fs_get_device_size(const char *file, int blocksize, |
| + | blk_t *retblocks); |
| + | |
| + | /* getsectsize.c */ |
| + | errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize); |
| + | |
| + | /* imager.c */ |
| + | extern errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags); |
| + | extern errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, int flags); |
| + | extern errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, int flags); |
| + | extern errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, int flags); |
| + | extern errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags); |
| + | extern errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags); |
| + | |
| + | /* ind_block.c */ |
| + | errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf); |
| + | errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf); |
| + | |
| + | /* initialize.c */ |
| + | extern errcode_t ext2fs_initialize(const char *name, int flags, |
| + | struct ext2_super_block *param, |
| + | io_manager manager, ext2_filsys *ret_fs); |
| + | |
| + | /* icount.c */ |
| + | extern void ext2fs_free_icount(ext2_icount_t icount); |
| + | extern errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, |
| + | unsigned int size, |
| + | ext2_icount_t hint, ext2_icount_t *ret); |
| + | extern errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, |
| + | unsigned int size, |
| + | ext2_icount_t *ret); |
| + | extern errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, |
| + | __u16 *ret); |
| + | extern errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino, |
| + | __u16 *ret); |
| + | extern errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino, |
| + | __u16 *ret); |
| + | extern errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino, |
| + | __u16 count); |
| + | extern ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount); |
| + | errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *); |
| + | |
| + | /* inode.c */ |
| + | extern errcode_t ext2fs_flush_icache(ext2_filsys fs); |
| + | extern errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, |
| + | ext2_ino_t *ino, |
| + | struct ext2_inode *inode, |
| + | int bufsize); |
| + | extern errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks, |
| + | ext2_inode_scan *ret_scan); |
| + | extern void ext2fs_close_inode_scan(ext2_inode_scan scan); |
| + | extern errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino, |
| + | struct ext2_inode *inode); |
| + | extern errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan, |
| + | int group); |
| + | extern void ext2fs_set_inode_callback |
| + | (ext2_inode_scan scan, |
| + | errcode_t (*done_group)(ext2_filsys fs, |
| + | ext2_inode_scan scan, |
| + | dgrp_t group, |
| + | void * priv_data), |
| + | void *done_group_data); |
| + | extern int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags, |
| + | int clear_flags); |
| + | extern errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino, |
| + | struct ext2_inode * inode, |
| + | int bufsize); |
| + | extern errcode_t ext2fs_read_inode (ext2_filsys fs, ext2_ino_t ino, |
| + | struct ext2_inode * inode); |
| + | extern errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino, |
| + | struct ext2_inode * inode, |
| + | int bufsize); |
| + | extern errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino, |
| + | struct ext2_inode * inode); |
| + | extern errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino, |
| + | struct ext2_inode * inode); |
| + | extern errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks); |
| + | extern errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino); |
| + | |
| + | /* inode_io.c */ |
| + | extern io_manager inode_io_manager; |
| + | extern errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino, |
| + | char **name); |
| + | extern errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino, |
| + | struct ext2_inode *inode, |
| + | char **name); |
| + | |
| + | /* ismounted.c */ |
| + | extern errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags); |
| + | extern errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags, |
| + | char *mtpt, int mtlen); |
| + | |
| + | /* namei.c */ |
| + | extern errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name, |
| + | int namelen, char *buf, ext2_ino_t *inode); |
| + | extern errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, |
| + | const char *name, ext2_ino_t *inode); |
| + | errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, |
| + | const char *name, ext2_ino_t *inode); |
| + | extern errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, |
| + | ext2_ino_t inode, ext2_ino_t *res_inode); |
| + | |
| + | /* native.c */ |
| + | int ext2fs_native_flag(void); |
| + | |
| + | /* newdir.c */ |
| + | extern errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, |
| + | ext2_ino_t parent_ino, char **block); |
| + | |
| + | /* mkdir.c */ |
| + | extern errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum, |
| + | const char *name); |
| + | |
| + | /* mkjournal.c */ |
| + | extern errcode_t ext2fs_create_journal_superblock(ext2_filsys fs, |
| + | __u32 size, int flags, |
| + | char **ret_jsb); |
| + | extern errcode_t ext2fs_add_journal_device(ext2_filsys fs, |
| + | ext2_filsys journal_dev); |
| + | extern errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, |
| + | int flags); |
| + | |
| + | /* openfs.c */ |
| + | extern errcode_t ext2fs_open(const char *name, int flags, int superblock, |
| + | unsigned int block_size, io_manager manager, |
| + | ext2_filsys *ret_fs); |
| + | extern errcode_t ext2fs_open2(const char *name, const char *io_options, |
| + | int flags, int superblock, |
| + | unsigned int block_size, io_manager manager, |
| + | ext2_filsys *ret_fs); |
| + | extern blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, |
| + | dgrp_t i); |
| + | errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io); |
| + | errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io); |
| + | errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io); |
| + | |
| + | /* get_pathname.c */ |
| + | extern errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino, |
| + | char **name); |
| + | |
| + | /* link.c */ |
| + | errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name, |
| + | ext2_ino_t ino, int flags); |
| + | errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir, const char *name, |
| + | ext2_ino_t ino, int flags); |
| + | |
| + | /* read_bb.c */ |
| + | extern errcode_t ext2fs_read_bb_inode(ext2_filsys fs, |
| + | ext2_badblocks_list *bb_list); |
| + | |
| + | /* read_bb_file.c */ |
| + | extern errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f, |
| + | ext2_badblocks_list *bb_list, |
| + | void *priv_data, |
| + | void (*invalid)(ext2_filsys fs, |
| + | blk_t blk, |
| + | char *badstr, |
| + | void *priv_data)); |
| + | extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f, |
| + | ext2_badblocks_list *bb_list, |
| + | void (*invalid)(ext2_filsys fs, |
| + | blk_t blk)); |
| + | |
| + | /* res_gdt.c */ |
| + | extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs); |
| + | |
| + | /* rs_bitmap.c */ |
| + | extern errcode_t ext2fs_resize_generic_bitmap(__u32 new_end, |
| + | __u32 new_real_end, |
| + | ext2fs_generic_bitmap bmap); |
| + | extern errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end, |
| + | ext2fs_inode_bitmap bmap); |
| + | extern errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end, |
| + | ext2fs_block_bitmap bmap); |
| + | extern errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src, |
| + | ext2fs_generic_bitmap *dest); |
| + | |
| + | /* swapfs.c */ |
| + | extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, |
| + | int has_header); |
| + | extern void ext2fs_swap_super(struct ext2_super_block * super); |
| + | extern void ext2fs_swap_group_desc(struct ext2_group_desc *gdp); |
| + | extern void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t, |
| + | struct ext2_inode_large *f, int hostorder, |
| + | int bufsize); |
| + | extern void ext2fs_swap_inode(ext2_filsys fs,struct ext2_inode *t, |
| + | struct ext2_inode *f, int hostorder); |
| + | |
| + | /* valid_blk.c */ |
| + | extern int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode); |
| + | |
| + | /* version.c */ |
| + | extern int ext2fs_parse_version_string(const char *ver_string); |
| + | extern int ext2fs_get_library_version(const char **ver_string, |
| + | const char **date_string); |
| + | |
| + | /* write_bb_file.c */ |
| + | extern errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list, |
| + | unsigned int flags, |
| + | FILE *f); |
| + | |
| + | |
| + | /* inline functions */ |
| + | extern errcode_t ext2fs_get_mem(unsigned long size, void *ptr); |
| + | extern errcode_t ext2fs_free_mem(void *ptr); |
| + | extern errcode_t ext2fs_resize_mem(unsigned long old_size, |
| + | unsigned long size, void *ptr); |
| + | extern void ext2fs_mark_super_dirty(ext2_filsys fs); |
| + | extern void ext2fs_mark_changed(ext2_filsys fs); |
| + | extern int ext2fs_test_changed(ext2_filsys fs); |
| + | extern void ext2fs_mark_valid(ext2_filsys fs); |
| + | extern void ext2fs_unmark_valid(ext2_filsys fs); |
| + | extern int ext2fs_test_valid(ext2_filsys fs); |
| + | extern void ext2fs_mark_ib_dirty(ext2_filsys fs); |
| + | extern void ext2fs_mark_bb_dirty(ext2_filsys fs); |
| + | extern int ext2fs_test_ib_dirty(ext2_filsys fs); |
| + | extern int ext2fs_test_bb_dirty(ext2_filsys fs); |
| + | extern int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk); |
| + | extern int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino); |
| + | extern blk_t ext2fs_inode_data_blocks(ext2_filsys fs, |
| + | struct ext2_inode *inode); |
| + | |
| + | /* |
| + | * The actual inlined functions definitions themselves... |
| + | * |
| + | * If NO_INLINE_FUNCS is defined, then we won't try to do inline |
| + | * functions at all! |
| + | */ |
| + | #if (defined(INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS)) |
| + | #ifdef INCLUDE_INLINE_FUNCS |
| + | #define _INLINE_ extern |
| + | #else |
| + | #ifdef __GNUC__ |
| + | #define _INLINE_ extern __inline__ |
| + | #else /* For Watcom C */ |
| + | #define _INLINE_ extern inline |
| + | #endif |
| + | #endif |
| + | |
| + | #ifndef EXT2_CUSTOM_MEMORY_ROUTINES |
| + | /* |
| + | * Allocate memory |
| + | */ |
| + | _INLINE_ errcode_t ext2fs_get_mem(unsigned long size, void *ptr) |
| + | { |
| + | void **pp = (void **)ptr; |
| + | |
| + | *pp = malloc(size); |
| + | if (!*pp) |
| + | return EXT2_ET_NO_MEMORY; |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * Free memory |
| + | */ |
| + | _INLINE_ errcode_t ext2fs_free_mem(void *ptr) |
| + | { |
| + | void **pp = (void **)ptr; |
| + | |
| + | free(*pp); |
| + | *pp = 0; |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * Resize memory |
| + | */ |
| + | _INLINE_ errcode_t ext2fs_resize_mem(unsigned long EXT2FS_ATTR((unused)) old_size, |
| + | unsigned long size, void *ptr) |
| + | { |
| + | void *p; |
| + | void **pp = (void **)ptr; |
| + | |
| + | p = realloc(*pp, size); |
| + | if (!p) |
| + | return EXT2_ET_NO_MEMORY; |
| + | *pp = p; |
| + | return 0; |
| + | } |
| + | #endif /* Custom memory routines */ |
| + | |
| + | /* |
| + | * Mark a filesystem superblock as dirty |
| + | */ |
| + | _INLINE_ void ext2fs_mark_super_dirty(ext2_filsys fs) |
| + | { |
| + | fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_CHANGED; |
| + | } |
| + | |
| + | /* |
| + | * Mark a filesystem as changed |
| + | */ |
| + | _INLINE_ void ext2fs_mark_changed(ext2_filsys fs) |
| + | { |
| + | fs->flags |= EXT2_FLAG_CHANGED; |
| + | } |
| + | |
| + | /* |
| + | * Check to see if a filesystem has changed |
| + | */ |
| + | _INLINE_ int ext2fs_test_changed(ext2_filsys fs) |
| + | { |
| + | return (fs->flags & EXT2_FLAG_CHANGED); |
| + | } |
| + | |
| + | /* |
| + | * Mark a filesystem as valid |
| + | */ |
| + | _INLINE_ void ext2fs_mark_valid(ext2_filsys fs) |
| + | { |
| + | fs->flags |= EXT2_FLAG_VALID; |
| + | } |
| + | |
| + | /* |
| + | * Mark a filesystem as NOT valid |
| + | */ |
| + | _INLINE_ void ext2fs_unmark_valid(ext2_filsys fs) |
| + | { |
| + | fs->flags &= ~EXT2_FLAG_VALID; |
| + | } |
| + | |
| + | /* |
| + | * Check to see if a filesystem is valid |
| + | */ |
| + | _INLINE_ int ext2fs_test_valid(ext2_filsys fs) |
| + | { |
| + | return (fs->flags & EXT2_FLAG_VALID); |
| + | } |
| + | |
| + | /* |
| + | * Mark the inode bitmap as dirty |
| + | */ |
| + | _INLINE_ void ext2fs_mark_ib_dirty(ext2_filsys fs) |
| + | { |
| + | fs->flags |= EXT2_FLAG_IB_DIRTY | EXT2_FLAG_CHANGED; |
| + | } |
| + | |
| + | /* |
| + | * Mark the block bitmap as dirty |
| + | */ |
| + | _INLINE_ void ext2fs_mark_bb_dirty(ext2_filsys fs) |
| + | { |
| + | fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_CHANGED; |
| + | } |
| + | |
| + | /* |
| + | * Check to see if a filesystem's inode bitmap is dirty |
| + | */ |
| + | _INLINE_ int ext2fs_test_ib_dirty(ext2_filsys fs) |
| + | { |
| + | return (fs->flags & EXT2_FLAG_IB_DIRTY); |
| + | } |
| + | |
| + | /* |
| + | * Check to see if a filesystem's block bitmap is dirty |
| + | */ |
| + | _INLINE_ int ext2fs_test_bb_dirty(ext2_filsys fs) |
| + | { |
| + | return (fs->flags & EXT2_FLAG_BB_DIRTY); |
| + | } |
| + | |
| + | /* |
| + | * Return the group # of a block |
| + | */ |
| + | _INLINE_ int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk) |
| + | { |
| + | return (blk - fs->super->s_first_data_block) / |
| + | fs->super->s_blocks_per_group; |
| + | } |
| + | |
| + | /* |
| + | * Return the group # of an inode number |
| + | */ |
| + | _INLINE_ int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino) |
| + | { |
| + | return (ino - 1) / fs->super->s_inodes_per_group; |
| + | } |
| + | |
| + | _INLINE_ blk_t ext2fs_inode_data_blocks(ext2_filsys fs, |
| + | struct ext2_inode *inode) |
| + | { |
| + | return inode->i_blocks - |
| + | (inode->i_file_acl ? fs->blocksize >> 9 : 0); |
| + | } |
| + | #undef _INLINE_ |
| + | #endif |
| + | |
| + | #ifdef __cplusplus |
| + | } |
| + | #endif |
| + | |
| + | #endif /* _EXT2FS_EXT2FS_H */ |
| @@ -0,0 +1,88 @@ | |
| + | /* |
| + | * ext2fsP.h --- private header file for ext2 library |
| + | * |
| + | * Copyright (C) 1997 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include "ext2fs.h" |
| + | |
| + | /* |
| + | * Badblocks list |
| + | */ |
| + | struct ext2_struct_u32_list { |
| + | int magic; |
| + | int num; |
| + | int size; |
| + | __u32 *list; |
| + | int badblocks_flags; |
| + | }; |
| + | |
| + | struct ext2_struct_u32_iterate { |
| + | int magic; |
| + | ext2_u32_list bb; |
| + | int ptr; |
| + | }; |
| + | |
| + | |
| + | /* |
| + | * Directory block iterator definition |
| + | */ |
| + | struct ext2_struct_dblist { |
| + | int magic; |
| + | ext2_filsys fs; |
| + | ext2_ino_t size; |
| + | ext2_ino_t count; |
| + | int sorted; |
| + | struct ext2_db_entry * list; |
| + | }; |
| + | |
| + | /* |
| + | * For directory iterators |
| + | */ |
| + | struct dir_context { |
| + | ext2_ino_t dir; |
| + | int flags; |
| + | char *buf; |
| + | int (*func)(ext2_ino_t dir, |
| + | int entry, |
| + | struct ext2_dir_entry *dirent, |
| + | int offset, |
| + | int blocksize, |
| + | char *buf, |
| + | void *priv_data); |
| + | void *priv_data; |
| + | errcode_t errcode; |
| + | }; |
| + | |
| + | /* |
| + | * Inode cache structure |
| + | */ |
| + | struct ext2_inode_cache { |
| + | void * buffer; |
| + | blk_t buffer_blk; |
| + | int cache_last; |
| + | int cache_size; |
| + | int refcount; |
| + | struct ext2_inode_cache_ent *cache; |
| + | }; |
| + | |
| + | struct ext2_inode_cache_ent { |
| + | ext2_ino_t ino; |
| + | struct ext2_inode inode; |
| + | }; |
| + | |
| + | /* Function prototypes */ |
| + | |
| + | extern int ext2fs_process_dir_block(ext2_filsys fs, |
| + | blk_t *blocknr, |
| + | e2_blkcnt_t blockcnt, |
| + | blk_t ref_block, |
| + | int ref_offset, |
| + | void *priv_data); |
| + | |
| + | |
| @@ -0,0 +1,105 @@ | |
| + | /* |
| + | * ext_attr.c --- extended attribute blocks |
| + | * |
| + | * Copyright (C) 2001 Andreas Gruenbacher, <[email protected]> |
| + | * |
| + | * Copyright (C) 2002 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <string.h> |
| + | #include <time.h> |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2_ext_attr.h" |
| + | |
| + | #include "ext2fs.h" |
| + | |
| + | errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf) |
| + | { |
| + | errcode_t retval; |
| + | |
| + | retval = io_channel_read_blk(fs->io, block, 1, buf); |
| + | if (retval) |
| + | return retval; |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | if ((fs->flags & (EXT2_FLAG_SWAP_BYTES| |
| + | EXT2_FLAG_SWAP_BYTES_READ)) != 0) |
| + | ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1); |
| + | #endif |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf) |
| + | { |
| + | errcode_t retval; |
| + | char *write_buf; |
| + | char *buf = NULL; |
| + | |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || |
| + | (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) { |
| + | retval = ext2fs_get_mem(fs->blocksize, &buf); |
| + | if (retval) |
| + | return retval; |
| + | write_buf = buf; |
| + | ext2fs_swap_ext_attr(buf, inbuf, fs->blocksize, 1); |
| + | } else |
| + | #endif |
| + | write_buf = (char *) inbuf; |
| + | retval = io_channel_write_blk(fs->io, block, 1, write_buf); |
| + | if (buf) |
| + | ext2fs_free_mem(&buf); |
| + | if (!retval) |
| + | ext2fs_mark_changed(fs); |
| + | return retval; |
| + | } |
| + | |
| + | /* |
| + | * This function adjusts the reference count of the EA block. |
| + | */ |
| + | errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk, |
| + | char *block_buf, int adjust, |
| + | __u32 *newcount) |
| + | { |
| + | errcode_t retval; |
| + | struct ext2_ext_attr_header *header; |
| + | char *buf = 0; |
| + | |
| + | if ((blk >= fs->super->s_blocks_count) || |
| + | (blk < fs->super->s_first_data_block)) |
| + | return EXT2_ET_BAD_EA_BLOCK_NUM; |
| + | |
| + | if (!block_buf) { |
| + | retval = ext2fs_get_mem(fs->blocksize, &buf); |
| + | if (retval) |
| + | return retval; |
| + | block_buf = buf; |
| + | } |
| + | |
| + | retval = ext2fs_read_ext_attr(fs, blk, block_buf); |
| + | if (retval) |
| + | goto errout; |
| + | |
| + | header = (struct ext2_ext_attr_header *) block_buf; |
| + | header->h_refcount += adjust; |
| + | if (newcount) |
| + | *newcount = header->h_refcount; |
| + | |
| + | retval = ext2fs_write_ext_attr(fs, blk, block_buf); |
| + | if (retval) |
| + | goto errout; |
| + | |
| + | errout: |
| + | if (buf) |
| + | ext2fs_free_mem(&buf); |
| + | return retval; |
| + | } |
| @@ -0,0 +1,378 @@ | |
| + | /* |
| + | * fileio.c --- Simple file I/O routines |
| + | * |
| + | * Copyright (C) 1997 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | struct ext2_file { |
| + | errcode_t magic; |
| + | ext2_filsys fs; |
| + | ext2_ino_t ino; |
| + | struct ext2_inode inode; |
| + | int flags; |
| + | __u64 pos; |
| + | blk_t blockno; |
| + | blk_t physblock; |
| + | char *buf; |
| + | }; |
| + | |
| + | #define BMAP_BUFFER (file->buf + fs->blocksize) |
| + | |
| + | errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino, |
| + | struct ext2_inode *inode, |
| + | int flags, ext2_file_t *ret) |
| + | { |
| + | ext2_file_t file; |
| + | errcode_t retval; |
| + | |
| + | /* |
| + | * Don't let caller create or open a file for writing if the |
| + | * filesystem is read-only. |
| + | */ |
| + | if ((flags & (EXT2_FILE_WRITE | EXT2_FILE_CREATE)) && |
| + | !(fs->flags & EXT2_FLAG_RW)) |
| + | return EXT2_ET_RO_FILSYS; |
| + | |
| + | retval = ext2fs_get_mem(sizeof(struct ext2_file), &file); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | memset(file, 0, sizeof(struct ext2_file)); |
| + | file->magic = EXT2_ET_MAGIC_EXT2_FILE; |
| + | file->fs = fs; |
| + | file->ino = ino; |
| + | file->flags = flags & EXT2_FILE_MASK; |
| + | |
| + | if (inode) { |
| + | memcpy(&file->inode, inode, sizeof(struct ext2_inode)); |
| + | } else { |
| + | retval = ext2fs_read_inode(fs, ino, &file->inode); |
| + | if (retval) |
| + | goto fail; |
| + | } |
| + | |
| + | retval = ext2fs_get_mem(fs->blocksize * 3, &file->buf); |
| + | if (retval) |
| + | goto fail; |
| + | |
| + | *ret = file; |
| + | return 0; |
| + | |
| + | fail: |
| + | if (file->buf) |
| + | ext2fs_free_mem(&file->buf); |
| + | ext2fs_free_mem(&file); |
| + | return retval; |
| + | } |
| + | |
| + | errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino, |
| + | int flags, ext2_file_t *ret) |
| + | { |
| + | return ext2fs_file_open2(fs, ino, NULL, flags, ret); |
| + | } |
| + | |
| + | /* |
| + | * This function returns the filesystem handle of a file from the structure |
| + | */ |
| + | ext2_filsys ext2fs_file_get_fs(ext2_file_t file) |
| + | { |
| + | if (file->magic != EXT2_ET_MAGIC_EXT2_FILE) |
| + | return 0; |
| + | return file->fs; |
| + | } |
| + | |
| + | /* |
| + | * This function flushes the dirty block buffer out to disk if |
| + | * necessary. |
| + | */ |
| + | errcode_t ext2fs_file_flush(ext2_file_t file) |
| + | { |
| + | errcode_t retval; |
| + | ext2_filsys fs; |
| + | |
| + | EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); |
| + | fs = file->fs; |
| + | |
| + | if (!(file->flags & EXT2_FILE_BUF_VALID) || |
| + | !(file->flags & EXT2_FILE_BUF_DIRTY)) |
| + | return 0; |
| + | |
| + | /* |
| + | * OK, the physical block hasn't been allocated yet. |
| + | * Allocate it. |
| + | */ |
| + | if (!file->physblock) { |
| + | retval = ext2fs_bmap(fs, file->ino, &file->inode, |
| + | BMAP_BUFFER, file->ino ? BMAP_ALLOC : 0, |
| + | file->blockno, &file->physblock); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | |
| + | retval = io_channel_write_blk(fs->io, file->physblock, |
| + | 1, file->buf); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | file->flags &= ~EXT2_FILE_BUF_DIRTY; |
| + | |
| + | return retval; |
| + | } |
| + | |
| + | /* |
| + | * This function synchronizes the file's block buffer and the current |
| + | * file position, possibly invalidating block buffer if necessary |
| + | */ |
| + | static errcode_t sync_buffer_position(ext2_file_t file) |
| + | { |
| + | blk_t b; |
| + | errcode_t retval; |
| + | |
| + | b = file->pos / file->fs->blocksize; |
| + | if (b != file->blockno) { |
| + | retval = ext2fs_file_flush(file); |
| + | if (retval) |
| + | return retval; |
| + | file->flags &= ~EXT2_FILE_BUF_VALID; |
| + | } |
| + | file->blockno = b; |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * This function loads the file's block buffer with valid data from |
| + | * the disk as necessary. |
| + | * |
| + | * If dontfill is true, then skip initializing the buffer since we're |
| + | * going to be replacing its entire contents anyway. If set, then the |
| + | * function basically only sets file->physblock and EXT2_FILE_BUF_VALID |
| + | */ |
| + | #define DONTFILL 1 |
| + | static errcode_t load_buffer(ext2_file_t file, int dontfill) |
| + | { |
| + | ext2_filsys fs = file->fs; |
| + | errcode_t retval; |
| + | |
| + | if (!(file->flags & EXT2_FILE_BUF_VALID)) { |
| + | retval = ext2fs_bmap(fs, file->ino, &file->inode, |
| + | BMAP_BUFFER, 0, file->blockno, |
| + | &file->physblock); |
| + | if (retval) |
| + | return retval; |
| + | if (!dontfill) { |
| + | if (file->physblock) { |
| + | retval = io_channel_read_blk(fs->io, |
| + | file->physblock, |
| + | 1, file->buf); |
| + | if (retval) |
| + | return retval; |
| + | } else |
| + | memset(file->buf, 0, fs->blocksize); |
| + | } |
| + | file->flags |= EXT2_FILE_BUF_VALID; |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | errcode_t ext2fs_file_close(ext2_file_t file) |
| + | { |
| + | errcode_t retval; |
| + | |
| + | EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); |
| + | |
| + | retval = ext2fs_file_flush(file); |
| + | |
| + | if (file->buf) |
| + | ext2fs_free_mem(&file->buf); |
| + | ext2fs_free_mem(&file); |
| + | |
| + | return retval; |
| + | } |
| + | |
| + | |
| + | errcode_t ext2fs_file_read(ext2_file_t file, void *buf, |
| + | unsigned int wanted, unsigned int *got) |
| + | { |
| + | ext2_filsys fs; |
| + | errcode_t retval = 0; |
| + | unsigned int start, c, count = 0; |
| + | __u64 left; |
| + | char *ptr = (char *) buf; |
| + | |
| + | EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); |
| + | fs = file->fs; |
| + | |
| + | while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) { |
| + | retval = sync_buffer_position(file); |
| + | if (retval) |
| + | goto fail; |
| + | retval = load_buffer(file, 0); |
| + | if (retval) |
| + | goto fail; |
| + | |
| + | start = file->pos % fs->blocksize; |
| + | c = fs->blocksize - start; |
| + | if (c > wanted) |
| + | c = wanted; |
| + | left = EXT2_I_SIZE(&file->inode) - file->pos ; |
| + | if (c > left) |
| + | c = left; |
| + | |
| + | memcpy(ptr, file->buf+start, c); |
| + | file->pos += c; |
| + | ptr += c; |
| + | count += c; |
| + | wanted -= c; |
| + | } |
| + | |
| + | fail: |
| + | if (got) |
| + | *got = count; |
| + | return retval; |
| + | } |
| + | |
| + | |
| + | errcode_t ext2fs_file_write(ext2_file_t file, const void *buf, |
| + | unsigned int nbytes, unsigned int *written) |
| + | { |
| + | ext2_filsys fs; |
| + | errcode_t retval = 0; |
| + | unsigned int start, c, count = 0; |
| + | const char *ptr = (const char *) buf; |
| + | |
| + | EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); |
| + | fs = file->fs; |
| + | |
| + | if (!(file->flags & EXT2_FILE_WRITE)) |
| + | return EXT2_ET_FILE_RO; |
| + | |
| + | while (nbytes > 0) { |
| + | retval = sync_buffer_position(file); |
| + | if (retval) |
| + | goto fail; |
| + | |
| + | start = file->pos % fs->blocksize; |
| + | c = fs->blocksize - start; |
| + | if (c > nbytes) |
| + | c = nbytes; |
| + | |
| + | /* |
| + | * We only need to do a read-modify-update cycle if |
| + | * we're doing a partial write. |
| + | */ |
| + | retval = load_buffer(file, (c == fs->blocksize)); |
| + | if (retval) |
| + | goto fail; |
| + | |
| + | file->flags |= EXT2_FILE_BUF_DIRTY; |
| + | memcpy(file->buf+start, ptr, c); |
| + | file->pos += c; |
| + | ptr += c; |
| + | count += c; |
| + | nbytes -= c; |
| + | } |
| + | |
| + | fail: |
| + | if (written) |
| + | *written = count; |
| + | return retval; |
| + | } |
| + | |
| + | errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset, |
| + | int whence, __u64 *ret_pos) |
| + | { |
| + | EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); |
| + | |
| + | if (whence == EXT2_SEEK_SET) |
| + | file->pos = offset; |
| + | else if (whence == EXT2_SEEK_CUR) |
| + | file->pos += offset; |
| + | else if (whence == EXT2_SEEK_END) |
| + | file->pos = EXT2_I_SIZE(&file->inode) + offset; |
| + | else |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | |
| + | if (ret_pos) |
| + | *ret_pos = file->pos; |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset, |
| + | int whence, ext2_off_t *ret_pos) |
| + | { |
| + | __u64 loffset, ret_loffset; |
| + | errcode_t retval; |
| + | |
| + | loffset = offset; |
| + | retval = ext2fs_file_llseek(file, loffset, whence, &ret_loffset); |
| + | if (ret_pos) |
| + | *ret_pos = (ext2_off_t) ret_loffset; |
| + | return retval; |
| + | } |
| + | |
| + | |
| + | /* |
| + | * This function returns the size of the file, according to the inode |
| + | */ |
| + | errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size) |
| + | { |
| + | if (file->magic != EXT2_ET_MAGIC_EXT2_FILE) |
| + | return EXT2_ET_MAGIC_EXT2_FILE; |
| + | *ret_size = EXT2_I_SIZE(&file->inode); |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * This function returns the size of the file, according to the inode |
| + | */ |
| + | ext2_off_t ext2fs_file_get_size(ext2_file_t file) |
| + | { |
| + | __u64 size; |
| + | |
| + | if (ext2fs_file_get_lsize(file, &size)) |
| + | return 0; |
| + | if ((size >> 32) != 0) |
| + | return 0; |
| + | return size; |
| + | } |
| + | |
| + | /* |
| + | * This function sets the size of the file, truncating it if necessary |
| + | * |
| + | * XXX still need to call truncate |
| + | */ |
| + | errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size) |
| + | { |
| + | errcode_t retval; |
| + | EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); |
| + | |
| + | file->inode.i_size = size; |
| + | file->inode.i_size_high = 0; |
| + | if (file->ino) { |
| + | retval = ext2fs_write_inode(file->fs, file->ino, &file->inode); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | |
| + | /* |
| + | * XXX truncate inode if necessary |
| + | */ |
| + | |
| + | return 0; |
| + | } |
| @@ -0,0 +1,208 @@ | |
| + | /* |
| + | * finddev.c -- this routine attempts to find a particular device in |
| + | * /dev |
| + | * |
| + | * Copyright (C) 2000 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <stdlib.h> |
| + | #include <string.h> |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #include <dirent.h> |
| + | #if HAVE_ERRNO_H |
| + | #include <errno.h> |
| + | #endif |
| + | #if HAVE_SYS_MKDEV_H |
| + | #include <sys/mkdev.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | struct dir_list { |
| + | char *name; |
| + | struct dir_list *next; |
| + | }; |
| + | |
| + | /* |
| + | * This function adds an entry to the directory list |
| + | */ |
| + | static void add_to_dirlist(const char *name, struct dir_list **list) |
| + | { |
| + | struct dir_list *dp; |
| + | |
| + | dp = malloc(sizeof(struct dir_list)); |
| + | if (!dp) |
| + | return; |
| + | dp->name = malloc(strlen(name)+1); |
| + | if (!dp->name) { |
| + | free(dp); |
| + | return; |
| + | } |
| + | strcpy(dp->name, name); |
| + | dp->next = *list; |
| + | *list = dp; |
| + | } |
| + | |
| + | /* |
| + | * This function frees a directory list |
| + | */ |
| + | static void free_dirlist(struct dir_list **list) |
| + | { |
| + | struct dir_list *dp, *next; |
| + | |
| + | for (dp = *list; dp; dp = next) { |
| + | next = dp->next; |
| + | free(dp->name); |
| + | free(dp); |
| + | } |
| + | *list = 0; |
| + | } |
| + | |
| + | static int scan_dir(char *dir_name, dev_t device, struct dir_list **list, |
| + | char **ret_path) |
| + | { |
| + | DIR *dir; |
| + | struct dirent *dp; |
| + | char path[1024], *cp; |
| + | int dirlen; |
| + | struct stat st; |
| + | |
| + | dirlen = strlen(dir_name); |
| + | if ((dir = opendir(dir_name)) == NULL) |
| + | return errno; |
| + | dp = readdir(dir); |
| + | while (dp) { |
| + | if (dirlen + strlen(dp->d_name) + 2 >= sizeof(path)) |
| + | goto skip_to_next; |
| + | if (dp->d_name[0] == '.' && |
| + | ((dp->d_name[1] == 0) || |
| + | ((dp->d_name[1] == '.') && (dp->d_name[2] == 0)))) |
| + | goto skip_to_next; |
| + | sprintf(path, "%s/%s", dir_name, dp->d_name); |
| + | if (stat(path, &st) < 0) |
| + | goto skip_to_next; |
| + | if (S_ISDIR(st.st_mode)) |
| + | add_to_dirlist(path, list); |
| + | if (S_ISBLK(st.st_mode) && st.st_rdev == device) { |
| + | cp = malloc(strlen(path)+1); |
| + | if (!cp) { |
| + | closedir(dir); |
| + | return ENOMEM; |
| + | } |
| + | strcpy(cp, path); |
| + | *ret_path = cp; |
| + | goto success; |
| + | } |
| + | skip_to_next: |
| + | dp = readdir(dir); |
| + | } |
| + | success: |
| + | closedir(dir); |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * This function finds the pathname to a block device with a given |
| + | * device number. It returns a pointer to allocated memory to the |
| + | * pathname on success, and NULL on failure. |
| + | */ |
| + | char *ext2fs_find_block_device(dev_t device) |
| + | { |
| + | struct dir_list *list = 0, *new_list = 0; |
| + | struct dir_list *current; |
| + | char *ret_path = 0; |
| + | |
| + | /* |
| + | * Add the starting directories to search... |
| + | */ |
| + | add_to_dirlist("/devices", &list); |
| + | add_to_dirlist("/devfs", &list); |
| + | add_to_dirlist("/dev", &list); |
| + | |
| + | while (list) { |
| + | current = list; |
| + | list = list->next; |
| + | #ifdef DEBUG |
| + | printf("Scanning directory %s\n", current->name); |
| + | #endif |
| + | scan_dir(current->name, device, &new_list, &ret_path); |
| + | free(current->name); |
| + | free(current); |
| + | if (ret_path) |
| + | break; |
| + | /* |
| + | * If we're done checking at this level, descend to |
| + | * the next level of subdirectories. (breadth-first) |
| + | */ |
| + | if (list == 0) { |
| + | list = new_list; |
| + | new_list = 0; |
| + | } |
| + | } |
| + | free_dirlist(&list); |
| + | free_dirlist(&new_list); |
| + | return ret_path; |
| + | } |
| + | |
| + | |
| + | #ifdef DEBUG |
| + | int main(int argc, char** argv) |
| + | { |
| + | char *devname, *tmp; |
| + | int major, minor; |
| + | dev_t device; |
| + | const char *errmsg = "Couldn't parse %s: %s\n"; |
| + | |
| + | if ((argc != 2) && (argc != 3)) { |
| + | fprintf(stderr, "Usage: %s device_number\n", argv[0]); |
| + | fprintf(stderr, "\t: %s major minor\n", argv[0]); |
| + | exit(1); |
| + | } |
| + | if (argc == 2) { |
| + | device = strtoul(argv[1], &tmp, 0); |
| + | if (*tmp) { |
| + | fprintf(stderr, errmsg, "device number", argv[1]); |
| + | exit(1); |
| + | } |
| + | } else { |
| + | major = strtoul(argv[1], &tmp, 0); |
| + | if (*tmp) { |
| + | fprintf(stderr, errmsg, "major number", argv[1]); |
| + | exit(1); |
| + | } |
| + | minor = strtoul(argv[2], &tmp, 0); |
| + | if (*tmp) { |
| + | fprintf(stderr, errmsg, "minor number", argv[2]); |
| + | exit(1); |
| + | } |
| + | device = makedev(major, minor); |
| + | printf("Looking for device 0x%04x (%d:%d)\n", device, |
| + | major, minor); |
| + | } |
| + | devname = ext2fs_find_block_device(device); |
| + | if (devname) { |
| + | printf("Found device! %s\n", devname); |
| + | free(devname); |
| + | } else { |
| + | printf("Couldn't find device.\n"); |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | #endif |
| @@ -0,0 +1,82 @@ | |
| + | /* |
| + | * flushb.c --- Hides system-dependent information for both syncing a |
| + | * device to disk and to flush any buffers from disk cache. |
| + | * |
| + | * Copyright (C) 2000 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #if HAVE_ERRNO_H |
| + | #include <errno.h> |
| + | #endif |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #if HAVE_SYS_IOCTL_H |
| + | #include <sys/ioctl.h> |
| + | #endif |
| + | #if HAVE_SYS_MOUNT_H |
| + | #include <sys/param.h> |
| + | #include <sys/mount.h> /* This may define BLKFLSBUF */ |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | /* |
| + | * For Linux, define BLKFLSBUF and FDFLUSH if necessary, since |
| + | * not all portable header file does so for us. This really should be |
| + | * fixed in the glibc header files. (Recent glibcs appear to define |
| + | * BLKFLSBUF in sys/mount.h, but FDFLUSH still doesn't seem to be |
| + | * defined anywhere portable.) Until then.... |
| + | */ |
| + | #ifdef __linux__ |
| + | #ifndef BLKFLSBUF |
| + | #define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */ |
| + | #endif |
| + | #ifndef FDFLUSH |
| + | #define FDFLUSH _IO(2,0x4b) /* flush floppy disk */ |
| + | #endif |
| + | #endif |
| + | |
| + | /* |
| + | * This function will sync a device/file, and optionally attempt to |
| + | * flush the buffer cache. The latter is basically only useful for |
| + | * system benchmarks and for torturing systems in burn-in tests. :) |
| + | */ |
| + | errcode_t ext2fs_sync_device(int fd, int flushb) |
| + | { |
| + | /* |
| + | * We always sync the device in case we're running on old |
| + | * kernels for which we can lose data if we don't. (There |
| + | * still is a race condition for those kernels, but this |
| + | * reduces it greatly.) |
| + | */ |
| + | if (fsync (fd) == -1) |
| + | return errno; |
| + | |
| + | if (flushb) { |
| + | |
| + | #ifdef BLKFLSBUF |
| + | if (ioctl (fd, BLKFLSBUF, 0) == 0) |
| + | return 0; |
| + | #else |
| + | #ifdef __GNUC__ |
| + | #warning BLKFLSBUF not defined |
| + | #endif /* __GNUC__ */ |
| + | #endif |
| + | #ifdef FDFLUSH |
| + | ioctl (fd, FDFLUSH, 0); /* In case this is a floppy */ |
| + | #else |
| + | #ifdef __GNUC__ |
| + | #warning FDFLUSH not defined |
| + | #endif /* __GNUC__ */ |
| + | #endif |
| + | } |
| + | return 0; |
| + | } |
| @@ -0,0 +1,147 @@ | |
| + | /* |
| + | * freefs.c --- free an ext2 filesystem |
| + | * |
| + | * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fsP.h" |
| + | |
| + | static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache); |
| + | |
| + | void ext2fs_free(ext2_filsys fs) |
| + | { |
| + | if (!fs || (fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS)) |
| + | return; |
| + | if (fs->image_io != fs->io) { |
| + | if (fs->image_io) |
| + | io_channel_close(fs->image_io); |
| + | } |
| + | if (fs->io) { |
| + | io_channel_close(fs->io); |
| + | } |
| + | if (fs->device_name) |
| + | ext2fs_free_mem(&fs->device_name); |
| + | if (fs->super) |
| + | ext2fs_free_mem(&fs->super); |
| + | if (fs->orig_super) |
| + | ext2fs_free_mem(&fs->orig_super); |
| + | if (fs->group_desc) |
| + | ext2fs_free_mem(&fs->group_desc); |
| + | if (fs->block_map) |
| + | ext2fs_free_block_bitmap(fs->block_map); |
| + | if (fs->inode_map) |
| + | ext2fs_free_inode_bitmap(fs->inode_map); |
| + | |
| + | if (fs->badblocks) |
| + | ext2fs_badblocks_list_free(fs->badblocks); |
| + | fs->badblocks = 0; |
| + | |
| + | if (fs->dblist) |
| + | ext2fs_free_dblist(fs->dblist); |
| + | |
| + | if (fs->icache) |
| + | ext2fs_free_inode_cache(fs->icache); |
| + | |
| + | fs->magic = 0; |
| + | |
| + | ext2fs_free_mem(&fs); |
| + | } |
| + | |
| + | void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap) |
| + | { |
| + | if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_GENERIC_BITMAP)) |
| + | return; |
| + | |
| + | bitmap->magic = 0; |
| + | if (bitmap->description) { |
| + | ext2fs_free_mem(&bitmap->description); |
| + | bitmap->description = 0; |
| + | } |
| + | if (bitmap->bitmap) { |
| + | ext2fs_free_mem(&bitmap->bitmap); |
| + | bitmap->bitmap = 0; |
| + | } |
| + | ext2fs_free_mem(&bitmap); |
| + | } |
| + | |
| + | void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap) |
| + | { |
| + | if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_INODE_BITMAP)) |
| + | return; |
| + | |
| + | bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP; |
| + | ext2fs_free_generic_bitmap(bitmap); |
| + | } |
| + | |
| + | void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap) |
| + | { |
| + | if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_BLOCK_BITMAP)) |
| + | return; |
| + | |
| + | bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP; |
| + | ext2fs_free_generic_bitmap(bitmap); |
| + | } |
| + | |
| + | /* |
| + | * Free the inode cache structure |
| + | */ |
| + | static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache) |
| + | { |
| + | if (--icache->refcount) |
| + | return; |
| + | if (icache->buffer) |
| + | ext2fs_free_mem(&icache->buffer); |
| + | if (icache->cache) |
| + | ext2fs_free_mem(&icache->cache); |
| + | icache->buffer_blk = 0; |
| + | ext2fs_free_mem(&icache); |
| + | } |
| + | |
| + | /* |
| + | * This procedure frees a badblocks list. |
| + | */ |
| + | void ext2fs_u32_list_free(ext2_u32_list bb) |
| + | { |
| + | if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) |
| + | return; |
| + | |
| + | if (bb->list) |
| + | ext2fs_free_mem(&bb->list); |
| + | bb->list = 0; |
| + | ext2fs_free_mem(&bb); |
| + | } |
| + | |
| + | void ext2fs_badblocks_list_free(ext2_badblocks_list bb) |
| + | { |
| + | ext2fs_u32_list_free((ext2_u32_list) bb); |
| + | } |
| + | |
| + | |
| + | /* |
| + | * Free a directory block list |
| + | */ |
| + | void ext2fs_free_dblist(ext2_dblist dblist) |
| + | { |
| + | if (!dblist || (dblist->magic != EXT2_ET_MAGIC_DBLIST)) |
| + | return; |
| + | |
| + | if (dblist->list) |
| + | ext2fs_free_mem(&dblist->list); |
| + | dblist->list = 0; |
| + | if (dblist->fs && dblist->fs->dblist == dblist) |
| + | dblist->fs->dblist = 0; |
| + | dblist->magic = 0; |
| + | ext2fs_free_mem(&dblist); |
| + | } |
| + | |
| @@ -0,0 +1,48 @@ | |
| + | /* |
| + | * gen_bitmap.c --- Generic bitmap routines that used to be inlined. |
| + | * |
| + | * Copyright (C) 2001 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap, |
| + | __u32 bitno) |
| + | { |
| + | if ((bitno < bitmap->start) || (bitno > bitmap->end)) { |
| + | ext2fs_warn_bitmap2(bitmap, EXT2FS_MARK_ERROR, bitno); |
| + | return 0; |
| + | } |
| + | return ext2fs_set_bit(bitno - bitmap->start, bitmap->bitmap); |
| + | } |
| + | |
| + | int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap, |
| + | blk_t bitno) |
| + | { |
| + | if ((bitno < bitmap->start) || (bitno > bitmap->end)) { |
| + | ext2fs_warn_bitmap2(bitmap, EXT2FS_UNMARK_ERROR, bitno); |
| + | return 0; |
| + | } |
| + | return ext2fs_clear_bit(bitno - bitmap->start, bitmap->bitmap); |
| + | } |
| @@ -0,0 +1,157 @@ | |
| + | /* |
| + | * get_pathname.c --- do directry/inode -> name translation |
| + | * |
| + | * Copyright (C) 1993, 1994, 1995 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | * |
| + | * ext2fs_get_pathname(fs, dir, ino, name) |
| + | * |
| + | * This function translates takes two inode numbers into a |
| + | * string, placing the result in <name>. <dir> is the containing |
| + | * directory inode, and <ino> is the inode number itself. If |
| + | * <ino> is zero, then ext2fs_get_pathname will return pathname |
| + | * of the the directory <dir>. |
| + | * |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | struct get_pathname_struct { |
| + | ext2_ino_t search_ino; |
| + | ext2_ino_t parent; |
| + | char *name; |
| + | errcode_t errcode; |
| + | }; |
| + | |
| + | #ifdef __TURBOC__ |
| + | #pragma argsused |
| + | #endif |
| + | static int get_pathname_proc(struct ext2_dir_entry *dirent, |
| + | int offset EXT2FS_ATTR((unused)), |
| + | int blocksize EXT2FS_ATTR((unused)), |
| + | char *buf EXT2FS_ATTR((unused)), |
| + | void *priv_data) |
| + | { |
| + | struct get_pathname_struct *gp; |
| + | errcode_t retval; |
| + | |
| + | gp = (struct get_pathname_struct *) priv_data; |
| + | |
| + | if (((dirent->name_len & 0xFF) == 2) && |
| + | !strncmp(dirent->name, "..", 2)) |
| + | gp->parent = dirent->inode; |
| + | if (dirent->inode == gp->search_ino) { |
| + | retval = ext2fs_get_mem((dirent->name_len & 0xFF) + 1, |
| + | &gp->name); |
| + | if (retval) { |
| + | gp->errcode = retval; |
| + | return DIRENT_ABORT; |
| + | } |
| + | strncpy(gp->name, dirent->name, (dirent->name_len & 0xFF)); |
| + | gp->name[dirent->name_len & 0xFF] = '\0'; |
| + | return DIRENT_ABORT; |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t ext2fs_get_pathname_int(ext2_filsys fs, ext2_ino_t dir, |
| + | ext2_ino_t ino, int maxdepth, |
| + | char *buf, char **name) |
| + | { |
| + | struct get_pathname_struct gp; |
| + | char *parent_name, *ret; |
| + | errcode_t retval; |
| + | |
| + | if (dir == ino) { |
| + | retval = ext2fs_get_mem(2, name); |
| + | if (retval) |
| + | return retval; |
| + | strcpy(*name, (dir == EXT2_ROOT_INO) ? "/" : "."); |
| + | return 0; |
| + | } |
| + | |
| + | if (!dir || (maxdepth < 0)) { |
| + | retval = ext2fs_get_mem(4, name); |
| + | if (retval) |
| + | return retval; |
| + | strcpy(*name, "..."); |
| + | return 0; |
| + | } |
| + | |
| + | gp.search_ino = ino; |
| + | gp.parent = 0; |
| + | gp.name = 0; |
| + | gp.errcode = 0; |
| + | |
| + | retval = ext2fs_dir_iterate(fs, dir, 0, buf, get_pathname_proc, &gp); |
| + | if (retval) |
| + | goto cleanup; |
| + | if (gp.errcode) { |
| + | retval = gp.errcode; |
| + | goto cleanup; |
| + | } |
| + | |
| + | retval = ext2fs_get_pathname_int(fs, gp.parent, dir, maxdepth-1, |
| + | buf, &parent_name); |
| + | if (retval) |
| + | goto cleanup; |
| + | if (!ino) { |
| + | *name = parent_name; |
| + | return 0; |
| + | } |
| + | |
| + | if (gp.name) |
| + | retval = ext2fs_get_mem(strlen(parent_name)+strlen(gp.name)+2, |
| + | &ret); |
| + | else |
| + | retval = ext2fs_get_mem(strlen(parent_name)+5, &ret); |
| + | if (retval) |
| + | goto cleanup; |
| + | |
| + | ret[0] = 0; |
| + | if (parent_name[1]) |
| + | strcat(ret, parent_name); |
| + | strcat(ret, "/"); |
| + | if (gp.name) |
| + | strcat(ret, gp.name); |
| + | else |
| + | strcat(ret, "???"); |
| + | *name = ret; |
| + | ext2fs_free_mem(&parent_name); |
| + | retval = 0; |
| + | |
| + | cleanup: |
| + | if (gp.name) |
| + | ext2fs_free_mem(&gp.name); |
| + | return retval; |
| + | } |
| + | |
| + | errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino, |
| + | char **name) |
| + | { |
| + | char *buf; |
| + | errcode_t retval; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | retval = ext2fs_get_mem(fs->blocksize, &buf); |
| + | if (retval) |
| + | return retval; |
| + | if (dir == ino) |
| + | ino = 0; |
| + | retval = ext2fs_get_pathname_int(fs, dir, ino, 32, buf, name); |
| + | ext2fs_free_mem(&buf); |
| + | return retval; |
| + | |
| + | } |
| @@ -0,0 +1,57 @@ | |
| + | /* |
| + | * getsectsize.c --- get the sector size of a device. |
| + | * |
| + | * Copyright (C) 1995, 1995 Theodore Ts'o. |
| + | * Copyright (C) 2003 VMware, Inc. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #if HAVE_ERRNO_H |
| + | #include <errno.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #ifdef HAVE_LINUX_FD_H |
| + | #include <sys/ioctl.h> |
| + | #include <linux/fd.h> |
| + | #endif |
| + | |
| + | #if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) |
| + | #define BLKSSZGET _IO(0x12,104)/* get block device sector size */ |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | /* |
| + | * Returns the number of blocks in a partition |
| + | */ |
| + | errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize) |
| + | { |
| + | int fd; |
| + | |
| + | #ifdef CONFIG_LFS |
| + | fd = open64(file, O_RDONLY); |
| + | #else |
| + | fd = open(file, O_RDONLY); |
| + | #endif |
| + | if (fd < 0) |
| + | return errno; |
| + | |
| + | #ifdef BLKSSZGET |
| + | if (ioctl(fd, BLKSSZGET, sectsize) >= 0) { |
| + | close(fd); |
| + | return 0; |
| + | } |
| + | #endif |
| + | *sectsize = 0; |
| + | close(fd); |
| + | return 0; |
| + | } |
| @@ -0,0 +1,290 @@ | |
| + | /* |
| + | * getsize.c --- get the size of a partition. |
| + | * |
| + | * Copyright (C) 1995, 1995 Theodore Ts'o. |
| + | * Copyright (C) 2003 VMware, Inc. |
| + | * |
| + | * Windows version of ext2fs_get_device_size by Chris Li, VMware. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #if HAVE_ERRNO_H |
| + | #include <errno.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #ifdef HAVE_SYS_IOCTL_H |
| + | #include <sys/ioctl.h> |
| + | #endif |
| + | #ifdef HAVE_LINUX_FD_H |
| + | #include <linux/fd.h> |
| + | #endif |
| + | #ifdef HAVE_SYS_DISKLABEL_H |
| + | #include <sys/disklabel.h> |
| + | #endif |
| + | #ifdef HAVE_SYS_DISK_H |
| + | #ifdef HAVE_SYS_QUEUE_H |
| + | #include <sys/queue.h> /* for LIST_HEAD */ |
| + | #endif |
| + | #include <sys/disk.h> |
| + | #endif |
| + | #ifdef __linux__ |
| + | #include <sys/utsname.h> |
| + | #endif |
| + | |
| + | #if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) |
| + | #define BLKGETSIZE _IO(0x12,96) /* return device size */ |
| + | #endif |
| + | |
| + | #if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64) |
| + | #define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ |
| + | #endif |
| + | |
| + | #ifdef APPLE_DARWIN |
| + | #define BLKGETSIZE DKIOCGETBLOCKCOUNT32 |
| + | #endif /* APPLE_DARWIN */ |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | #if defined(__CYGWIN__) || defined (WIN32) |
| + | #include "windows.h" |
| + | #include "winioctl.h" |
| + | |
| + | #if (_WIN32_WINNT >= 0x0500) |
| + | #define HAVE_GET_FILE_SIZE_EX 1 |
| + | #endif |
| + | |
| + | errcode_t ext2fs_get_device_size(const char *file, int blocksize, |
| + | blk_t *retblocks) |
| + | { |
| + | HANDLE dev; |
| + | PARTITION_INFORMATION pi; |
| + | DISK_GEOMETRY gi; |
| + | DWORD retbytes; |
| + | #ifdef HAVE_GET_FILE_SIZE_EX |
| + | LARGE_INTEGER filesize; |
| + | #else |
| + | DWORD filesize; |
| + | #endif /* HAVE_GET_FILE_SIZE_EX */ |
| + | |
| + | dev = CreateFile(file, GENERIC_READ, |
| + | FILE_SHARE_READ | FILE_SHARE_WRITE , |
| + | NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); |
| + | |
| + | if (dev == INVALID_HANDLE_VALUE) |
| + | return EBADF; |
| + | if (DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, |
| + | &pi, sizeof(PARTITION_INFORMATION), |
| + | &pi, sizeof(PARTITION_INFORMATION), |
| + | &retbytes, NULL)) { |
| + | |
| + | *retblocks = pi.PartitionLength.QuadPart / blocksize; |
| + | |
| + | } else if (DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, |
| + | &gi, sizeof(DISK_GEOMETRY), |
| + | &gi, sizeof(DISK_GEOMETRY), |
| + | &retbytes, NULL)) { |
| + | |
| + | *retblocks = gi.BytesPerSector * |
| + | gi.SectorsPerTrack * |
| + | gi.TracksPerCylinder * |
| + | gi.Cylinders.QuadPart / blocksize; |
| + | |
| + | #ifdef HAVE_GET_FILE_SIZE_EX |
| + | } else if (GetFileSizeEx(dev, &filesize)) { |
| + | *retblocks = filesize.QuadPart / blocksize; |
| + | } |
| + | #else |
| + | } else { |
| + | filesize = GetFileSize(dev, NULL); |
| + | if (INVALID_FILE_SIZE != filesize) { |
| + | *retblocks = filesize / blocksize; |
| + | } |
| + | } |
| + | #endif /* HAVE_GET_FILE_SIZE_EX */ |
| + | |
| + | CloseHandle(dev); |
| + | return 0; |
| + | } |
| + | |
| + | #else |
| + | |
| + | static int valid_offset (int fd, ext2_loff_t offset) |
| + | { |
| + | char ch; |
| + | |
| + | if (ext2fs_llseek (fd, offset, 0) < 0) |
| + | return 0; |
| + | if (read (fd, &ch, 1) < 1) |
| + | return 0; |
| + | return 1; |
| + | } |
| + | |
| + | /* |
| + | * Returns the number of blocks in a partition |
| + | */ |
| + | errcode_t ext2fs_get_device_size(const char *file, int blocksize, |
| + | blk_t *retblocks) |
| + | { |
| + | int fd; |
| + | int valid_blkgetsize64 = 1; |
| + | #ifdef __linux__ |
| + | struct utsname ut; |
| + | #endif |
| + | unsigned long long size64; |
| + | unsigned long size; |
| + | ext2_loff_t high, low; |
| + | #ifdef FDGETPRM |
| + | struct floppy_struct this_floppy; |
| + | #endif |
| + | #ifdef HAVE_SYS_DISKLABEL_H |
| + | int part; |
| + | struct disklabel lab; |
| + | struct partition *pp; |
| + | char ch; |
| + | #endif /* HAVE_SYS_DISKLABEL_H */ |
| + | |
| + | #ifdef CONFIG_LFS |
| + | fd = open64(file, O_RDONLY); |
| + | #else |
| + | fd = open(file, O_RDONLY); |
| + | #endif |
| + | if (fd < 0) |
| + | return errno; |
| + | |
| + | #ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */ |
| + | if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) { |
| + | if ((sizeof(*retblocks) < sizeof(unsigned long long)) |
| + | && ((size64 / (blocksize / 512)) > 0xFFFFFFFF)) |
| + | return EFBIG; |
| + | close(fd); |
| + | *retblocks = size64 / (blocksize / 512); |
| + | return 0; |
| + | } |
| + | #endif |
| + | |
| + | #ifdef BLKGETSIZE64 |
| + | #ifdef __linux__ |
| + | if ((uname(&ut) == 0) && |
| + | ((ut.release[0] == '2') && (ut.release[1] == '.') && |
| + | (ut.release[2] < '6') && (ut.release[3] == '.'))) |
| + | valid_blkgetsize64 = 0; |
| + | #endif |
| + | if (valid_blkgetsize64 && |
| + | ioctl(fd, BLKGETSIZE64, &size64) >= 0) { |
| + | if ((sizeof(*retblocks) < sizeof(unsigned long long)) |
| + | && ((size64 / blocksize) > 0xFFFFFFFF)) |
| + | return EFBIG; |
| + | close(fd); |
| + | *retblocks = size64 / blocksize; |
| + | return 0; |
| + | } |
| + | #endif |
| + | |
| + | #ifdef BLKGETSIZE |
| + | if (ioctl(fd, BLKGETSIZE, &size) >= 0) { |
| + | close(fd); |
| + | *retblocks = size / (blocksize / 512); |
| + | return 0; |
| + | } |
| + | #endif |
| + | |
| + | #ifdef FDGETPRM |
| + | if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) { |
| + | close(fd); |
| + | *retblocks = this_floppy.size / (blocksize / 512); |
| + | return 0; |
| + | } |
| + | #endif |
| + | |
| + | #ifdef HAVE_SYS_DISKLABEL_H |
| + | #if defined(DIOCGMEDIASIZE) |
| + | { |
| + | off_t ms; |
| + | u_int bs; |
| + | if (ioctl(fd, DIOCGMEDIASIZE, &ms) >= 0) { |
| + | *retblocks = ms / blocksize; |
| + | return 0; |
| + | } |
| + | } |
| + | #elif defined(DIOCGDINFO) |
| + | /* old disklabel interface */ |
| + | part = strlen(file) - 1; |
| + | if (part >= 0) { |
| + | ch = file[part]; |
| + | if (isdigit(ch)) |
| + | part = 0; |
| + | else if (ch >= 'a' && ch <= 'h') |
| + | part = ch - 'a'; |
| + | else |
| + | part = -1; |
| + | } |
| + | if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { |
| + | pp = &lab.d_partitions[part]; |
| + | if (pp->p_size) { |
| + | close(fd); |
| + | *retblocks = pp->p_size / (blocksize / 512); |
| + | return 0; |
| + | } |
| + | } |
| + | #endif /* defined(DIOCG*) */ |
| + | #endif /* HAVE_SYS_DISKLABEL_H */ |
| + | |
| + | /* |
| + | * OK, we couldn't figure it out by using a specialized ioctl, |
| + | * which is generally the best way. So do binary search to |
| + | * find the size of the partition. |
| + | */ |
| + | low = 0; |
| + | for (high = 1024; valid_offset (fd, high); high *= 2) |
| + | low = high; |
| + | while (low < high - 1) |
| + | { |
| + | const ext2_loff_t mid = (low + high) / 2; |
| + | |
| + | if (valid_offset (fd, mid)) |
| + | low = mid; |
| + | else |
| + | high = mid; |
| + | } |
| + | valid_offset (fd, 0); |
| + | close(fd); |
| + | size64 = low + 1; |
| + | if ((sizeof(*retblocks) < sizeof(unsigned long long)) |
| + | && ((size64 / blocksize) > 0xFFFFFFFF)) |
| + | return EFBIG; |
| + | *retblocks = size64 / blocksize; |
| + | return 0; |
| + | } |
| + | |
| + | #endif /* WIN32 */ |
| + | |
| + | #ifdef DEBUG |
| + | int main(int argc, char **argv) |
| + | { |
| + | blk_t blocks; |
| + | int retval; |
| + | |
| + | if (argc < 2) { |
| + | fprintf(stderr, "Usage: %s device\n", argv[0]); |
| + | exit(1); |
| + | } |
| + | |
| + | retval = ext2fs_get_device_size(argv[1], 1024, &blocks); |
| + | if (retval) { |
| + | com_err(argv[0], retval, |
| + | "while calling ext2fs_get_device_size"); |
| + | exit(1); |
| + | } |
| + | printf("Device %s has %d 1k blocks.\n", argv[1], blocks); |
| + | exit(0); |
| + | } |
| + | #endif |
| @@ -0,0 +1,483 @@ | |
| + | /* |
| + | * icount.c --- an efficient inode count abstraction |
| + | * |
| + | * Copyright (C) 1997 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <string.h> |
| + | #include <stdio.h> |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | /* |
| + | * The data storage strategy used by icount relies on the observation |
| + | * that most inode counts are either zero (for non-allocated inodes), |
| + | * one (for most files), and only a few that are two or more |
| + | * (directories and files that are linked to more than one directory). |
| + | * |
| + | * Also, e2fsck tends to load the icount data sequentially. |
| + | * |
| + | * So, we use an inode bitmap to indicate which inodes have a count of |
| + | * one, and then use a sorted list to store the counts for inodes |
| + | * which are greater than one. |
| + | * |
| + | * We also use an optional bitmap to indicate which inodes are already |
| + | * in the sorted list, to speed up the use of this abstraction by |
| + | * e2fsck's pass 2. Pass 2 increments inode counts as it finds them, |
| + | * so this extra bitmap avoids searching the sorted list to see if a |
| + | * particular inode is on the sorted list already. |
| + | */ |
| + | |
| + | struct ext2_icount_el { |
| + | ext2_ino_t ino; |
| + | __u16 count; |
| + | }; |
| + | |
| + | struct ext2_icount { |
| + | errcode_t magic; |
| + | ext2fs_inode_bitmap single; |
| + | ext2fs_inode_bitmap multiple; |
| + | ext2_ino_t count; |
| + | ext2_ino_t size; |
| + | ext2_ino_t num_inodes; |
| + | ext2_ino_t cursor; |
| + | struct ext2_icount_el *list; |
| + | }; |
| + | |
| + | void ext2fs_free_icount(ext2_icount_t icount) |
| + | { |
| + | if (!icount) |
| + | return; |
| + | |
| + | icount->magic = 0; |
| + | if (icount->list) |
| + | ext2fs_free_mem(&icount->list); |
| + | if (icount->single) |
| + | ext2fs_free_inode_bitmap(icount->single); |
| + | if (icount->multiple) |
| + | ext2fs_free_inode_bitmap(icount->multiple); |
| + | ext2fs_free_mem(&icount); |
| + | } |
| + | |
| + | errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size, |
| + | ext2_icount_t hint, ext2_icount_t *ret) |
| + | { |
| + | ext2_icount_t icount; |
| + | errcode_t retval; |
| + | size_t bytes; |
| + | ext2_ino_t i; |
| + | |
| + | if (hint) { |
| + | EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT); |
| + | if (hint->size > size) |
| + | size = (size_t) hint->size; |
| + | } |
| + | |
| + | retval = ext2fs_get_mem(sizeof(struct ext2_icount), &icount); |
| + | if (retval) |
| + | return retval; |
| + | memset(icount, 0, sizeof(struct ext2_icount)); |
| + | |
| + | retval = ext2fs_allocate_inode_bitmap(fs, 0, |
| + | &icount->single); |
| + | if (retval) |
| + | goto errout; |
| + | |
| + | if (flags & EXT2_ICOUNT_OPT_INCREMENT) { |
| + | retval = ext2fs_allocate_inode_bitmap(fs, 0, |
| + | &icount->multiple); |
| + | if (retval) |
| + | goto errout; |
| + | } else |
| + | icount->multiple = 0; |
| + | |
| + | if (size) { |
| + | icount->size = size; |
| + | } else { |
| + | /* |
| + | * Figure out how many special case inode counts we will |
| + | * have. We know we will need one for each directory; |
| + | * we also need to reserve some extra room for file links |
| + | */ |
| + | retval = ext2fs_get_num_dirs(fs, &icount->size); |
| + | if (retval) |
| + | goto errout; |
| + | icount->size += fs->super->s_inodes_count / 50; |
| + | } |
| + | |
| + | bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el)); |
| + | #if 0 |
| + | printf("Icount allocated %d entries, %d bytes.\n", |
| + | icount->size, bytes); |
| + | #endif |
| + | retval = ext2fs_get_mem(bytes, &icount->list); |
| + | if (retval) |
| + | goto errout; |
| + | memset(icount->list, 0, bytes); |
| + | |
| + | icount->magic = EXT2_ET_MAGIC_ICOUNT; |
| + | icount->count = 0; |
| + | icount->cursor = 0; |
| + | icount->num_inodes = fs->super->s_inodes_count; |
| + | |
| + | /* |
| + | * Populate the sorted list with those entries which were |
| + | * found in the hint icount (since those are ones which will |
| + | * likely need to be in the sorted list this time around). |
| + | */ |
| + | if (hint) { |
| + | for (i=0; i < hint->count; i++) |
| + | icount->list[i].ino = hint->list[i].ino; |
| + | icount->count = hint->count; |
| + | } |
| + | |
| + | *ret = icount; |
| + | return 0; |
| + | |
| + | errout: |
| + | ext2fs_free_icount(icount); |
| + | return(retval); |
| + | } |
| + | |
| + | errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, |
| + | unsigned int size, |
| + | ext2_icount_t *ret) |
| + | { |
| + | return ext2fs_create_icount2(fs, flags, size, 0, ret); |
| + | } |
| + | |
| + | /* |
| + | * insert_icount_el() --- Insert a new entry into the sorted list at a |
| + | * specified position. |
| + | */ |
| + | static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount, |
| + | ext2_ino_t ino, int pos) |
| + | { |
| + | struct ext2_icount_el *el; |
| + | errcode_t retval; |
| + | ext2_ino_t new_size = 0; |
| + | int num; |
| + | |
| + | if (icount->count >= icount->size) { |
| + | if (icount->count) { |
| + | new_size = icount->list[(unsigned)icount->count-1].ino; |
| + | new_size = (ext2_ino_t) (icount->count * |
| + | ((float) icount->num_inodes / new_size)); |
| + | } |
| + | if (new_size < (icount->size + 100)) |
| + | new_size = icount->size + 100; |
| + | #if 0 |
| + | printf("Reallocating icount %d entries...\n", new_size); |
| + | #endif |
| + | retval = ext2fs_resize_mem((size_t) icount->size * |
| + | sizeof(struct ext2_icount_el), |
| + | (size_t) new_size * |
| + | sizeof(struct ext2_icount_el), |
| + | &icount->list); |
| + | if (retval) |
| + | return 0; |
| + | icount->size = new_size; |
| + | } |
| + | num = (int) icount->count - pos; |
| + | if (num < 0) |
| + | return 0; /* should never happen */ |
| + | if (num) { |
| + | memmove(&icount->list[pos+1], &icount->list[pos], |
| + | sizeof(struct ext2_icount_el) * num); |
| + | } |
| + | icount->count++; |
| + | el = &icount->list[pos]; |
| + | el->count = 0; |
| + | el->ino = ino; |
| + | return el; |
| + | } |
| + | |
| + | /* |
| + | * get_icount_el() --- given an inode number, try to find icount |
| + | * information in the sorted list. If the create flag is set, |
| + | * and we can't find an entry, create one in the sorted list. |
| + | */ |
| + | static struct ext2_icount_el *get_icount_el(ext2_icount_t icount, |
| + | ext2_ino_t ino, int create) |
| + | { |
| + | float range; |
| + | int low, high, mid; |
| + | ext2_ino_t lowval, highval; |
| + | |
| + | if (!icount || !icount->list) |
| + | return 0; |
| + | |
| + | if (create && ((icount->count == 0) || |
| + | (ino > icount->list[(unsigned)icount->count-1].ino))) { |
| + | return insert_icount_el(icount, ino, (unsigned) icount->count); |
| + | } |
| + | if (icount->count == 0) |
| + | return 0; |
| + | |
| + | if (icount->cursor >= icount->count) |
| + | icount->cursor = 0; |
| + | if (ino == icount->list[icount->cursor].ino) |
| + | return &icount->list[icount->cursor++]; |
| + | #if 0 |
| + | printf("Non-cursor get_icount_el: %u\n", ino); |
| + | #endif |
| + | low = 0; |
| + | high = (int) icount->count-1; |
| + | while (low <= high) { |
| + | #if 0 |
| + | mid = (low+high)/2; |
| + | #else |
| + | if (low == high) |
| + | mid = low; |
| + | else { |
| + | /* Interpolate for efficiency */ |
| + | lowval = icount->list[low].ino; |
| + | highval = icount->list[high].ino; |
| + | |
| + | if (ino < lowval) |
| + | range = 0; |
| + | else if (ino > highval) |
| + | range = 1; |
| + | else |
| + | range = ((float) (ino - lowval)) / |
| + | (highval - lowval); |
| + | mid = low + ((int) (range * (high-low))); |
| + | } |
| + | #endif |
| + | if (ino == icount->list[mid].ino) { |
| + | icount->cursor = mid+1; |
| + | return &icount->list[mid]; |
| + | } |
| + | if (ino < icount->list[mid].ino) |
| + | high = mid-1; |
| + | else |
| + | low = mid+1; |
| + | } |
| + | /* |
| + | * If we need to create a new entry, it should be right at |
| + | * low (where high will be left at low-1). |
| + | */ |
| + | if (create) |
| + | return insert_icount_el(icount, ino, low); |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out) |
| + | { |
| + | errcode_t ret = 0; |
| + | unsigned int i; |
| + | const char *bad = "bad icount"; |
| + | |
| + | EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); |
| + | |
| + | if (icount->count > icount->size) { |
| + | fprintf(out, "%s: count > size\n", bad); |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | } |
| + | for (i=1; i < icount->count; i++) { |
| + | if (icount->list[i-1].ino >= icount->list[i].ino) { |
| + | fprintf(out, "%s: list[%d].ino=%u, list[%d].ino=%u\n", |
| + | bad, i-1, icount->list[i-1].ino, |
| + | i, icount->list[i].ino); |
| + | ret = EXT2_ET_INVALID_ARGUMENT; |
| + | } |
| + | } |
| + | return ret; |
| + | } |
| + | |
| + | errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret) |
| + | { |
| + | struct ext2_icount_el *el; |
| + | |
| + | EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); |
| + | |
| + | if (!ino || (ino > icount->num_inodes)) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | |
| + | if (ext2fs_test_inode_bitmap(icount->single, ino)) { |
| + | *ret = 1; |
| + | return 0; |
| + | } |
| + | if (icount->multiple && |
| + | !ext2fs_test_inode_bitmap(icount->multiple, ino)) { |
| + | *ret = 0; |
| + | return 0; |
| + | } |
| + | el = get_icount_el(icount, ino, 0); |
| + | if (!el) { |
| + | *ret = 0; |
| + | return 0; |
| + | } |
| + | *ret = el->count; |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino, |
| + | __u16 *ret) |
| + | { |
| + | struct ext2_icount_el *el; |
| + | |
| + | EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); |
| + | |
| + | if (!ino || (ino > icount->num_inodes)) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | |
| + | if (ext2fs_test_inode_bitmap(icount->single, ino)) { |
| + | /* |
| + | * If the existing count is 1, then we know there is |
| + | * no entry in the list. |
| + | */ |
| + | el = get_icount_el(icount, ino, 1); |
| + | if (!el) |
| + | return EXT2_ET_NO_MEMORY; |
| + | ext2fs_unmark_inode_bitmap(icount->single, ino); |
| + | el->count = 2; |
| + | } else if (icount->multiple) { |
| + | /* |
| + | * The count is either zero or greater than 1; if the |
| + | * inode is set in icount->multiple, then there should |
| + | * be an entry in the list, so find it using |
| + | * get_icount_el(). |
| + | */ |
| + | if (ext2fs_test_inode_bitmap(icount->multiple, ino)) { |
| + | el = get_icount_el(icount, ino, 1); |
| + | if (!el) |
| + | return EXT2_ET_NO_MEMORY; |
| + | el->count++; |
| + | } else { |
| + | /* |
| + | * The count was zero; mark the single bitmap |
| + | * and return. |
| + | */ |
| + | zero_count: |
| + | ext2fs_mark_inode_bitmap(icount->single, ino); |
| + | if (ret) |
| + | *ret = 1; |
| + | return 0; |
| + | } |
| + | } else { |
| + | /* |
| + | * The count is either zero or greater than 1; try to |
| + | * find an entry in the list to determine which. |
| + | */ |
| + | el = get_icount_el(icount, ino, 0); |
| + | if (!el) { |
| + | /* No entry means the count was zero */ |
| + | goto zero_count; |
| + | } |
| + | el = get_icount_el(icount, ino, 1); |
| + | if (!el) |
| + | return EXT2_ET_NO_MEMORY; |
| + | el->count++; |
| + | } |
| + | if (icount->multiple) |
| + | ext2fs_mark_inode_bitmap(icount->multiple, ino); |
| + | if (ret) |
| + | *ret = el->count; |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino, |
| + | __u16 *ret) |
| + | { |
| + | struct ext2_icount_el *el; |
| + | |
| + | if (!ino || (ino > icount->num_inodes)) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | |
| + | EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); |
| + | |
| + | if (ext2fs_test_inode_bitmap(icount->single, ino)) { |
| + | ext2fs_unmark_inode_bitmap(icount->single, ino); |
| + | if (icount->multiple) |
| + | ext2fs_unmark_inode_bitmap(icount->multiple, ino); |
| + | else { |
| + | el = get_icount_el(icount, ino, 0); |
| + | if (el) |
| + | el->count = 0; |
| + | } |
| + | if (ret) |
| + | *ret = 0; |
| + | return 0; |
| + | } |
| + | |
| + | if (icount->multiple && |
| + | !ext2fs_test_inode_bitmap(icount->multiple, ino)) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | |
| + | el = get_icount_el(icount, ino, 0); |
| + | if (!el || el->count == 0) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | |
| + | el->count--; |
| + | if (el->count == 1) |
| + | ext2fs_mark_inode_bitmap(icount->single, ino); |
| + | if ((el->count == 0) && icount->multiple) |
| + | ext2fs_unmark_inode_bitmap(icount->multiple, ino); |
| + | |
| + | if (ret) |
| + | *ret = el->count; |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino, |
| + | __u16 count) |
| + | { |
| + | struct ext2_icount_el *el; |
| + | |
| + | if (!ino || (ino > icount->num_inodes)) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | |
| + | EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); |
| + | |
| + | if (count == 1) { |
| + | ext2fs_mark_inode_bitmap(icount->single, ino); |
| + | if (icount->multiple) |
| + | ext2fs_unmark_inode_bitmap(icount->multiple, ino); |
| + | return 0; |
| + | } |
| + | if (count == 0) { |
| + | ext2fs_unmark_inode_bitmap(icount->single, ino); |
| + | if (icount->multiple) { |
| + | /* |
| + | * If the icount->multiple bitmap is enabled, |
| + | * we can just clear both bitmaps and we're done |
| + | */ |
| + | ext2fs_unmark_inode_bitmap(icount->multiple, ino); |
| + | } else { |
| + | el = get_icount_el(icount, ino, 0); |
| + | if (el) |
| + | el->count = 0; |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * Get the icount element |
| + | */ |
| + | el = get_icount_el(icount, ino, 1); |
| + | if (!el) |
| + | return EXT2_ET_NO_MEMORY; |
| + | el->count = count; |
| + | ext2fs_unmark_inode_bitmap(icount->single, ino); |
| + | if (icount->multiple) |
| + | ext2fs_mark_inode_bitmap(icount->multiple, ino); |
| + | return 0; |
| + | } |
| + | |
| + | ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount) |
| + | { |
| + | if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT) |
| + | return 0; |
| + | |
| + | return icount->size; |
| + | } |
| @@ -0,0 +1,387 @@ | |
| + | /* |
| + | * image.c --- writes out the critical parts of the filesystem as a |
| + | * flat file. |
| + | * |
| + | * Copyright (C) 2000 Theodore Ts'o. |
| + | * |
| + | * Note: this uses the POSIX IO interfaces, unlike most of the other |
| + | * functions in this library. So sue me. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #if HAVE_ERRNO_H |
| + | #include <errno.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | #ifndef HAVE_TYPE_SSIZE_T |
| + | typedef int ssize_t; |
| + | #endif |
| + | |
| + | /* |
| + | * This function returns 1 if the specified block is all zeros |
| + | */ |
| + | static int check_zero_block(char *buf, int blocksize) |
| + | { |
| + | char *cp = buf; |
| + | int left = blocksize; |
| + | |
| + | while (left > 0) { |
| + | if (*cp++) |
| + | return 0; |
| + | left--; |
| + | } |
| + | return 1; |
| + | } |
| + | |
| + | /* |
| + | * Write the inode table out as a single block. |
| + | */ |
| + | #define BUF_BLOCKS 32 |
| + | |
| + | errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags) |
| + | { |
| + | unsigned int group, left, c, d; |
| + | char *buf, *cp; |
| + | blk_t blk; |
| + | ssize_t actual; |
| + | errcode_t retval; |
| + | |
| + | buf = malloc(fs->blocksize * BUF_BLOCKS); |
| + | if (!buf) |
| + | return ENOMEM; |
| + | |
| + | for (group = 0; group < fs->group_desc_count; group++) { |
| + | blk = fs->group_desc[(unsigned)group].bg_inode_table; |
| + | if (!blk) |
| + | return EXT2_ET_MISSING_INODE_TABLE; |
| + | left = fs->inode_blocks_per_group; |
| + | while (left) { |
| + | c = BUF_BLOCKS; |
| + | if (c > left) |
| + | c = left; |
| + | retval = io_channel_read_blk(fs->io, blk, c, buf); |
| + | if (retval) |
| + | goto errout; |
| + | cp = buf; |
| + | while (c) { |
| + | if (!(flags & IMAGER_FLAG_SPARSEWRITE)) { |
| + | d = c; |
| + | goto skip_sparse; |
| + | } |
| + | /* Skip zero blocks */ |
| + | if (check_zero_block(cp, fs->blocksize)) { |
| + | c--; |
| + | blk++; |
| + | left--; |
| + | cp += fs->blocksize; |
| + | lseek(fd, fs->blocksize, SEEK_CUR); |
| + | continue; |
| + | } |
| + | /* Find non-zero blocks */ |
| + | for (d=1; d < c; d++) { |
| + | if (check_zero_block(cp + d*fs->blocksize, fs->blocksize)) |
| + | break; |
| + | } |
| + | skip_sparse: |
| + | actual = write(fd, cp, fs->blocksize * d); |
| + | if (actual == -1) { |
| + | retval = errno; |
| + | goto errout; |
| + | } |
| + | if (actual != (ssize_t) (fs->blocksize * d)) { |
| + | retval = EXT2_ET_SHORT_WRITE; |
| + | goto errout; |
| + | } |
| + | blk += d; |
| + | left -= d; |
| + | cp += fs->blocksize * d; |
| + | c -= d; |
| + | } |
| + | } |
| + | } |
| + | retval = 0; |
| + | |
| + | errout: |
| + | free(buf); |
| + | return retval; |
| + | } |
| + | |
| + | /* |
| + | * Read in the inode table and stuff it into place |
| + | */ |
| + | errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, |
| + | int flags EXT2FS_ATTR((unused))) |
| + | { |
| + | unsigned int group, c, left; |
| + | char *buf; |
| + | blk_t blk; |
| + | ssize_t actual; |
| + | errcode_t retval; |
| + | |
| + | buf = malloc(fs->blocksize * BUF_BLOCKS); |
| + | if (!buf) |
| + | return ENOMEM; |
| + | |
| + | for (group = 0; group < fs->group_desc_count; group++) { |
| + | blk = fs->group_desc[(unsigned)group].bg_inode_table; |
| + | if (!blk) { |
| + | retval = EXT2_ET_MISSING_INODE_TABLE; |
| + | goto errout; |
| + | } |
| + | left = fs->inode_blocks_per_group; |
| + | while (left) { |
| + | c = BUF_BLOCKS; |
| + | if (c > left) |
| + | c = left; |
| + | actual = read(fd, buf, fs->blocksize * c); |
| + | if (actual == -1) { |
| + | retval = errno; |
| + | goto errout; |
| + | } |
| + | if (actual != (ssize_t) (fs->blocksize * c)) { |
| + | retval = EXT2_ET_SHORT_READ; |
| + | goto errout; |
| + | } |
| + | retval = io_channel_write_blk(fs->io, blk, c, buf); |
| + | if (retval) |
| + | goto errout; |
| + | |
| + | blk += c; |
| + | left -= c; |
| + | } |
| + | } |
| + | retval = ext2fs_flush_icache(fs); |
| + | |
| + | errout: |
| + | free(buf); |
| + | return retval; |
| + | } |
| + | |
| + | /* |
| + | * Write out superblock and group descriptors |
| + | */ |
| + | errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, |
| + | int flags EXT2FS_ATTR((unused))) |
| + | { |
| + | char *buf, *cp; |
| + | ssize_t actual; |
| + | errcode_t retval; |
| + | |
| + | buf = malloc(fs->blocksize); |
| + | if (!buf) |
| + | return ENOMEM; |
| + | |
| + | /* |
| + | * Write out the superblock |
| + | */ |
| + | memset(buf, 0, fs->blocksize); |
| + | memcpy(buf, fs->super, SUPERBLOCK_SIZE); |
| + | actual = write(fd, buf, fs->blocksize); |
| + | if (actual == -1) { |
| + | retval = errno; |
| + | goto errout; |
| + | } |
| + | if (actual != (ssize_t) fs->blocksize) { |
| + | retval = EXT2_ET_SHORT_WRITE; |
| + | goto errout; |
| + | } |
| + | |
| + | /* |
| + | * Now write out the block group descriptors |
| + | */ |
| + | cp = (char *) fs->group_desc; |
| + | actual = write(fd, cp, fs->blocksize * fs->desc_blocks); |
| + | if (actual == -1) { |
| + | retval = errno; |
| + | goto errout; |
| + | } |
| + | if (actual != (ssize_t) (fs->blocksize * fs->desc_blocks)) { |
| + | retval = EXT2_ET_SHORT_WRITE; |
| + | goto errout; |
| + | } |
| + | |
| + | retval = 0; |
| + | |
| + | errout: |
| + | free(buf); |
| + | return retval; |
| + | } |
| + | |
| + | /* |
| + | * Read the superblock and group descriptors and overwrite them. |
| + | */ |
| + | errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, |
| + | int flags EXT2FS_ATTR((unused))) |
| + | { |
| + | char *buf; |
| + | ssize_t actual, size; |
| + | errcode_t retval; |
| + | |
| + | size = fs->blocksize * (fs->group_desc_count + 1); |
| + | buf = malloc(size); |
| + | if (!buf) |
| + | return ENOMEM; |
| + | |
| + | /* |
| + | * Read it all in. |
| + | */ |
| + | actual = read(fd, buf, size); |
| + | if (actual == -1) { |
| + | retval = errno; |
| + | goto errout; |
| + | } |
| + | if (actual != size) { |
| + | retval = EXT2_ET_SHORT_READ; |
| + | goto errout; |
| + | } |
| + | |
| + | /* |
| + | * Now copy in the superblock and group descriptors |
| + | */ |
| + | memcpy(fs->super, buf, SUPERBLOCK_SIZE); |
| + | |
| + | memcpy(fs->group_desc, buf + fs->blocksize, |
| + | fs->blocksize * fs->group_desc_count); |
| + | |
| + | retval = 0; |
| + | |
| + | errout: |
| + | free(buf); |
| + | return retval; |
| + | } |
| + | |
| + | /* |
| + | * Write the block/inode bitmaps. |
| + | */ |
| + | errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags) |
| + | { |
| + | char *ptr; |
| + | int c, size; |
| + | char zero_buf[1024]; |
| + | ssize_t actual; |
| + | errcode_t retval; |
| + | |
| + | if (flags & IMAGER_FLAG_INODEMAP) { |
| + | if (!fs->inode_map) { |
| + | retval = ext2fs_read_inode_bitmap(fs); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | ptr = fs->inode_map->bitmap; |
| + | size = (EXT2_INODES_PER_GROUP(fs->super) / 8); |
| + | } else { |
| + | if (!fs->block_map) { |
| + | retval = ext2fs_read_block_bitmap(fs); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | ptr = fs->block_map->bitmap; |
| + | size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; |
| + | } |
| + | size = size * fs->group_desc_count; |
| + | |
| + | actual = write(fd, ptr, size); |
| + | if (actual == -1) { |
| + | retval = errno; |
| + | goto errout; |
| + | } |
| + | if (actual != size) { |
| + | retval = EXT2_ET_SHORT_WRITE; |
| + | goto errout; |
| + | } |
| + | size = size % fs->blocksize; |
| + | memset(zero_buf, 0, sizeof(zero_buf)); |
| + | if (size) { |
| + | size = fs->blocksize - size; |
| + | while (size) { |
| + | c = size; |
| + | if (c > (int) sizeof(zero_buf)) |
| + | c = sizeof(zero_buf); |
| + | actual = write(fd, zero_buf, c); |
| + | if (actual == -1) { |
| + | retval = errno; |
| + | goto errout; |
| + | } |
| + | if (actual != c) { |
| + | retval = EXT2_ET_SHORT_WRITE; |
| + | goto errout; |
| + | } |
| + | size -= c; |
| + | } |
| + | } |
| + | retval = 0; |
| + | errout: |
| + | return (retval); |
| + | } |
| + | |
| + | |
| + | /* |
| + | * Read the block/inode bitmaps. |
| + | */ |
| + | errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags) |
| + | { |
| + | char *ptr, *buf = 0; |
| + | int size; |
| + | ssize_t actual; |
| + | errcode_t retval; |
| + | |
| + | if (flags & IMAGER_FLAG_INODEMAP) { |
| + | if (!fs->inode_map) { |
| + | retval = ext2fs_read_inode_bitmap(fs); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | ptr = fs->inode_map->bitmap; |
| + | size = (EXT2_INODES_PER_GROUP(fs->super) / 8); |
| + | } else { |
| + | if (!fs->block_map) { |
| + | retval = ext2fs_read_block_bitmap(fs); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | ptr = fs->block_map->bitmap; |
| + | size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; |
| + | } |
| + | size = size * fs->group_desc_count; |
| + | |
| + | buf = malloc(size); |
| + | if (!buf) |
| + | return ENOMEM; |
| + | |
| + | actual = read(fd, buf, size); |
| + | if (actual == -1) { |
| + | retval = errno; |
| + | goto errout; |
| + | } |
| + | if (actual != size) { |
| + | retval = EXT2_ET_SHORT_WRITE; |
| + | goto errout; |
| + | } |
| + | memcpy(ptr, buf, size); |
| + | |
| + | retval = 0; |
| + | errout: |
| + | if (buf) |
| + | free(buf); |
| + | return (retval); |
| + | } |
| @@ -0,0 +1,66 @@ | |
| + | /* |
| + | * ind_block.c --- indirect block I/O routines |
| + | * |
| + | * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, |
| + | * 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf) |
| + | { |
| + | errcode_t retval; |
| + | blk_t *block_nr; |
| + | int i; |
| + | int limit = fs->blocksize >> 2; |
| + | |
| + | if ((fs->flags & EXT2_FLAG_IMAGE_FILE) && |
| + | (fs->io != fs->image_io)) |
| + | memset(buf, 0, fs->blocksize); |
| + | else { |
| + | retval = io_channel_read_blk(fs->io, blk, 1, buf); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_READ)) { |
| + | block_nr = (blk_t *) buf; |
| + | for (i = 0; i < limit; i++, block_nr++) |
| + | *block_nr = ext2fs_swab32(*block_nr); |
| + | } |
| + | #endif |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf) |
| + | { |
| + | blk_t *block_nr; |
| + | int i; |
| + | int limit = fs->blocksize >> 2; |
| + | |
| + | if (fs->flags & EXT2_FLAG_IMAGE_FILE) |
| + | return 0; |
| + | |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_WRITE)) { |
| + | block_nr = (blk_t *) buf; |
| + | for (i = 0; i < limit; i++, block_nr++) |
| + | *block_nr = ext2fs_swab32(*block_nr); |
| + | } |
| + | #endif |
| + | return io_channel_write_blk(fs->io, blk, 1, buf); |
| + | } |
| + | |
| + | |
| @@ -0,0 +1,387 @@ | |
| + | /* |
| + | * initialize.c --- initialize a filesystem handle given superblock |
| + | * parameters. Used by mke2fs when initializing a filesystem. |
| + | * |
| + | * Copyright (C) 1994, 1995, 1996 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | #if defined(__linux__) && defined(EXT2_OS_LINUX) |
| + | #define CREATOR_OS EXT2_OS_LINUX |
| + | #else |
| + | #if defined(__GNU__) && defined(EXT2_OS_HURD) |
| + | #define CREATOR_OS EXT2_OS_HURD |
| + | #else |
| + | #if defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) |
| + | #define CREATOR_OS EXT2_OS_FREEBSD |
| + | #else |
| + | #if defined(LITES) && defined(EXT2_OS_LITES) |
| + | #define CREATOR_OS EXT2_OS_LITES |
| + | #else |
| + | #define CREATOR_OS EXT2_OS_LINUX /* by default */ |
| + | #endif /* defined(LITES) && defined(EXT2_OS_LITES) */ |
| + | #endif /* defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) */ |
| + | #endif /* defined(__GNU__) && defined(EXT2_OS_HURD) */ |
| + | #endif /* defined(__linux__) && defined(EXT2_OS_LINUX) */ |
| + | |
| + | /* |
| + | * Note we override the kernel include file's idea of what the default |
| + | * check interval (never) should be. It's a good idea to check at |
| + | * least *occasionally*, specially since servers will never rarely get |
| + | * to reboot, since Linux is so robust these days. :-) |
| + | * |
| + | * 180 days (six months) seems like a good value. |
| + | */ |
| + | #ifdef EXT2_DFL_CHECKINTERVAL |
| + | #undef EXT2_DFL_CHECKINTERVAL |
| + | #endif |
| + | #define EXT2_DFL_CHECKINTERVAL (86400L * 180L) |
| + | |
| + | /* |
| + | * Calculate the number of GDT blocks to reserve for online filesystem growth. |
| + | * The absolute maximum number of GDT blocks we can reserve is determined by |
| + | * the number of block pointers that can fit into a single block. |
| + | */ |
| + | static int calc_reserved_gdt_blocks(ext2_filsys fs) |
| + | { |
| + | struct ext2_super_block *sb = fs->super; |
| + | unsigned long bpg = sb->s_blocks_per_group; |
| + | unsigned int gdpb = fs->blocksize / sizeof(struct ext2_group_desc); |
| + | unsigned long max_blocks = 0xffffffff; |
| + | unsigned long rsv_groups; |
| + | int rsv_gdb; |
| + | |
| + | /* We set it at 1024x the current filesystem size, or |
| + | * the upper block count limit (2^32), whichever is lower. |
| + | */ |
| + | if (sb->s_blocks_count < max_blocks / 1024) |
| + | max_blocks = sb->s_blocks_count * 1024; |
| + | rsv_groups = (max_blocks - sb->s_first_data_block + bpg - 1) / bpg; |
| + | rsv_gdb = (rsv_groups + gdpb - 1) / gdpb - fs->desc_blocks; |
| + | if (rsv_gdb > EXT2_ADDR_PER_BLOCK(sb)) |
| + | rsv_gdb = EXT2_ADDR_PER_BLOCK(sb); |
| + | #ifdef RES_GDT_DEBUG |
| + | printf("max_blocks %lu, rsv_groups = %lu, rsv_gdb = %lu\n", |
| + | max_blocks, rsv_groups, rsv_gdb); |
| + | #endif |
| + | |
| + | return rsv_gdb; |
| + | } |
| + | |
| + | errcode_t ext2fs_initialize(const char *name, int flags, |
| + | struct ext2_super_block *param, |
| + | io_manager manager, ext2_filsys *ret_fs) |
| + | { |
| + | ext2_filsys fs; |
| + | errcode_t retval; |
| + | struct ext2_super_block *super; |
| + | int frags_per_block; |
| + | unsigned int rem; |
| + | unsigned int overhead = 0; |
| + | blk_t group_block; |
| + | unsigned int ipg; |
| + | dgrp_t i; |
| + | blk_t numblocks; |
| + | int rsv_gdt; |
| + | char *buf; |
| + | |
| + | if (!param || !param->s_blocks_count) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | |
| + | retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | memset(fs, 0, sizeof(struct struct_ext2_filsys)); |
| + | fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; |
| + | fs->flags = flags | EXT2_FLAG_RW; |
| + | fs->umask = 022; |
| + | #ifdef WORDS_BIGENDIAN |
| + | fs->flags |= EXT2_FLAG_SWAP_BYTES; |
| + | #endif |
| + | retval = manager->open(name, IO_FLAG_RW, &fs->io); |
| + | if (retval) |
| + | goto cleanup; |
| + | fs->image_io = fs->io; |
| + | fs->io->app_data = fs; |
| + | retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); |
| + | if (retval) |
| + | goto cleanup; |
| + | |
| + | strcpy(fs->device_name, name); |
| + | retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super); |
| + | if (retval) |
| + | goto cleanup; |
| + | fs->super = super; |
| + | |
| + | memset(super, 0, SUPERBLOCK_SIZE); |
| + | |
| + | #define set_field(field, default) (super->field = param->field ? \ |
| + | param->field : (default)) |
| + | |
| + | super->s_magic = EXT2_SUPER_MAGIC; |
| + | super->s_state = EXT2_VALID_FS; |
| + | |
| + | set_field(s_log_block_size, 0); /* default blocksize: 1024 bytes */ |
| + | set_field(s_log_frag_size, 0); /* default fragsize: 1024 bytes */ |
| + | set_field(s_first_data_block, super->s_log_block_size ? 0 : 1); |
| + | set_field(s_max_mnt_count, EXT2_DFL_MAX_MNT_COUNT); |
| + | set_field(s_errors, EXT2_ERRORS_DEFAULT); |
| + | set_field(s_feature_compat, 0); |
| + | set_field(s_feature_incompat, 0); |
| + | set_field(s_feature_ro_compat, 0); |
| + | set_field(s_first_meta_bg, 0); |
| + | if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { |
| + | retval = EXT2_ET_UNSUPP_FEATURE; |
| + | goto cleanup; |
| + | } |
| + | if (super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) { |
| + | retval = EXT2_ET_RO_UNSUPP_FEATURE; |
| + | goto cleanup; |
| + | } |
| + | |
| + | set_field(s_rev_level, EXT2_GOOD_OLD_REV); |
| + | if (super->s_rev_level >= EXT2_DYNAMIC_REV) { |
| + | set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO); |
| + | set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE); |
| + | } |
| + | |
| + | set_field(s_checkinterval, EXT2_DFL_CHECKINTERVAL); |
| + | super->s_mkfs_time = super->s_lastcheck = time(NULL); |
| + | |
| + | super->s_creator_os = CREATOR_OS; |
| + | |
| + | fs->blocksize = EXT2_BLOCK_SIZE(super); |
| + | fs->fragsize = EXT2_FRAG_SIZE(super); |
| + | frags_per_block = fs->blocksize / fs->fragsize; |
| + | |
| + | /* default: (fs->blocksize*8) blocks/group, up to 2^16 (GDT limit) */ |
| + | set_field(s_blocks_per_group, fs->blocksize * 8); |
| + | if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super)) |
| + | super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super); |
| + | super->s_frags_per_group = super->s_blocks_per_group * frags_per_block; |
| + | |
| + | super->s_blocks_count = param->s_blocks_count; |
| + | super->s_r_blocks_count = param->s_r_blocks_count; |
| + | if (super->s_r_blocks_count >= param->s_blocks_count) { |
| + | retval = EXT2_ET_INVALID_ARGUMENT; |
| + | goto cleanup; |
| + | } |
| + | |
| + | /* |
| + | * If we're creating an external journal device, we don't need |
| + | * to bother with the rest. |
| + | */ |
| + | if (super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { |
| + | fs->group_desc_count = 0; |
| + | ext2fs_mark_super_dirty(fs); |
| + | *ret_fs = fs; |
| + | return 0; |
| + | } |
| + | |
| + | retry: |
| + | fs->group_desc_count = (super->s_blocks_count - |
| + | super->s_first_data_block + |
| + | EXT2_BLOCKS_PER_GROUP(super) - 1) |
| + | / EXT2_BLOCKS_PER_GROUP(super); |
| + | if (fs->group_desc_count == 0) { |
| + | retval = EXT2_ET_TOOSMALL; |
| + | goto cleanup; |
| + | } |
| + | fs->desc_blocks = (fs->group_desc_count + |
| + | EXT2_DESC_PER_BLOCK(super) - 1) |
| + | / EXT2_DESC_PER_BLOCK(super); |
| + | |
| + | i = fs->blocksize >= 4096 ? 1 : 4096 / fs->blocksize; |
| + | set_field(s_inodes_count, super->s_blocks_count / i); |
| + | |
| + | /* |
| + | * Make sure we have at least EXT2_FIRST_INO + 1 inodes, so |
| + | * that we have enough inodes for the filesystem(!) |
| + | */ |
| + | if (super->s_inodes_count < EXT2_FIRST_INODE(super)+1) |
| + | super->s_inodes_count = EXT2_FIRST_INODE(super)+1; |
| + | |
| + | /* |
| + | * There should be at least as many inodes as the user |
| + | * requested. Figure out how many inodes per group that |
| + | * should be. But make sure that we don't allocate more than |
| + | * one bitmap's worth of inodes each group. |
| + | */ |
| + | ipg = (super->s_inodes_count + fs->group_desc_count - 1) / |
| + | fs->group_desc_count; |
| + | if (ipg > fs->blocksize * 8) { |
| + | if (super->s_blocks_per_group >= 256) { |
| + | /* Try again with slightly different parameters */ |
| + | super->s_blocks_per_group -= 8; |
| + | super->s_blocks_count = param->s_blocks_count; |
| + | super->s_frags_per_group = super->s_blocks_per_group * |
| + | frags_per_block; |
| + | goto retry; |
| + | } else |
| + | return EXT2_ET_TOO_MANY_INODES; |
| + | } |
| + | |
| + | if (ipg > (unsigned) EXT2_MAX_INODES_PER_GROUP(super)) |
| + | ipg = EXT2_MAX_INODES_PER_GROUP(super); |
| + | |
| + | super->s_inodes_per_group = ipg; |
| + | if (super->s_inodes_count > ipg * fs->group_desc_count) |
| + | super->s_inodes_count = ipg * fs->group_desc_count; |
| + | |
| + | /* |
| + | * Make sure the number of inodes per group completely fills |
| + | * the inode table blocks in the descriptor. If not, add some |
| + | * additional inodes/group. Waste not, want not... |
| + | */ |
| + | fs->inode_blocks_per_group = (((super->s_inodes_per_group * |
| + | EXT2_INODE_SIZE(super)) + |
| + | EXT2_BLOCK_SIZE(super) - 1) / |
| + | EXT2_BLOCK_SIZE(super)); |
| + | super->s_inodes_per_group = ((fs->inode_blocks_per_group * |
| + | EXT2_BLOCK_SIZE(super)) / |
| + | EXT2_INODE_SIZE(super)); |
| + | /* |
| + | * Finally, make sure the number of inodes per group is a |
| + | * multiple of 8. This is needed to simplify the bitmap |
| + | * splicing code. |
| + | */ |
| + | super->s_inodes_per_group &= ~7; |
| + | fs->inode_blocks_per_group = (((super->s_inodes_per_group * |
| + | EXT2_INODE_SIZE(super)) + |
| + | EXT2_BLOCK_SIZE(super) - 1) / |
| + | EXT2_BLOCK_SIZE(super)); |
| + | |
| + | /* |
| + | * adjust inode count to reflect the adjusted inodes_per_group |
| + | */ |
| + | super->s_inodes_count = super->s_inodes_per_group * |
| + | fs->group_desc_count; |
| + | super->s_free_inodes_count = super->s_inodes_count; |
| + | |
| + | /* |
| + | * check the number of reserved group descriptor table blocks |
| + | */ |
| + | if (super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) |
| + | rsv_gdt = calc_reserved_gdt_blocks(fs); |
| + | else |
| + | rsv_gdt = 0; |
| + | set_field(s_reserved_gdt_blocks, rsv_gdt); |
| + | if (super->s_reserved_gdt_blocks > EXT2_ADDR_PER_BLOCK(super)) { |
| + | retval = EXT2_ET_RES_GDT_BLOCKS; |
| + | goto cleanup; |
| + | } |
| + | |
| + | /* |
| + | * Overhead is the number of bookkeeping blocks per group. It |
| + | * includes the superblock backup, the group descriptor |
| + | * backups, the inode bitmap, the block bitmap, and the inode |
| + | * table. |
| + | */ |
| + | |
| + | overhead = (int) (2 + fs->inode_blocks_per_group); |
| + | |
| + | if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1)) |
| + | overhead += 1 + fs->desc_blocks + super->s_reserved_gdt_blocks; |
| + | |
| + | /* This can only happen if the user requested too many inodes */ |
| + | if (overhead > super->s_blocks_per_group) |
| + | return EXT2_ET_TOO_MANY_INODES; |
| + | |
| + | /* |
| + | * See if the last group is big enough to support the |
| + | * necessary data structures. If not, we need to get rid of |
| + | * it. |
| + | */ |
| + | rem = ((super->s_blocks_count - super->s_first_data_block) % |
| + | super->s_blocks_per_group); |
| + | if ((fs->group_desc_count == 1) && rem && (rem < overhead)) |
| + | return EXT2_ET_TOOSMALL; |
| + | if (rem && (rem < overhead+50)) { |
| + | super->s_blocks_count -= rem; |
| + | goto retry; |
| + | } |
| + | |
| + | /* |
| + | * At this point we know how big the filesystem will be. So |
| + | * we can do any and all allocations that depend on the block |
| + | * count. |
| + | */ |
| + | |
| + | retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); |
| + | if (retval) |
| + | goto cleanup; |
| + | |
| + | sprintf(buf, "block bitmap for %s", fs->device_name); |
| + | retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); |
| + | if (retval) |
| + | goto cleanup; |
| + | |
| + | sprintf(buf, "inode bitmap for %s", fs->device_name); |
| + | retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); |
| + | if (retval) |
| + | goto cleanup; |
| + | |
| + | ext2fs_free_mem(&buf); |
| + | |
| + | retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize, |
| + | &fs->group_desc); |
| + | if (retval) |
| + | goto cleanup; |
| + | |
| + | memset(fs->group_desc, 0, (size_t) fs->desc_blocks * fs->blocksize); |
| + | |
| + | /* |
| + | * Reserve the superblock and group descriptors for each |
| + | * group, and fill in the correct group statistics for group. |
| + | * Note that although the block bitmap, inode bitmap, and |
| + | * inode table have not been allocated (and in fact won't be |
| + | * by this routine), they are accounted for nevertheless. |
| + | */ |
| + | group_block = super->s_first_data_block; |
| + | super->s_free_blocks_count = 0; |
| + | for (i = 0; i < fs->group_desc_count; i++) { |
| + | numblocks = ext2fs_reserve_super_and_bgd(fs, i, fs->block_map); |
| + | |
| + | super->s_free_blocks_count += numblocks; |
| + | fs->group_desc[i].bg_free_blocks_count = numblocks; |
| + | fs->group_desc[i].bg_free_inodes_count = |
| + | fs->super->s_inodes_per_group; |
| + | fs->group_desc[i].bg_used_dirs_count = 0; |
| + | |
| + | group_block += super->s_blocks_per_group; |
| + | } |
| + | |
| + | ext2fs_mark_super_dirty(fs); |
| + | ext2fs_mark_bb_dirty(fs); |
| + | ext2fs_mark_ib_dirty(fs); |
| + | |
| + | io_channel_set_blksize(fs->io, fs->blocksize); |
| + | |
| + | *ret_fs = fs; |
| + | return 0; |
| + | cleanup: |
| + | ext2fs_free(fs); |
| + | return retval; |
| + | } |
| @@ -0,0 +1,32 @@ | |
| + | /* |
| + | * inline.c --- Includes the inlined functions defined in the header |
| + | * files as standalone functions, in case the application program |
| + | * is compiled with inlining turned off. |
| + | * |
| + | * Copyright (C) 1993, 1994 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #define INCLUDE_INLINE_FUNCS |
| + | #include "ext2fs.h" |
| + | |
| @@ -0,0 +1,794 @@ | |
| + | /* |
| + | * inode.c --- utility routines to read and write inodes |
| + | * |
| + | * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #if HAVE_ERRNO_H |
| + | #include <errno.h> |
| + | #endif |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fsP.h" |
| + | #include "e2image.h" |
| + | |
| + | struct ext2_struct_inode_scan { |
| + | errcode_t magic; |
| + | ext2_filsys fs; |
| + | ext2_ino_t current_inode; |
| + | blk_t current_block; |
| + | dgrp_t current_group; |
| + | ext2_ino_t inodes_left; |
| + | blk_t blocks_left; |
| + | dgrp_t groups_left; |
| + | blk_t inode_buffer_blocks; |
| + | char * inode_buffer; |
| + | int inode_size; |
| + | char * ptr; |
| + | int bytes_left; |
| + | char *temp_buffer; |
| + | errcode_t (*done_group)(ext2_filsys fs, |
| + | ext2_inode_scan scan, |
| + | dgrp_t group, |
| + | void * priv_data); |
| + | void * done_group_data; |
| + | int bad_block_ptr; |
| + | int scan_flags; |
| + | int reserved[6]; |
| + | }; |
| + | |
| + | /* |
| + | * This routine flushes the icache, if it exists. |
| + | */ |
| + | errcode_t ext2fs_flush_icache(ext2_filsys fs) |
| + | { |
| + | int i; |
| + | |
| + | if (!fs->icache) |
| + | return 0; |
| + | |
| + | for (i=0; i < fs->icache->cache_size; i++) |
| + | fs->icache->cache[i].ino = 0; |
| + | |
| + | fs->icache->buffer_blk = 0; |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t create_icache(ext2_filsys fs) |
| + | { |
| + | errcode_t retval; |
| + | |
| + | if (fs->icache) |
| + | return 0; |
| + | retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache), &fs->icache); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | memset(fs->icache, 0, sizeof(struct ext2_inode_cache)); |
| + | retval = ext2fs_get_mem(fs->blocksize, &fs->icache->buffer); |
| + | if (retval) { |
| + | ext2fs_free_mem(&fs->icache); |
| + | return retval; |
| + | } |
| + | fs->icache->buffer_blk = 0; |
| + | fs->icache->cache_last = -1; |
| + | fs->icache->cache_size = 4; |
| + | fs->icache->refcount = 1; |
| + | retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache_ent) |
| + | * fs->icache->cache_size, |
| + | &fs->icache->cache); |
| + | if (retval) { |
| + | ext2fs_free_mem(&fs->icache->buffer); |
| + | ext2fs_free_mem(&fs->icache); |
| + | return retval; |
| + | } |
| + | ext2fs_flush_icache(fs); |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks, |
| + | ext2_inode_scan *ret_scan) |
| + | { |
| + | ext2_inode_scan scan; |
| + | errcode_t retval; |
| + | errcode_t (*save_get_blocks)(ext2_filsys f, ext2_ino_t ino, blk_t *blocks); |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | /* |
| + | * If fs->badblocks isn't set, then set it --- since the inode |
| + | * scanning functions require it. |
| + | */ |
| + | if (fs->badblocks == 0) { |
| + | /* |
| + | * Temporarly save fs->get_blocks and set it to zero, |
| + | * for compatibility with old e2fsck's. |
| + | */ |
| + | save_get_blocks = fs->get_blocks; |
| + | fs->get_blocks = 0; |
| + | retval = ext2fs_read_bb_inode(fs, &fs->badblocks); |
| + | if (retval && fs->badblocks) { |
| + | ext2fs_badblocks_list_free(fs->badblocks); |
| + | fs->badblocks = 0; |
| + | } |
| + | fs->get_blocks = save_get_blocks; |
| + | } |
| + | |
| + | retval = ext2fs_get_mem(sizeof(struct ext2_struct_inode_scan), &scan); |
| + | if (retval) |
| + | return retval; |
| + | memset(scan, 0, sizeof(struct ext2_struct_inode_scan)); |
| + | |
| + | scan->magic = EXT2_ET_MAGIC_INODE_SCAN; |
| + | scan->fs = fs; |
| + | scan->inode_size = EXT2_INODE_SIZE(fs->super); |
| + | scan->bytes_left = 0; |
| + | scan->current_group = 0; |
| + | scan->groups_left = fs->group_desc_count - 1; |
| + | scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8; |
| + | scan->current_block = scan->fs-> |
| + | group_desc[scan->current_group].bg_inode_table; |
| + | scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super); |
| + | scan->blocks_left = scan->fs->inode_blocks_per_group; |
| + | retval = ext2fs_get_mem((size_t) (scan->inode_buffer_blocks * |
| + | fs->blocksize), |
| + | &scan->inode_buffer); |
| + | scan->done_group = 0; |
| + | scan->done_group_data = 0; |
| + | scan->bad_block_ptr = 0; |
| + | if (retval) { |
| + | ext2fs_free_mem(&scan); |
| + | return retval; |
| + | } |
| + | retval = ext2fs_get_mem(scan->inode_size, &scan->temp_buffer); |
| + | if (retval) { |
| + | ext2fs_free_mem(&scan->inode_buffer); |
| + | ext2fs_free_mem(&scan); |
| + | return retval; |
| + | } |
| + | if (scan->fs->badblocks && scan->fs->badblocks->num) |
| + | scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS; |
| + | *ret_scan = scan; |
| + | return 0; |
| + | } |
| + | |
| + | void ext2fs_close_inode_scan(ext2_inode_scan scan) |
| + | { |
| + | if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) |
| + | return; |
| + | |
| + | ext2fs_free_mem(&scan->inode_buffer); |
| + | scan->inode_buffer = NULL; |
| + | ext2fs_free_mem(&scan->temp_buffer); |
| + | scan->temp_buffer = NULL; |
| + | ext2fs_free_mem(&scan); |
| + | return; |
| + | } |
| + | |
| + | void ext2fs_set_inode_callback(ext2_inode_scan scan, |
| + | errcode_t (*done_group)(ext2_filsys fs, |
| + | ext2_inode_scan scan, |
| + | dgrp_t group, |
| + | void * priv_data), |
| + | void *done_group_data) |
| + | { |
| + | if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) |
| + | return; |
| + | |
| + | scan->done_group = done_group; |
| + | scan->done_group_data = done_group_data; |
| + | } |
| + | |
| + | int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags, |
| + | int clear_flags) |
| + | { |
| + | int old_flags; |
| + | |
| + | if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) |
| + | return 0; |
| + | |
| + | old_flags = scan->scan_flags; |
| + | scan->scan_flags &= ~clear_flags; |
| + | scan->scan_flags |= set_flags; |
| + | return old_flags; |
| + | } |
| + | |
| + | /* |
| + | * This function is called by ext2fs_get_next_inode when it needs to |
| + | * get ready to read in a new blockgroup. |
| + | */ |
| + | static errcode_t get_next_blockgroup(ext2_inode_scan scan) |
| + | { |
| + | scan->current_group++; |
| + | scan->groups_left--; |
| + | |
| + | scan->current_block = scan->fs-> |
| + | group_desc[scan->current_group].bg_inode_table; |
| + | |
| + | scan->current_inode = scan->current_group * |
| + | EXT2_INODES_PER_GROUP(scan->fs->super); |
| + | |
| + | scan->bytes_left = 0; |
| + | scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super); |
| + | scan->blocks_left = scan->fs->inode_blocks_per_group; |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan, |
| + | int group) |
| + | { |
| + | scan->current_group = group - 1; |
| + | scan->groups_left = scan->fs->group_desc_count - group; |
| + | return get_next_blockgroup(scan); |
| + | } |
| + | |
| + | /* |
| + | * This function is called by get_next_blocks() to check for bad |
| + | * blocks in the inode table. |
| + | * |
| + | * This function assumes that badblocks_list->list is sorted in |
| + | * increasing order. |
| + | */ |
| + | static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan, |
| + | blk_t *num_blocks) |
| + | { |
| + | blk_t blk = scan->current_block; |
| + | badblocks_list bb = scan->fs->badblocks; |
| + | |
| + | /* |
| + | * If the inode table is missing, then obviously there are no |
| + | * bad blocks. :-) |
| + | */ |
| + | if (blk == 0) |
| + | return 0; |
| + | |
| + | /* |
| + | * If the current block is greater than the bad block listed |
| + | * in the bad block list, then advance the pointer until this |
| + | * is no longer the case. If we run out of bad blocks, then |
| + | * we don't need to do any more checking! |
| + | */ |
| + | while (blk > bb->list[scan->bad_block_ptr]) { |
| + | if (++scan->bad_block_ptr >= bb->num) { |
| + | scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS; |
| + | return 0; |
| + | } |
| + | } |
| + | |
| + | /* |
| + | * If the current block is equal to the bad block listed in |
| + | * the bad block list, then handle that one block specially. |
| + | * (We could try to handle runs of bad blocks, but that |
| + | * only increases CPU efficiency by a small amount, at the |
| + | * expense of a huge expense of code complexity, and for an |
| + | * uncommon case at that.) |
| + | */ |
| + | if (blk == bb->list[scan->bad_block_ptr]) { |
| + | scan->scan_flags |= EXT2_SF_BAD_INODE_BLK; |
| + | *num_blocks = 1; |
| + | if (++scan->bad_block_ptr >= bb->num) |
| + | scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS; |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * If there is a bad block in the range that we're about to |
| + | * read in, adjust the number of blocks to read so that we we |
| + | * don't read in the bad block. (Then the next block to read |
| + | * will be the bad block, which is handled in the above case.) |
| + | */ |
| + | if ((blk + *num_blocks) > bb->list[scan->bad_block_ptr]) |
| + | *num_blocks = (int) (bb->list[scan->bad_block_ptr] - blk); |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * This function is called by ext2fs_get_next_inode when it needs to |
| + | * read in more blocks from the current blockgroup's inode table. |
| + | */ |
| + | static errcode_t get_next_blocks(ext2_inode_scan scan) |
| + | { |
| + | blk_t num_blocks; |
| + | errcode_t retval; |
| + | |
| + | /* |
| + | * Figure out how many blocks to read; we read at most |
| + | * inode_buffer_blocks, and perhaps less if there aren't that |
| + | * many blocks left to read. |
| + | */ |
| + | num_blocks = scan->inode_buffer_blocks; |
| + | if (num_blocks > scan->blocks_left) |
| + | num_blocks = scan->blocks_left; |
| + | |
| + | /* |
| + | * If the past block "read" was a bad block, then mark the |
| + | * left-over extra bytes as also being bad. |
| + | */ |
| + | if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) { |
| + | if (scan->bytes_left) |
| + | scan->scan_flags |= EXT2_SF_BAD_EXTRA_BYTES; |
| + | scan->scan_flags &= ~EXT2_SF_BAD_INODE_BLK; |
| + | } |
| + | |
| + | /* |
| + | * Do inode bad block processing, if necessary. |
| + | */ |
| + | if (scan->scan_flags & EXT2_SF_CHK_BADBLOCKS) { |
| + | retval = check_for_inode_bad_blocks(scan, &num_blocks); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | |
| + | if ((scan->scan_flags & EXT2_SF_BAD_INODE_BLK) || |
| + | (scan->current_block == 0)) { |
| + | memset(scan->inode_buffer, 0, |
| + | (size_t) num_blocks * scan->fs->blocksize); |
| + | } else { |
| + | retval = io_channel_read_blk(scan->fs->io, |
| + | scan->current_block, |
| + | (int) num_blocks, |
| + | scan->inode_buffer); |
| + | if (retval) |
| + | return EXT2_ET_NEXT_INODE_READ; |
| + | } |
| + | scan->ptr = scan->inode_buffer; |
| + | scan->bytes_left = num_blocks * scan->fs->blocksize; |
| + | |
| + | scan->blocks_left -= num_blocks; |
| + | if (scan->current_block) |
| + | scan->current_block += num_blocks; |
| + | return 0; |
| + | } |
| + | |
| + | #if 0 |
| + | /* |
| + | * Returns 1 if the entire inode_buffer has a non-zero size and |
| + | * contains all zeros. (Not just deleted inodes, since that means |
| + | * that part of the inode table was used at one point; we want all |
| + | * zeros, which means that the inode table is pristine.) |
| + | */ |
| + | static inline int is_empty_scan(ext2_inode_scan scan) |
| + | { |
| + | int i; |
| + | |
| + | if (scan->bytes_left == 0) |
| + | return 0; |
| + | |
| + | for (i=0; i < scan->bytes_left; i++) |
| + | if (scan->ptr[i]) |
| + | return 0; |
| + | return 1; |
| + | } |
| + | #endif |
| + | |
| + | errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino, |
| + | struct ext2_inode *inode, int bufsize) |
| + | { |
| + | errcode_t retval; |
| + | int extra_bytes = 0; |
| + | |
| + | EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN); |
| + | |
| + | /* |
| + | * Do we need to start reading a new block group? |
| + | */ |
| + | if (scan->inodes_left <= 0) { |
| + | force_new_group: |
| + | if (scan->done_group) { |
| + | retval = (scan->done_group) |
| + | (scan->fs, scan, scan->current_group, |
| + | scan->done_group_data); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | if (scan->groups_left <= 0) { |
| + | *ino = 0; |
| + | return 0; |
| + | } |
| + | retval = get_next_blockgroup(scan); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | /* |
| + | * This is done outside the above if statement so that the |
| + | * check can be done for block group #0. |
| + | */ |
| + | if (scan->current_block == 0) { |
| + | if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) { |
| + | goto force_new_group; |
| + | } else |
| + | return EXT2_ET_MISSING_INODE_TABLE; |
| + | } |
| + | |
| + | |
| + | /* |
| + | * Have we run out of space in the inode buffer? If so, we |
| + | * need to read in more blocks. |
| + | */ |
| + | if (scan->bytes_left < scan->inode_size) { |
| + | memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left); |
| + | extra_bytes = scan->bytes_left; |
| + | |
| + | retval = get_next_blocks(scan); |
| + | if (retval) |
| + | return retval; |
| + | #if 0 |
| + | /* |
| + | * XXX test Need check for used inode somehow. |
| + | * (Note: this is hard.) |
| + | */ |
| + | if (is_empty_scan(scan)) |
| + | goto force_new_group; |
| + | #endif |
| + | } |
| + | |
| + | retval = 0; |
| + | if (extra_bytes) { |
| + | memcpy(scan->temp_buffer+extra_bytes, scan->ptr, |
| + | scan->inode_size - extra_bytes); |
| + | scan->ptr += scan->inode_size - extra_bytes; |
| + | scan->bytes_left -= scan->inode_size - extra_bytes; |
| + | |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) || |
| + | (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) |
| + | ext2fs_swap_inode_full(scan->fs, |
| + | (struct ext2_inode_large *) inode, |
| + | (struct ext2_inode_large *) scan->temp_buffer, |
| + | 0, bufsize); |
| + | else |
| + | #endif |
| + | *inode = *((struct ext2_inode *) scan->temp_buffer); |
| + | if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES) |
| + | retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE; |
| + | scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES; |
| + | } else { |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) || |
| + | (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) |
| + | ext2fs_swap_inode_full(scan->fs, |
| + | (struct ext2_inode_large *) inode, |
| + | (struct ext2_inode_large *) scan->ptr, |
| + | 0, bufsize); |
| + | else |
| + | #endif |
| + | memcpy(inode, scan->ptr, bufsize); |
| + | scan->ptr += scan->inode_size; |
| + | scan->bytes_left -= scan->inode_size; |
| + | if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) |
| + | retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE; |
| + | } |
| + | |
| + | scan->inodes_left--; |
| + | scan->current_inode++; |
| + | *ino = scan->current_inode; |
| + | return retval; |
| + | } |
| + | |
| + | errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino, |
| + | struct ext2_inode *inode) |
| + | { |
| + | return ext2fs_get_next_inode_full(scan, ino, inode, |
| + | sizeof(struct ext2_inode)); |
| + | } |
| + | |
| + | /* |
| + | * Functions to read and write a single inode. |
| + | */ |
| + | errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino, |
| + | struct ext2_inode * inode, int bufsize) |
| + | { |
| + | unsigned long group, block, block_nr, offset; |
| + | char *ptr; |
| + | errcode_t retval; |
| + | int clen, i, inodes_per_block, length; |
| + | io_channel io; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | /* Check to see if user has an override function */ |
| + | if (fs->read_inode) { |
| + | retval = (fs->read_inode)(fs, ino, inode); |
| + | if (retval != EXT2_ET_CALLBACK_NOTHANDLED) |
| + | return retval; |
| + | } |
| + | /* Create inode cache if not present */ |
| + | if (!fs->icache) { |
| + | retval = create_icache(fs); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | /* Check to see if it's in the inode cache */ |
| + | if (bufsize == sizeof(struct ext2_inode)) { |
| + | /* only old good inode can be retrieve from the cache */ |
| + | for (i=0; i < fs->icache->cache_size; i++) { |
| + | if (fs->icache->cache[i].ino == ino) { |
| + | *inode = fs->icache->cache[i].inode; |
| + | return 0; |
| + | } |
| + | } |
| + | } |
| + | if ((ino == 0) || (ino > fs->super->s_inodes_count)) |
| + | return EXT2_ET_BAD_INODE_NUM; |
| + | if (fs->flags & EXT2_FLAG_IMAGE_FILE) { |
| + | inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super); |
| + | block_nr = fs->image_header->offset_inode / fs->blocksize; |
| + | block_nr += (ino - 1) / inodes_per_block; |
| + | offset = ((ino - 1) % inodes_per_block) * |
| + | EXT2_INODE_SIZE(fs->super); |
| + | io = fs->image_io; |
| + | } else { |
| + | group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); |
| + | offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * |
| + | EXT2_INODE_SIZE(fs->super); |
| + | block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); |
| + | if (!fs->group_desc[(unsigned)group].bg_inode_table) |
| + | return EXT2_ET_MISSING_INODE_TABLE; |
| + | block_nr = fs->group_desc[(unsigned)group].bg_inode_table + |
| + | block; |
| + | io = fs->io; |
| + | } |
| + | offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); |
| + | |
| + | length = EXT2_INODE_SIZE(fs->super); |
| + | if (bufsize < length) |
| + | length = bufsize; |
| + | |
| + | ptr = (char *) inode; |
| + | while (length) { |
| + | clen = length; |
| + | if ((offset + length) > fs->blocksize) |
| + | clen = fs->blocksize - offset; |
| + | |
| + | if (block_nr != fs->icache->buffer_blk) { |
| + | retval = io_channel_read_blk(io, block_nr, 1, |
| + | fs->icache->buffer); |
| + | if (retval) |
| + | return retval; |
| + | fs->icache->buffer_blk = block_nr; |
| + | } |
| + | |
| + | memcpy(ptr, ((char *) fs->icache->buffer) + (unsigned) offset, |
| + | clen); |
| + | |
| + | offset = 0; |
| + | length -= clen; |
| + | ptr += clen; |
| + | block_nr++; |
| + | } |
| + | |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || |
| + | (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) |
| + | ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) inode, |
| + | (struct ext2_inode_large *) inode, |
| + | 0, length); |
| + | #endif |
| + | |
| + | /* Update the inode cache */ |
| + | fs->icache->cache_last = (fs->icache->cache_last + 1) % |
| + | fs->icache->cache_size; |
| + | fs->icache->cache[fs->icache->cache_last].ino = ino; |
| + | fs->icache->cache[fs->icache->cache_last].inode = *inode; |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_read_inode(ext2_filsys fs, ext2_ino_t ino, |
| + | struct ext2_inode * inode) |
| + | { |
| + | return ext2fs_read_inode_full(fs, ino, inode, |
| + | sizeof(struct ext2_inode)); |
| + | } |
| + | |
| + | errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino, |
| + | struct ext2_inode * inode, int bufsize) |
| + | { |
| + | unsigned long group, block, block_nr, offset; |
| + | errcode_t retval = 0; |
| + | struct ext2_inode_large temp_inode, *w_inode; |
| + | char *ptr; |
| + | int clen, i, length; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | /* Check to see if user provided an override function */ |
| + | if (fs->write_inode) { |
| + | retval = (fs->write_inode)(fs, ino, inode); |
| + | if (retval != EXT2_ET_CALLBACK_NOTHANDLED) |
| + | return retval; |
| + | } |
| + | |
| + | /* Check to see if the inode cache needs to be updated */ |
| + | if (fs->icache) { |
| + | for (i=0; i < fs->icache->cache_size; i++) { |
| + | if (fs->icache->cache[i].ino == ino) { |
| + | fs->icache->cache[i].inode = *inode; |
| + | break; |
| + | } |
| + | } |
| + | } else { |
| + | retval = create_icache(fs); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | |
| + | if (!(fs->flags & EXT2_FLAG_RW)) |
| + | return EXT2_ET_RO_FILSYS; |
| + | |
| + | if ((ino == 0) || (ino > fs->super->s_inodes_count)) |
| + | return EXT2_ET_BAD_INODE_NUM; |
| + | |
| + | length = bufsize; |
| + | if (length < EXT2_INODE_SIZE(fs->super)) |
| + | length = EXT2_INODE_SIZE(fs->super); |
| + | |
| + | if (length > (int) sizeof(struct ext2_inode_large)) { |
| + | w_inode = malloc(length); |
| + | if (!w_inode) |
| + | return ENOMEM; |
| + | } else |
| + | w_inode = &temp_inode; |
| + | memset(w_inode, 0, length); |
| + | |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || |
| + | (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) |
| + | ext2fs_swap_inode_full(fs, w_inode, |
| + | (struct ext2_inode_large *) inode, |
| + | 1, bufsize); |
| + | else |
| + | #endif |
| + | memcpy(w_inode, inode, bufsize); |
| + | |
| + | group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); |
| + | offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * |
| + | EXT2_INODE_SIZE(fs->super); |
| + | block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); |
| + | if (!fs->group_desc[(unsigned) group].bg_inode_table) |
| + | return EXT2_ET_MISSING_INODE_TABLE; |
| + | block_nr = fs->group_desc[(unsigned) group].bg_inode_table + block; |
| + | |
| + | offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); |
| + | |
| + | length = EXT2_INODE_SIZE(fs->super); |
| + | if (length > bufsize) |
| + | length = bufsize; |
| + | |
| + | ptr = (char *) w_inode; |
| + | |
| + | while (length) { |
| + | clen = length; |
| + | if ((offset + length) > fs->blocksize) |
| + | clen = fs->blocksize - offset; |
| + | |
| + | if (fs->icache->buffer_blk != block_nr) { |
| + | retval = io_channel_read_blk(fs->io, block_nr, 1, |
| + | fs->icache->buffer); |
| + | if (retval) |
| + | goto errout; |
| + | fs->icache->buffer_blk = block_nr; |
| + | } |
| + | |
| + | |
| + | memcpy((char *) fs->icache->buffer + (unsigned) offset, |
| + | ptr, clen); |
| + | |
| + | retval = io_channel_write_blk(fs->io, block_nr, 1, |
| + | fs->icache->buffer); |
| + | if (retval) |
| + | goto errout; |
| + | |
| + | offset = 0; |
| + | ptr += clen; |
| + | length -= clen; |
| + | block_nr++; |
| + | } |
| + | |
| + | fs->flags |= EXT2_FLAG_CHANGED; |
| + | errout: |
| + | if (w_inode && w_inode != &temp_inode) |
| + | free(w_inode); |
| + | return retval; |
| + | } |
| + | |
| + | errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino, |
| + | struct ext2_inode *inode) |
| + | { |
| + | return ext2fs_write_inode_full(fs, ino, inode, |
| + | sizeof(struct ext2_inode)); |
| + | } |
| + | |
| + | /* |
| + | * This function should be called when writing a new inode. It makes |
| + | * sure that extra part of large inodes is initialized properly. |
| + | */ |
| + | errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino, |
| + | struct ext2_inode *inode) |
| + | { |
| + | struct ext2_inode *buf; |
| + | int size = EXT2_INODE_SIZE(fs->super); |
| + | struct ext2_inode_large *large_inode; |
| + | |
| + | if (size == sizeof(struct ext2_inode)) |
| + | return ext2fs_write_inode_full(fs, ino, inode, |
| + | sizeof(struct ext2_inode)); |
| + | |
| + | buf = malloc(size); |
| + | if (!buf) |
| + | return ENOMEM; |
| + | |
| + | memset(buf, 0, size); |
| + | *buf = *inode; |
| + | |
| + | large_inode = (struct ext2_inode_large *) buf; |
| + | large_inode->i_extra_isize = sizeof(struct ext2_inode_large) - |
| + | EXT2_GOOD_OLD_INODE_SIZE; |
| + | |
| + | return ext2fs_write_inode_full(fs, ino, buf, size); |
| + | } |
| + | |
| + | |
| + | errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks) |
| + | { |
| + | struct ext2_inode inode; |
| + | int i; |
| + | errcode_t retval; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | if (ino > fs->super->s_inodes_count) |
| + | return EXT2_ET_BAD_INODE_NUM; |
| + | |
| + | if (fs->get_blocks) { |
| + | if (!(*fs->get_blocks)(fs, ino, blocks)) |
| + | return 0; |
| + | } |
| + | retval = ext2fs_read_inode(fs, ino, &inode); |
| + | if (retval) |
| + | return retval; |
| + | for (i=0; i < EXT2_N_BLOCKS; i++) |
| + | blocks[i] = inode.i_block[i]; |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino) |
| + | { |
| + | struct ext2_inode inode; |
| + | errcode_t retval; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | if (ino > fs->super->s_inodes_count) |
| + | return EXT2_ET_BAD_INODE_NUM; |
| + | |
| + | if (fs->check_directory) { |
| + | retval = (fs->check_directory)(fs, ino); |
| + | if (retval != EXT2_ET_CALLBACK_NOTHANDLED) |
| + | return retval; |
| + | } |
| + | retval = ext2fs_read_inode(fs, ino, &inode); |
| + | if (retval) |
| + | return retval; |
| + | if (!LINUX_S_ISDIR(inode.i_mode)) |
| + | return EXT2_ET_NO_DIRECTORY; |
| + | return 0; |
| + | } |
| + | |
| @@ -0,0 +1,270 @@ | |
| + | /* |
| + | * inode_io.c --- This is allows an inode in an ext2 filesystem image |
| + | * to be accessed via the I/O manager interface. |
| + | * |
| + | * Copyright (C) 2002 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #if HAVE_ERRNO_H |
| + | #include <errno.h> |
| + | #endif |
| + | #include <time.h> |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | /* |
| + | * For checking structure magic numbers... |
| + | */ |
| + | |
| + | #define EXT2_CHECK_MAGIC(struct, code) \ |
| + | if ((struct)->magic != (code)) return (code) |
| + | |
| + | struct inode_private_data { |
| + | int magic; |
| + | char name[32]; |
| + | ext2_file_t file; |
| + | ext2_filsys fs; |
| + | ext2_ino_t ino; |
| + | struct ext2_inode inode; |
| + | int flags; |
| + | struct inode_private_data *next; |
| + | }; |
| + | |
| + | #define CHANNEL_HAS_INODE 0x8000 |
| + | |
| + | static struct inode_private_data *top_intern; |
| + | static int ino_unique = 0; |
| + | |
| + | static errcode_t inode_open(const char *name, int flags, io_channel *channel); |
| + | static errcode_t inode_close(io_channel channel); |
| + | static errcode_t inode_set_blksize(io_channel channel, int blksize); |
| + | static errcode_t inode_read_blk(io_channel channel, unsigned long block, |
| + | int count, void *data); |
| + | static errcode_t inode_write_blk(io_channel channel, unsigned long block, |
| + | int count, const void *data); |
| + | static errcode_t inode_flush(io_channel channel); |
| + | static errcode_t inode_write_byte(io_channel channel, unsigned long offset, |
| + | int size, const void *data); |
| + | |
| + | static struct struct_io_manager struct_inode_manager = { |
| + | EXT2_ET_MAGIC_IO_MANAGER, |
| + | "Inode I/O Manager", |
| + | inode_open, |
| + | inode_close, |
| + | inode_set_blksize, |
| + | inode_read_blk, |
| + | inode_write_blk, |
| + | inode_flush, |
| + | inode_write_byte |
| + | }; |
| + | |
| + | io_manager inode_io_manager = &struct_inode_manager; |
| + | |
| + | errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino, |
| + | struct ext2_inode *inode, |
| + | char **name) |
| + | { |
| + | struct inode_private_data *data; |
| + | errcode_t retval; |
| + | |
| + | if ((retval = ext2fs_get_mem(sizeof(struct inode_private_data), |
| + | &data))) |
| + | return retval; |
| + | data->magic = EXT2_ET_MAGIC_INODE_IO_CHANNEL; |
| + | sprintf(data->name, "%u:%d", ino, ino_unique++); |
| + | data->file = 0; |
| + | data->fs = fs; |
| + | data->ino = ino; |
| + | data->flags = 0; |
| + | if (inode) { |
| + | memcpy(&data->inode, inode, sizeof(struct ext2_inode)); |
| + | data->flags |= CHANNEL_HAS_INODE; |
| + | } |
| + | data->next = top_intern; |
| + | top_intern = data; |
| + | *name = data->name; |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino, |
| + | char **name) |
| + | { |
| + | return ext2fs_inode_io_intern2(fs, ino, NULL, name); |
| + | } |
| + | |
| + | |
| + | static errcode_t inode_open(const char *name, int flags, io_channel *channel) |
| + | { |
| + | io_channel io = NULL; |
| + | struct inode_private_data *prev, *data = NULL; |
| + | errcode_t retval; |
| + | int open_flags; |
| + | |
| + | if (name == 0) |
| + | return EXT2_ET_BAD_DEVICE_NAME; |
| + | |
| + | for (data = top_intern, prev = NULL; data; |
| + | prev = data, data = data->next) |
| + | if (strcmp(name, data->name) == 0) |
| + | break; |
| + | if (!data) |
| + | return ENOENT; |
| + | if (prev) |
| + | prev->next = data->next; |
| + | else |
| + | top_intern = data->next; |
| + | |
| + | retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); |
| + | if (retval) |
| + | goto cleanup; |
| + | memset(io, 0, sizeof(struct struct_io_channel)); |
| + | |
| + | io->magic = EXT2_ET_MAGIC_IO_CHANNEL; |
| + | io->manager = inode_io_manager; |
| + | retval = ext2fs_get_mem(strlen(name)+1, &io->name); |
| + | if (retval) |
| + | goto cleanup; |
| + | |
| + | strcpy(io->name, name); |
| + | io->private_data = data; |
| + | io->block_size = 1024; |
| + | io->read_error = 0; |
| + | io->write_error = 0; |
| + | io->refcount = 1; |
| + | |
| + | open_flags = (flags & IO_FLAG_RW) ? EXT2_FILE_WRITE : 0; |
| + | retval = ext2fs_file_open2(data->fs, data->ino, |
| + | (data->flags & CHANNEL_HAS_INODE) ? |
| + | &data->inode : 0, open_flags, |
| + | &data->file); |
| + | if (retval) |
| + | goto cleanup; |
| + | |
| + | *channel = io; |
| + | return 0; |
| + | |
| + | cleanup: |
| + | if (data) { |
| + | ext2fs_free_mem(&data); |
| + | } |
| + | if (io) |
| + | ext2fs_free_mem(&io); |
| + | return retval; |
| + | } |
| + | |
| + | static errcode_t inode_close(io_channel channel) |
| + | { |
| + | struct inode_private_data *data; |
| + | errcode_t retval = 0; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct inode_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); |
| + | |
| + | if (--channel->refcount > 0) |
| + | return 0; |
| + | |
| + | retval = ext2fs_file_close(data->file); |
| + | |
| + | ext2fs_free_mem(&channel->private_data); |
| + | if (channel->name) |
| + | ext2fs_free_mem(&channel->name); |
| + | ext2fs_free_mem(&channel); |
| + | return retval; |
| + | } |
| + | |
| + | static errcode_t inode_set_blksize(io_channel channel, int blksize) |
| + | { |
| + | struct inode_private_data *data; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct inode_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); |
| + | |
| + | channel->block_size = blksize; |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | static errcode_t inode_read_blk(io_channel channel, unsigned long block, |
| + | int count, void *buf) |
| + | { |
| + | struct inode_private_data *data; |
| + | errcode_t retval; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct inode_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); |
| + | |
| + | if ((retval = ext2fs_file_lseek(data->file, |
| + | block * channel->block_size, |
| + | EXT2_SEEK_SET, 0))) |
| + | return retval; |
| + | |
| + | count = (count < 0) ? -count : (count * channel->block_size); |
| + | |
| + | return ext2fs_file_read(data->file, buf, count, 0); |
| + | } |
| + | |
| + | static errcode_t inode_write_blk(io_channel channel, unsigned long block, |
| + | int count, const void *buf) |
| + | { |
| + | struct inode_private_data *data; |
| + | errcode_t retval; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct inode_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); |
| + | |
| + | if ((retval = ext2fs_file_lseek(data->file, |
| + | block * channel->block_size, |
| + | EXT2_SEEK_SET, 0))) |
| + | return retval; |
| + | |
| + | count = (count < 0) ? -count : (count * channel->block_size); |
| + | |
| + | return ext2fs_file_write(data->file, buf, count, 0); |
| + | } |
| + | |
| + | static errcode_t inode_write_byte(io_channel channel, unsigned long offset, |
| + | int size, const void *buf) |
| + | { |
| + | struct inode_private_data *data; |
| + | errcode_t retval = 0; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct inode_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); |
| + | |
| + | if ((retval = ext2fs_file_lseek(data->file, offset, |
| + | EXT2_SEEK_SET, 0))) |
| + | return retval; |
| + | |
| + | return ext2fs_file_write(data->file, buf, size, 0); |
| + | } |
| + | |
| + | /* |
| + | * Flush data buffers to disk. |
| + | */ |
| + | static errcode_t inode_flush(io_channel channel) |
| + | { |
| + | struct inode_private_data *data; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct inode_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); |
| + | |
| + | return ext2fs_file_flush(data->file); |
| + | } |
| + | |
| @@ -0,0 +1,69 @@ | |
| + | /* |
| + | * io_manager.c --- the I/O manager abstraction |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | errcode_t io_channel_set_options(io_channel channel, const char *opts) |
| + | { |
| + | errcode_t retval = 0; |
| + | char *next, *ptr, *options, *arg; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | |
| + | if (!opts) |
| + | return 0; |
| + | |
| + | if (!channel->manager->set_option) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | |
| + | options = malloc(strlen(opts)+1); |
| + | if (!options) |
| + | return EXT2_ET_NO_MEMORY; |
| + | strcpy(options, opts); |
| + | ptr = options; |
| + | |
| + | while (ptr && *ptr) { |
| + | next = strchr(ptr, '&'); |
| + | if (next) |
| + | *next++ = 0; |
| + | |
| + | arg = strchr(ptr, '='); |
| + | if (arg) |
| + | *arg++ = 0; |
| + | |
| + | retval = (channel->manager->set_option)(channel, ptr, arg); |
| + | if (retval) |
| + | break; |
| + | ptr = next; |
| + | } |
| + | free(options); |
| + | return retval; |
| + | } |
| + | |
| + | errcode_t io_channel_write_byte(io_channel channel, unsigned long offset, |
| + | int count, const void *data) |
| + | { |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | |
| + | if (channel->manager->write_byte) |
| + | return channel->manager->write_byte(channel, offset, |
| + | count, data); |
| + | |
| + | return EXT2_ET_UNIMPLEMENTED; |
| + | } |
| @@ -0,0 +1,114 @@ | |
| + | /* |
| + | * irel.h |
| + | * |
| + | * Copyright (C) 1996, 1997 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | struct ext2_inode_reference { |
| + | blk_t block; |
| + | __u16 offset; |
| + | }; |
| + | |
| + | struct ext2_inode_relocate_entry { |
| + | ext2_ino_t new; |
| + | ext2_ino_t orig; |
| + | __u16 flags; |
| + | __u16 max_refs; |
| + | }; |
| + | |
| + | typedef struct ext2_inode_relocation_table *ext2_irel; |
| + | |
| + | struct ext2_inode_relocation_table { |
| + | __u32 magic; |
| + | char *name; |
| + | ext2_ino_t current; |
| + | void *priv_data; |
| + | |
| + | /* |
| + | * Add an inode relocation entry. |
| + | */ |
| + | errcode_t (*put)(ext2_irel irel, ext2_ino_t old, |
| + | struct ext2_inode_relocate_entry *ent); |
| + | /* |
| + | * Get an inode relocation entry. |
| + | */ |
| + | errcode_t (*get)(ext2_irel irel, ext2_ino_t old, |
| + | struct ext2_inode_relocate_entry *ent); |
| + | |
| + | /* |
| + | * Get an inode relocation entry by its original inode number |
| + | */ |
| + | errcode_t (*get_by_orig)(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old, |
| + | struct ext2_inode_relocate_entry *ent); |
| + | |
| + | /* |
| + | * Initialize for iterating over the inode relocation entries. |
| + | */ |
| + | errcode_t (*start_iter)(ext2_irel irel); |
| + | |
| + | /* |
| + | * The iterator function for the inode relocation entries. |
| + | * Returns an inode number of 0 when out of entries. |
| + | */ |
| + | errcode_t (*next)(ext2_irel irel, ext2_ino_t *old, |
| + | struct ext2_inode_relocate_entry *ent); |
| + | |
| + | /* |
| + | * Add an inode reference (i.e., note the fact that a |
| + | * particular block/offset contains a reference to an inode) |
| + | */ |
| + | errcode_t (*add_ref)(ext2_irel irel, ext2_ino_t ino, |
| + | struct ext2_inode_reference *ref); |
| + | |
| + | /* |
| + | * Initialize for iterating over the inode references for a |
| + | * particular inode. |
| + | */ |
| + | errcode_t (*start_iter_ref)(ext2_irel irel, ext2_ino_t ino); |
| + | |
| + | /* |
| + | * The iterator function for the inode references for an |
| + | * inode. The references for only one inode can be interator |
| + | * over at a time, as the iterator state is stored in ext2_irel. |
| + | */ |
| + | errcode_t (*next_ref)(ext2_irel irel, |
| + | struct ext2_inode_reference *ref); |
| + | |
| + | /* |
| + | * Move the inode relocation table from one inode number to |
| + | * another. Note that the inode references also must move. |
| + | */ |
| + | errcode_t (*move)(ext2_irel irel, ext2_ino_t old, ext2_ino_t new); |
| + | |
| + | /* |
| + | * Remove an inode relocation entry, along with all of the |
| + | * inode references. |
| + | */ |
| + | errcode_t (*delete)(ext2_irel irel, ext2_ino_t old); |
| + | |
| + | /* |
| + | * Free the inode relocation table. |
| + | */ |
| + | errcode_t (*free)(ext2_irel irel); |
| + | }; |
| + | |
| + | errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode, |
| + | ext2_irel *irel); |
| + | |
| + | #define ext2fs_irel_put(irel, old, ent) ((irel)->put((irel), old, ent)) |
| + | #define ext2fs_irel_get(irel, old, ent) ((irel)->get((irel), old, ent)) |
| + | #define ext2fs_irel_get_by_orig(irel, orig, old, ent) \ |
| + | ((irel)->get_by_orig((irel), orig, old, ent)) |
| + | #define ext2fs_irel_start_iter(irel) ((irel)->start_iter((irel))) |
| + | #define ext2fs_irel_next(irel, old, ent) ((irel)->next((irel), old, ent)) |
| + | #define ext2fs_irel_add_ref(irel, ino, ref) ((irel)->add_ref((irel), ino, ref)) |
| + | #define ext2fs_irel_start_iter_ref(irel, ino) ((irel)->start_iter_ref((irel), ino)) |
| + | #define ext2fs_irel_next_ref(irel, ref) ((irel)->next_ref((irel), ref)) |
| + | #define ext2fs_irel_move(irel, old, new) ((irel)->move((irel), old, new)) |
| + | #define ext2fs_irel_delete(irel, old) ((irel)->delete((irel), old)) |
| + | #define ext2fs_irel_free(irel) ((irel)->free((irel))) |
| @@ -0,0 +1,372 @@ | |
| + | /* |
| + | * irel_ma.c |
| + | * |
| + | * Copyright (C) 1996, 1997 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <fcntl.h> |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #if HAVE_ERRNO_H |
| + | #include <errno.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | #include "irel.h" |
| + | |
| + | static errcode_t ima_put(ext2_irel irel, ext2_ino_t old, |
| + | struct ext2_inode_relocate_entry *ent); |
| + | static errcode_t ima_get(ext2_irel irel, ext2_ino_t old, |
| + | struct ext2_inode_relocate_entry *ent); |
| + | static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old, |
| + | struct ext2_inode_relocate_entry *ent); |
| + | static errcode_t ima_start_iter(ext2_irel irel); |
| + | static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old, |
| + | struct ext2_inode_relocate_entry *ent); |
| + | static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino, |
| + | struct ext2_inode_reference *ref); |
| + | static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino); |
| + | static errcode_t ima_next_ref(ext2_irel irel, struct ext2_inode_reference *ref); |
| + | static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new); |
| + | static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old); |
| + | static errcode_t ima_free(ext2_irel irel); |
| + | |
| + | /* |
| + | * This data structure stores the array of inode references; there is |
| + | * a structure for each inode. |
| + | */ |
| + | struct inode_reference_entry { |
| + | __u16 num; |
| + | struct ext2_inode_reference *refs; |
| + | }; |
| + | |
| + | struct irel_ma { |
| + | __u32 magic; |
| + | ext2_ino_t max_inode; |
| + | ext2_ino_t ref_current; |
| + | int ref_iter; |
| + | ext2_ino_t *orig_map; |
| + | struct ext2_inode_relocate_entry *entries; |
| + | struct inode_reference_entry *ref_entries; |
| + | }; |
| + | |
| + | errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode, |
| + | ext2_irel *new_irel) |
| + | { |
| + | ext2_irel irel = 0; |
| + | errcode_t retval; |
| + | struct irel_ma *ma = 0; |
| + | size_t size; |
| + | |
| + | *new_irel = 0; |
| + | |
| + | /* |
| + | * Allocate memory structures |
| + | */ |
| + | retval = ext2fs_get_mem(sizeof(struct ext2_inode_relocation_table), |
| + | &irel); |
| + | if (retval) |
| + | goto errout; |
| + | memset(irel, 0, sizeof(struct ext2_inode_relocation_table)); |
| + | |
| + | retval = ext2fs_get_mem(strlen(name)+1, &irel->name); |
| + | if (retval) |
| + | goto errout; |
| + | strcpy(irel->name, name); |
| + | |
| + | retval = ext2fs_get_mem(sizeof(struct irel_ma), &ma); |
| + | if (retval) |
| + | goto errout; |
| + | memset(ma, 0, sizeof(struct irel_ma)); |
| + | irel->priv_data = ma; |
| + | |
| + | size = (size_t) (sizeof(ext2_ino_t) * (max_inode+1)); |
| + | retval = ext2fs_get_mem(size, &ma->orig_map); |
| + | if (retval) |
| + | goto errout; |
| + | memset(ma->orig_map, 0, size); |
| + | |
| + | size = (size_t) (sizeof(struct ext2_inode_relocate_entry) * |
| + | (max_inode+1)); |
| + | retval = ext2fs_get_mem(size, &ma->entries); |
| + | if (retval) |
| + | goto errout; |
| + | memset(ma->entries, 0, size); |
| + | |
| + | size = (size_t) (sizeof(struct inode_reference_entry) * |
| + | (max_inode+1)); |
| + | retval = ext2fs_get_mem(size, &ma->ref_entries); |
| + | if (retval) |
| + | goto errout; |
| + | memset(ma->ref_entries, 0, size); |
| + | ma->max_inode = max_inode; |
| + | |
| + | /* |
| + | * Fill in the irel data structure |
| + | */ |
| + | irel->put = ima_put; |
| + | irel->get = ima_get; |
| + | irel->get_by_orig = ima_get_by_orig; |
| + | irel->start_iter = ima_start_iter; |
| + | irel->next = ima_next; |
| + | irel->add_ref = ima_add_ref; |
| + | irel->start_iter_ref = ima_start_iter_ref; |
| + | irel->next_ref = ima_next_ref; |
| + | irel->move = ima_move; |
| + | irel->delete = ima_delete; |
| + | irel->free = ima_free; |
| + | |
| + | *new_irel = irel; |
| + | return 0; |
| + | |
| + | errout: |
| + | ima_free(irel); |
| + | return retval; |
| + | } |
| + | |
| + | static errcode_t ima_put(ext2_irel irel, ext2_ino_t old, |
| + | struct ext2_inode_relocate_entry *ent) |
| + | { |
| + | struct inode_reference_entry *ref_ent; |
| + | struct irel_ma *ma; |
| + | errcode_t retval; |
| + | size_t size, old_size; |
| + | |
| + | ma = irel->priv_data; |
| + | if (old > ma->max_inode) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | |
| + | /* |
| + | * Force the orig field to the correct value; the application |
| + | * program shouldn't be messing with this field. |
| + | */ |
| + | if (ma->entries[(unsigned) old].new == 0) |
| + | ent->orig = old; |
| + | else |
| + | ent->orig = ma->entries[(unsigned) old].orig; |
| + | |
| + | /* |
| + | * If max_refs has changed, reallocate the refs array |
| + | */ |
| + | ref_ent = ma->ref_entries + (unsigned) old; |
| + | if (ref_ent->refs && ent->max_refs != |
| + | ma->entries[(unsigned) old].max_refs) { |
| + | size = (sizeof(struct ext2_inode_reference) * ent->max_refs); |
| + | old_size = (sizeof(struct ext2_inode_reference) * |
| + | ma->entries[(unsigned) old].max_refs); |
| + | retval = ext2fs_resize_mem(old_size, size, &ref_ent->refs); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | |
| + | ma->entries[(unsigned) old] = *ent; |
| + | ma->orig_map[(unsigned) ent->orig] = old; |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t ima_get(ext2_irel irel, ext2_ino_t old, |
| + | struct ext2_inode_relocate_entry *ent) |
| + | { |
| + | struct irel_ma *ma; |
| + | |
| + | ma = irel->priv_data; |
| + | if (old > ma->max_inode) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | if (ma->entries[(unsigned) old].new == 0) |
| + | return ENOENT; |
| + | *ent = ma->entries[(unsigned) old]; |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old, |
| + | struct ext2_inode_relocate_entry *ent) |
| + | { |
| + | struct irel_ma *ma; |
| + | ext2_ino_t ino; |
| + | |
| + | ma = irel->priv_data; |
| + | if (orig > ma->max_inode) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | ino = ma->orig_map[(unsigned) orig]; |
| + | if (ino == 0) |
| + | return ENOENT; |
| + | *old = ino; |
| + | *ent = ma->entries[(unsigned) ino]; |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t ima_start_iter(ext2_irel irel) |
| + | { |
| + | irel->current = 0; |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old, |
| + | struct ext2_inode_relocate_entry *ent) |
| + | { |
| + | struct irel_ma *ma; |
| + | |
| + | ma = irel->priv_data; |
| + | while (++irel->current < ma->max_inode) { |
| + | if (ma->entries[(unsigned) irel->current].new == 0) |
| + | continue; |
| + | *old = irel->current; |
| + | *ent = ma->entries[(unsigned) irel->current]; |
| + | return 0; |
| + | } |
| + | *old = 0; |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino, |
| + | struct ext2_inode_reference *ref) |
| + | { |
| + | struct irel_ma *ma; |
| + | size_t size; |
| + | struct inode_reference_entry *ref_ent; |
| + | struct ext2_inode_relocate_entry *ent; |
| + | errcode_t retval; |
| + | |
| + | ma = irel->priv_data; |
| + | if (ino > ma->max_inode) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | |
| + | ref_ent = ma->ref_entries + (unsigned) ino; |
| + | ent = ma->entries + (unsigned) ino; |
| + | |
| + | /* |
| + | * If the inode reference array doesn't exist, create it. |
| + | */ |
| + | if (ref_ent->refs == 0) { |
| + | size = (size_t) ((sizeof(struct ext2_inode_reference) * |
| + | ent->max_refs)); |
| + | retval = ext2fs_get_mem(size, &ref_ent->refs); |
| + | if (retval) |
| + | return retval; |
| + | memset(ref_ent->refs, 0, size); |
| + | ref_ent->num = 0; |
| + | } |
| + | |
| + | if (ref_ent->num >= ent->max_refs) |
| + | return EXT2_ET_TOO_MANY_REFS; |
| + | |
| + | ref_ent->refs[(unsigned) ref_ent->num++] = *ref; |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino) |
| + | { |
| + | struct irel_ma *ma; |
| + | |
| + | ma = irel->priv_data; |
| + | if (ino > ma->max_inode) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | if (ma->entries[(unsigned) ino].new == 0) |
| + | return ENOENT; |
| + | ma->ref_current = ino; |
| + | ma->ref_iter = 0; |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t ima_next_ref(ext2_irel irel, |
| + | struct ext2_inode_reference *ref) |
| + | { |
| + | struct irel_ma *ma; |
| + | struct inode_reference_entry *ref_ent; |
| + | |
| + | ma = irel->priv_data; |
| + | |
| + | ref_ent = ma->ref_entries + ma->ref_current; |
| + | |
| + | if ((ref_ent->refs == NULL) || |
| + | (ma->ref_iter >= ref_ent->num)) { |
| + | ref->block = 0; |
| + | ref->offset = 0; |
| + | return 0; |
| + | } |
| + | *ref = ref_ent->refs[ma->ref_iter++]; |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new) |
| + | { |
| + | struct irel_ma *ma; |
| + | |
| + | ma = irel->priv_data; |
| + | if ((old > ma->max_inode) || (new > ma->max_inode)) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | if (ma->entries[(unsigned) old].new == 0) |
| + | return ENOENT; |
| + | |
| + | ma->entries[(unsigned) new] = ma->entries[(unsigned) old]; |
| + | if (ma->ref_entries[(unsigned) new].refs) |
| + | ext2fs_free_mem(&ma->ref_entries[(unsigned) new].refs); |
| + | ma->ref_entries[(unsigned) new] = ma->ref_entries[(unsigned) old]; |
| + | |
| + | ma->entries[(unsigned) old].new = 0; |
| + | ma->ref_entries[(unsigned) old].num = 0; |
| + | ma->ref_entries[(unsigned) old].refs = 0; |
| + | |
| + | ma->orig_map[ma->entries[new].orig] = new; |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old) |
| + | { |
| + | struct irel_ma *ma; |
| + | |
| + | ma = irel->priv_data; |
| + | if (old > ma->max_inode) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | if (ma->entries[(unsigned) old].new == 0) |
| + | return ENOENT; |
| + | |
| + | ma->entries[old].new = 0; |
| + | if (ma->ref_entries[(unsigned) old].refs) |
| + | ext2fs_free_mem(&ma->ref_entries[(unsigned) old].refs); |
| + | ma->orig_map[ma->entries[(unsigned) old].orig] = 0; |
| + | |
| + | ma->ref_entries[(unsigned) old].num = 0; |
| + | ma->ref_entries[(unsigned) old].refs = 0; |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t ima_free(ext2_irel irel) |
| + | { |
| + | struct irel_ma *ma; |
| + | ext2_ino_t ino; |
| + | |
| + | if (!irel) |
| + | return 0; |
| + | |
| + | ma = irel->priv_data; |
| + | |
| + | if (ma) { |
| + | if (ma->orig_map) |
| + | ext2fs_free_mem(&ma->orig_map); |
| + | if (ma->entries) |
| + | ext2fs_free_mem(&ma->entries); |
| + | if (ma->ref_entries) { |
| + | for (ino = 0; ino <= ma->max_inode; ino++) { |
| + | if (ma->ref_entries[(unsigned) ino].refs) |
| + | ext2fs_free_mem(&ma->ref_entries[(unsigned) ino].refs); |
| + | } |
| + | ext2fs_free_mem(&ma->ref_entries); |
| + | } |
| + | ext2fs_free_mem(&ma); |
| + | } |
| + | if (irel->name) |
| + | ext2fs_free_mem(&irel->name); |
| + | ext2fs_free_mem(&irel); |
| + | return 0; |
| + | } |
| @@ -0,0 +1,358 @@ | |
| + | /* |
| + | * ismounted.c --- Check to see if the filesystem was mounted |
| + | * |
| + | * Copyright (C) 1995,1996,1997,1998,1999,2000 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #if HAVE_ERRNO_H |
| + | #include <errno.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #ifdef HAVE_LINUX_FD_H |
| + | #include <linux/fd.h> |
| + | #endif |
| + | #ifdef HAVE_MNTENT_H |
| + | #include <mntent.h> |
| + | #endif |
| + | #ifdef HAVE_GETMNTINFO |
| + | #include <paths.h> |
| + | #include <sys/param.h> |
| + | #include <sys/mount.h> |
| + | #endif /* HAVE_GETMNTINFO */ |
| + | #include <string.h> |
| + | #include <sys/stat.h> |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | #ifdef HAVE_MNTENT_H |
| + | /* |
| + | * Helper function which checks a file in /etc/mtab format to see if a |
| + | * filesystem is mounted. Returns an error if the file doesn't exist |
| + | * or can't be opened. |
| + | */ |
| + | static errcode_t check_mntent_file(const char *mtab_file, const char *file, |
| + | int *mount_flags, char *mtpt, int mtlen) |
| + | { |
| + | struct mntent *mnt; |
| + | struct stat st_buf; |
| + | errcode_t retval = 0; |
| + | dev_t file_dev=0, file_rdev=0; |
| + | ino_t file_ino=0; |
| + | FILE *f; |
| + | int fd; |
| + | |
| + | *mount_flags = 0; |
| + | if ((f = setmntent (mtab_file, "r")) == NULL) |
| + | return errno; |
| + | if (stat(file, &st_buf) == 0) { |
| + | if (S_ISBLK(st_buf.st_mode)) { |
| + | #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ |
| + | file_rdev = st_buf.st_rdev; |
| + | #endif /* __GNU__ */ |
| + | } else { |
| + | file_dev = st_buf.st_dev; |
| + | file_ino = st_buf.st_ino; |
| + | } |
| + | } |
| + | while ((mnt = getmntent (f)) != NULL) { |
| + | if (strcmp(file, mnt->mnt_fsname) == 0) |
| + | break; |
| + | if (stat(mnt->mnt_fsname, &st_buf) == 0) { |
| + | if (S_ISBLK(st_buf.st_mode)) { |
| + | #ifndef __GNU__ |
| + | if (file_rdev && (file_rdev == st_buf.st_rdev)) |
| + | break; |
| + | #endif /* __GNU__ */ |
| + | } else { |
| + | if (file_dev && ((file_dev == st_buf.st_dev) && |
| + | (file_ino == st_buf.st_ino))) |
| + | break; |
| + | } |
| + | } |
| + | } |
| + | |
| + | if (mnt == 0) { |
| + | #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ |
| + | /* |
| + | * Do an extra check to see if this is the root device. We |
| + | * can't trust /etc/mtab, and /proc/mounts will only list |
| + | * /dev/root for the root filesystem. Argh. Instead we |
| + | * check if the given device has the same major/minor number |
| + | * as the device that the root directory is on. |
| + | */ |
| + | if (file_rdev && stat("/", &st_buf) == 0) { |
| + | if (st_buf.st_dev == file_rdev) { |
| + | *mount_flags = EXT2_MF_MOUNTED; |
| + | if (mtpt) |
| + | strncpy(mtpt, "/", mtlen); |
| + | goto is_root; |
| + | } |
| + | } |
| + | #endif /* __GNU__ */ |
| + | goto errout; |
| + | } |
| + | #ifndef __GNU__ /* The GNU hurd is deficient; what else is new? */ |
| + | /* Validate the entry in case /etc/mtab is out of date */ |
| + | /* |
| + | * We need to be paranoid, because some broken distributions |
| + | * (read: Slackware) don't initialize /etc/mtab before checking |
| + | * all of the non-root filesystems on the disk. |
| + | */ |
| + | if (stat(mnt->mnt_dir, &st_buf) < 0) { |
| + | retval = errno; |
| + | if (retval == ENOENT) { |
| + | #ifdef DEBUG |
| + | printf("Bogus entry in %s! (%s does not exist)\n", |
| + | mtab_file, mnt->mnt_dir); |
| + | #endif /* DEBUG */ |
| + | retval = 0; |
| + | } |
| + | goto errout; |
| + | } |
| + | if (file_rdev && (st_buf.st_dev != file_rdev)) { |
| + | #ifdef DEBUG |
| + | printf("Bogus entry in %s! (%s not mounted on %s)\n", |
| + | mtab_file, file, mnt->mnt_dir); |
| + | #endif /* DEBUG */ |
| + | goto errout; |
| + | } |
| + | #endif /* __GNU__ */ |
| + | *mount_flags = EXT2_MF_MOUNTED; |
| + | |
| + | #ifdef MNTOPT_RO |
| + | /* Check to see if the ro option is set */ |
| + | if (hasmntopt(mnt, MNTOPT_RO)) |
| + | *mount_flags |= EXT2_MF_READONLY; |
| + | #endif |
| + | |
| + | if (mtpt) |
| + | strncpy(mtpt, mnt->mnt_dir, mtlen); |
| + | /* |
| + | * Check to see if we're referring to the root filesystem. |
| + | * If so, do a manual check to see if we can open /etc/mtab |
| + | * read/write, since if the root is mounted read/only, the |
| + | * contents of /etc/mtab may not be accurate. |
| + | */ |
| + | if (!strcmp(mnt->mnt_dir, "/")) { |
| + | is_root: |
| + | #define TEST_FILE "/.ismount-test-file" |
| + | *mount_flags |= EXT2_MF_ISROOT; |
| + | fd = open(TEST_FILE, O_RDWR|O_CREAT); |
| + | if (fd < 0) { |
| + | if (errno == EROFS) |
| + | *mount_flags |= EXT2_MF_READONLY; |
| + | } else |
| + | close(fd); |
| + | (void) unlink(TEST_FILE); |
| + | } |
| + | retval = 0; |
| + | errout: |
| + | endmntent (f); |
| + | return retval; |
| + | } |
| + | |
| + | static errcode_t check_mntent(const char *file, int *mount_flags, |
| + | char *mtpt, int mtlen) |
| + | { |
| + | errcode_t retval; |
| + | |
| + | #ifdef DEBUG |
| + | retval = check_mntent_file("/tmp/mtab", file, mount_flags, |
| + | mtpt, mtlen); |
| + | if (retval == 0) |
| + | return 0; |
| + | #endif /* DEBUG */ |
| + | #ifdef __linux__ |
| + | retval = check_mntent_file("/proc/mounts", file, mount_flags, |
| + | mtpt, mtlen); |
| + | if (retval == 0 && (*mount_flags != 0)) |
| + | return 0; |
| + | #endif /* __linux__ */ |
| + | #if defined(MOUNTED) || defined(_PATH_MOUNTED) |
| + | #ifndef MOUNTED |
| + | #define MOUNTED _PATH_MOUNTED |
| + | #endif /* MOUNTED */ |
| + | retval = check_mntent_file(MOUNTED, file, mount_flags, mtpt, mtlen); |
| + | return retval; |
| + | #else |
| + | *mount_flags = 0; |
| + | return 0; |
| + | #endif /* defined(MOUNTED) || defined(_PATH_MOUNTED) */ |
| + | } |
| + | |
| + | #else |
| + | #if defined(HAVE_GETMNTINFO) |
| + | |
| + | static errcode_t check_getmntinfo(const char *file, int *mount_flags, |
| + | char *mtpt, int mtlen) |
| + | { |
| + | struct statfs *mp; |
| + | int len, n; |
| + | const char *s1; |
| + | char *s2; |
| + | |
| + | n = getmntinfo(&mp, MNT_NOWAIT); |
| + | if (n == 0) |
| + | return errno; |
| + | |
| + | len = sizeof(_PATH_DEV) - 1; |
| + | s1 = file; |
| + | if (strncmp(_PATH_DEV, s1, len) == 0) |
| + | s1 += len; |
| + | |
| + | *mount_flags = 0; |
| + | while (--n >= 0) { |
| + | s2 = mp->f_mntfromname; |
| + | if (strncmp(_PATH_DEV, s2, len) == 0) { |
| + | s2 += len - 1; |
| + | *s2 = 'r'; |
| + | } |
| + | if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) { |
| + | *mount_flags = EXT2_MF_MOUNTED; |
| + | break; |
| + | } |
| + | ++mp; |
| + | } |
| + | if (mtpt) |
| + | strncpy(mtpt, mp->f_mntonname, mtlen); |
| + | return 0; |
| + | } |
| + | #endif /* HAVE_GETMNTINFO */ |
| + | #endif /* HAVE_MNTENT_H */ |
| + | |
| + | /* |
| + | * Check to see if we're dealing with the swap device. |
| + | */ |
| + | static int is_swap_device(const char *file) |
| + | { |
| + | FILE *f; |
| + | char buf[1024], *cp; |
| + | dev_t file_dev; |
| + | struct stat st_buf; |
| + | int ret = 0; |
| + | |
| + | file_dev = 0; |
| + | #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ |
| + | if ((stat(file, &st_buf) == 0) && |
| + | S_ISBLK(st_buf.st_mode)) |
| + | file_dev = st_buf.st_rdev; |
| + | #endif /* __GNU__ */ |
| + | |
| + | if (!(f = fopen("/proc/swaps", "r"))) |
| + | return 0; |
| + | /* Skip the first line */ |
| + | fgets(buf, sizeof(buf), f); |
| + | while (!feof(f)) { |
| + | if (!fgets(buf, sizeof(buf), f)) |
| + | break; |
| + | if ((cp = strchr(buf, ' ')) != NULL) |
| + | *cp = 0; |
| + | if ((cp = strchr(buf, '\t')) != NULL) |
| + | *cp = 0; |
| + | if (strcmp(buf, file) == 0) { |
| + | ret++; |
| + | break; |
| + | } |
| + | #ifndef __GNU__ |
| + | if (file_dev && (stat(buf, &st_buf) == 0) && |
| + | S_ISBLK(st_buf.st_mode) && |
| + | file_dev == st_buf.st_rdev) { |
| + | ret++; |
| + | break; |
| + | } |
| + | #endif /* __GNU__ */ |
| + | } |
| + | fclose(f); |
| + | return ret; |
| + | } |
| + | |
| + | |
| + | /* |
| + | * ext2fs_check_mount_point() returns 1 if the device is mounted, 0 |
| + | * otherwise. If mtpt is non-NULL, the directory where the device is |
| + | * mounted is copied to where mtpt is pointing, up to mtlen |
| + | * characters. |
| + | */ |
| + | #ifdef __TURBOC__ |
| + | #pragma argsused |
| + | #endif |
| + | errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags, |
| + | char *mtpt, int mtlen) |
| + | { |
| + | if (is_swap_device(device)) { |
| + | *mount_flags = EXT2_MF_MOUNTED | EXT2_MF_SWAP; |
| + | strncpy(mtpt, "<swap>", mtlen); |
| + | return 0; |
| + | } |
| + | #ifdef HAVE_MNTENT_H |
| + | return check_mntent(device, mount_flags, mtpt, mtlen); |
| + | #else |
| + | #ifdef HAVE_GETMNTINFO |
| + | return check_getmntinfo(device, mount_flags, mtpt, mtlen); |
| + | #else |
| + | #ifdef __GNUC__ |
| + | #warning "Can't use getmntent or getmntinfo to check for mounted filesystems!" |
| + | #endif |
| + | *mount_flags = 0; |
| + | return 0; |
| + | #endif /* HAVE_GETMNTINFO */ |
| + | #endif /* HAVE_MNTENT_H */ |
| + | } |
| + | |
| + | /* |
| + | * ext2fs_check_if_mounted() sets the mount_flags EXT2_MF_MOUNTED, |
| + | * EXT2_MF_READONLY, and EXT2_MF_ROOT |
| + | * |
| + | */ |
| + | errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags) |
| + | { |
| + | return ext2fs_check_mount_point(file, mount_flags, NULL, 0); |
| + | } |
| + | |
| + | #ifdef DEBUG |
| + | int main(int argc, char **argv) |
| + | { |
| + | int retval, mount_flags; |
| + | char mntpt[80]; |
| + | |
| + | if (argc < 2) { |
| + | fprintf(stderr, "Usage: %s device\n", argv[0]); |
| + | exit(1); |
| + | } |
| + | |
| + | mntpt[0] = 0; |
| + | retval = ext2fs_check_mount_point(argv[1], &mount_flags, |
| + | mntpt, sizeof(mntpt)); |
| + | if (retval) { |
| + | com_err(argv[0], retval, |
| + | "while calling ext2fs_check_if_mounted"); |
| + | exit(1); |
| + | } |
| + | printf("Device %s reports flags %02x\n", argv[1], mount_flags); |
| + | if (mount_flags & EXT2_MF_MOUNTED) |
| + | printf("\t%s is mounted.\n", argv[1]); |
| + | |
| + | if (mount_flags & EXT2_MF_SWAP) |
| + | printf("\t%s is a swap device.\n", argv[1]); |
| + | |
| + | if (mount_flags & EXT2_MF_READONLY) |
| + | printf("\t%s is read-only.\n", argv[1]); |
| + | |
| + | if (mount_flags & EXT2_MF_ISROOT) |
| + | printf("\t%s is the root filesystem.\n", argv[1]); |
| + | if (mntpt[0]) |
| + | printf("\t%s is mounted on %s.\n", argv[1], mntpt); |
| + | |
| + | exit(0); |
| + | } |
| + | #endif /* DEBUG */ |
| @@ -0,0 +1,67 @@ | |
| + | |
| + | #ifndef _JFS_COMPAT_H |
| + | #define _JFS_COMPAT_H |
| + | |
| + | #include "kernel-list.h" |
| + | #include <errno.h> |
| + | #ifdef HAVE_NETINET_IN_H |
| + | #include <netinet/in.h> |
| + | #endif |
| + | |
| + | #define printk printf |
| + | #define KERN_ERR "" |
| + | #define KERN_DEBUG "" |
| + | |
| + | #define READ 0 |
| + | #define WRITE 1 |
| + | |
| + | #define cpu_to_be32(n) htonl(n) |
| + | #define be32_to_cpu(n) ntohl(n) |
| + | |
| + | typedef unsigned int tid_t; |
| + | typedef struct journal_s journal_t; |
| + | |
| + | struct buffer_head; |
| + | struct inode; |
| + | |
| + | struct journal_s |
| + | { |
| + | unsigned long j_flags; |
| + | int j_errno; |
| + | struct buffer_head * j_sb_buffer; |
| + | struct journal_superblock_s *j_superblock; |
| + | int j_format_version; |
| + | unsigned long j_head; |
| + | unsigned long j_tail; |
| + | unsigned long j_free; |
| + | unsigned long j_first, j_last; |
| + | kdev_t j_dev; |
| + | kdev_t j_fs_dev; |
| + | int j_blocksize; |
| + | unsigned int j_blk_offset; |
| + | unsigned int j_maxlen; |
| + | struct inode * j_inode; |
| + | tid_t j_tail_sequence; |
| + | tid_t j_transaction_sequence; |
| + | __u8 j_uuid[16]; |
| + | struct jbd_revoke_table_s *j_revoke; |
| + | }; |
| + | |
| + | #define J_ASSERT(assert) \ |
| + | do { if (!(assert)) { \ |
| + | printf ("Assertion failure in %s() at %s line %d: " \ |
| + | "\"%s\"\n", \ |
| + | __FUNCTION__, __FILE__, __LINE__, # assert); \ |
| + | fatal_error(e2fsck_global_ctx, 0); \ |
| + | } } while (0) |
| + | |
| + | #define is_journal_abort(x) 0 |
| + | |
| + | #define BUFFER_TRACE(bh, info) do {} while (0) |
| + | |
| + | /* Need this so we can compile with configure --enable-gcc-wall */ |
| + | #ifdef NO_INLINE_FUNCS |
| + | #define inline |
| + | #endif |
| + | |
| + | #endif /* _JFS_COMPAT_H */ |
| @@ -0,0 +1,64 @@ | |
| + | /* |
| + | * jfs_dat.h --- stripped down header file which only contains the JFS |
| + | * on-disk data structures |
| + | */ |
| + | |
| + | #define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */ |
| + | |
| + | /* |
| + | * On-disk structures |
| + | */ |
| + | |
| + | /* |
| + | * Descriptor block types: |
| + | */ |
| + | |
| + | #define JFS_DESCRIPTOR_BLOCK 1 |
| + | #define JFS_COMMIT_BLOCK 2 |
| + | #define JFS_SUPERBLOCK 3 |
| + | |
| + | /* |
| + | * Standard header for all descriptor blocks: |
| + | */ |
| + | typedef struct journal_header_s |
| + | { |
| + | __u32 h_magic; |
| + | __u32 h_blocktype; |
| + | __u32 h_sequence; |
| + | } journal_header_t; |
| + | |
| + | |
| + | /* |
| + | * The block tag: used to describe a single buffer in the journal |
| + | */ |
| + | typedef struct journal_block_tag_s |
| + | { |
| + | __u32 t_blocknr; /* The on-disk block number */ |
| + | __u32 t_flags; /* See below */ |
| + | } journal_block_tag_t; |
| + | |
| + | /* Definitions for the journal tag flags word: */ |
| + | #define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */ |
| + | #define JFS_FLAG_SAME_UUID 2 /* block has same uuid as previous */ |
| + | #define JFS_FLAG_DELETED 4 /* block deleted by this transaction */ |
| + | #define JFS_FLAG_LAST_TAG 8 /* last tag in this descriptor block */ |
| + | |
| + | |
| + | /* |
| + | * The journal superblock |
| + | */ |
| + | typedef struct journal_superblock_s |
| + | { |
| + | journal_header_t s_header; |
| + | |
| + | /* Static information describing the journal */ |
| + | __u32 s_blocksize; /* journal device blocksize */ |
| + | __u32 s_maxlen; /* total blocks in journal file */ |
| + | __u32 s_first; /* first block of log information */ |
| + | |
| + | /* Dynamic information describing the current state of the log */ |
| + | __u32 s_sequence; /* first commit ID expected in log */ |
| + | __u32 s_start; /* blocknr of start of log */ |
| + | |
| + | } journal_superblock_t; |
| + | |
| @@ -0,0 +1,8 @@ | |
| + | #ifndef _JFS_USER_H |
| + | #define _JFS_USER_H |
| + | |
| + | typedef unsigned short kdev_t; |
| + | |
| + | #include "kernel-jbd.h" |
| + | |
| + | #endif /* _JFS_USER_H */ |
| @@ -0,0 +1,910 @@ | |
| + | /* |
| + | * linux/include/linux/jbd.h |
| + | * |
| + | * Written by Stephen C. Tweedie <[email protected]> |
| + | * |
| + | * Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved |
| + | * |
| + | * This file is part of the Linux kernel and is made available under |
| + | * the terms of the GNU General Public License, version 2, or at your |
| + | * option, any later version, incorporated herein by reference. |
| + | * |
| + | * Definitions for transaction data structures for the buffer cache |
| + | * filesystem journaling support. |
| + | */ |
| + | |
| + | #ifndef _LINUX_JBD_H |
| + | #define _LINUX_JBD_H |
| + | |
| + | #if defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE) || !defined(__KERNEL__) |
| + | |
| + | /* Allow this file to be included directly into e2fsprogs */ |
| + | #ifndef __KERNEL__ |
| + | #include "jfs_compat.h" |
| + | #define JFS_DEBUG |
| + | #define jfs_debug jbd_debug |
| + | #else |
| + | |
| + | #include <linux/journal-head.h> |
| + | #include <linux/stddef.h> |
| + | #include <asm/semaphore.h> |
| + | #endif |
| + | |
| + | #ifndef __GNUC__ |
| + | #define __FUNCTION__ "" |
| + | #endif |
| + | |
| + | #define journal_oom_retry 1 |
| + | |
| + | #ifdef __STDC__ |
| + | #ifdef __CONFIG_JBD_DEBUG__E2FS |
| + | /* |
| + | * Define JBD_EXPENSIVE_CHECKING to enable more expensive internal |
| + | * consistency checks. By default we don't do this unless |
| + | * __CONFIG_JBD_DEBUG__E2FS is on. |
| + | */ |
| + | #define JBD_EXPENSIVE_CHECKING |
| + | extern int journal_enable_debug; |
| + | |
| + | #define jbd_debug(n, f, a...) \ |
| + | do { \ |
| + | if ((n) <= journal_enable_debug) { \ |
| + | printk (KERN_DEBUG "(%s, %d): %s: ", \ |
| + | __FILE__, __LINE__, __FUNCTION__); \ |
| + | printk (f, ## a); \ |
| + | } \ |
| + | } while (0) |
| + | #else |
| + | #ifdef __GNUC__ |
| + | #define jbd_debug(f, a...) /**/ |
| + | #else |
| + | #define jbd_debug(f, ...) /**/ |
| + | #endif |
| + | #endif |
| + | #else |
| + | #define jbd_debug(x) /* AIX doesn't do STDC */ |
| + | #endif |
| + | |
| + | extern void * __jbd_kmalloc (char *where, size_t size, int flags, int retry); |
| + | #define jbd_kmalloc(size, flags) \ |
| + | __jbd_kmalloc(__FUNCTION__, (size), (flags), journal_oom_retry) |
| + | #define jbd_rep_kmalloc(size, flags) \ |
| + | __jbd_kmalloc(__FUNCTION__, (size), (flags), 1) |
| + | |
| + | #define JFS_MIN_JOURNAL_BLOCKS 1024 |
| + | |
| + | #ifdef __KERNEL__ |
| + | typedef struct handle_s handle_t; /* Atomic operation type */ |
| + | typedef struct journal_s journal_t; /* Journal control structure */ |
| + | #endif |
| + | |
| + | /* |
| + | * Internal structures used by the logging mechanism: |
| + | */ |
| + | |
| + | #define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */ |
| + | |
| + | /* |
| + | * On-disk structures |
| + | */ |
| + | |
| + | /* |
| + | * Descriptor block types: |
| + | */ |
| + | |
| + | #define JFS_DESCRIPTOR_BLOCK 1 |
| + | #define JFS_COMMIT_BLOCK 2 |
| + | #define JFS_SUPERBLOCK_V1 3 |
| + | #define JFS_SUPERBLOCK_V2 4 |
| + | #define JFS_REVOKE_BLOCK 5 |
| + | |
| + | /* |
| + | * Standard header for all descriptor blocks: |
| + | */ |
| + | typedef struct journal_header_s |
| + | { |
| + | __u32 h_magic; |
| + | __u32 h_blocktype; |
| + | __u32 h_sequence; |
| + | } journal_header_t; |
| + | |
| + | |
| + | /* |
| + | * The block tag: used to describe a single buffer in the journal |
| + | */ |
| + | typedef struct journal_block_tag_s |
| + | { |
| + | __u32 t_blocknr; /* The on-disk block number */ |
| + | __u32 t_flags; /* See below */ |
| + | } journal_block_tag_t; |
| + | |
| + | /* |
| + | * The revoke descriptor: used on disk to describe a series of blocks to |
| + | * be revoked from the log |
| + | */ |
| + | typedef struct journal_revoke_header_s |
| + | { |
| + | journal_header_t r_header; |
| + | int r_count; /* Count of bytes used in the block */ |
| + | } journal_revoke_header_t; |
| + | |
| + | |
| + | /* Definitions for the journal tag flags word: */ |
| + | #define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */ |
| + | #define JFS_FLAG_SAME_UUID 2 /* block has same uuid as previous */ |
| + | #define JFS_FLAG_DELETED 4 /* block deleted by this transaction */ |
| + | #define JFS_FLAG_LAST_TAG 8 /* last tag in this descriptor block */ |
| + | |
| + | |
| + | /* |
| + | * The journal superblock. All fields are in big-endian byte order. |
| + | */ |
| + | typedef struct journal_superblock_s |
| + | { |
| + | /* 0x0000 */ |
| + | journal_header_t s_header; |
| + | |
| + | /* 0x000C */ |
| + | /* Static information describing the journal */ |
| + | __u32 s_blocksize; /* journal device blocksize */ |
| + | __u32 s_maxlen; /* total blocks in journal file */ |
| + | __u32 s_first; /* first block of log information */ |
| + | |
| + | /* 0x0018 */ |
| + | /* Dynamic information describing the current state of the log */ |
| + | __u32 s_sequence; /* first commit ID expected in log */ |
| + | __u32 s_start; /* blocknr of start of log */ |
| + | |
| + | /* 0x0020 */ |
| + | /* Error value, as set by journal_abort(). */ |
| + | __s32 s_errno; |
| + | |
| + | /* 0x0024 */ |
| + | /* Remaining fields are only valid in a version-2 superblock */ |
| + | __u32 s_feature_compat; /* compatible feature set */ |
| + | __u32 s_feature_incompat; /* incompatible feature set */ |
| + | __u32 s_feature_ro_compat; /* readonly-compatible feature set */ |
| + | /* 0x0030 */ |
| + | __u8 s_uuid[16]; /* 128-bit uuid for journal */ |
| + | |
| + | /* 0x0040 */ |
| + | __u32 s_nr_users; /* Nr of filesystems sharing log */ |
| + | |
| + | __u32 s_dynsuper; /* Blocknr of dynamic superblock copy*/ |
| + | |
| + | /* 0x0048 */ |
| + | __u32 s_max_transaction; /* Limit of journal blocks per trans.*/ |
| + | __u32 s_max_trans_data; /* Limit of data blocks per trans. */ |
| + | |
| + | /* 0x0050 */ |
| + | __u32 s_padding[44]; |
| + | |
| + | /* 0x0100 */ |
| + | __u8 s_users[16*48]; /* ids of all fs'es sharing the log */ |
| + | /* 0x0400 */ |
| + | } journal_superblock_t; |
| + | |
| + | #define JFS_HAS_COMPAT_FEATURE(j,mask) \ |
| + | ((j)->j_format_version >= 2 && \ |
| + | ((j)->j_superblock->s_feature_compat & cpu_to_be32((mask)))) |
| + | #define JFS_HAS_RO_COMPAT_FEATURE(j,mask) \ |
| + | ((j)->j_format_version >= 2 && \ |
| + | ((j)->j_superblock->s_feature_ro_compat & cpu_to_be32((mask)))) |
| + | #define JFS_HAS_INCOMPAT_FEATURE(j,mask) \ |
| + | ((j)->j_format_version >= 2 && \ |
| + | ((j)->j_superblock->s_feature_incompat & cpu_to_be32((mask)))) |
| + | |
| + | #define JFS_FEATURE_INCOMPAT_REVOKE 0x00000001 |
| + | |
| + | /* Features known to this kernel version: */ |
| + | #define JFS_KNOWN_COMPAT_FEATURES 0 |
| + | #define JFS_KNOWN_ROCOMPAT_FEATURES 0 |
| + | #define JFS_KNOWN_INCOMPAT_FEATURES JFS_FEATURE_INCOMPAT_REVOKE |
| + | |
| + | #ifdef __KERNEL__ |
| + | |
| + | #include <linux/fs.h> |
| + | #include <linux/sched.h> |
| + | |
| + | #define JBD_ASSERTIONS |
| + | #ifdef JBD_ASSERTIONS |
| + | #define J_ASSERT(assert) \ |
| + | do { \ |
| + | if (!(assert)) { \ |
| + | printk (KERN_EMERG \ |
| + | "Assertion failure in %s() at %s:%d: \"%s\"\n", \ |
| + | __FUNCTION__, __FILE__, __LINE__, # assert); \ |
| + | BUG(); \ |
| + | } \ |
| + | } while (0) |
| + | |
| + | #if defined(CONFIG_BUFFER_DEBUG) |
| + | void buffer_assertion_failure(struct buffer_head *bh); |
| + | #define J_ASSERT_BH(bh, expr) \ |
| + | do { \ |
| + | if (!(expr)) \ |
| + | buffer_assertion_failure(bh); \ |
| + | J_ASSERT(expr); \ |
| + | } while (0) |
| + | #define J_ASSERT_JH(jh, expr) J_ASSERT_BH(jh2bh(jh), expr) |
| + | #else |
| + | #define J_ASSERT_BH(bh, expr) J_ASSERT(expr) |
| + | #define J_ASSERT_JH(jh, expr) J_ASSERT(expr) |
| + | #endif |
| + | |
| + | #else |
| + | #define J_ASSERT(assert) |
| + | #endif /* JBD_ASSERTIONS */ |
| + | |
| + | enum jbd_state_bits { |
| + | BH_JWrite |
| + | = BH_PrivateStart, /* 1 if being written to log (@@@ DEBUGGING) */ |
| + | BH_Freed, /* 1 if buffer has been freed (truncated) */ |
| + | BH_Revoked, /* 1 if buffer has been revoked from the log */ |
| + | BH_RevokeValid, /* 1 if buffer revoked flag is valid */ |
| + | BH_JBDDirty, /* 1 if buffer is dirty but journaled */ |
| + | }; |
| + | |
| + | /* Return true if the buffer is one which JBD is managing */ |
| + | static inline int buffer_jbd(struct buffer_head *bh) |
| + | { |
| + | return __buffer_state(bh, JBD); |
| + | } |
| + | |
| + | static inline struct buffer_head *jh2bh(struct journal_head *jh) |
| + | { |
| + | return jh->b_bh; |
| + | } |
| + | |
| + | static inline struct journal_head *bh2jh(struct buffer_head *bh) |
| + | { |
| + | return bh->b_private; |
| + | } |
| + | |
| + | struct jbd_revoke_table_s; |
| + | |
| + | /* The handle_t type represents a single atomic update being performed |
| + | * by some process. All filesystem modifications made by the process go |
| + | * through this handle. Recursive operations (such as quota operations) |
| + | * are gathered into a single update. |
| + | * |
| + | * The buffer credits field is used to account for journaled buffers |
| + | * being modified by the running process. To ensure that there is |
| + | * enough log space for all outstanding operations, we need to limit the |
| + | * number of outstanding buffers possible at any time. When the |
| + | * operation completes, any buffer credits not used are credited back to |
| + | * the transaction, so that at all times we know how many buffers the |
| + | * outstanding updates on a transaction might possibly touch. */ |
| + | |
| + | struct handle_s |
| + | { |
| + | /* Which compound transaction is this update a part of? */ |
| + | transaction_t * h_transaction; |
| + | |
| + | /* Number of remaining buffers we are allowed to dirty: */ |
| + | int h_buffer_credits; |
| + | |
| + | /* Reference count on this handle */ |
| + | int h_ref; |
| + | |
| + | /* Field for caller's use to track errors through large fs |
| + | operations */ |
| + | int h_err; |
| + | |
| + | /* Flags */ |
| + | unsigned int h_sync: 1; /* sync-on-close */ |
| + | unsigned int h_jdata: 1; /* force data journaling */ |
| + | unsigned int h_aborted: 1; /* fatal error on handle */ |
| + | }; |
| + | |
| + | |
| + | /* The transaction_t type is the guts of the journaling mechanism. It |
| + | * tracks a compound transaction through its various states: |
| + | * |
| + | * RUNNING: accepting new updates |
| + | * LOCKED: Updates still running but we don't accept new ones |
| + | * RUNDOWN: Updates are tidying up but have finished requesting |
| + | * new buffers to modify (state not used for now) |
| + | * FLUSH: All updates complete, but we are still writing to disk |
| + | * COMMIT: All data on disk, writing commit record |
| + | * FINISHED: We still have to keep the transaction for checkpointing. |
| + | * |
| + | * The transaction keeps track of all of the buffers modified by a |
| + | * running transaction, and all of the buffers committed but not yet |
| + | * flushed to home for finished transactions. |
| + | */ |
| + | |
| + | struct transaction_s |
| + | { |
| + | /* Pointer to the journal for this transaction. */ |
| + | journal_t * t_journal; |
| + | |
| + | /* Sequence number for this transaction */ |
| + | tid_t t_tid; |
| + | |
| + | /* Transaction's current state */ |
| + | enum { |
| + | T_RUNNING, |
| + | T_LOCKED, |
| + | T_RUNDOWN, |
| + | T_FLUSH, |
| + | T_COMMIT, |
| + | T_FINISHED |
| + | } t_state; |
| + | |
| + | /* Where in the log does this transaction's commit start? */ |
| + | unsigned long t_log_start; |
| + | |
| + | /* Doubly-linked circular list of all inodes owned by this |
| + | transaction */ /* AKPM: unused */ |
| + | struct inode * t_ilist; |
| + | |
| + | /* Number of buffers on the t_buffers list */ |
| + | int t_nr_buffers; |
| + | |
| + | /* Doubly-linked circular list of all buffers reserved but not |
| + | yet modified by this transaction */ |
| + | struct journal_head * t_reserved_list; |
| + | |
| + | /* Doubly-linked circular list of all metadata buffers owned by this |
| + | transaction */ |
| + | struct journal_head * t_buffers; |
| + | |
| + | /* |
| + | * Doubly-linked circular list of all data buffers still to be |
| + | * flushed before this transaction can be committed. |
| + | * Protected by journal_datalist_lock. |
| + | */ |
| + | struct journal_head * t_sync_datalist; |
| + | |
| + | /* |
| + | * Doubly-linked circular list of all writepage data buffers |
| + | * still to be written before this transaction can be committed. |
| + | * Protected by journal_datalist_lock. |
| + | */ |
| + | struct journal_head * t_async_datalist; |
| + | |
| + | /* Doubly-linked circular list of all forget buffers (superceded |
| + | buffers which we can un-checkpoint once this transaction |
| + | commits) */ |
| + | struct journal_head * t_forget; |
| + | |
| + | /* |
| + | * Doubly-linked circular list of all buffers still to be |
| + | * flushed before this transaction can be checkpointed. |
| + | */ |
| + | /* Protected by journal_datalist_lock */ |
| + | struct journal_head * t_checkpoint_list; |
| + | |
| + | /* Doubly-linked circular list of temporary buffers currently |
| + | undergoing IO in the log */ |
| + | struct journal_head * t_iobuf_list; |
| + | |
| + | /* Doubly-linked circular list of metadata buffers being |
| + | shadowed by log IO. The IO buffers on the iobuf list and the |
| + | shadow buffers on this list match each other one for one at |
| + | all times. */ |
| + | struct journal_head * t_shadow_list; |
| + | |
| + | /* Doubly-linked circular list of control buffers being written |
| + | to the log. */ |
| + | struct journal_head * t_log_list; |
| + | |
| + | /* Number of outstanding updates running on this transaction */ |
| + | int t_updates; |
| + | |
| + | /* Number of buffers reserved for use by all handles in this |
| + | * transaction handle but not yet modified. */ |
| + | int t_outstanding_credits; |
| + | |
| + | /* |
| + | * Forward and backward links for the circular list of all |
| + | * transactions awaiting checkpoint. |
| + | */ |
| + | /* Protected by journal_datalist_lock */ |
| + | transaction_t *t_cpnext, *t_cpprev; |
| + | |
| + | /* When will the transaction expire (become due for commit), in |
| + | * jiffies ? */ |
| + | unsigned long t_expires; |
| + | |
| + | /* How many handles used this transaction? */ |
| + | int t_handle_count; |
| + | }; |
| + | |
| + | |
| + | /* The journal_t maintains all of the journaling state information for a |
| + | * single filesystem. It is linked to from the fs superblock structure. |
| + | * |
| + | * We use the journal_t to keep track of all outstanding transaction |
| + | * activity on the filesystem, and to manage the state of the log |
| + | * writing process. */ |
| + | |
| + | struct journal_s |
| + | { |
| + | /* General journaling state flags */ |
| + | unsigned long j_flags; |
| + | |
| + | /* Is there an outstanding uncleared error on the journal (from |
| + | * a prior abort)? */ |
| + | int j_errno; |
| + | |
| + | /* The superblock buffer */ |
| + | struct buffer_head * j_sb_buffer; |
| + | journal_superblock_t * j_superblock; |
| + | |
| + | /* Version of the superblock format */ |
| + | int j_format_version; |
| + | |
| + | /* Number of processes waiting to create a barrier lock */ |
| + | int j_barrier_count; |
| + | |
| + | /* The barrier lock itself */ |
| + | struct semaphore j_barrier; |
| + | |
| + | /* Transactions: The current running transaction... */ |
| + | transaction_t * j_running_transaction; |
| + | |
| + | /* ... the transaction we are pushing to disk ... */ |
| + | transaction_t * j_committing_transaction; |
| + | |
| + | /* ... and a linked circular list of all transactions waiting |
| + | * for checkpointing. */ |
| + | /* Protected by journal_datalist_lock */ |
| + | transaction_t * j_checkpoint_transactions; |
| + | |
| + | /* Wait queue for waiting for a locked transaction to start |
| + | committing, or for a barrier lock to be released */ |
| + | wait_queue_head_t j_wait_transaction_locked; |
| + | |
| + | /* Wait queue for waiting for checkpointing to complete */ |
| + | wait_queue_head_t j_wait_logspace; |
| + | |
| + | /* Wait queue for waiting for commit to complete */ |
| + | wait_queue_head_t j_wait_done_commit; |
| + | |
| + | /* Wait queue to trigger checkpointing */ |
| + | wait_queue_head_t j_wait_checkpoint; |
| + | |
| + | /* Wait queue to trigger commit */ |
| + | wait_queue_head_t j_wait_commit; |
| + | |
| + | /* Wait queue to wait for updates to complete */ |
| + | wait_queue_head_t j_wait_updates; |
| + | |
| + | /* Semaphore for locking against concurrent checkpoints */ |
| + | struct semaphore j_checkpoint_sem; |
| + | |
| + | /* The main journal lock, used by lock_journal() */ |
| + | struct semaphore j_sem; |
| + | |
| + | /* Journal head: identifies the first unused block in the journal. */ |
| + | unsigned long j_head; |
| + | |
| + | /* Journal tail: identifies the oldest still-used block in the |
| + | * journal. */ |
| + | unsigned long j_tail; |
| + | |
| + | /* Journal free: how many free blocks are there in the journal? */ |
| + | unsigned long j_free; |
| + | |
| + | /* Journal start and end: the block numbers of the first usable |
| + | * block and one beyond the last usable block in the journal. */ |
| + | unsigned long j_first, j_last; |
| + | |
| + | /* Device, blocksize and starting block offset for the location |
| + | * where we store the journal. */ |
| + | kdev_t j_dev; |
| + | int j_blocksize; |
| + | unsigned int j_blk_offset; |
| + | |
| + | /* Device which holds the client fs. For internal journal this |
| + | * will be equal to j_dev. */ |
| + | kdev_t j_fs_dev; |
| + | |
| + | /* Total maximum capacity of the journal region on disk. */ |
| + | unsigned int j_maxlen; |
| + | |
| + | /* Optional inode where we store the journal. If present, all |
| + | * journal block numbers are mapped into this inode via |
| + | * bmap(). */ |
| + | struct inode * j_inode; |
| + | |
| + | /* Sequence number of the oldest transaction in the log */ |
| + | tid_t j_tail_sequence; |
| + | /* Sequence number of the next transaction to grant */ |
| + | tid_t j_transaction_sequence; |
| + | /* Sequence number of the most recently committed transaction */ |
| + | tid_t j_commit_sequence; |
| + | /* Sequence number of the most recent transaction wanting commit */ |
| + | tid_t j_commit_request; |
| + | |
| + | /* Journal uuid: identifies the object (filesystem, LVM volume |
| + | * etc) backed by this journal. This will eventually be |
| + | * replaced by an array of uuids, allowing us to index multiple |
| + | * devices within a single journal and to perform atomic updates |
| + | * across them. */ |
| + | |
| + | __u8 j_uuid[16]; |
| + | |
| + | /* Pointer to the current commit thread for this journal */ |
| + | struct task_struct * j_task; |
| + | |
| + | /* Maximum number of metadata buffers to allow in a single |
| + | * compound commit transaction */ |
| + | int j_max_transaction_buffers; |
| + | |
| + | /* What is the maximum transaction lifetime before we begin a |
| + | * commit? */ |
| + | unsigned long j_commit_interval; |
| + | |
| + | /* The timer used to wakeup the commit thread: */ |
| + | struct timer_list * j_commit_timer; |
| + | int j_commit_timer_active; |
| + | |
| + | /* Link all journals together - system-wide */ |
| + | struct list_head j_all_journals; |
| + | |
| + | /* The revoke table: maintains the list of revoked blocks in the |
| + | current transaction. */ |
| + | struct jbd_revoke_table_s *j_revoke; |
| + | }; |
| + | |
| + | /* |
| + | * Journal flag definitions |
| + | */ |
| + | #define JFS_UNMOUNT 0x001 /* Journal thread is being destroyed */ |
| + | #define JFS_ABORT 0x002 /* Journaling has been aborted for errors. */ |
| + | #define JFS_ACK_ERR 0x004 /* The errno in the sb has been acked */ |
| + | #define JFS_FLUSHED 0x008 /* The journal superblock has been flushed */ |
| + | #define JFS_LOADED 0x010 /* The journal superblock has been loaded */ |
| + | |
| + | /* |
| + | * Function declarations for the journaling transaction and buffer |
| + | * management |
| + | */ |
| + | |
| + | /* Filing buffers */ |
| + | extern void __journal_unfile_buffer(struct journal_head *); |
| + | extern void journal_unfile_buffer(struct journal_head *); |
| + | extern void __journal_refile_buffer(struct journal_head *); |
| + | extern void journal_refile_buffer(struct journal_head *); |
| + | extern void __journal_file_buffer(struct journal_head *, transaction_t *, int); |
| + | extern void __journal_free_buffer(struct journal_head *bh); |
| + | extern void journal_file_buffer(struct journal_head *, transaction_t *, int); |
| + | extern void __journal_clean_data_list(transaction_t *transaction); |
| + | |
| + | /* Log buffer allocation */ |
| + | extern struct journal_head * journal_get_descriptor_buffer(journal_t *); |
| + | extern unsigned long journal_next_log_block(journal_t *); |
| + | |
| + | /* Commit management */ |
| + | extern void journal_commit_transaction(journal_t *); |
| + | |
| + | /* Checkpoint list management */ |
| + | int __journal_clean_checkpoint_list(journal_t *journal); |
| + | extern void journal_remove_checkpoint(struct journal_head *); |
| + | extern void __journal_remove_checkpoint(struct journal_head *); |
| + | extern void journal_insert_checkpoint(struct journal_head *, transaction_t *); |
| + | extern void __journal_insert_checkpoint(struct journal_head *,transaction_t *); |
| + | |
| + | /* Buffer IO */ |
| + | extern int |
| + | journal_write_metadata_buffer(transaction_t *transaction, |
| + | struct journal_head *jh_in, |
| + | struct journal_head **jh_out, |
| + | int blocknr); |
| + | |
| + | /* Transaction locking */ |
| + | extern void __wait_on_journal (journal_t *); |
| + | |
| + | /* |
| + | * Journal locking. |
| + | * |
| + | * We need to lock the journal during transaction state changes so that |
| + | * nobody ever tries to take a handle on the running transaction while |
| + | * we are in the middle of moving it to the commit phase. |
| + | * |
| + | * Note that the locking is completely interrupt unsafe. We never touch |
| + | * journal structures from interrupts. |
| + | * |
| + | * In 2.2, the BKL was required for lock_journal. This is no longer |
| + | * the case. |
| + | */ |
| + | |
| + | static inline void lock_journal(journal_t *journal) |
| + | { |
| + | down(&journal->j_sem); |
| + | } |
| + | |
| + | /* This returns zero if we acquired the semaphore */ |
| + | static inline int try_lock_journal(journal_t * journal) |
| + | { |
| + | return down_trylock(&journal->j_sem); |
| + | } |
| + | |
| + | static inline void unlock_journal(journal_t * journal) |
| + | { |
| + | up(&journal->j_sem); |
| + | } |
| + | |
| + | |
| + | static inline handle_t *journal_current_handle(void) |
| + | { |
| + | return current->journal_info; |
| + | } |
| + | |
| + | /* The journaling code user interface: |
| + | * |
| + | * Create and destroy handles |
| + | * Register buffer modifications against the current transaction. |
| + | */ |
| + | |
| + | extern handle_t *journal_start(journal_t *, int nblocks); |
| + | extern handle_t *journal_try_start(journal_t *, int nblocks); |
| + | extern int journal_restart (handle_t *, int nblocks); |
| + | extern int journal_extend (handle_t *, int nblocks); |
| + | extern int journal_get_write_access (handle_t *, struct buffer_head *); |
| + | extern int journal_get_create_access (handle_t *, struct buffer_head *); |
| + | extern int journal_get_undo_access (handle_t *, struct buffer_head *); |
| + | extern int journal_dirty_data (handle_t *, |
| + | struct buffer_head *, int async); |
| + | extern int journal_dirty_metadata (handle_t *, struct buffer_head *); |
| + | extern void journal_release_buffer (handle_t *, struct buffer_head *); |
| + | extern void journal_forget (handle_t *, struct buffer_head *); |
| + | extern void journal_sync_buffer (struct buffer_head *); |
| + | extern int journal_flushpage(journal_t *, struct page *, unsigned long); |
| + | extern int journal_try_to_free_buffers(journal_t *, struct page *, int); |
| + | extern int journal_stop(handle_t *); |
| + | extern int journal_flush (journal_t *); |
| + | |
| + | extern void journal_lock_updates (journal_t *); |
| + | extern void journal_unlock_updates (journal_t *); |
| + | |
| + | extern journal_t * journal_init_dev(kdev_t dev, kdev_t fs_dev, |
| + | int start, int len, int bsize); |
| + | extern journal_t * journal_init_inode (struct inode *); |
| + | extern int journal_update_format (journal_t *); |
| + | extern int journal_check_used_features |
| + | (journal_t *, unsigned long, unsigned long, unsigned long); |
| + | extern int journal_check_available_features |
| + | (journal_t *, unsigned long, unsigned long, unsigned long); |
| + | extern int journal_set_features |
| + | (journal_t *, unsigned long, unsigned long, unsigned long); |
| + | extern int journal_create (journal_t *); |
| + | extern int journal_load (journal_t *journal); |
| + | extern void journal_destroy (journal_t *); |
| + | extern int journal_recover (journal_t *journal); |
| + | extern int journal_wipe (journal_t *, int); |
| + | extern int journal_skip_recovery (journal_t *); |
| + | extern void journal_update_superblock (journal_t *, int); |
| + | extern void __journal_abort (journal_t *); |
| + | extern void journal_abort (journal_t *, int); |
| + | extern int journal_errno (journal_t *); |
| + | extern void journal_ack_err (journal_t *); |
| + | extern int journal_clear_err (journal_t *); |
| + | extern unsigned long journal_bmap(journal_t *journal, unsigned long blocknr); |
| + | extern int journal_force_commit(journal_t *journal); |
| + | |
| + | /* |
| + | * journal_head management |
| + | */ |
| + | extern struct journal_head |
| + | *journal_add_journal_head(struct buffer_head *bh); |
| + | extern void journal_remove_journal_head(struct buffer_head *bh); |
| + | extern void __journal_remove_journal_head(struct buffer_head *bh); |
| + | extern void journal_unlock_journal_head(struct journal_head *jh); |
| + | |
| + | /* Primary revoke support */ |
| + | #define JOURNAL_REVOKE_DEFAULT_HASH 256 |
| + | extern int journal_init_revoke(journal_t *, int); |
| + | extern void journal_destroy_revoke_caches(void); |
| + | extern int journal_init_revoke_caches(void); |
| + | |
| + | extern void journal_destroy_revoke(journal_t *); |
| + | extern int journal_revoke (handle_t *, |
| + | unsigned long, struct buffer_head *); |
| + | extern int journal_cancel_revoke(handle_t *, struct journal_head *); |
| + | extern void journal_write_revoke_records(journal_t *, transaction_t *); |
| + | |
| + | /* Recovery revoke support */ |
| + | extern int journal_set_revoke(journal_t *, unsigned long, tid_t); |
| + | extern int journal_test_revoke(journal_t *, unsigned long, tid_t); |
| + | extern void journal_clear_revoke(journal_t *); |
| + | extern void journal_brelse_array(struct buffer_head *b[], int n); |
| + | |
| + | /* The log thread user interface: |
| + | * |
| + | * Request space in the current transaction, and force transaction commit |
| + | * transitions on demand. |
| + | */ |
| + | |
| + | extern int log_space_left (journal_t *); /* Called with journal locked */ |
| + | extern tid_t log_start_commit (journal_t *, transaction_t *); |
| + | extern void log_wait_commit (journal_t *, tid_t); |
| + | extern int log_do_checkpoint (journal_t *, int); |
| + | |
| + | extern void log_wait_for_space(journal_t *, int nblocks); |
| + | extern void __journal_drop_transaction(journal_t *, transaction_t *); |
| + | extern int cleanup_journal_tail(journal_t *); |
| + | |
| + | /* Reduce journal memory usage by flushing */ |
| + | extern void shrink_journal_memory(void); |
| + | |
| + | /* Debugging code only: */ |
| + | |
| + | #define jbd_ENOSYS() \ |
| + | do { \ |
| + | printk (KERN_ERR "JBD unimplemented function " __FUNCTION__); \ |
| + | current->state = TASK_UNINTERRUPTIBLE; \ |
| + | schedule(); \ |
| + | } while (1) |
| + | |
| + | /* |
| + | * is_journal_abort |
| + | * |
| + | * Simple test wrapper function to test the JFS_ABORT state flag. This |
| + | * bit, when set, indicates that we have had a fatal error somewhere, |
| + | * either inside the journaling layer or indicated to us by the client |
| + | * (eg. ext3), and that we and should not commit any further |
| + | * transactions. |
| + | */ |
| + | |
| + | static inline int is_journal_aborted(journal_t *journal) |
| + | { |
| + | return journal->j_flags & JFS_ABORT; |
| + | } |
| + | |
| + | static inline int is_handle_aborted(handle_t *handle) |
| + | { |
| + | if (handle->h_aborted) |
| + | return 1; |
| + | return is_journal_aborted(handle->h_transaction->t_journal); |
| + | } |
| + | |
| + | static inline void journal_abort_handle(handle_t *handle) |
| + | { |
| + | handle->h_aborted = 1; |
| + | } |
| + | |
| + | /* Not all architectures define BUG() */ |
| + | #ifndef BUG |
| + | #define BUG() do { \ |
| + | printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ |
| + | * ((char *) 0) = 0; \ |
| + | } while (0) |
| + | #endif /* BUG */ |
| + | |
| + | #else |
| + | |
| + | extern int journal_recover (journal_t *journal); |
| + | extern int journal_skip_recovery (journal_t *); |
| + | |
| + | /* Primary revoke support */ |
| + | extern int journal_init_revoke(journal_t *, int); |
| + | extern void journal_destroy_revoke_caches(void); |
| + | extern int journal_init_revoke_caches(void); |
| + | |
| + | /* Recovery revoke support */ |
| + | extern int journal_set_revoke(journal_t *, unsigned long, tid_t); |
| + | extern int journal_test_revoke(journal_t *, unsigned long, tid_t); |
| + | extern void journal_clear_revoke(journal_t *); |
| + | extern void journal_brelse_array(struct buffer_head *b[], int n); |
| + | |
| + | extern void journal_destroy_revoke(journal_t *); |
| + | #endif /* __KERNEL__ */ |
| + | |
| + | /* Comparison functions for transaction IDs: perform comparisons using |
| + | * modulo arithmetic so that they work over sequence number wraps. */ |
| + | |
| + | static inline int tid_gt(tid_t x, tid_t y) |
| + | { |
| + | int difference = (x - y); |
| + | return (difference > 0); |
| + | } |
| + | |
| + | static inline int tid_geq(tid_t x, tid_t y) |
| + | { |
| + | int difference = (x - y); |
| + | return (difference >= 0); |
| + | } |
| + | |
| + | extern int journal_blocks_per_page(struct inode *inode); |
| + | |
| + | /* |
| + | * Definitions which augment the buffer_head layer |
| + | */ |
| + | |
| + | /* journaling buffer types */ |
| + | #define BJ_None 0 /* Not journaled */ |
| + | #define BJ_SyncData 1 /* Normal data: flush before commit */ |
| + | #define BJ_AsyncData 2 /* writepage data: wait on it before commit */ |
| + | #define BJ_Metadata 3 /* Normal journaled metadata */ |
| + | #define BJ_Forget 4 /* Buffer superceded by this transaction */ |
| + | #define BJ_IO 5 /* Buffer is for temporary IO use */ |
| + | #define BJ_Shadow 6 /* Buffer contents being shadowed to the log */ |
| + | #define BJ_LogCtl 7 /* Buffer contains log descriptors */ |
| + | #define BJ_Reserved 8 /* Buffer is reserved for access by journal */ |
| + | #define BJ_Types 9 |
| + | |
| + | extern int jbd_blocks_per_page(struct inode *inode); |
| + | |
| + | #ifdef __KERNEL__ |
| + | |
| + | extern spinlock_t jh_splice_lock; |
| + | /* |
| + | * Once `expr1' has been found true, take jh_splice_lock |
| + | * and then reevaluate everything. |
| + | */ |
| + | #define SPLICE_LOCK(expr1, expr2) \ |
| + | ({ \ |
| + | int ret = (expr1); \ |
| + | if (ret) { \ |
| + | spin_lock(&jh_splice_lock); \ |
| + | ret = (expr1) && (expr2); \ |
| + | spin_unlock(&jh_splice_lock); \ |
| + | } \ |
| + | ret; \ |
| + | }) |
| + | |
| + | /* |
| + | * A number of buffer state predicates. They test for |
| + | * buffer_jbd() because they are used in core kernel code. |
| + | * |
| + | * These will be racy on SMP unless we're *sure* that the |
| + | * buffer won't be detached from the journalling system |
| + | * in parallel. |
| + | */ |
| + | |
| + | /* Return true if the buffer is on journal list `list' */ |
| + | static inline int buffer_jlist_eq(struct buffer_head *bh, int list) |
| + | { |
| + | return SPLICE_LOCK(buffer_jbd(bh), bh2jh(bh)->b_jlist == list); |
| + | } |
| + | |
| + | /* Return true if this bufer is dirty wrt the journal */ |
| + | static inline int buffer_jdirty(struct buffer_head *bh) |
| + | { |
| + | return buffer_jbd(bh) && __buffer_state(bh, JBDDirty); |
| + | } |
| + | |
| + | /* Return true if it's a data buffer which journalling is managing */ |
| + | static inline int buffer_jbd_data(struct buffer_head *bh) |
| + | { |
| + | return SPLICE_LOCK(buffer_jbd(bh), |
| + | bh2jh(bh)->b_jlist == BJ_SyncData || |
| + | bh2jh(bh)->b_jlist == BJ_AsyncData); |
| + | } |
| + | |
| + | #ifdef CONFIG_SMP |
| + | #define assert_spin_locked(lock) J_ASSERT(spin_is_locked(lock)) |
| + | #else |
| + | #define assert_spin_locked(lock) do {} while(0) |
| + | #endif |
| + | |
| + | #define buffer_trace_init(bh) do {} while (0) |
| + | #define print_buffer_fields(bh) do {} while (0) |
| + | #define print_buffer_trace(bh) do {} while (0) |
| + | #define BUFFER_TRACE(bh, info) do {} while (0) |
| + | #define BUFFER_TRACE2(bh, bh2, info) do {} while (0) |
| + | #define JBUFFER_TRACE(jh, info) do {} while (0) |
| + | |
| + | #endif /* __KERNEL__ */ |
| + | |
| + | #endif /* CONFIG_JBD || CONFIG_JBD_MODULE || !__KERNEL__ */ |
| + | |
| + | /* |
| + | * Compatibility no-ops which allow the kernel to compile without CONFIG_JBD |
| + | * go here. |
| + | */ |
| + | |
| + | #if defined(__KERNEL__) && !(defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE)) |
| + | |
| + | #define J_ASSERT(expr) do {} while (0) |
| + | #define J_ASSERT_BH(bh, expr) do {} while (0) |
| + | #define buffer_jbd(bh) 0 |
| + | #define buffer_jlist_eq(bh, val) 0 |
| + | #define journal_buffer_journal_lru(bh) 0 |
| + | |
| + | #endif /* defined(__KERNEL__) && !defined(CONFIG_JBD) */ |
| + | #endif /* _LINUX_JBD_H */ |
| @@ -0,0 +1,112 @@ | |
| + | #ifndef _LINUX_LIST_H |
| + | #define _LINUX_LIST_H |
| + | |
| + | /* |
| + | * Simple doubly linked list implementation. |
| + | * |
| + | * Some of the internal functions ("__xxx") are useful when |
| + | * manipulating whole lists rather than single entries, as |
| + | * sometimes we already know the next/prev entries and we can |
| + | * generate better code by using them directly rather than |
| + | * using the generic single-entry routines. |
| + | */ |
| + | |
| + | struct list_head { |
| + | struct list_head *next, *prev; |
| + | }; |
| + | |
| + | #define LIST_HEAD_INIT(name) { &(name), &(name) } |
| + | |
| + | #define LIST_HEAD(name) \ |
| + | struct list_head name = { &name, &name } |
| + | |
| + | #define INIT_LIST_HEAD(ptr) do { \ |
| + | (ptr)->next = (ptr); (ptr)->prev = (ptr); \ |
| + | } while (0) |
| + | |
| + | #if (!defined(__GNUC__) && !defined(__WATCOMC__)) |
| + | #define __inline__ |
| + | #endif |
| + | |
| + | /* |
| + | * Insert a new entry between two known consecutive entries. |
| + | * |
| + | * This is only for internal list manipulation where we know |
| + | * the prev/next entries already! |
| + | */ |
| + | static __inline__ void __list_add(struct list_head * new, |
| + | struct list_head * prev, |
| + | struct list_head * next) |
| + | { |
| + | next->prev = new; |
| + | new->next = next; |
| + | new->prev = prev; |
| + | prev->next = new; |
| + | } |
| + | |
| + | /* |
| + | * Insert a new entry after the specified head.. |
| + | */ |
| + | static __inline__ void list_add(struct list_head *new, struct list_head *head) |
| + | { |
| + | __list_add(new, head, head->next); |
| + | } |
| + | |
| + | /* |
| + | * Insert a new entry at the tail |
| + | */ |
| + | static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) |
| + | { |
| + | __list_add(new, head->prev, head); |
| + | } |
| + | |
| + | /* |
| + | * Delete a list entry by making the prev/next entries |
| + | * point to each other. |
| + | * |
| + | * This is only for internal list manipulation where we know |
| + | * the prev/next entries already! |
| + | */ |
| + | static __inline__ void __list_del(struct list_head * prev, |
| + | struct list_head * next) |
| + | { |
| + | next->prev = prev; |
| + | prev->next = next; |
| + | } |
| + | |
| + | static __inline__ void list_del(struct list_head *entry) |
| + | { |
| + | __list_del(entry->prev, entry->next); |
| + | } |
| + | |
| + | static __inline__ int list_empty(struct list_head *head) |
| + | { |
| + | return head->next == head; |
| + | } |
| + | |
| + | /* |
| + | * Splice in "list" into "head" |
| + | */ |
| + | static __inline__ void list_splice(struct list_head *list, struct list_head *head) |
| + | { |
| + | struct list_head *first = list->next; |
| + | |
| + | if (first != list) { |
| + | struct list_head *last = list->prev; |
| + | struct list_head *at = head->next; |
| + | |
| + | first->prev = head; |
| + | head->next = first; |
| + | |
| + | last->next = at; |
| + | at->prev = last; |
| + | } |
| + | } |
| + | |
| + | #define list_entry(ptr, type, member) \ |
| + | ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) |
| + | |
| + | #define list_for_each(pos, head) \ |
| + | for (pos = (head)->next; pos != (head); pos = pos->next) |
| + | |
| + | #endif |
| @@ -0,0 +1,134 @@ | |
| + | /* |
| + | * link.c --- create links in a ext2fs directory |
| + | * |
| + | * Copyright (C) 1993, 1994 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | struct link_struct { |
| + | const char *name; |
| + | int namelen; |
| + | ext2_ino_t inode; |
| + | int flags; |
| + | int done; |
| + | struct ext2_super_block *sb; |
| + | }; |
| + | |
| + | static int link_proc(struct ext2_dir_entry *dirent, |
| + | int offset, |
| + | int blocksize, |
| + | char *buf, |
| + | void *priv_data) |
| + | { |
| + | struct link_struct *ls = (struct link_struct *) priv_data; |
| + | struct ext2_dir_entry *next; |
| + | int rec_len, min_rec_len; |
| + | int ret = 0; |
| + | |
| + | rec_len = EXT2_DIR_REC_LEN(ls->namelen); |
| + | |
| + | /* |
| + | * See if the following directory entry (if any) is unused; |
| + | * if so, absorb it into this one. |
| + | */ |
| + | next = (struct ext2_dir_entry *) (buf + offset + dirent->rec_len); |
| + | if ((offset + dirent->rec_len < blocksize - 8) && |
| + | (next->inode == 0) && |
| + | (offset + dirent->rec_len + next->rec_len <= blocksize)) { |
| + | dirent->rec_len += next->rec_len; |
| + | ret = DIRENT_CHANGED; |
| + | } |
| + | |
| + | /* |
| + | * If the directory entry is used, see if we can split the |
| + | * directory entry to make room for the new name. If so, |
| + | * truncate it and return. |
| + | */ |
| + | if (dirent->inode) { |
| + | min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF); |
| + | if (dirent->rec_len < (min_rec_len + rec_len)) |
| + | return ret; |
| + | rec_len = dirent->rec_len - min_rec_len; |
| + | dirent->rec_len = min_rec_len; |
| + | next = (struct ext2_dir_entry *) (buf + offset + |
| + | dirent->rec_len); |
| + | next->inode = 0; |
| + | next->name_len = 0; |
| + | next->rec_len = rec_len; |
| + | return DIRENT_CHANGED; |
| + | } |
| + | |
| + | /* |
| + | * If we get this far, then the directory entry is not used. |
| + | * See if we can fit the request entry in. If so, do it. |
| + | */ |
| + | if (dirent->rec_len < rec_len) |
| + | return ret; |
| + | dirent->inode = ls->inode; |
| + | dirent->name_len = ls->namelen; |
| + | strncpy(dirent->name, ls->name, ls->namelen); |
| + | if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) |
| + | dirent->name_len |= (ls->flags & 0x7) << 8; |
| + | |
| + | ls->done++; |
| + | return DIRENT_ABORT|DIRENT_CHANGED; |
| + | } |
| + | |
| + | /* |
| + | * Note: the low 3 bits of the flags field are used as the directory |
| + | * entry filetype. |
| + | */ |
| + | #ifdef __TURBOC__ |
| + | #pragma argsused |
| + | #endif |
| + | errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name, |
| + | ext2_ino_t ino, int flags) |
| + | { |
| + | errcode_t retval; |
| + | struct link_struct ls; |
| + | struct ext2_inode inode; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | if (!(fs->flags & EXT2_FLAG_RW)) |
| + | return EXT2_ET_RO_FILSYS; |
| + | |
| + | ls.name = name; |
| + | ls.namelen = name ? strlen(name) : 0; |
| + | ls.inode = ino; |
| + | ls.flags = flags; |
| + | ls.done = 0; |
| + | ls.sb = fs->super; |
| + | |
| + | retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY, |
| + | 0, link_proc, &ls); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | if (!ls.done) |
| + | return EXT2_ET_DIR_NO_SPACE; |
| + | |
| + | if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0) |
| + | return retval; |
| + | |
| + | if (inode.i_flags & EXT2_INDEX_FL) { |
| + | inode.i_flags &= ~EXT2_INDEX_FL; |
| + | if ((retval = ext2fs_write_inode(fs, dir, &inode)) != 0) |
| + | return retval; |
| + | } |
| + | |
| + | return 0; |
| + | } |
| @@ -0,0 +1,135 @@ | |
| + | /* |
| + | * llseek.c -- stub calling the llseek system call |
| + | * |
| + | * Copyright (C) 1994, 1995, 1996, 1997 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #if HAVE_ERRNO_H |
| + | #include <errno.h> |
| + | #endif |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #ifdef __MSDOS__ |
| + | #include <io.h> |
| + | #endif |
| + | #include "et/com_err.h" |
| + | #include "ext2fs/ext2_io.h" |
| + | |
| + | #ifdef __linux__ |
| + | |
| + | #if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE) |
| + | |
| + | #define my_llseek lseek64 |
| + | |
| + | #else |
| + | #if defined(HAVE_LLSEEK) |
| + | #include <syscall.h> |
| + | |
| + | #ifndef HAVE_LLSEEK_PROTOTYPE |
| + | extern long long llseek (int fd, long long offset, int origin); |
| + | #endif |
| + | |
| + | #define my_llseek llseek |
| + | |
| + | #else /* ! HAVE_LLSEEK */ |
| + | |
| + | #if defined(__alpha__) || defined (__ia64__) |
| + | |
| + | #define llseek lseek |
| + | |
| + | #else /* !__alpha__ && !__ia64__*/ |
| + | |
| + | #include <linux/unistd.h> |
| + | |
| + | #ifndef __NR__llseek |
| + | #define __NR__llseek 140 |
| + | #endif |
| + | |
| + | #ifndef __i386__ |
| + | static int _llseek (unsigned int, unsigned long, |
| + | unsigned long, ext2_loff_t *, unsigned int); |
| + | |
| + | static _syscall5(int,_llseek,unsigned int,fd,unsigned long,offset_high, |
| + | unsigned long, offset_low,ext2_loff_t *,result, |
| + | unsigned int, origin) |
| + | #endif |
| + | |
| + | static ext2_loff_t my_llseek (int fd, ext2_loff_t offset, int origin) |
| + | { |
| + | ext2_loff_t result; |
| + | int retval; |
| + | |
| + | #ifndef __i386__ |
| + | retval = _llseek(fd, ((unsigned long long) offset) >> 32, |
| + | #else |
| + | retval = syscall(__NR__llseek, fd, (unsigned long long) (offset >> 32), |
| + | #endif |
| + | ((unsigned long long) offset) & 0xffffffff, |
| + | &result, origin); |
| + | return (retval == -1 ? (ext2_loff_t) retval : result); |
| + | } |
| + | |
| + | #endif /* __alpha__ || __ia64__ */ |
| + | |
| + | #endif /* HAVE_LLSEEK */ |
| + | #endif /* defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE) */ |
| + | |
| + | ext2_loff_t ext2fs_llseek (int fd, ext2_loff_t offset, int origin) |
| + | { |
| + | ext2_loff_t result; |
| + | static int do_compat = 0; |
| + | |
| + | if ((sizeof(off_t) >= sizeof(ext2_loff_t)) || |
| + | (offset < ((ext2_loff_t) 1 << ((sizeof(off_t)*8) -1)))) |
| + | return lseek(fd, (off_t) offset, origin); |
| + | |
| + | if (do_compat) { |
| + | errno = EINVAL; |
| + | return -1; |
| + | } |
| + | |
| + | result = my_llseek (fd, offset, origin); |
| + | if (result == -1 && errno == ENOSYS) { |
| + | /* |
| + | * Just in case this code runs on top of an old kernel |
| + | * which does not support the llseek system call |
| + | */ |
| + | do_compat++; |
| + | errno = EINVAL; |
| + | } |
| + | return result; |
| + | } |
| + | |
| + | #else /* !linux */ |
| + | |
| + | #ifndef EINVAL |
| + | #define EINVAL EXT2_ET_INVALID_ARGUMENT |
| + | #endif |
| + | |
| + | ext2_loff_t ext2fs_llseek (int fd, ext2_loff_t offset, int origin) |
| + | { |
| + | #if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE) |
| + | return lseek64 (fd, offset, origin); |
| + | #else |
| + | if ((sizeof(off_t) < sizeof(ext2_loff_t)) && |
| + | (offset >= ((ext2_loff_t) 1 << ((sizeof(off_t)*8) -1)))) { |
| + | errno = EINVAL; |
| + | return -1; |
| + | } |
| + | return lseek (fd, (off_t) offset, origin); |
| + | #endif |
| + | } |
| + | |
| + | #endif /* linux */ |
| + | |
| + | |
| @@ -0,0 +1,69 @@ | |
| + | /* |
| + | * lookup.c --- ext2fs directory lookup operations |
| + | * |
| + | * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | struct lookup_struct { |
| + | const char *name; |
| + | int len; |
| + | ext2_ino_t *inode; |
| + | int found; |
| + | }; |
| + | |
| + | #ifdef __TURBOC__ |
| + | #pragma argsused |
| + | #endif |
| + | static int lookup_proc(struct ext2_dir_entry *dirent, |
| + | int offset EXT2FS_ATTR((unused)), |
| + | int blocksize EXT2FS_ATTR((unused)), |
| + | char *buf EXT2FS_ATTR((unused)), |
| + | void *priv_data) |
| + | { |
| + | struct lookup_struct *ls = (struct lookup_struct *) priv_data; |
| + | |
| + | if (ls->len != (dirent->name_len & 0xFF)) |
| + | return 0; |
| + | if (strncmp(ls->name, dirent->name, (dirent->name_len & 0xFF))) |
| + | return 0; |
| + | *ls->inode = dirent->inode; |
| + | ls->found++; |
| + | return DIRENT_ABORT; |
| + | } |
| + | |
| + | |
| + | errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name, |
| + | int namelen, char *buf, ext2_ino_t *inode) |
| + | { |
| + | errcode_t retval; |
| + | struct lookup_struct ls; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | ls.name = name; |
| + | ls.len = namelen; |
| + | ls.inode = inode; |
| + | ls.found = 0; |
| + | |
| + | retval = ext2fs_dir_iterate(fs, dir, 0, buf, lookup_proc, &ls); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | return (ls.found) ? 0 : EXT2_ET_FILE_NOT_FOUND; |
| + | } |
| + | |
| + | |
| @@ -0,0 +1,142 @@ | |
| + | /* |
| + | * mkdir.c --- make a directory in the filesystem |
| + | * |
| + | * Copyright (C) 1994, 1995 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | #ifndef EXT2_FT_DIR |
| + | #define EXT2_FT_DIR 2 |
| + | #endif |
| + | |
| + | errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum, |
| + | const char *name) |
| + | { |
| + | errcode_t retval; |
| + | struct ext2_inode parent_inode, inode; |
| + | ext2_ino_t ino = inum; |
| + | ext2_ino_t scratch_ino; |
| + | blk_t blk; |
| + | char *block = 0; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | /* |
| + | * Allocate an inode, if necessary |
| + | */ |
| + | if (!ino) { |
| + | retval = ext2fs_new_inode(fs, parent, LINUX_S_IFDIR | 0755, |
| + | 0, &ino); |
| + | if (retval) |
| + | goto cleanup; |
| + | } |
| + | |
| + | /* |
| + | * Allocate a data block for the directory |
| + | */ |
| + | retval = ext2fs_new_block(fs, 0, 0, &blk); |
| + | if (retval) |
| + | goto cleanup; |
| + | |
| + | /* |
| + | * Create a scratch template for the directory |
| + | */ |
| + | retval = ext2fs_new_dir_block(fs, ino, parent, &block); |
| + | if (retval) |
| + | goto cleanup; |
| + | |
| + | /* |
| + | * Get the parent's inode, if necessary |
| + | */ |
| + | if (parent != ino) { |
| + | retval = ext2fs_read_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(struct ext2_inode)); |
| + | inode.i_mode = 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; |
| + | inode.i_ctime = inode.i_atime = inode.i_mtime = time(NULL); |
| + | inode.i_size = fs->blocksize; |
| + | |
| + | /* |
| + | * Write out the inode and inode data block |
| + | */ |
| + | retval = ext2fs_write_dir_block(fs, blk, block); |
| + | if (retval) |
| + | goto cleanup; |
| + | retval = ext2fs_write_new_inode(fs, ino, &inode); |
| + | if (retval) |
| + | goto cleanup; |
| + | |
| + | /* |
| + | * Link the directory into the filesystem hierarchy |
| + | */ |
| + | if (name) { |
| + | retval = ext2fs_lookup(fs, parent, name, strlen(name), 0, |
| + | &scratch_ino); |
| + | if (!retval) { |
| + | retval = EXT2_ET_DIR_EXISTS; |
| + | name = 0; |
| + | goto cleanup; |
| + | } |
| + | if (retval != EXT2_ET_FILE_NOT_FOUND) |
| + | goto cleanup; |
| + | retval = ext2fs_link(fs, parent, name, ino, EXT2_FT_DIR); |
| + | if (retval) |
| + | goto cleanup; |
| + | } |
| + | |
| + | /* |
| + | * Update parent inode's counts |
| + | */ |
| + | if (parent != ino) { |
| + | parent_inode.i_links_count++; |
| + | retval = ext2fs_write_inode(fs, parent, &parent_inode); |
| + | if (retval) |
| + | goto cleanup; |
| + | } |
| + | |
| + | /* |
| + | * Update accounting.... |
| + | */ |
| + | ext2fs_block_alloc_stats(fs, blk, +1); |
| + | ext2fs_inode_alloc_stats2(fs, ino, +1, 1); |
| + | |
| + | cleanup: |
| + | if (block) |
| + | ext2fs_free_mem(&block); |
| + | return retval; |
| + | |
| + | } |
| + | |
| + | |
| @@ -0,0 +1,425 @@ | |
| + | /* |
| + | * mkjournal.c --- make a journal for a filesystem |
| + | * |
| + | * Copyright (C) 2000 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #if HAVE_ERRNO_H |
| + | #include <errno.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | #if HAVE_SYS_IOCTL_H |
| + | #include <sys/ioctl.h> |
| + | #endif |
| + | #if HAVE_NETINET_IN_H |
| + | #include <netinet/in.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "e2p/e2p.h" |
| + | #include "ext2fs.h" |
| + | #include "jfs_user.h" |
| + | |
| + | /* |
| + | * This function automatically sets up the journal superblock and |
| + | * returns it as an allocated block. |
| + | */ |
| + | errcode_t ext2fs_create_journal_superblock(ext2_filsys fs, |
| + | __u32 size, int flags, |
| + | char **ret_jsb) |
| + | { |
| + | errcode_t retval; |
| + | journal_superblock_t *jsb; |
| + | |
| + | if (size < 1024) |
| + | return EXT2_ET_JOURNAL_TOO_SMALL; |
| + | |
| + | if ((retval = ext2fs_get_mem(fs->blocksize, &jsb))) |
| + | return retval; |
| + | |
| + | memset (jsb, 0, fs->blocksize); |
| + | |
| + | jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER); |
| + | if (flags & EXT2_MKJOURNAL_V1_SUPER) |
| + | jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V1); |
| + | else |
| + | jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2); |
| + | jsb->s_blocksize = htonl(fs->blocksize); |
| + | jsb->s_maxlen = htonl(size); |
| + | jsb->s_nr_users = htonl(1); |
| + | jsb->s_first = htonl(1); |
| + | jsb->s_sequence = htonl(1); |
| + | memcpy(jsb->s_uuid, fs->super->s_uuid, sizeof(fs->super->s_uuid)); |
| + | /* |
| + | * If we're creating an external journal device, we need to |
| + | * adjust these fields. |
| + | */ |
| + | if (fs->super->s_feature_incompat & |
| + | EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { |
| + | jsb->s_nr_users = 0; |
| + | if (fs->blocksize == 1024) |
| + | jsb->s_first = htonl(3); |
| + | else |
| + | jsb->s_first = htonl(2); |
| + | } |
| + | |
| + | *ret_jsb = (char *) jsb; |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * This function writes a journal using POSIX routines. It is used |
| + | * for creating external journals and creating journals on live |
| + | * filesystems. |
| + | */ |
| + | static errcode_t write_journal_file(ext2_filsys fs, char *filename, |
| + | blk_t size, int flags) |
| + | { |
| + | errcode_t retval; |
| + | char *buf = 0; |
| + | int fd, ret_size; |
| + | blk_t i; |
| + | |
| + | if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf))) |
| + | return retval; |
| + | |
| + | /* Open the device or journal file */ |
| + | if ((fd = open(filename, O_WRONLY)) < 0) { |
| + | retval = errno; |
| + | goto errout; |
| + | } |
| + | |
| + | /* Write the superblock out */ |
| + | retval = EXT2_ET_SHORT_WRITE; |
| + | ret_size = write(fd, buf, fs->blocksize); |
| + | if (ret_size < 0) { |
| + | retval = errno; |
| + | goto errout; |
| + | } |
| + | if (ret_size != (int) fs->blocksize) |
| + | goto errout; |
| + | memset(buf, 0, fs->blocksize); |
| + | |
| + | for (i = 1; i < size; i++) { |
| + | ret_size = write(fd, buf, fs->blocksize); |
| + | if (ret_size < 0) { |
| + | retval = errno; |
| + | goto errout; |
| + | } |
| + | if (ret_size != (int) fs->blocksize) |
| + | goto errout; |
| + | } |
| + | close(fd); |
| + | |
| + | retval = 0; |
| + | errout: |
| + | ext2fs_free_mem(&buf); |
| + | return retval; |
| + | } |
| + | |
| + | /* |
| + | * Helper function for creating the journal using direct I/O routines |
| + | */ |
| + | struct mkjournal_struct { |
| + | int num_blocks; |
| + | int newblocks; |
| + | char *buf; |
| + | errcode_t err; |
| + | }; |
| + | |
| + | static int mkjournal_proc(ext2_filsys fs, |
| + | blk_t *blocknr, |
| + | e2_blkcnt_t blockcnt, |
| + | blk_t ref_block EXT2FS_ATTR((unused)), |
| + | int ref_offset EXT2FS_ATTR((unused)), |
| + | void *priv_data) |
| + | { |
| + | struct mkjournal_struct *es = (struct mkjournal_struct *) priv_data; |
| + | blk_t new_blk; |
| + | static blk_t last_blk = 0; |
| + | errcode_t retval; |
| + | |
| + | if (*blocknr) { |
| + | last_blk = *blocknr; |
| + | return 0; |
| + | } |
| + | retval = ext2fs_new_block(fs, last_blk, 0, &new_blk); |
| + | if (retval) { |
| + | es->err = retval; |
| + | return BLOCK_ABORT; |
| + | } |
| + | if (blockcnt > 0) |
| + | es->num_blocks--; |
| + | |
| + | es->newblocks++; |
| + | retval = io_channel_write_blk(fs->io, new_blk, 1, es->buf); |
| + | |
| + | if (blockcnt == 0) |
| + | memset(es->buf, 0, fs->blocksize); |
| + | |
| + | if (retval) { |
| + | es->err = retval; |
| + | return BLOCK_ABORT; |
| + | } |
| + | *blocknr = new_blk; |
| + | last_blk = new_blk; |
| + | ext2fs_block_alloc_stats(fs, new_blk, +1); |
| + | |
| + | if (es->num_blocks == 0) |
| + | return (BLOCK_CHANGED | BLOCK_ABORT); |
| + | else |
| + | return BLOCK_CHANGED; |
| + | |
| + | } |
| + | |
| + | /* |
| + | * This function creates a journal using direct I/O routines. |
| + | */ |
| + | static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino, |
| + | blk_t size, int flags) |
| + | { |
| + | char *buf; |
| + | errcode_t retval; |
| + | struct ext2_inode inode; |
| + | struct mkjournal_struct es; |
| + | |
| + | if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf))) |
| + | return retval; |
| + | |
| + | if ((retval = ext2fs_read_bitmaps(fs))) |
| + | return retval; |
| + | |
| + | if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) |
| + | return retval; |
| + | |
| + | if (inode.i_blocks > 0) |
| + | return EEXIST; |
| + | |
| + | es.num_blocks = size; |
| + | es.newblocks = 0; |
| + | es.buf = buf; |
| + | es.err = 0; |
| + | |
| + | retval = ext2fs_block_iterate2(fs, journal_ino, BLOCK_FLAG_APPEND, |
| + | 0, mkjournal_proc, &es); |
| + | if (es.err) { |
| + | retval = es.err; |
| + | goto errout; |
| + | } |
| + | |
| + | if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) |
| + | goto errout; |
| + | |
| + | inode.i_size += fs->blocksize * size; |
| + | inode.i_blocks += (fs->blocksize / 512) * es.newblocks; |
| + | inode.i_mtime = inode.i_ctime = time(0); |
| + | inode.i_links_count = 1; |
| + | inode.i_mode = LINUX_S_IFREG | 0600; |
| + | |
| + | if ((retval = ext2fs_write_inode(fs, journal_ino, &inode))) |
| + | goto errout; |
| + | retval = 0; |
| + | |
| + | memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4); |
| + | fs->super->s_jnl_blocks[16] = inode.i_size; |
| + | fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS; |
| + | ext2fs_mark_super_dirty(fs); |
| + | |
| + | errout: |
| + | ext2fs_free_mem(&buf); |
| + | return retval; |
| + | } |
| + | |
| + | /* |
| + | * This function adds a journal device to a filesystem |
| + | */ |
| + | errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev) |
| + | { |
| + | struct stat st; |
| + | errcode_t retval; |
| + | char buf[1024]; |
| + | journal_superblock_t *jsb; |
| + | int start; |
| + | __u32 i, nr_users; |
| + | |
| + | /* Make sure the device exists and is a block device */ |
| + | if (stat(journal_dev->device_name, &st) < 0) |
| + | return errno; |
| + | |
| + | if (!S_ISBLK(st.st_mode)) |
| + | return EXT2_ET_JOURNAL_NOT_BLOCK; /* Must be a block device */ |
| + | |
| + | /* Get the journal superblock */ |
| + | start = 1; |
| + | if (journal_dev->blocksize == 1024) |
| + | start++; |
| + | if ((retval = io_channel_read_blk(journal_dev->io, start, -1024, buf))) |
| + | return retval; |
| + | |
| + | jsb = (journal_superblock_t *) buf; |
| + | if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) || |
| + | (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) |
| + | return EXT2_ET_NO_JOURNAL_SB; |
| + | |
| + | if (ntohl(jsb->s_blocksize) != (unsigned long) fs->blocksize) |
| + | return EXT2_ET_UNEXPECTED_BLOCK_SIZE; |
| + | |
| + | /* Check and see if this filesystem has already been added */ |
| + | nr_users = ntohl(jsb->s_nr_users); |
| + | for (i=0; i < nr_users; i++) { |
| + | if (memcmp(fs->super->s_uuid, |
| + | &jsb->s_users[i*16], 16) == 0) |
| + | break; |
| + | } |
| + | if (i >= nr_users) { |
| + | memcpy(&jsb->s_users[nr_users*16], |
| + | fs->super->s_uuid, 16); |
| + | jsb->s_nr_users = htonl(nr_users+1); |
| + | } |
| + | |
| + | /* Writeback the journal superblock */ |
| + | if ((retval = io_channel_write_blk(journal_dev->io, start, -1024, buf))) |
| + | return retval; |
| + | |
| + | fs->super->s_journal_inum = 0; |
| + | fs->super->s_journal_dev = st.st_rdev; |
| + | memcpy(fs->super->s_journal_uuid, jsb->s_uuid, |
| + | sizeof(fs->super->s_journal_uuid)); |
| + | fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; |
| + | ext2fs_mark_super_dirty(fs); |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * This function adds a journal inode to a filesystem, using either |
| + | * POSIX routines if the filesystem is mounted, or using direct I/O |
| + | * functions if it is not. |
| + | */ |
| + | errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, int flags) |
| + | { |
| + | errcode_t retval; |
| + | ext2_ino_t journal_ino; |
| + | struct stat st; |
| + | char jfile[1024]; |
| + | int fd, mount_flags, f; |
| + | |
| + | if ((retval = ext2fs_check_mount_point(fs->device_name, &mount_flags, |
| + | jfile, sizeof(jfile)-10))) |
| + | return retval; |
| + | |
| + | if (mount_flags & EXT2_MF_MOUNTED) { |
| + | strcat(jfile, "/.journal"); |
| + | |
| + | /* |
| + | * If .../.journal already exists, make sure any |
| + | * immutable or append-only flags are cleared. |
| + | */ |
| + | #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) |
| + | (void) chflags (jfile, 0); |
| + | #else |
| + | #if HAVE_EXT2_IOCTLS |
| + | fd = open(jfile, O_RDONLY); |
| + | if (fd >= 0) { |
| + | f = 0; |
| + | ioctl(fd, EXT2_IOC_SETFLAGS, &f); |
| + | close(fd); |
| + | } |
| + | #endif |
| + | #endif |
| + | |
| + | /* Create the journal file */ |
| + | if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0) |
| + | return errno; |
| + | |
| + | if ((retval = write_journal_file(fs, jfile, size, flags))) |
| + | goto errout; |
| + | |
| + | /* Get inode number of the journal file */ |
| + | if (fstat(fd, &st) < 0) |
| + | goto errout; |
| + | |
| + | #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) |
| + | retval = fchflags (fd, UF_NODUMP|UF_IMMUTABLE); |
| + | #else |
| + | #if HAVE_EXT2_IOCTLS |
| + | f = EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL; |
| + | retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f); |
| + | #endif |
| + | #endif |
| + | if (retval) |
| + | goto errout; |
| + | |
| + | close(fd); |
| + | journal_ino = st.st_ino; |
| + | } else { |
| + | journal_ino = EXT2_JOURNAL_INO; |
| + | if ((retval = write_journal_inode(fs, journal_ino, |
| + | size, flags))) |
| + | return retval; |
| + | } |
| + | |
| + | fs->super->s_journal_inum = journal_ino; |
| + | fs->super->s_journal_dev = 0; |
| + | memset(fs->super->s_journal_uuid, 0, |
| + | sizeof(fs->super->s_journal_uuid)); |
| + | fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; |
| + | |
| + | ext2fs_mark_super_dirty(fs); |
| + | return 0; |
| + | errout: |
| + | close(fd); |
| + | return retval; |
| + | } |
| + | |
| + | #ifdef DEBUG |
| + | main(int argc, char **argv) |
| + | { |
| + | errcode_t retval; |
| + | char *device_name; |
| + | ext2_filsys fs; |
| + | |
| + | if (argc < 2) { |
| + | fprintf(stderr, "Usage: %s filesystem\n", argv[0]); |
| + | exit(1); |
| + | } |
| + | device_name = argv[1]; |
| + | |
| + | retval = ext2fs_open (device_name, EXT2_FLAG_RW, 0, 0, |
| + | unix_io_manager, &fs); |
| + | if (retval) { |
| + | com_err(argv[0], retval, "while opening %s", device_name); |
| + | exit(1); |
| + | } |
| + | |
| + | retval = ext2fs_add_journal_inode(fs, 1024); |
| + | if (retval) { |
| + | com_err(argv[0], retval, "while adding journal to %s", |
| + | device_name); |
| + | exit(1); |
| + | } |
| + | retval = ext2fs_flush(fs); |
| + | if (retval) { |
| + | printf("Warning, had trouble writing out superblocks.\n"); |
| + | } |
| + | ext2fs_close(fs); |
| + | exit(0); |
| + | |
| + | } |
| + | #endif |
| @@ -0,0 +1,205 @@ | |
| + | /* |
| + | * namei.c --- ext2fs directory lookup operations |
| + | * |
| + | * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | |
| + | /* #define NAMEI_DEBUG */ |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base, |
| + | const char *pathname, size_t pathlen, int follow, |
| + | int link_count, char *buf, ext2_ino_t *res_inode); |
| + | |
| + | static errcode_t follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir, |
| + | ext2_ino_t inode, int link_count, |
| + | char *buf, ext2_ino_t *res_inode) |
| + | { |
| + | char *pathname; |
| + | char *buffer = 0; |
| + | errcode_t retval; |
| + | struct ext2_inode ei; |
| + | |
| + | #ifdef NAMEI_DEBUG |
| + | printf("follow_link: root=%lu, dir=%lu, inode=%lu, lc=%d\n", |
| + | root, dir, inode, link_count); |
| + | |
| + | #endif |
| + | retval = ext2fs_read_inode (fs, inode, &ei); |
| + | if (retval) return retval; |
| + | if (!LINUX_S_ISLNK (ei.i_mode)) { |
| + | *res_inode = inode; |
| + | return 0; |
| + | } |
| + | if (link_count++ > 5) { |
| + | return EXT2_ET_SYMLINK_LOOP; |
| + | } |
| + | if (ext2fs_inode_data_blocks(fs,&ei)) { |
| + | retval = ext2fs_get_mem(fs->blocksize, &buffer); |
| + | if (retval) |
| + | return retval; |
| + | retval = io_channel_read_blk(fs->io, ei.i_block[0], 1, buffer); |
| + | if (retval) { |
| + | ext2fs_free_mem(&buffer); |
| + | return retval; |
| + | } |
| + | pathname = buffer; |
| + | } else |
| + | pathname = (char *)&(ei.i_block[0]); |
| + | retval = open_namei(fs, root, dir, pathname, ei.i_size, 1, |
| + | link_count, buf, res_inode); |
| + | if (buffer) |
| + | ext2fs_free_mem(&buffer); |
| + | return retval; |
| + | } |
| + | |
| + | /* |
| + | * This routine interprets a pathname in the context of the current |
| + | * directory and the root directory, and returns the inode of the |
| + | * containing directory, and a pointer to the filename of the file |
| + | * (pointing into the pathname) and the length of the filename. |
| + | */ |
| + | static errcode_t dir_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir, |
| + | const char *pathname, int pathlen, |
| + | int link_count, char *buf, |
| + | const char **name, int *namelen, |
| + | ext2_ino_t *res_inode) |
| + | { |
| + | char c; |
| + | const char *thisname; |
| + | int len; |
| + | ext2_ino_t inode; |
| + | errcode_t retval; |
| + | |
| + | if ((c = *pathname) == '/') { |
| + | dir = root; |
| + | pathname++; |
| + | pathlen--; |
| + | } |
| + | while (1) { |
| + | thisname = pathname; |
| + | for (len=0; --pathlen >= 0;len++) { |
| + | c = *(pathname++); |
| + | if (c == '/') |
| + | break; |
| + | } |
| + | if (pathlen < 0) |
| + | break; |
| + | retval = ext2fs_lookup (fs, dir, thisname, len, buf, &inode); |
| + | if (retval) return retval; |
| + | retval = follow_link (fs, root, dir, inode, |
| + | link_count, buf, &dir); |
| + | if (retval) return retval; |
| + | } |
| + | *name = thisname; |
| + | *namelen = len; |
| + | *res_inode = dir; |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base, |
| + | const char *pathname, size_t pathlen, int follow, |
| + | int link_count, char *buf, ext2_ino_t *res_inode) |
| + | { |
| + | const char *basename; |
| + | int namelen; |
| + | ext2_ino_t dir, inode; |
| + | errcode_t retval; |
| + | |
| + | #ifdef NAMEI_DEBUG |
| + | printf("open_namei: root=%lu, dir=%lu, path=%*s, lc=%d\n", |
| + | root, base, pathlen, pathname, link_count); |
| + | #endif |
| + | retval = dir_namei(fs, root, base, pathname, pathlen, |
| + | link_count, buf, &basename, &namelen, &dir); |
| + | if (retval) return retval; |
| + | if (!namelen) { /* special case: '/usr/' etc */ |
| + | *res_inode=dir; |
| + | return 0; |
| + | } |
| + | retval = ext2fs_lookup (fs, dir, basename, namelen, buf, &inode); |
| + | if (retval) |
| + | return retval; |
| + | if (follow) { |
| + | retval = follow_link(fs, root, dir, inode, link_count, |
| + | buf, &inode); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | #ifdef NAMEI_DEBUG |
| + | printf("open_namei: (link_count=%d) returns %lu\n", |
| + | link_count, inode); |
| + | #endif |
| + | *res_inode = inode; |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, |
| + | const char *name, ext2_ino_t *inode) |
| + | { |
| + | char *buf; |
| + | errcode_t retval; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | retval = ext2fs_get_mem(fs->blocksize, &buf); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | retval = open_namei(fs, root, cwd, name, strlen(name), 0, 0, |
| + | buf, inode); |
| + | |
| + | ext2fs_free_mem(&buf); |
| + | return retval; |
| + | } |
| + | |
| + | errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, |
| + | const char *name, ext2_ino_t *inode) |
| + | { |
| + | char *buf; |
| + | errcode_t retval; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | retval = ext2fs_get_mem(fs->blocksize, &buf); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | retval = open_namei(fs, root, cwd, name, strlen(name), 1, 0, |
| + | buf, inode); |
| + | |
| + | ext2fs_free_mem(&buf); |
| + | return retval; |
| + | } |
| + | |
| + | errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, |
| + | ext2_ino_t inode, ext2_ino_t *res_inode) |
| + | { |
| + | char *buf; |
| + | errcode_t retval; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | retval = ext2fs_get_mem(fs->blocksize, &buf); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | retval = follow_link(fs, root, cwd, inode, 0, buf, res_inode); |
| + | |
| + | ext2fs_free_mem(&buf); |
| + | return retval; |
| + | } |
| + | |
| @@ -0,0 +1,27 @@ | |
| + | /* |
| + | * native.c --- returns the ext2_flag for a native byte order |
| + | * |
| + | * Copyright (C) 1996 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | int ext2fs_native_flag(void) |
| + | { |
| + | #ifdef WORDS_BIGENDIAN |
| + | return EXT2_FLAG_SWAP_BYTES; |
| + | #else |
| + | return 0; |
| + | #endif |
| + | } |
| + | |
| + | |
| + | |
| @@ -0,0 +1,72 @@ | |
| + | /* |
| + | * newdir.c --- create a new directory block |
| + | * |
| + | * Copyright (C) 1994, 1995 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | #ifndef EXT2_FT_DIR |
| + | #define EXT2_FT_DIR 2 |
| + | #endif |
| + | |
| + | /* |
| + | * Create new directory block |
| + | */ |
| + | errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, |
| + | ext2_ino_t parent_ino, char **block) |
| + | { |
| + | struct ext2_dir_entry *dir = NULL; |
| + | errcode_t retval; |
| + | char *buf; |
| + | int rec_len; |
| + | int filetype = 0; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | retval = ext2fs_get_mem(fs->blocksize, &buf); |
| + | if (retval) |
| + | return retval; |
| + | memset(buf, 0, fs->blocksize); |
| + | dir = (struct ext2_dir_entry *) buf; |
| + | dir->rec_len = fs->blocksize; |
| + | |
| + | if (dir_ino) { |
| + | if (fs->super->s_feature_incompat & |
| + | EXT2_FEATURE_INCOMPAT_FILETYPE) |
| + | filetype = EXT2_FT_DIR << 8; |
| + | /* |
| + | * Set up entry for '.' |
| + | */ |
| + | dir->inode = dir_ino; |
| + | dir->name_len = 1 | filetype; |
| + | dir->name[0] = '.'; |
| + | rec_len = dir->rec_len - EXT2_DIR_REC_LEN(1); |
| + | dir->rec_len = EXT2_DIR_REC_LEN(1); |
| + | |
| + | /* |
| + | * Set up entry for '..' |
| + | */ |
| + | dir = (struct ext2_dir_entry *) (buf + dir->rec_len); |
| + | dir->rec_len = rec_len; |
| + | dir->inode = parent_ino; |
| + | dir->name_len = 2 | filetype; |
| + | dir->name[0] = '.'; |
| + | dir->name[1] = '.'; |
| + | |
| + | } |
| + | *block = buf; |
| + | return 0; |
| + | } |
| @@ -0,0 +1,326 @@ | |
| + | /* |
| + | * openfs.c --- open an ext2 filesystem |
| + | * |
| + | * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | |
| + | |
| + | #include "ext2fs.h" |
| + | #include "e2image.h" |
| + | |
| + | blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i) |
| + | { |
| + | int bg; |
| + | int has_super = 0; |
| + | int ret_blk; |
| + | |
| + | if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) || |
| + | (i < fs->super->s_first_meta_bg)) |
| + | return (group_block + i + 1); |
| + | |
| + | bg = (fs->blocksize / sizeof (struct ext2_group_desc)) * i; |
| + | if (ext2fs_bg_has_super(fs, bg)) |
| + | has_super = 1; |
| + | ret_blk = (fs->super->s_first_data_block + has_super + |
| + | (bg * fs->super->s_blocks_per_group)); |
| + | /* |
| + | * If group_block is not the normal value, we're trying to use |
| + | * the backup group descriptors and superblock --- so use the |
| + | * alternate location of the second block group in the |
| + | * metablock group. Ideally we should be testing each bg |
| + | * descriptor block individually for correctness, but we don't |
| + | * have the infrastructure in place to do that. |
| + | */ |
| + | if (group_block != fs->super->s_first_data_block && |
| + | ((ret_blk + fs->super->s_blocks_per_group) < |
| + | fs->super->s_blocks_count)) |
| + | ret_blk += fs->super->s_blocks_per_group; |
| + | return ret_blk; |
| + | } |
| + | |
| + | errcode_t ext2fs_open(const char *name, int flags, int superblock, |
| + | unsigned int block_size, io_manager manager, |
| + | ext2_filsys *ret_fs) |
| + | { |
| + | return ext2fs_open2(name, 0, flags, superblock, block_size, |
| + | manager, ret_fs); |
| + | } |
| + | |
| + | /* |
| + | * Note: if superblock is non-zero, block-size must also be non-zero. |
| + | * Superblock and block_size can be zero to use the default size. |
| + | * |
| + | * Valid flags for ext2fs_open() |
| + | * |
| + | * EXT2_FLAG_RW - Open the filesystem for read/write. |
| + | * EXT2_FLAG_FORCE - Open the filesystem even if some of the |
| + | * features aren't supported. |
| + | * EXT2_FLAG_JOURNAL_DEV_OK - Open an ext3 journal device |
| + | */ |
| + | errcode_t ext2fs_open2(const char *name, const char *io_options, |
| + | int flags, int superblock, |
| + | unsigned int block_size, io_manager manager, |
| + | ext2_filsys *ret_fs) |
| + | { |
| + | ext2_filsys fs; |
| + | errcode_t retval; |
| + | unsigned long i; |
| + | int j, groups_per_block, blocks_per_group; |
| + | blk_t group_block, blk; |
| + | char *dest, *cp; |
| + | struct ext2_group_desc *gdp; |
| + | |
| + | EXT2_CHECK_MAGIC(manager, EXT2_ET_MAGIC_IO_MANAGER); |
| + | |
| + | retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | memset(fs, 0, sizeof(struct struct_ext2_filsys)); |
| + | fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; |
| + | fs->flags = flags; |
| + | fs->umask = 022; |
| + | retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); |
| + | if (retval) |
| + | goto cleanup; |
| + | strcpy(fs->device_name, name); |
| + | cp = strchr(fs->device_name, '?'); |
| + | if (!io_options && cp) { |
| + | *cp++ = 0; |
| + | io_options = cp; |
| + | } |
| + | |
| + | retval = manager->open(fs->device_name, |
| + | (flags & EXT2_FLAG_RW) ? IO_FLAG_RW : 0, |
| + | &fs->io); |
| + | if (retval) |
| + | goto cleanup; |
| + | if (io_options && |
| + | (retval = io_channel_set_options(fs->io, io_options))) |
| + | goto cleanup; |
| + | fs->image_io = fs->io; |
| + | fs->io->app_data = fs; |
| + | retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super); |
| + | if (retval) |
| + | goto cleanup; |
| + | if (flags & EXT2_FLAG_IMAGE_FILE) { |
| + | retval = ext2fs_get_mem(sizeof(struct ext2_image_hdr), |
| + | &fs->image_header); |
| + | if (retval) |
| + | goto cleanup; |
| + | retval = io_channel_read_blk(fs->io, 0, |
| + | -(int)sizeof(struct ext2_image_hdr), |
| + | fs->image_header); |
| + | if (retval) |
| + | goto cleanup; |
| + | if (fs->image_header->magic_number != EXT2_ET_MAGIC_E2IMAGE) |
| + | return EXT2_ET_MAGIC_E2IMAGE; |
| + | superblock = 1; |
| + | block_size = fs->image_header->fs_blocksize; |
| + | } |
| + | |
| + | /* |
| + | * If the user specifies a specific block # for the |
| + | * superblock, then he/she must also specify the block size! |
| + | * Otherwise, read the master superblock located at offset |
| + | * SUPERBLOCK_OFFSET from the start of the partition. |
| + | * |
| + | * Note: we only save a backup copy of the superblock if we |
| + | * are reading the superblock from the primary superblock location. |
| + | */ |
| + | if (superblock) { |
| + | if (!block_size) { |
| + | retval = EXT2_ET_INVALID_ARGUMENT; |
| + | goto cleanup; |
| + | } |
| + | io_channel_set_blksize(fs->io, block_size); |
| + | group_block = superblock; |
| + | fs->orig_super = 0; |
| + | } else { |
| + | io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); |
| + | superblock = 1; |
| + | group_block = 0; |
| + | retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super); |
| + | if (retval) |
| + | goto cleanup; |
| + | } |
| + | retval = io_channel_read_blk(fs->io, superblock, -SUPERBLOCK_SIZE, |
| + | fs->super); |
| + | if (retval) |
| + | goto cleanup; |
| + | if (fs->orig_super) |
| + | memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE); |
| + | |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | if ((fs->super->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) || |
| + | (fs->flags & EXT2_FLAG_SWAP_BYTES)) { |
| + | fs->flags |= EXT2_FLAG_SWAP_BYTES; |
| + | |
| + | ext2fs_swap_super(fs->super); |
| + | } |
| + | #endif |
| + | |
| + | if (fs->super->s_magic != EXT2_SUPER_MAGIC) { |
| + | retval = EXT2_ET_BAD_MAGIC; |
| + | goto cleanup; |
| + | } |
| + | if (fs->super->s_rev_level > EXT2_LIB_CURRENT_REV) { |
| + | retval = EXT2_ET_REV_TOO_HIGH; |
| + | goto cleanup; |
| + | } |
| + | |
| + | /* |
| + | * Check for feature set incompatibility |
| + | */ |
| + | if (!(flags & EXT2_FLAG_FORCE)) { |
| + | if (fs->super->s_feature_incompat & |
| + | ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { |
| + | retval = EXT2_ET_UNSUPP_FEATURE; |
| + | goto cleanup; |
| + | } |
| + | if ((flags & EXT2_FLAG_RW) && |
| + | (fs->super->s_feature_ro_compat & |
| + | ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP)) { |
| + | retval = EXT2_ET_RO_UNSUPP_FEATURE; |
| + | goto cleanup; |
| + | } |
| + | if (!(flags & EXT2_FLAG_JOURNAL_DEV_OK) && |
| + | (fs->super->s_feature_incompat & |
| + | EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { |
| + | retval = EXT2_ET_UNSUPP_FEATURE; |
| + | goto cleanup; |
| + | } |
| + | } |
| + | |
| + | fs->blocksize = EXT2_BLOCK_SIZE(fs->super); |
| + | if (fs->blocksize == 0) { |
| + | retval = EXT2_ET_CORRUPT_SUPERBLOCK; |
| + | goto cleanup; |
| + | } |
| + | fs->fragsize = EXT2_FRAG_SIZE(fs->super); |
| + | fs->inode_blocks_per_group = ((fs->super->s_inodes_per_group * |
| + | EXT2_INODE_SIZE(fs->super) + |
| + | EXT2_BLOCK_SIZE(fs->super) - 1) / |
| + | EXT2_BLOCK_SIZE(fs->super)); |
| + | if (block_size) { |
| + | if (block_size != fs->blocksize) { |
| + | retval = EXT2_ET_UNEXPECTED_BLOCK_SIZE; |
| + | goto cleanup; |
| + | } |
| + | } |
| + | /* |
| + | * Set the blocksize to the filesystem's blocksize. |
| + | */ |
| + | io_channel_set_blksize(fs->io, fs->blocksize); |
| + | |
| + | /* |
| + | * If this is an external journal device, don't try to read |
| + | * the group descriptors, because they're not there. |
| + | */ |
| + | if (fs->super->s_feature_incompat & |
| + | EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { |
| + | fs->group_desc_count = 0; |
| + | *ret_fs = fs; |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * Read group descriptors |
| + | */ |
| + | blocks_per_group = EXT2_BLOCKS_PER_GROUP(fs->super); |
| + | if (blocks_per_group == 0 || |
| + | blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(fs->super) || |
| + | fs->inode_blocks_per_group > EXT2_MAX_INODES_PER_GROUP(fs->super)) { |
| + | retval = EXT2_ET_CORRUPT_SUPERBLOCK; |
| + | goto cleanup; |
| + | } |
| + | fs->group_desc_count = (fs->super->s_blocks_count - |
| + | fs->super->s_first_data_block + |
| + | blocks_per_group - 1) / blocks_per_group; |
| + | fs->desc_blocks = (fs->group_desc_count + |
| + | EXT2_DESC_PER_BLOCK(fs->super) - 1) |
| + | / EXT2_DESC_PER_BLOCK(fs->super); |
| + | retval = ext2fs_get_mem(fs->desc_blocks * fs->blocksize, |
| + | &fs->group_desc); |
| + | if (retval) |
| + | goto cleanup; |
| + | if (!group_block) |
| + | group_block = fs->super->s_first_data_block; |
| + | dest = (char *) fs->group_desc; |
| + | groups_per_block = fs->blocksize / sizeof(struct ext2_group_desc); |
| + | for (i=0 ; i < fs->desc_blocks; i++) { |
| + | blk = ext2fs_descriptor_block_loc(fs, group_block, i); |
| + | retval = io_channel_read_blk(fs->io, blk, 1, dest); |
| + | if (retval) |
| + | goto cleanup; |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | if (fs->flags & EXT2_FLAG_SWAP_BYTES) { |
| + | gdp = (struct ext2_group_desc *) dest; |
| + | for (j=0; j < groups_per_block; j++) |
| + | ext2fs_swap_group_desc(gdp++); |
| + | } |
| + | #endif |
| + | dest += fs->blocksize; |
| + | } |
| + | |
| + | *ret_fs = fs; |
| + | return 0; |
| + | cleanup: |
| + | ext2fs_free(fs); |
| + | return retval; |
| + | } |
| + | |
| + | /* |
| + | * Set/get the filesystem data I/O channel. |
| + | * |
| + | * These functions are only valid if EXT2_FLAG_IMAGE_FILE is true. |
| + | */ |
| + | errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io) |
| + | { |
| + | if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0) |
| + | return EXT2_ET_NOT_IMAGE_FILE; |
| + | if (old_io) { |
| + | *old_io = (fs->image_io == fs->io) ? 0 : fs->io; |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io) |
| + | { |
| + | if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0) |
| + | return EXT2_ET_NOT_IMAGE_FILE; |
| + | fs->io = new_io ? new_io : fs->image_io; |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io) |
| + | { |
| + | if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0) |
| + | return EXT2_ET_NOT_IMAGE_FILE; |
| + | fs->io = fs->image_io = new_io; |
| + | fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_RW | |
| + | EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY; |
| + | fs->flags &= ~EXT2_FLAG_IMAGE_FILE; |
| + | return 0; |
| + | } |
| @@ -0,0 +1,97 @@ | |
| + | /* |
| + | * read_bb --- read the bad blocks inode |
| + | * |
| + | * Copyright (C) 1994 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | struct read_bb_record { |
| + | ext2_badblocks_list bb_list; |
| + | errcode_t err; |
| + | }; |
| + | |
| + | /* |
| + | * Helper function for ext2fs_read_bb_inode() |
| + | */ |
| + | #ifdef __TURBOC__ |
| + | #pragma argsused |
| + | #endif |
| + | static int mark_bad_block(ext2_filsys fs, blk_t *block_nr, |
| + | e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), |
| + | blk_t ref_block EXT2FS_ATTR((unused)), |
| + | int ref_offset EXT2FS_ATTR((unused)), |
| + | void *priv_data) |
| + | { |
| + | struct read_bb_record *rb = (struct read_bb_record *) priv_data; |
| + | |
| + | if (blockcnt < 0) |
| + | return 0; |
| + | |
| + | if ((*block_nr < fs->super->s_first_data_block) || |
| + | (*block_nr >= fs->super->s_blocks_count)) |
| + | return 0; /* Ignore illegal blocks */ |
| + | |
| + | rb->err = ext2fs_badblocks_list_add(rb->bb_list, *block_nr); |
| + | if (rb->err) |
| + | return BLOCK_ABORT; |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * Reads the current bad blocks from the bad blocks inode. |
| + | */ |
| + | errcode_t ext2fs_read_bb_inode(ext2_filsys fs, ext2_badblocks_list *bb_list) |
| + | { |
| + | errcode_t retval; |
| + | struct read_bb_record rb; |
| + | struct ext2_inode inode; |
| + | blk_t numblocks; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | if (!*bb_list) { |
| + | retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode); |
| + | if (retval) |
| + | return retval; |
| + | if (inode.i_blocks < 500) |
| + | numblocks = (inode.i_blocks / |
| + | (fs->blocksize / 512)) + 20; |
| + | else |
| + | numblocks = 500; |
| + | retval = ext2fs_badblocks_list_create(bb_list, numblocks); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | |
| + | rb.bb_list = *bb_list; |
| + | rb.err = 0; |
| + | retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, 0, 0, |
| + | mark_bad_block, &rb); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | return rb.err; |
| + | } |
| + | |
| + | |
| @@ -0,0 +1,97 @@ | |
| + | /* |
| + | * read_bb_file.c --- read a list of bad blocks from a FILE * |
| + | * |
| + | * Copyright (C) 1994, 1995, 2000 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | /* |
| + | * Reads a list of bad blocks from a FILE * |
| + | */ |
| + | errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f, |
| + | ext2_badblocks_list *bb_list, |
| + | void *priv_data, |
| + | void (*invalid)(ext2_filsys fs, |
| + | blk_t blk, |
| + | char *badstr, |
| + | void *priv_data)) |
| + | { |
| + | errcode_t retval; |
| + | blk_t blockno; |
| + | int count; |
| + | char buf[128]; |
| + | |
| + | if (fs) |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | if (!*bb_list) { |
| + | retval = ext2fs_badblocks_list_create(bb_list, 10); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | |
| + | while (!feof (f)) { |
| + | if (fgets(buf, sizeof(buf), f) == NULL) |
| + | break; |
| + | count = sscanf(buf, "%u", &blockno); |
| + | if (count <= 0) |
| + | continue; |
| + | if (fs && |
| + | ((blockno < fs->super->s_first_data_block) || |
| + | (blockno >= fs->super->s_blocks_count))) { |
| + | if (invalid) |
| + | (invalid)(fs, blockno, buf, priv_data); |
| + | continue; |
| + | } |
| + | retval = ext2fs_badblocks_list_add(*bb_list, blockno); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | static void call_compat_invalid(ext2_filsys fs, blk_t blk, |
| + | char *badstr EXT2FS_ATTR((unused)), |
| + | void *priv_data) |
| + | { |
| + | void (*invalid)(ext2_filsys, blk_t); |
| + | |
| + | invalid = (void (*)(ext2_filsys, blk_t)) priv_data; |
| + | if (invalid) |
| + | invalid(fs, blk); |
| + | } |
| + | |
| + | |
| + | /* |
| + | * Reads a list of bad blocks from a FILE * |
| + | */ |
| + | errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f, |
| + | ext2_badblocks_list *bb_list, |
| + | void (*invalid)(ext2_filsys fs, blk_t blk)) |
| + | { |
| + | return ext2fs_read_bb_FILE2(fs, f, bb_list, (void *) invalid, |
| + | call_compat_invalid); |
| + | } |
| + | |
| + | |
| @@ -0,0 +1,220 @@ | |
| + | /* |
| + | * res_gdt.c --- reserve blocks for growing the group descriptor table |
| + | * during online resizing. |
| + | * |
| + | * Copyright (C) 2002 Andreas Dilger |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #include <time.h> |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | /* |
| + | * Iterate through the groups which hold BACKUP superblock/GDT copies in an |
| + | * ext3 filesystem. The counters should be initialized to 1, 5, and 7 before |
| + | * calling this for the first time. In a sparse filesystem it will be the |
| + | * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ... |
| + | * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ... |
| + | */ |
| + | static unsigned int list_backups(ext2_filsys fs, unsigned int *three, |
| + | unsigned int *five, unsigned int *seven) |
| + | { |
| + | unsigned int *min = three; |
| + | int mult = 3; |
| + | unsigned int ret; |
| + | |
| + | if (!(fs->super->s_feature_ro_compat & |
| + | EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { |
| + | ret = *min; |
| + | *min += 1; |
| + | return ret; |
| + | } |
| + | |
| + | if (*five < *min) { |
| + | min = five; |
| + | mult = 5; |
| + | } |
| + | if (*seven < *min) { |
| + | min = seven; |
| + | mult = 7; |
| + | } |
| + | |
| + | ret = *min; |
| + | *min *= mult; |
| + | |
| + | return ret; |
| + | } |
| + | |
| + | /* |
| + | * This code assumes that the reserved blocks have already been marked in-use |
| + | * during ext2fs_initialize(), so that they are not allocated for other |
| + | * uses before we can add them to the resize inode (which has to come |
| + | * after the creation of the inode table). |
| + | */ |
| + | errcode_t ext2fs_create_resize_inode(ext2_filsys fs) |
| + | { |
| + | errcode_t retval, retval2; |
| + | struct ext2_super_block *sb; |
| + | struct ext2_inode inode; |
| + | __u32 *dindir_buf, *gdt_buf; |
| + | int rsv_add; |
| + | unsigned long long apb, inode_size; |
| + | blk_t dindir_blk, rsv_off, gdt_off, gdt_blk; |
| + | int dindir_dirty = 0, inode_dirty = 0; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | sb = fs->super; |
| + | |
| + | retval = ext2fs_get_mem(2 * fs->blocksize, (void **)&dindir_buf); |
| + | if (retval) |
| + | goto out_free; |
| + | gdt_buf = (__u32 *)((char *)dindir_buf + fs->blocksize); |
| + | |
| + | retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode); |
| + | if (retval) |
| + | goto out_free; |
| + | |
| + | /* Maximum possible file size (we donly use the dindirect blocks) */ |
| + | apb = EXT2_ADDR_PER_BLOCK(sb); |
| + | rsv_add = fs->blocksize / 512; |
| + | if ((dindir_blk = inode.i_block[EXT2_DIND_BLOCK])) { |
| + | #ifdef RES_GDT_DEBUG |
| + | printf("reading GDT dindir %u\n", dindir_blk); |
| + | #endif |
| + | retval = ext2fs_read_ind_block(fs, dindir_blk, dindir_buf); |
| + | if (retval) |
| + | goto out_inode; |
| + | } else { |
| + | blk_t goal = 3 + sb->s_reserved_gdt_blocks + |
| + | fs->desc_blocks + fs->inode_blocks_per_group; |
| + | |
| + | retval = ext2fs_alloc_block(fs, goal, 0, &dindir_blk); |
| + | if (retval) |
| + | goto out_free; |
| + | inode.i_mode = LINUX_S_IFREG | 0600; |
| + | inode.i_links_count = 1; |
| + | inode.i_block[EXT2_DIND_BLOCK] = dindir_blk; |
| + | inode.i_blocks = rsv_add; |
| + | memset(dindir_buf, 0, fs->blocksize); |
| + | #ifdef RES_GDT_DEBUG |
| + | printf("allocated GDT dindir %u\n", dindir_blk); |
| + | #endif |
| + | dindir_dirty = inode_dirty = 1; |
| + | inode_size = apb*apb + apb + EXT2_NDIR_BLOCKS; |
| + | inode_size *= fs->blocksize; |
| + | inode.i_size = inode_size & 0xFFFFFFFF; |
| + | inode.i_size_high = (inode_size >> 32) & 0xFFFFFFFF; |
| + | if(inode.i_size_high) { |
| + | sb->s_feature_ro_compat |= |
| + | EXT2_FEATURE_RO_COMPAT_LARGE_FILE; |
| + | } |
| + | inode.i_ctime = time(0); |
| + | } |
| + | |
| + | for (rsv_off = 0, gdt_off = fs->desc_blocks, |
| + | gdt_blk = sb->s_first_data_block + 1 + fs->desc_blocks; |
| + | rsv_off < sb->s_reserved_gdt_blocks; |
| + | rsv_off++, gdt_off++, gdt_blk++) { |
| + | unsigned int three = 1, five = 5, seven = 7; |
| + | unsigned int grp, last = 0; |
| + | int gdt_dirty = 0; |
| + | |
| + | gdt_off %= apb; |
| + | if (!dindir_buf[gdt_off]) { |
| + | /* FIXME XXX XXX |
| + | blk_t new_blk; |
| + | |
| + | retval = ext2fs_new_block(fs, gdt_blk, 0, &new_blk); |
| + | if (retval) |
| + | goto out_free; |
| + | if (new_blk != gdt_blk) { |
| + | // XXX free block |
| + | retval = -1; // XXX |
| + | } |
| + | */ |
| + | gdt_dirty = dindir_dirty = inode_dirty = 1; |
| + | memset(gdt_buf, 0, fs->blocksize); |
| + | dindir_buf[gdt_off] = gdt_blk; |
| + | inode.i_blocks += rsv_add; |
| + | #ifdef RES_GDT_DEBUG |
| + | printf("added primary GDT block %u at %u[%u]\n", |
| + | gdt_blk, dindir_blk, gdt_off); |
| + | #endif |
| + | } else if (dindir_buf[gdt_off] == gdt_blk) { |
| + | #ifdef RES_GDT_DEBUG |
| + | printf("reading primary GDT block %u\n", gdt_blk); |
| + | #endif |
| + | retval = ext2fs_read_ind_block(fs, gdt_blk, gdt_buf); |
| + | if (retval) |
| + | goto out_dindir; |
| + | } else { |
| + | #ifdef RES_GDT_DEBUG |
| + | printf("bad primary GDT %u != %u at %u[%u]\n", |
| + | dindir_buf[gdt_off], gdt_blk,dindir_blk,gdt_off); |
| + | #endif |
| + | retval = EXT2_ET_RESIZE_INODE_CORRUPT; |
| + | goto out_dindir; |
| + | } |
| + | |
| + | while ((grp = list_backups(fs, &three, &five, &seven)) < |
| + | fs->group_desc_count) { |
| + | blk_t expect = gdt_blk + grp * sb->s_blocks_per_group; |
| + | |
| + | if (!gdt_buf[last]) { |
| + | #ifdef RES_GDT_DEBUG |
| + | printf("added backup GDT %u grp %u@%u[%u]\n", |
| + | expect, grp, gdt_blk, last); |
| + | #endif |
| + | gdt_buf[last] = expect; |
| + | inode.i_blocks += rsv_add; |
| + | gdt_dirty = inode_dirty = 1; |
| + | } else if (gdt_buf[last] != expect) { |
| + | #ifdef RES_GDT_DEBUG |
| + | printf("bad backup GDT %u != %u at %u[%u]\n", |
| + | gdt_buf[last], expect, gdt_blk, last); |
| + | #endif |
| + | retval = EXT2_ET_RESIZE_INODE_CORRUPT; |
| + | goto out_dindir; |
| + | } |
| + | last++; |
| + | } |
| + | if (gdt_dirty) { |
| + | #ifdef RES_GDT_DEBUG |
| + | printf("writing primary GDT block %u\n", gdt_blk); |
| + | #endif |
| + | retval = ext2fs_write_ind_block(fs, gdt_blk, gdt_buf); |
| + | if (retval) |
| + | goto out_dindir; |
| + | } |
| + | } |
| + | |
| + | out_dindir: |
| + | if (dindir_dirty) { |
| + | retval2 = ext2fs_write_ind_block(fs, dindir_blk, dindir_buf); |
| + | if (!retval) |
| + | retval = retval2; |
| + | } |
| + | out_inode: |
| + | #ifdef RES_GDT_DEBUG |
| + | printf("inode.i_blocks = %u, i_size = %u\n", inode.i_blocks, |
| + | inode.i_size); |
| + | #endif |
| + | if (inode_dirty) { |
| + | inode.i_atime = inode.i_mtime = time(0); |
| + | retval2 = ext2fs_write_inode(fs, EXT2_RESIZE_INO, &inode); |
| + | if (!retval) |
| + | retval = retval2; |
| + | } |
| + | out_free: |
| + | ext2fs_free_mem((void **)&dindir_buf); |
| + | return retval; |
| + | } |
| + | |
| @@ -0,0 +1,106 @@ | |
| + | /* |
| + | * rs_bitmap.c --- routine for changing the size of a bitmap |
| + | * |
| + | * Copyright (C) 1996, 1997 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #ifdef HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #ifdef HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | errcode_t ext2fs_resize_generic_bitmap(__u32 new_end, __u32 new_real_end, |
| + | ext2fs_generic_bitmap bmap) |
| + | { |
| + | errcode_t retval; |
| + | size_t size, new_size; |
| + | __u32 bitno; |
| + | |
| + | if (!bmap) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | |
| + | EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_GENERIC_BITMAP); |
| + | |
| + | /* |
| + | * If we're expanding the bitmap, make sure all of the new |
| + | * parts of the bitmap are zero. |
| + | */ |
| + | if (new_end > bmap->end) { |
| + | bitno = bmap->real_end; |
| + | if (bitno > new_end) |
| + | bitno = new_end; |
| + | for (; bitno > bmap->end; bitno--) |
| + | ext2fs_clear_bit(bitno - bmap->start, bmap->bitmap); |
| + | } |
| + | if (new_real_end == bmap->real_end) { |
| + | bmap->end = new_end; |
| + | return 0; |
| + | } |
| + | |
| + | size = ((bmap->real_end - bmap->start) / 8) + 1; |
| + | new_size = ((new_real_end - bmap->start) / 8) + 1; |
| + | |
| + | if (size != new_size) { |
| + | retval = ext2fs_resize_mem(size, new_size, &bmap->bitmap); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | if (new_size > size) |
| + | memset(bmap->bitmap + size, 0, new_size - size); |
| + | |
| + | bmap->end = new_end; |
| + | bmap->real_end = new_real_end; |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end, |
| + | ext2fs_inode_bitmap bmap) |
| + | { |
| + | errcode_t retval; |
| + | |
| + | if (!bmap) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | |
| + | EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_INODE_BITMAP); |
| + | |
| + | bmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP; |
| + | retval = ext2fs_resize_generic_bitmap(new_end, new_real_end, |
| + | bmap); |
| + | bmap->magic = EXT2_ET_MAGIC_INODE_BITMAP; |
| + | return retval; |
| + | } |
| + | |
| + | errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end, |
| + | ext2fs_block_bitmap bmap) |
| + | { |
| + | errcode_t retval; |
| + | |
| + | if (!bmap) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | |
| + | EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_BLOCK_BITMAP); |
| + | |
| + | bmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP; |
| + | retval = ext2fs_resize_generic_bitmap(new_end, new_real_end, |
| + | bmap); |
| + | bmap->magic = EXT2_ET_MAGIC_BLOCK_BITMAP; |
| + | return retval; |
| + | } |
| + | |
| @@ -0,0 +1,300 @@ | |
| + | /* |
| + | * rw_bitmaps.c --- routines to read and write the inode and block bitmaps. |
| + | * |
| + | * Copyright (C) 1993, 1994, 1994, 1996 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #ifdef HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #ifdef HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | #include "e2image.h" |
| + | |
| + | #if defined(__powerpc__) && defined(EXT2FS_ENABLE_SWAPFS) |
| + | /* |
| + | * On the PowerPC, the big-endian variant of the ext2 filesystem |
| + | * has its bitmaps stored as 32-bit words with bit 0 as the LSB |
| + | * of each word. Thus a bitmap with only bit 0 set would be, as |
| + | * a string of bytes, 00 00 00 01 00 ... |
| + | * To cope with this, we byte-reverse each word of a bitmap if |
| + | * we have a big-endian filesystem, that is, if we are *not* |
| + | * byte-swapping other word-sized numbers. |
| + | */ |
| + | #define EXT2_BIG_ENDIAN_BITMAPS |
| + | #endif |
| + | |
| + | #ifdef EXT2_BIG_ENDIAN_BITMAPS |
| + | static void ext2fs_swap_bitmap(ext2_filsys fs, char *bitmap, int nbytes) |
| + | { |
| + | __u32 *p = (__u32 *) bitmap; |
| + | int n; |
| + | |
| + | for (n = nbytes / sizeof(__u32); n > 0; --n, ++p) |
| + | *p = ext2fs_swab32(*p); |
| + | } |
| + | #endif |
| + | |
| + | errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs) |
| + | { |
| + | dgrp_t i; |
| + | size_t nbytes; |
| + | errcode_t retval; |
| + | char * inode_bitmap = fs->inode_map->bitmap; |
| + | char * bitmap_block = NULL; |
| + | blk_t blk; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | if (!(fs->flags & EXT2_FLAG_RW)) |
| + | return EXT2_ET_RO_FILSYS; |
| + | if (!inode_bitmap) |
| + | return 0; |
| + | nbytes = (size_t) ((EXT2_INODES_PER_GROUP(fs->super)+7) / 8); |
| + | |
| + | retval = ext2fs_get_mem(fs->blocksize, &bitmap_block); |
| + | if (retval) |
| + | return retval; |
| + | memset(bitmap_block, 0xff, fs->blocksize); |
| + | for (i = 0; i < fs->group_desc_count; i++) { |
| + | memcpy(bitmap_block, inode_bitmap, nbytes); |
| + | blk = fs->group_desc[i].bg_inode_bitmap; |
| + | if (blk) { |
| + | #ifdef EXT2_BIG_ENDIAN_BITMAPS |
| + | if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) || |
| + | (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))) |
| + | ext2fs_swap_bitmap(fs, bitmap_block, nbytes); |
| + | #endif |
| + | retval = io_channel_write_blk(fs->io, blk, 1, |
| + | bitmap_block); |
| + | if (retval) |
| + | return EXT2_ET_INODE_BITMAP_WRITE; |
| + | } |
| + | inode_bitmap += nbytes; |
| + | } |
| + | fs->flags &= ~EXT2_FLAG_IB_DIRTY; |
| + | ext2fs_free_mem(&bitmap_block); |
| + | return 0; |
| + | } |
| + | |
| + | errcode_t ext2fs_write_block_bitmap (ext2_filsys fs) |
| + | { |
| + | dgrp_t i; |
| + | unsigned int j; |
| + | int nbytes; |
| + | unsigned int nbits; |
| + | errcode_t retval; |
| + | char * block_bitmap = fs->block_map->bitmap; |
| + | char * bitmap_block = NULL; |
| + | blk_t blk; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | if (!(fs->flags & EXT2_FLAG_RW)) |
| + | return EXT2_ET_RO_FILSYS; |
| + | if (!block_bitmap) |
| + | return 0; |
| + | nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; |
| + | retval = ext2fs_get_mem(fs->blocksize, &bitmap_block); |
| + | if (retval) |
| + | return retval; |
| + | memset(bitmap_block, 0xff, fs->blocksize); |
| + | for (i = 0; i < fs->group_desc_count; i++) { |
| + | memcpy(bitmap_block, block_bitmap, nbytes); |
| + | if (i == fs->group_desc_count - 1) { |
| + | /* Force bitmap padding for the last group */ |
| + | nbits = ((fs->super->s_blocks_count |
| + | - fs->super->s_first_data_block) |
| + | % EXT2_BLOCKS_PER_GROUP(fs->super)); |
| + | if (nbits) |
| + | for (j = nbits; j < fs->blocksize * 8; j++) |
| + | ext2fs_set_bit(j, bitmap_block); |
| + | } |
| + | blk = fs->group_desc[i].bg_block_bitmap; |
| + | if (blk) { |
| + | #ifdef EXT2_BIG_ENDIAN_BITMAPS |
| + | if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) || |
| + | (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))) |
| + | ext2fs_swap_bitmap(fs, bitmap_block, nbytes); |
| + | #endif |
| + | retval = io_channel_write_blk(fs->io, blk, 1, |
| + | bitmap_block); |
| + | if (retval) |
| + | return EXT2_ET_BLOCK_BITMAP_WRITE; |
| + | } |
| + | block_bitmap += nbytes; |
| + | } |
| + | fs->flags &= ~EXT2_FLAG_BB_DIRTY; |
| + | ext2fs_free_mem(&bitmap_block); |
| + | return 0; |
| + | } |
| + | |
| + | static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) |
| + | { |
| + | dgrp_t i; |
| + | char *block_bitmap = 0, *inode_bitmap = 0; |
| + | char *buf; |
| + | errcode_t retval; |
| + | int block_nbytes = (int) EXT2_BLOCKS_PER_GROUP(fs->super) / 8; |
| + | int inode_nbytes = (int) EXT2_INODES_PER_GROUP(fs->super) / 8; |
| + | blk_t blk; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | fs->write_bitmaps = ext2fs_write_bitmaps; |
| + | |
| + | retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); |
| + | if (retval) |
| + | return retval; |
| + | if (do_block) { |
| + | if (fs->block_map) |
| + | ext2fs_free_block_bitmap(fs->block_map); |
| + | sprintf(buf, "block bitmap for %s", fs->device_name); |
| + | retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); |
| + | if (retval) |
| + | goto cleanup; |
| + | block_bitmap = fs->block_map->bitmap; |
| + | } |
| + | if (do_inode) { |
| + | if (fs->inode_map) |
| + | ext2fs_free_inode_bitmap(fs->inode_map); |
| + | sprintf(buf, "inode bitmap for %s", fs->device_name); |
| + | retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); |
| + | if (retval) |
| + | goto cleanup; |
| + | inode_bitmap = fs->inode_map->bitmap; |
| + | } |
| + | ext2fs_free_mem(&buf); |
| + | |
| + | if (fs->flags & EXT2_FLAG_IMAGE_FILE) { |
| + | if (inode_bitmap) { |
| + | blk = (fs->image_header->offset_inodemap / |
| + | fs->blocksize); |
| + | retval = io_channel_read_blk(fs->image_io, blk, |
| + | -(inode_nbytes * fs->group_desc_count), |
| + | inode_bitmap); |
| + | if (retval) |
| + | goto cleanup; |
| + | } |
| + | if (block_bitmap) { |
| + | blk = (fs->image_header->offset_blockmap / |
| + | fs->blocksize); |
| + | retval = io_channel_read_blk(fs->image_io, blk, |
| + | -(block_nbytes * fs->group_desc_count), |
| + | block_bitmap); |
| + | if (retval) |
| + | goto cleanup; |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | for (i = 0; i < fs->group_desc_count; i++) { |
| + | if (block_bitmap) { |
| + | blk = fs->group_desc[i].bg_block_bitmap; |
| + | if (blk) { |
| + | retval = io_channel_read_blk(fs->io, blk, |
| + | -block_nbytes, block_bitmap); |
| + | if (retval) { |
| + | retval = EXT2_ET_BLOCK_BITMAP_READ; |
| + | goto cleanup; |
| + | } |
| + | #ifdef EXT2_BIG_ENDIAN_BITMAPS |
| + | if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) || |
| + | (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))) |
| + | ext2fs_swap_bitmap(fs, block_bitmap, block_nbytes); |
| + | #endif |
| + | } else |
| + | memset(block_bitmap, 0, block_nbytes); |
| + | block_bitmap += block_nbytes; |
| + | } |
| + | if (inode_bitmap) { |
| + | blk = fs->group_desc[i].bg_inode_bitmap; |
| + | if (blk) { |
| + | retval = io_channel_read_blk(fs->io, blk, |
| + | -inode_nbytes, inode_bitmap); |
| + | if (retval) { |
| + | retval = EXT2_ET_INODE_BITMAP_READ; |
| + | goto cleanup; |
| + | } |
| + | #ifdef EXT2_BIG_ENDIAN_BITMAPS |
| + | if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) || |
| + | (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))) |
| + | ext2fs_swap_bitmap(fs, inode_bitmap, inode_nbytes); |
| + | #endif |
| + | } else |
| + | memset(inode_bitmap, 0, inode_nbytes); |
| + | inode_bitmap += inode_nbytes; |
| + | } |
| + | } |
| + | return 0; |
| + | |
| + | cleanup: |
| + | if (do_block) { |
| + | ext2fs_free_mem(&fs->block_map); |
| + | fs->block_map = 0; |
| + | } |
| + | if (do_inode) { |
| + | ext2fs_free_mem(&fs->inode_map); |
| + | fs->inode_map = 0; |
| + | } |
| + | if (buf) |
| + | ext2fs_free_mem(&buf); |
| + | return retval; |
| + | } |
| + | |
| + | errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs) |
| + | { |
| + | return read_bitmaps(fs, 1, 0); |
| + | } |
| + | |
| + | errcode_t ext2fs_read_block_bitmap(ext2_filsys fs) |
| + | { |
| + | return read_bitmaps(fs, 0, 1); |
| + | } |
| + | |
| + | errcode_t ext2fs_read_bitmaps(ext2_filsys fs) |
| + | { |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | if (fs->inode_map && fs->block_map) |
| + | return 0; |
| + | |
| + | return read_bitmaps(fs, !fs->inode_map, !fs->block_map); |
| + | } |
| + | |
| + | errcode_t ext2fs_write_bitmaps(ext2_filsys fs) |
| + | { |
| + | errcode_t retval; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | if (fs->block_map && ext2fs_test_bb_dirty(fs)) { |
| + | retval = ext2fs_write_block_bitmap(fs); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | if (fs->inode_map && ext2fs_test_ib_dirty(fs)) { |
| + | retval = ext2fs_write_inode_bitmap(fs); |
| + | if (retval) |
| + | return retval; |
| + | } |
| + | return 0; |
| + | } |
| + | |
| @@ -0,0 +1,78 @@ | |
| + | /* |
| + | * sparse.c --- find the groups in an ext2 filesystem with metadata backups |
| + | * |
| + | * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. |
| + | * Copyright (C) 2002 Andreas Dilger. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fsP.h" |
| + | |
| + | static int test_root(int a, int b) |
| + | { |
| + | if (a == 0) |
| + | return 1; |
| + | while (1) { |
| + | if (a == 1) |
| + | return 1; |
| + | if (a % b) |
| + | return 0; |
| + | a = a / b; |
| + | } |
| + | } |
| + | |
| + | int ext2fs_bg_has_super(ext2_filsys fs, int group_block) |
| + | { |
| + | if (!(fs->super->s_feature_ro_compat & |
| + | EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) |
| + | return 1; |
| + | |
| + | if (test_root(group_block, 3) || (test_root(group_block, 5)) || |
| + | test_root(group_block, 7)) |
| + | return 1; |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * Iterate through the groups which hold BACKUP superblock/GDT copies in an |
| + | * ext3 filesystem. The counters should be initialized to 1, 5, and 7 before |
| + | * calling this for the first time. In a sparse filesystem it will be the |
| + | * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ... |
| + | * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ... |
| + | */ |
| + | unsigned int ext2fs_list_backups(ext2_filsys fs, unsigned int *three, |
| + | unsigned int *five, unsigned int *seven) |
| + | { |
| + | unsigned int *min = three; |
| + | int mult = 3; |
| + | unsigned int ret; |
| + | |
| + | if (!(fs->super->s_feature_ro_compat & |
| + | EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { |
| + | ret = *min; |
| + | *min += 1; |
| + | return ret; |
| + | } |
| + | |
| + | if (*five < *min) { |
| + | min = five; |
| + | mult = 5; |
| + | } |
| + | if (*seven < *min) { |
| + | min = seven; |
| + | mult = 7; |
| + | } |
| + | |
| + | ret = *min; |
| + | *min *= mult; |
| + | |
| + | return ret; |
| + | } |
| @@ -0,0 +1,237 @@ | |
| + | /* |
| + | * swapfs.c --- swap ext2 filesystem data structures |
| + | * |
| + | * Copyright (C) 1995, 1996, 2002 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <string.h> |
| + | #include <time.h> |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | #include <ext2fs/ext2_ext_attr.h> |
| + | |
| + | #ifdef EXT2FS_ENABLE_SWAPFS |
| + | void ext2fs_swap_super(struct ext2_super_block * sb) |
| + | { |
| + | int i; |
| + | sb->s_inodes_count = ext2fs_swab32(sb->s_inodes_count); |
| + | sb->s_blocks_count = ext2fs_swab32(sb->s_blocks_count); |
| + | sb->s_r_blocks_count = ext2fs_swab32(sb->s_r_blocks_count); |
| + | sb->s_free_blocks_count = ext2fs_swab32(sb->s_free_blocks_count); |
| + | sb->s_free_inodes_count = ext2fs_swab32(sb->s_free_inodes_count); |
| + | sb->s_first_data_block = ext2fs_swab32(sb->s_first_data_block); |
| + | sb->s_log_block_size = ext2fs_swab32(sb->s_log_block_size); |
| + | sb->s_log_frag_size = ext2fs_swab32(sb->s_log_frag_size); |
| + | sb->s_blocks_per_group = ext2fs_swab32(sb->s_blocks_per_group); |
| + | sb->s_frags_per_group = ext2fs_swab32(sb->s_frags_per_group); |
| + | sb->s_inodes_per_group = ext2fs_swab32(sb->s_inodes_per_group); |
| + | sb->s_mtime = ext2fs_swab32(sb->s_mtime); |
| + | sb->s_wtime = ext2fs_swab32(sb->s_wtime); |
| + | sb->s_mnt_count = ext2fs_swab16(sb->s_mnt_count); |
| + | sb->s_max_mnt_count = ext2fs_swab16(sb->s_max_mnt_count); |
| + | sb->s_magic = ext2fs_swab16(sb->s_magic); |
| + | sb->s_state = ext2fs_swab16(sb->s_state); |
| + | sb->s_errors = ext2fs_swab16(sb->s_errors); |
| + | sb->s_minor_rev_level = ext2fs_swab16(sb->s_minor_rev_level); |
| + | sb->s_lastcheck = ext2fs_swab32(sb->s_lastcheck); |
| + | sb->s_checkinterval = ext2fs_swab32(sb->s_checkinterval); |
| + | sb->s_creator_os = ext2fs_swab32(sb->s_creator_os); |
| + | sb->s_rev_level = ext2fs_swab32(sb->s_rev_level); |
| + | sb->s_def_resuid = ext2fs_swab16(sb->s_def_resuid); |
| + | sb->s_def_resgid = ext2fs_swab16(sb->s_def_resgid); |
| + | sb->s_first_ino = ext2fs_swab32(sb->s_first_ino); |
| + | sb->s_inode_size = ext2fs_swab16(sb->s_inode_size); |
| + | sb->s_block_group_nr = ext2fs_swab16(sb->s_block_group_nr); |
| + | sb->s_feature_compat = ext2fs_swab32(sb->s_feature_compat); |
| + | sb->s_feature_incompat = ext2fs_swab32(sb->s_feature_incompat); |
| + | sb->s_feature_ro_compat = ext2fs_swab32(sb->s_feature_ro_compat); |
| + | sb->s_algorithm_usage_bitmap = ext2fs_swab32(sb->s_algorithm_usage_bitmap); |
| + | sb->s_reserved_gdt_blocks = ext2fs_swab16(sb->s_reserved_gdt_blocks); |
| + | sb->s_journal_inum = ext2fs_swab32(sb->s_journal_inum); |
| + | sb->s_journal_dev = ext2fs_swab32(sb->s_journal_dev); |
| + | sb->s_last_orphan = ext2fs_swab32(sb->s_last_orphan); |
| + | sb->s_default_mount_opts = ext2fs_swab32(sb->s_default_mount_opts); |
| + | sb->s_first_meta_bg = ext2fs_swab32(sb->s_first_meta_bg); |
| + | sb->s_mkfs_time = ext2fs_swab32(sb->s_mkfs_time); |
| + | for (i=0; i < 4; i++) |
| + | sb->s_hash_seed[i] = ext2fs_swab32(sb->s_hash_seed[i]); |
| + | for (i=0; i < 17; i++) |
| + | sb->s_jnl_blocks[i] = ext2fs_swab32(sb->s_jnl_blocks[i]); |
| + | |
| + | } |
| + | |
| + | void ext2fs_swap_group_desc(struct ext2_group_desc *gdp) |
| + | { |
| + | gdp->bg_block_bitmap = ext2fs_swab32(gdp->bg_block_bitmap); |
| + | gdp->bg_inode_bitmap = ext2fs_swab32(gdp->bg_inode_bitmap); |
| + | gdp->bg_inode_table = ext2fs_swab32(gdp->bg_inode_table); |
| + | gdp->bg_free_blocks_count = ext2fs_swab16(gdp->bg_free_blocks_count); |
| + | gdp->bg_free_inodes_count = ext2fs_swab16(gdp->bg_free_inodes_count); |
| + | gdp->bg_used_dirs_count = ext2fs_swab16(gdp->bg_used_dirs_count); |
| + | } |
| + | |
| + | void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, int has_header) |
| + | { |
| + | struct ext2_ext_attr_header *from_header = |
| + | (struct ext2_ext_attr_header *)from; |
| + | struct ext2_ext_attr_header *to_header = |
| + | (struct ext2_ext_attr_header *)to; |
| + | struct ext2_ext_attr_entry *from_entry, *to_entry; |
| + | char *from_end = (char *)from_header + bufsize; |
| + | int n; |
| + | |
| + | if (to_header != from_header) |
| + | memcpy(to_header, from_header, bufsize); |
| + | |
| + | from_entry = (struct ext2_ext_attr_entry *)from_header; |
| + | to_entry = (struct ext2_ext_attr_entry *)to_header; |
| + | |
| + | if (has_header) { |
| + | to_header->h_magic = ext2fs_swab32(from_header->h_magic); |
| + | to_header->h_blocks = ext2fs_swab32(from_header->h_blocks); |
| + | to_header->h_refcount = ext2fs_swab32(from_header->h_refcount); |
| + | for (n=0; n<4; n++) |
| + | to_header->h_reserved[n] = |
| + | ext2fs_swab32(from_header->h_reserved[n]); |
| + | from_entry = (struct ext2_ext_attr_entry *)(from_header+1); |
| + | to_entry = (struct ext2_ext_attr_entry *)(to_header+1); |
| + | } |
| + | |
| + | while ((char *)from_entry < from_end && *(__u32 *)from_entry) { |
| + | to_entry->e_value_offs = |
| + | ext2fs_swab16(from_entry->e_value_offs); |
| + | to_entry->e_value_block = |
| + | ext2fs_swab32(from_entry->e_value_block); |
| + | to_entry->e_value_size = |
| + | ext2fs_swab32(from_entry->e_value_size); |
| + | from_entry = EXT2_EXT_ATTR_NEXT(from_entry); |
| + | to_entry = EXT2_EXT_ATTR_NEXT(to_entry); |
| + | } |
| + | } |
| + | |
| + | void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t, |
| + | struct ext2_inode_large *f, int hostorder, |
| + | int bufsize) |
| + | { |
| + | unsigned i; |
| + | int islnk = 0; |
| + | __u32 *eaf, *eat; |
| + | |
| + | if (hostorder && LINUX_S_ISLNK(f->i_mode)) |
| + | islnk = 1; |
| + | t->i_mode = ext2fs_swab16(f->i_mode); |
| + | if (!hostorder && LINUX_S_ISLNK(t->i_mode)) |
| + | islnk = 1; |
| + | t->i_uid = ext2fs_swab16(f->i_uid); |
| + | t->i_size = ext2fs_swab32(f->i_size); |
| + | t->i_atime = ext2fs_swab32(f->i_atime); |
| + | t->i_ctime = ext2fs_swab32(f->i_ctime); |
| + | t->i_mtime = ext2fs_swab32(f->i_mtime); |
| + | t->i_dtime = ext2fs_swab32(f->i_dtime); |
| + | t->i_gid = ext2fs_swab16(f->i_gid); |
| + | t->i_links_count = ext2fs_swab16(f->i_links_count); |
| + | t->i_blocks = ext2fs_swab32(f->i_blocks); |
| + | t->i_flags = ext2fs_swab32(f->i_flags); |
| + | t->i_file_acl = ext2fs_swab32(f->i_file_acl); |
| + | t->i_dir_acl = ext2fs_swab32(f->i_dir_acl); |
| + | if (!islnk || ext2fs_inode_data_blocks(fs, (struct ext2_inode *)t)) { |
| + | for (i = 0; i < EXT2_N_BLOCKS; i++) |
| + | t->i_block[i] = ext2fs_swab32(f->i_block[i]); |
| + | } else if (t != f) { |
| + | for (i = 0; i < EXT2_N_BLOCKS; i++) |
| + | t->i_block[i] = f->i_block[i]; |
| + | } |
| + | t->i_generation = ext2fs_swab32(f->i_generation); |
| + | t->i_faddr = ext2fs_swab32(f->i_faddr); |
| + | |
| + | switch (fs->super->s_creator_os) { |
| + | case EXT2_OS_LINUX: |
| + | t->osd1.linux1.l_i_reserved1 = |
| + | ext2fs_swab32(f->osd1.linux1.l_i_reserved1); |
| + | t->osd2.linux2.l_i_frag = f->osd2.linux2.l_i_frag; |
| + | t->osd2.linux2.l_i_fsize = f->osd2.linux2.l_i_fsize; |
| + | t->osd2.linux2.i_pad1 = ext2fs_swab16(f->osd2.linux2.i_pad1); |
| + | t->osd2.linux2.l_i_uid_high = |
| + | ext2fs_swab16 (f->osd2.linux2.l_i_uid_high); |
| + | t->osd2.linux2.l_i_gid_high = |
| + | ext2fs_swab16 (f->osd2.linux2.l_i_gid_high); |
| + | t->osd2.linux2.l_i_reserved2 = |
| + | ext2fs_swab32(f->osd2.linux2.l_i_reserved2); |
| + | break; |
| + | case EXT2_OS_HURD: |
| + | t->osd1.hurd1.h_i_translator = |
| + | ext2fs_swab32 (f->osd1.hurd1.h_i_translator); |
| + | t->osd2.hurd2.h_i_frag = f->osd2.hurd2.h_i_frag; |
| + | t->osd2.hurd2.h_i_fsize = f->osd2.hurd2.h_i_fsize; |
| + | t->osd2.hurd2.h_i_mode_high = |
| + | ext2fs_swab16 (f->osd2.hurd2.h_i_mode_high); |
| + | t->osd2.hurd2.h_i_uid_high = |
| + | ext2fs_swab16 (f->osd2.hurd2.h_i_uid_high); |
| + | t->osd2.hurd2.h_i_gid_high = |
| + | ext2fs_swab16 (f->osd2.hurd2.h_i_gid_high); |
| + | t->osd2.hurd2.h_i_author = |
| + | ext2fs_swab32 (f->osd2.hurd2.h_i_author); |
| + | break; |
| + | case EXT2_OS_MASIX: |
| + | t->osd1.masix1.m_i_reserved1 = |
| + | ext2fs_swab32(f->osd1.masix1.m_i_reserved1); |
| + | t->osd2.masix2.m_i_frag = f->osd2.masix2.m_i_frag; |
| + | t->osd2.masix2.m_i_fsize = f->osd2.masix2.m_i_fsize; |
| + | t->osd2.masix2.m_pad1 = ext2fs_swab16(f->osd2.masix2.m_pad1); |
| + | t->osd2.masix2.m_i_reserved2[0] = |
| + | ext2fs_swab32(f->osd2.masix2.m_i_reserved2[0]); |
| + | t->osd2.masix2.m_i_reserved2[1] = |
| + | ext2fs_swab32(f->osd2.masix2.m_i_reserved2[1]); |
| + | break; |
| + | } |
| + | |
| + | if (bufsize < (int) (sizeof(struct ext2_inode) + sizeof(__u16))) |
| + | return; /* no i_extra_isize field */ |
| + | |
| + | t->i_extra_isize = ext2fs_swab16(f->i_extra_isize); |
| + | if (t->i_extra_isize > EXT2_INODE_SIZE(fs->super) - |
| + | sizeof(struct ext2_inode)) { |
| + | /* this is error case: i_extra_size is too large */ |
| + | return; |
| + | } |
| + | |
| + | i = sizeof(struct ext2_inode) + t->i_extra_isize + sizeof(__u32); |
| + | if (bufsize < (int) i) |
| + | return; /* no space for EA magic */ |
| + | |
| + | eaf = (__u32 *) (((char *) f) + sizeof(struct ext2_inode) + |
| + | f->i_extra_isize); |
| + | |
| + | if (ext2fs_swab32(*eaf) != EXT2_EXT_ATTR_MAGIC) |
| + | return; /* it seems no magic here */ |
| + | |
| + | eat = (__u32 *) (((char *) t) + sizeof(struct ext2_inode) + |
| + | f->i_extra_isize); |
| + | *eat = ext2fs_swab32(*eaf); |
| + | |
| + | /* convert EA(s) */ |
| + | ext2fs_swap_ext_attr((char *) (eat + 1), (char *) (eaf + 1), |
| + | bufsize - sizeof(struct ext2_inode) - |
| + | t->i_extra_isize - sizeof(__u32), 0); |
| + | |
| + | } |
| + | |
| + | void ext2fs_swap_inode(ext2_filsys fs, struct ext2_inode *t, |
| + | struct ext2_inode *f, int hostorder) |
| + | { |
| + | ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) t, |
| + | (struct ext2_inode_large *) f, hostorder, |
| + | sizeof(struct ext2_inode)); |
| + | } |
| + | |
| + | #endif |
| @@ -0,0 +1,382 @@ | |
| + | /* |
| + | * test_io.c --- This is the Test I/O interface. |
| + | * |
| + | * Copyright (C) 1996 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | /* |
| + | * For checking structure magic numbers... |
| + | */ |
| + | |
| + | #define EXT2_CHECK_MAGIC(struct, code) \ |
| + | if ((struct)->magic != (code)) return (code) |
| + | |
| + | struct test_private_data { |
| + | int magic; |
| + | io_channel real; |
| + | int flags; |
| + | FILE *outfile; |
| + | unsigned long block; |
| + | int read_abort_count, write_abort_count; |
| + | void (*read_blk)(unsigned long block, int count, errcode_t err); |
| + | void (*write_blk)(unsigned long block, int count, errcode_t err); |
| + | void (*set_blksize)(int blksize, errcode_t err); |
| + | void (*write_byte)(unsigned long block, int count, errcode_t err); |
| + | }; |
| + | |
| + | static errcode_t test_open(const char *name, int flags, io_channel *channel); |
| + | static errcode_t test_close(io_channel channel); |
| + | static errcode_t test_set_blksize(io_channel channel, int blksize); |
| + | static errcode_t test_read_blk(io_channel channel, unsigned long block, |
| + | int count, void *data); |
| + | static errcode_t test_write_blk(io_channel channel, unsigned long block, |
| + | int count, const void *data); |
| + | static errcode_t test_flush(io_channel channel); |
| + | static errcode_t test_write_byte(io_channel channel, unsigned long offset, |
| + | int count, const void *buf); |
| + | static errcode_t test_set_option(io_channel channel, const char *option, |
| + | const char *arg); |
| + | |
| + | static struct struct_io_manager struct_test_manager = { |
| + | EXT2_ET_MAGIC_IO_MANAGER, |
| + | "Test I/O Manager", |
| + | test_open, |
| + | test_close, |
| + | test_set_blksize, |
| + | test_read_blk, |
| + | test_write_blk, |
| + | test_flush, |
| + | test_write_byte, |
| + | test_set_option |
| + | }; |
| + | |
| + | io_manager test_io_manager = &struct_test_manager; |
| + | |
| + | /* |
| + | * These global variable can be set by the test program as |
| + | * necessary *before* calling test_open |
| + | */ |
| + | io_manager test_io_backing_manager = 0; |
| + | void (*test_io_cb_read_blk) |
| + | (unsigned long block, int count, errcode_t err) = 0; |
| + | void (*test_io_cb_write_blk) |
| + | (unsigned long block, int count, errcode_t err) = 0; |
| + | void (*test_io_cb_set_blksize) |
| + | (int blksize, errcode_t err) = 0; |
| + | void (*test_io_cb_write_byte) |
| + | (unsigned long block, int count, errcode_t err) = 0; |
| + | |
| + | /* |
| + | * Test flags |
| + | */ |
| + | #define TEST_FLAG_READ 0x01 |
| + | #define TEST_FLAG_WRITE 0x02 |
| + | #define TEST_FLAG_SET_BLKSIZE 0x04 |
| + | #define TEST_FLAG_FLUSH 0x08 |
| + | #define TEST_FLAG_DUMP 0x10 |
| + | #define TEST_FLAG_SET_OPTION 0x20 |
| + | |
| + | static void test_dump_block(io_channel channel, |
| + | struct test_private_data *data, |
| + | unsigned long block, const void *buf) |
| + | { |
| + | const unsigned char *cp; |
| + | FILE *f = data->outfile; |
| + | int i; |
| + | unsigned long cksum = 0; |
| + | |
| + | for (i=0, cp = buf; i < channel->block_size; i++, cp++) { |
| + | cksum += *cp; |
| + | } |
| + | fprintf(f, "Contents of block %lu, checksum %08lu: \n", block, cksum); |
| + | for (i=0, cp = buf; i < channel->block_size; i++, cp++) { |
| + | if ((i % 16) == 0) |
| + | fprintf(f, "%04x: ", i); |
| + | fprintf(f, "%02x%c", *cp, ((i % 16) == 15) ? '\n' : ' '); |
| + | } |
| + | } |
| + | |
| + | static void test_abort(io_channel channel, unsigned long block) |
| + | { |
| + | struct test_private_data *data; |
| + | FILE *f; |
| + | |
| + | data = (struct test_private_data *) channel->private_data; |
| + | f = data->outfile; |
| + | test_flush(channel); |
| + | |
| + | fprintf(f, "Aborting due to I/O to block %lu\n", block); |
| + | fflush(f); |
| + | abort(); |
| + | } |
| + | |
| + | static errcode_t test_open(const char *name, int flags, io_channel *channel) |
| + | { |
| + | io_channel io = NULL; |
| + | struct test_private_data *data = NULL; |
| + | errcode_t retval; |
| + | char *value; |
| + | |
| + | if (name == 0) |
| + | return EXT2_ET_BAD_DEVICE_NAME; |
| + | retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); |
| + | if (retval) |
| + | return retval; |
| + | memset(io, 0, sizeof(struct struct_io_channel)); |
| + | io->magic = EXT2_ET_MAGIC_IO_CHANNEL; |
| + | retval = ext2fs_get_mem(sizeof(struct test_private_data), &data); |
| + | if (retval) { |
| + | retval = EXT2_ET_NO_MEMORY; |
| + | goto cleanup; |
| + | } |
| + | io->manager = test_io_manager; |
| + | retval = ext2fs_get_mem(strlen(name)+1, &io->name); |
| + | if (retval) |
| + | goto cleanup; |
| + | |
| + | strcpy(io->name, name); |
| + | io->private_data = data; |
| + | io->block_size = 1024; |
| + | io->read_error = 0; |
| + | io->write_error = 0; |
| + | io->refcount = 1; |
| + | |
| + | memset(data, 0, sizeof(struct test_private_data)); |
| + | data->magic = EXT2_ET_MAGIC_TEST_IO_CHANNEL; |
| + | if (test_io_backing_manager) { |
| + | retval = test_io_backing_manager->open(name, flags, |
| + | &data->real); |
| + | if (retval) |
| + | goto cleanup; |
| + | } else |
| + | data->real = 0; |
| + | data->read_blk = test_io_cb_read_blk; |
| + | data->write_blk = test_io_cb_write_blk; |
| + | data->set_blksize = test_io_cb_set_blksize; |
| + | data->write_byte = test_io_cb_write_byte; |
| + | |
| + | data->outfile = NULL; |
| + | if ((value = getenv("TEST_IO_LOGFILE")) != NULL) |
| + | data->outfile = fopen(value, "w"); |
| + | if (!data->outfile) |
| + | data->outfile = stderr; |
| + | |
| + | data->flags = 0; |
| + | if ((value = getenv("TEST_IO_FLAGS")) != NULL) |
| + | data->flags = strtoul(value, NULL, 0); |
| + | |
| + | data->block = 0; |
| + | if ((value = getenv("TEST_IO_BLOCK")) != NULL) |
| + | data->block = strtoul(value, NULL, 0); |
| + | |
| + | data->read_abort_count = 0; |
| + | if ((value = getenv("TEST_IO_READ_ABORT")) != NULL) |
| + | data->read_abort_count = strtoul(value, NULL, 0); |
| + | |
| + | data->write_abort_count = 0; |
| + | if ((value = getenv("TEST_IO_WRITE_ABORT")) != NULL) |
| + | data->write_abort_count = strtoul(value, NULL, 0); |
| + | |
| + | *channel = io; |
| + | return 0; |
| + | |
| + | cleanup: |
| + | if (io) |
| + | ext2fs_free_mem(&io); |
| + | if (data) |
| + | ext2fs_free_mem(&data); |
| + | return retval; |
| + | } |
| + | |
| + | static errcode_t test_close(io_channel channel) |
| + | { |
| + | struct test_private_data *data; |
| + | errcode_t retval = 0; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct test_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); |
| + | |
| + | if (--channel->refcount > 0) |
| + | return 0; |
| + | |
| + | if (data->real) |
| + | retval = io_channel_close(data->real); |
| + | |
| + | if (data->outfile && data->outfile != stderr) |
| + | fclose(data->outfile); |
| + | |
| + | ext2fs_free_mem(&channel->private_data); |
| + | if (channel->name) |
| + | ext2fs_free_mem(&channel->name); |
| + | ext2fs_free_mem(&channel); |
| + | return retval; |
| + | } |
| + | |
| + | static errcode_t test_set_blksize(io_channel channel, int blksize) |
| + | { |
| + | struct test_private_data *data; |
| + | errcode_t retval = 0; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct test_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); |
| + | |
| + | if (data->real) |
| + | retval = io_channel_set_blksize(data->real, blksize); |
| + | if (data->set_blksize) |
| + | data->set_blksize(blksize, retval); |
| + | if (data->flags & TEST_FLAG_SET_BLKSIZE) |
| + | fprintf(data->outfile, |
| + | "Test_io: set_blksize(%d) returned %s\n", |
| + | blksize, retval ? error_message(retval) : "OK"); |
| + | channel->block_size = blksize; |
| + | return retval; |
| + | } |
| + | |
| + | |
| + | static errcode_t test_read_blk(io_channel channel, unsigned long block, |
| + | int count, void *buf) |
| + | { |
| + | struct test_private_data *data; |
| + | errcode_t retval = 0; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct test_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); |
| + | |
| + | if (data->real) |
| + | retval = io_channel_read_blk(data->real, block, count, buf); |
| + | if (data->read_blk) |
| + | data->read_blk(block, count, retval); |
| + | if (data->flags & TEST_FLAG_READ) |
| + | fprintf(data->outfile, |
| + | "Test_io: read_blk(%lu, %d) returned %s\n", |
| + | block, count, retval ? error_message(retval) : "OK"); |
| + | if (data->block && data->block == block) { |
| + | if (data->flags & TEST_FLAG_DUMP) |
| + | test_dump_block(channel, data, block, buf); |
| + | if (--data->read_abort_count == 0) |
| + | test_abort(channel, block); |
| + | } |
| + | return retval; |
| + | } |
| + | |
| + | static errcode_t test_write_blk(io_channel channel, unsigned long block, |
| + | int count, const void *buf) |
| + | { |
| + | struct test_private_data *data; |
| + | errcode_t retval = 0; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct test_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); |
| + | |
| + | if (data->real) |
| + | retval = io_channel_write_blk(data->real, block, count, buf); |
| + | if (data->write_blk) |
| + | data->write_blk(block, count, retval); |
| + | if (data->flags & TEST_FLAG_WRITE) |
| + | fprintf(data->outfile, |
| + | "Test_io: write_blk(%lu, %d) returned %s\n", |
| + | block, count, retval ? error_message(retval) : "OK"); |
| + | if (data->block && data->block == block) { |
| + | if (data->flags & TEST_FLAG_DUMP) |
| + | test_dump_block(channel, data, block, buf); |
| + | if (--data->write_abort_count == 0) |
| + | test_abort(channel, block); |
| + | } |
| + | return retval; |
| + | } |
| + | |
| + | static errcode_t test_write_byte(io_channel channel, unsigned long offset, |
| + | int count, const void *buf) |
| + | { |
| + | struct test_private_data *data; |
| + | errcode_t retval = 0; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct test_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); |
| + | |
| + | if (data->real && data->real->manager->write_byte) |
| + | retval = io_channel_write_byte(data->real, offset, count, buf); |
| + | if (data->write_byte) |
| + | data->write_byte(offset, count, retval); |
| + | if (data->flags & TEST_FLAG_WRITE) |
| + | fprintf(data->outfile, |
| + | "Test_io: write_byte(%lu, %d) returned %s\n", |
| + | offset, count, retval ? error_message(retval) : "OK"); |
| + | return retval; |
| + | } |
| + | |
| + | /* |
| + | * Flush data buffers to disk. |
| + | */ |
| + | static errcode_t test_flush(io_channel channel) |
| + | { |
| + | struct test_private_data *data; |
| + | errcode_t retval = 0; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct test_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); |
| + | |
| + | if (data->real) |
| + | retval = io_channel_flush(data->real); |
| + | |
| + | if (data->flags & TEST_FLAG_FLUSH) |
| + | fprintf(data->outfile, "Test_io: flush() returned %s\n", |
| + | retval ? error_message(retval) : "OK"); |
| + | |
| + | return retval; |
| + | } |
| + | |
| + | static errcode_t test_set_option(io_channel channel, const char *option, |
| + | const char *arg) |
| + | { |
| + | struct test_private_data *data; |
| + | errcode_t retval = 0; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct test_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); |
| + | |
| + | |
| + | if (data->flags & TEST_FLAG_SET_OPTION) |
| + | fprintf(data->outfile, "Test_io: set_option(%s, %s) ", |
| + | option, arg); |
| + | if (data->real && data->real->manager->set_option) { |
| + | retval = (data->real->manager->set_option)(data->real, |
| + | option, arg); |
| + | if (data->flags & TEST_FLAG_SET_OPTION) |
| + | fprintf(data->outfile, "returned %s\n", |
| + | retval ? error_message(retval) : "OK"); |
| + | } else { |
| + | if (data->flags & TEST_FLAG_SET_OPTION) |
| + | fprintf(data->outfile, "not implemented\n"); |
| + | } |
| + | return retval; |
| + | } |
| @@ -0,0 +1,707 @@ | |
| + | /* |
| + | * unix_io.c --- This is the Unix (well, really POSIX) implementation |
| + | * of the I/O manager. |
| + | * |
| + | * Implements a one-block write-through cache. |
| + | * |
| + | * Includes support for Windows NT support under Cygwin. |
| + | * |
| + | * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, |
| + | * 2002 by Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #if HAVE_ERRNO_H |
| + | #include <errno.h> |
| + | #endif |
| + | #include <fcntl.h> |
| + | #include <time.h> |
| + | #ifdef __linux__ |
| + | #include <sys/utsname.h> |
| + | #endif |
| + | #if HAVE_SYS_STAT_H |
| + | #include <sys/stat.h> |
| + | #endif |
| + | #if HAVE_SYS_TYPES_H |
| + | #include <sys/types.h> |
| + | #endif |
| + | #if HAVE_SYS_RESOURCE_H |
| + | #include <sys/resource.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | /* |
| + | * For checking structure magic numbers... |
| + | */ |
| + | |
| + | #define EXT2_CHECK_MAGIC(struct, code) \ |
| + | if ((struct)->magic != (code)) return (code) |
| + | |
| + | struct unix_cache { |
| + | char *buf; |
| + | unsigned long block; |
| + | int access_time; |
| + | unsigned dirty:1; |
| + | unsigned in_use:1; |
| + | }; |
| + | |
| + | #define CACHE_SIZE 8 |
| + | #define WRITE_DIRECT_SIZE 4 /* Must be smaller than CACHE_SIZE */ |
| + | #define READ_DIRECT_SIZE 4 /* Should be smaller than CACHE_SIZE */ |
| + | |
| + | struct unix_private_data { |
| + | int magic; |
| + | int dev; |
| + | int flags; |
| + | int access_time; |
| + | ext2_loff_t offset; |
| + | struct unix_cache cache[CACHE_SIZE]; |
| + | }; |
| + | |
| + | static errcode_t unix_open(const char *name, int flags, io_channel *channel); |
| + | static errcode_t unix_close(io_channel channel); |
| + | static errcode_t unix_set_blksize(io_channel channel, int blksize); |
| + | static errcode_t unix_read_blk(io_channel channel, unsigned long block, |
| + | int count, void *data); |
| + | static errcode_t unix_write_blk(io_channel channel, unsigned long block, |
| + | int count, const void *data); |
| + | static errcode_t unix_flush(io_channel channel); |
| + | static errcode_t unix_write_byte(io_channel channel, unsigned long offset, |
| + | int size, const void *data); |
| + | static errcode_t unix_set_option(io_channel channel, const char *option, |
| + | const char *arg); |
| + | |
| + | static void reuse_cache(io_channel channel, struct unix_private_data *data, |
| + | struct unix_cache *cache, unsigned long block); |
| + | |
| + | /* __FreeBSD_kernel__ is defined by GNU/kFreeBSD - the FreeBSD kernel |
| + | * does not know buffered block devices - everything is raw. */ |
| + | #if defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) |
| + | #define NEED_BOUNCE_BUFFER |
| + | #else |
| + | #undef NEED_BOUNCE_BUFFER |
| + | #endif |
| + | |
| + | static struct struct_io_manager struct_unix_manager = { |
| + | EXT2_ET_MAGIC_IO_MANAGER, |
| + | "Unix I/O Manager", |
| + | unix_open, |
| + | unix_close, |
| + | unix_set_blksize, |
| + | unix_read_blk, |
| + | unix_write_blk, |
| + | unix_flush, |
| + | #ifdef NEED_BOUNCE_BUFFER |
| + | 0, |
| + | #else |
| + | unix_write_byte, |
| + | #endif |
| + | unix_set_option |
| + | }; |
| + | |
| + | io_manager unix_io_manager = &struct_unix_manager; |
| + | |
| + | /* |
| + | * Here are the raw I/O functions |
| + | */ |
| + | #ifndef NEED_BOUNCE_BUFFER |
| + | static errcode_t raw_read_blk(io_channel channel, |
| + | struct unix_private_data *data, |
| + | unsigned long block, |
| + | int count, void *buf) |
| + | { |
| + | errcode_t retval; |
| + | ssize_t size; |
| + | ext2_loff_t location; |
| + | int actual = 0; |
| + | |
| + | size = (count < 0) ? -count : count * channel->block_size; |
| + | location = ((ext2_loff_t) block * channel->block_size) + data->offset; |
| + | if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { |
| + | retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; |
| + | goto error_out; |
| + | } |
| + | actual = read(data->dev, buf, size); |
| + | if (actual != size) { |
| + | if (actual < 0) |
| + | actual = 0; |
| + | retval = EXT2_ET_SHORT_READ; |
| + | goto error_out; |
| + | } |
| + | return 0; |
| + | |
| + | error_out: |
| + | memset((char *) buf+actual, 0, size-actual); |
| + | if (channel->read_error) |
| + | retval = (channel->read_error)(channel, block, count, buf, |
| + | size, actual, retval); |
| + | return retval; |
| + | } |
| + | #else /* NEED_BOUNCE_BUFFER */ |
| + | /* |
| + | * Windows and FreeBSD block devices only allow sector alignment IO in offset and size |
| + | */ |
| + | static errcode_t raw_read_blk(io_channel channel, |
| + | struct unix_private_data *data, |
| + | unsigned long block, |
| + | int count, void *buf) |
| + | { |
| + | errcode_t retval; |
| + | size_t size, alignsize, fragment; |
| + | ext2_loff_t location; |
| + | int total = 0, actual; |
| + | #define BLOCKALIGN 512 |
| + | char sector[BLOCKALIGN]; |
| + | |
| + | size = (count < 0) ? -count : count * channel->block_size; |
| + | location = ((ext2_loff_t) block * channel->block_size) + data->offset; |
| + | #ifdef DEBUG |
| + | printf("count=%d, size=%d, block=%d, blk_size=%d, location=%lx\n", |
| + | count, size, block, channel->block_size, location); |
| + | #endif |
| + | if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { |
| + | retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; |
| + | goto error_out; |
| + | } |
| + | fragment = size % BLOCKALIGN; |
| + | alignsize = size - fragment; |
| + | if (alignsize) { |
| + | actual = read(data->dev, buf, alignsize); |
| + | if (actual != alignsize) |
| + | goto short_read; |
| + | } |
| + | if (fragment) { |
| + | actual = read(data->dev, sector, BLOCKALIGN); |
| + | if (actual != BLOCKALIGN) |
| + | goto short_read; |
| + | memcpy(buf+alignsize, sector, fragment); |
| + | } |
| + | return 0; |
| + | |
| + | short_read: |
| + | if (actual>0) |
| + | total += actual; |
| + | retval = EXT2_ET_SHORT_READ; |
| + | |
| + | error_out: |
| + | memset((char *) buf+total, 0, size-actual); |
| + | if (channel->read_error) |
| + | retval = (channel->read_error)(channel, block, count, buf, |
| + | size, actual, retval); |
| + | return retval; |
| + | } |
| + | #endif |
| + | |
| + | static errcode_t raw_write_blk(io_channel channel, |
| + | struct unix_private_data *data, |
| + | unsigned long block, |
| + | int count, const void *buf) |
| + | { |
| + | ssize_t size; |
| + | ext2_loff_t location; |
| + | int actual = 0; |
| + | errcode_t retval; |
| + | |
| + | if (count == 1) |
| + | size = channel->block_size; |
| + | else { |
| + | if (count < 0) |
| + | size = -count; |
| + | else |
| + | size = count * channel->block_size; |
| + | } |
| + | |
| + | location = ((ext2_loff_t) block * channel->block_size) + data->offset; |
| + | if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { |
| + | retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; |
| + | goto error_out; |
| + | } |
| + | |
| + | actual = write(data->dev, buf, size); |
| + | if (actual != size) { |
| + | retval = EXT2_ET_SHORT_WRITE; |
| + | goto error_out; |
| + | } |
| + | return 0; |
| + | |
| + | error_out: |
| + | if (channel->write_error) |
| + | retval = (channel->write_error)(channel, block, count, buf, |
| + | size, actual, retval); |
| + | return retval; |
| + | } |
| + | |
| + | |
| + | /* |
| + | * Here we implement the cache functions |
| + | */ |
| + | |
| + | /* Allocate the cache buffers */ |
| + | static errcode_t alloc_cache(io_channel channel, |
| + | struct unix_private_data *data) |
| + | { |
| + | errcode_t retval; |
| + | struct unix_cache *cache; |
| + | int i; |
| + | |
| + | data->access_time = 0; |
| + | for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { |
| + | cache->block = 0; |
| + | cache->access_time = 0; |
| + | cache->dirty = 0; |
| + | cache->in_use = 0; |
| + | if ((retval = ext2fs_get_mem(channel->block_size, |
| + | &cache->buf))) |
| + | return retval; |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | /* Free the cache buffers */ |
| + | static void free_cache(struct unix_private_data *data) |
| + | { |
| + | struct unix_cache *cache; |
| + | int i; |
| + | |
| + | data->access_time = 0; |
| + | for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { |
| + | cache->block = 0; |
| + | cache->access_time = 0; |
| + | cache->dirty = 0; |
| + | cache->in_use = 0; |
| + | if (cache->buf) |
| + | ext2fs_free_mem(&cache->buf); |
| + | cache->buf = 0; |
| + | } |
| + | } |
| + | |
| + | #ifndef NO_IO_CACHE |
| + | /* |
| + | * Try to find a block in the cache. If the block is not found, and |
| + | * eldest is a non-zero pointer, then fill in eldest with the cache |
| + | * entry to that should be reused. |
| + | */ |
| + | static struct unix_cache *find_cached_block(struct unix_private_data *data, |
| + | unsigned long block, |
| + | struct unix_cache **eldest) |
| + | { |
| + | struct unix_cache *cache, *unused_cache, *oldest_cache; |
| + | int i; |
| + | |
| + | unused_cache = oldest_cache = 0; |
| + | for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { |
| + | if (!cache->in_use) { |
| + | if (!unused_cache) |
| + | unused_cache = cache; |
| + | continue; |
| + | } |
| + | if (cache->block == block) { |
| + | cache->access_time = ++data->access_time; |
| + | return cache; |
| + | } |
| + | if (!oldest_cache || |
| + | (cache->access_time < oldest_cache->access_time)) |
| + | oldest_cache = cache; |
| + | } |
| + | if (eldest) |
| + | *eldest = (unused_cache) ? unused_cache : oldest_cache; |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * Reuse a particular cache entry for another block. |
| + | */ |
| + | static void reuse_cache(io_channel channel, struct unix_private_data *data, |
| + | struct unix_cache *cache, unsigned long block) |
| + | { |
| + | if (cache->dirty && cache->in_use) |
| + | raw_write_blk(channel, data, cache->block, 1, cache->buf); |
| + | |
| + | cache->in_use = 1; |
| + | cache->dirty = 0; |
| + | cache->block = block; |
| + | cache->access_time = ++data->access_time; |
| + | } |
| + | |
| + | /* |
| + | * Flush all of the blocks in the cache |
| + | */ |
| + | static errcode_t flush_cached_blocks(io_channel channel, |
| + | struct unix_private_data *data, |
| + | int invalidate) |
| + | |
| + | { |
| + | struct unix_cache *cache; |
| + | errcode_t retval, retval2; |
| + | int i; |
| + | |
| + | retval2 = 0; |
| + | for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { |
| + | if (!cache->in_use) |
| + | continue; |
| + | |
| + | if (invalidate) |
| + | cache->in_use = 0; |
| + | |
| + | if (!cache->dirty) |
| + | continue; |
| + | |
| + | retval = raw_write_blk(channel, data, |
| + | cache->block, 1, cache->buf); |
| + | if (retval) |
| + | retval2 = retval; |
| + | else |
| + | cache->dirty = 0; |
| + | } |
| + | return retval2; |
| + | } |
| + | #endif /* NO_IO_CACHE */ |
| + | |
| + | static errcode_t unix_open(const char *name, int flags, io_channel *channel) |
| + | { |
| + | io_channel io = NULL; |
| + | struct unix_private_data *data = NULL; |
| + | errcode_t retval; |
| + | int open_flags; |
| + | struct stat st; |
| + | #ifdef __linux__ |
| + | struct utsname ut; |
| + | #endif |
| + | |
| + | if (name == 0) |
| + | return EXT2_ET_BAD_DEVICE_NAME; |
| + | retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); |
| + | if (retval) |
| + | return retval; |
| + | memset(io, 0, sizeof(struct struct_io_channel)); |
| + | io->magic = EXT2_ET_MAGIC_IO_CHANNEL; |
| + | retval = ext2fs_get_mem(sizeof(struct unix_private_data), &data); |
| + | if (retval) |
| + | goto cleanup; |
| + | |
| + | io->manager = unix_io_manager; |
| + | retval = ext2fs_get_mem(strlen(name)+1, &io->name); |
| + | if (retval) |
| + | goto cleanup; |
| + | |
| + | strcpy(io->name, name); |
| + | io->private_data = data; |
| + | io->block_size = 1024; |
| + | io->read_error = 0; |
| + | io->write_error = 0; |
| + | io->refcount = 1; |
| + | |
| + | memset(data, 0, sizeof(struct unix_private_data)); |
| + | data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL; |
| + | |
| + | if ((retval = alloc_cache(io, data))) |
| + | goto cleanup; |
| + | |
| + | open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY; |
| + | #ifdef CONFIG_LFS |
| + | data->dev = open64(io->name, open_flags); |
| + | #else |
| + | data->dev = open(io->name, open_flags); |
| + | #endif |
| + | if (data->dev < 0) { |
| + | retval = errno; |
| + | goto cleanup; |
| + | } |
| + | |
| + | #ifdef __linux__ |
| + | #undef RLIM_INFINITY |
| + | #if (defined(__alpha__) || ((defined(__sparc__) || defined(__mips__)) && (SIZEOF_LONG == 4))) |
| + | #define RLIM_INFINITY ((unsigned long)(~0UL>>1)) |
| + | #else |
| + | #define RLIM_INFINITY (~0UL) |
| + | #endif |
| + | /* |
| + | * Work around a bug in 2.4.10-2.4.18 kernels where writes to |
| + | * block devices are wrongly getting hit by the filesize |
| + | * limit. This workaround isn't perfect, since it won't work |
| + | * if glibc wasn't built against 2.2 header files. (Sigh.) |
| + | * |
| + | */ |
| + | if ((flags & IO_FLAG_RW) && |
| + | (uname(&ut) == 0) && |
| + | ((ut.release[0] == '2') && (ut.release[1] == '.') && |
| + | (ut.release[2] == '4') && (ut.release[3] == '.') && |
| + | (ut.release[4] == '1') && (ut.release[5] >= '0') && |
| + | (ut.release[5] < '8')) && |
| + | (fstat(data->dev, &st) == 0) && |
| + | (S_ISBLK(st.st_mode))) { |
| + | struct rlimit rlim; |
| + | |
| + | rlim.rlim_cur = rlim.rlim_max = (unsigned long) RLIM_INFINITY; |
| + | setrlimit(RLIMIT_FSIZE, &rlim); |
| + | getrlimit(RLIMIT_FSIZE, &rlim); |
| + | if (((unsigned long) rlim.rlim_cur) < |
| + | ((unsigned long) rlim.rlim_max)) { |
| + | rlim.rlim_cur = rlim.rlim_max; |
| + | setrlimit(RLIMIT_FSIZE, &rlim); |
| + | } |
| + | } |
| + | #endif |
| + | *channel = io; |
| + | return 0; |
| + | |
| + | cleanup: |
| + | if (data) { |
| + | free_cache(data); |
| + | ext2fs_free_mem(&data); |
| + | } |
| + | if (io) |
| + | ext2fs_free_mem(&io); |
| + | return retval; |
| + | } |
| + | |
| + | static errcode_t unix_close(io_channel channel) |
| + | { |
| + | struct unix_private_data *data; |
| + | errcode_t retval = 0; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct unix_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); |
| + | |
| + | if (--channel->refcount > 0) |
| + | return 0; |
| + | |
| + | #ifndef NO_IO_CACHE |
| + | retval = flush_cached_blocks(channel, data, 0); |
| + | #endif |
| + | |
| + | if (close(data->dev) < 0) |
| + | retval = errno; |
| + | free_cache(data); |
| + | |
| + | ext2fs_free_mem(&channel->private_data); |
| + | if (channel->name) |
| + | ext2fs_free_mem(&channel->name); |
| + | ext2fs_free_mem(&channel); |
| + | return retval; |
| + | } |
| + | |
| + | static errcode_t unix_set_blksize(io_channel channel, int blksize) |
| + | { |
| + | struct unix_private_data *data; |
| + | errcode_t retval; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct unix_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); |
| + | |
| + | if (channel->block_size != blksize) { |
| + | #ifndef NO_IO_CACHE |
| + | if ((retval = flush_cached_blocks(channel, data, 0))) |
| + | return retval; |
| + | #endif |
| + | |
| + | channel->block_size = blksize; |
| + | free_cache(data); |
| + | if ((retval = alloc_cache(channel, data))) |
| + | return retval; |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | static errcode_t unix_read_blk(io_channel channel, unsigned long block, |
| + | int count, void *buf) |
| + | { |
| + | struct unix_private_data *data; |
| + | struct unix_cache *cache, *reuse[READ_DIRECT_SIZE]; |
| + | errcode_t retval; |
| + | char *cp; |
| + | int i, j; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct unix_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); |
| + | |
| + | #ifdef NO_IO_CACHE |
| + | return raw_read_blk(channel, data, block, count, buf); |
| + | #else |
| + | /* |
| + | * If we're doing an odd-sized read or a very large read, |
| + | * flush out the cache and then do a direct read. |
| + | */ |
| + | if (count < 0 || count > WRITE_DIRECT_SIZE) { |
| + | if ((retval = flush_cached_blocks(channel, data, 0))) |
| + | return retval; |
| + | return raw_read_blk(channel, data, block, count, buf); |
| + | } |
| + | |
| + | cp = buf; |
| + | while (count > 0) { |
| + | /* If it's in the cache, use it! */ |
| + | if ((cache = find_cached_block(data, block, &reuse[0]))) { |
| + | #ifdef DEBUG |
| + | printf("Using cached block %d\n", block); |
| + | #endif |
| + | memcpy(cp, cache->buf, channel->block_size); |
| + | count--; |
| + | block++; |
| + | cp += channel->block_size; |
| + | continue; |
| + | } |
| + | /* |
| + | * Find the number of uncached blocks so we can do a |
| + | * single read request |
| + | */ |
| + | for (i=1; i < count; i++) |
| + | if (find_cached_block(data, block+i, &reuse[i])) |
| + | break; |
| + | #ifdef DEBUG |
| + | printf("Reading %d blocks starting at %d\n", i, block); |
| + | #endif |
| + | if ((retval = raw_read_blk(channel, data, block, i, cp))) |
| + | return retval; |
| + | |
| + | /* Save the results in the cache */ |
| + | for (j=0; j < i; j++) { |
| + | count--; |
| + | cache = reuse[j]; |
| + | reuse_cache(channel, data, cache, block++); |
| + | memcpy(cache->buf, cp, channel->block_size); |
| + | cp += channel->block_size; |
| + | } |
| + | } |
| + | return 0; |
| + | #endif /* NO_IO_CACHE */ |
| + | } |
| + | |
| + | static errcode_t unix_write_blk(io_channel channel, unsigned long block, |
| + | int count, const void *buf) |
| + | { |
| + | struct unix_private_data *data; |
| + | struct unix_cache *cache, *reuse; |
| + | errcode_t retval = 0; |
| + | const char *cp; |
| + | int writethrough; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct unix_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); |
| + | |
| + | #ifdef NO_IO_CACHE |
| + | return raw_write_blk(channel, data, block, count, buf); |
| + | #else |
| + | /* |
| + | * If we're doing an odd-sized write or a very large write, |
| + | * flush out the cache completely and then do a direct write. |
| + | */ |
| + | if (count < 0 || count > WRITE_DIRECT_SIZE) { |
| + | if ((retval = flush_cached_blocks(channel, data, 1))) |
| + | return retval; |
| + | return raw_write_blk(channel, data, block, count, buf); |
| + | } |
| + | |
| + | /* |
| + | * For a moderate-sized multi-block write, first force a write |
| + | * if we're in write-through cache mode, and then fill the |
| + | * cache with the blocks. |
| + | */ |
| + | writethrough = channel->flags & CHANNEL_FLAGS_WRITETHROUGH; |
| + | if (writethrough) |
| + | retval = raw_write_blk(channel, data, block, count, buf); |
| + | |
| + | cp = buf; |
| + | while (count > 0) { |
| + | cache = find_cached_block(data, block, &reuse); |
| + | if (!cache) { |
| + | cache = reuse; |
| + | reuse_cache(channel, data, cache, block); |
| + | } |
| + | memcpy(cache->buf, cp, channel->block_size); |
| + | cache->dirty = !writethrough; |
| + | count--; |
| + | block++; |
| + | cp += channel->block_size; |
| + | } |
| + | return retval; |
| + | #endif /* NO_IO_CACHE */ |
| + | } |
| + | |
| + | static errcode_t unix_write_byte(io_channel channel, unsigned long offset, |
| + | int size, const void *buf) |
| + | { |
| + | struct unix_private_data *data; |
| + | errcode_t retval = 0; |
| + | ssize_t actual; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct unix_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); |
| + | |
| + | #ifndef NO_IO_CACHE |
| + | /* |
| + | * Flush out the cache completely |
| + | */ |
| + | if ((retval = flush_cached_blocks(channel, data, 1))) |
| + | return retval; |
| + | #endif |
| + | |
| + | if (lseek(data->dev, offset + data->offset, SEEK_SET) < 0) |
| + | return errno; |
| + | |
| + | actual = write(data->dev, buf, size); |
| + | if (actual != size) |
| + | return EXT2_ET_SHORT_WRITE; |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | /* |
| + | * Flush data buffers to disk. |
| + | */ |
| + | static errcode_t unix_flush(io_channel channel) |
| + | { |
| + | struct unix_private_data *data; |
| + | errcode_t retval = 0; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct unix_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); |
| + | |
| + | #ifndef NO_IO_CACHE |
| + | retval = flush_cached_blocks(channel, data, 0); |
| + | #endif |
| + | fsync(data->dev); |
| + | return retval; |
| + | } |
| + | |
| + | static errcode_t unix_set_option(io_channel channel, const char *option, |
| + | const char *arg) |
| + | { |
| + | struct unix_private_data *data; |
| + | unsigned long tmp; |
| + | char *end; |
| + | |
| + | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); |
| + | data = (struct unix_private_data *) channel->private_data; |
| + | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); |
| + | |
| + | if (!strcmp(option, "offset")) { |
| + | if (!arg) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | |
| + | tmp = strtoul(arg, &end, 0); |
| + | if (*end) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | data->offset = tmp; |
| + | return 0; |
| + | } |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | } |
| @@ -0,0 +1,99 @@ | |
| + | /* |
| + | * unlink.c --- delete links in a ext2fs directory |
| + | * |
| + | * Copyright (C) 1993, 1994, 1997 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | struct link_struct { |
| + | const char *name; |
| + | int namelen; |
| + | ext2_ino_t inode; |
| + | int flags; |
| + | struct ext2_dir_entry *prev; |
| + | int done; |
| + | }; |
| + | |
| + | #ifdef __TURBOC__ |
| + | #pragma argsused |
| + | #endif |
| + | static int unlink_proc(struct ext2_dir_entry *dirent, |
| + | int offset EXT2FS_ATTR((unused)), |
| + | int blocksize EXT2FS_ATTR((unused)), |
| + | char *buf EXT2FS_ATTR((unused)), |
| + | void *priv_data) |
| + | { |
| + | struct link_struct *ls = (struct link_struct *) priv_data; |
| + | struct ext2_dir_entry *prev; |
| + | |
| + | prev = ls->prev; |
| + | ls->prev = dirent; |
| + | |
| + | if (ls->name) { |
| + | if ((dirent->name_len & 0xFF) != ls->namelen) |
| + | return 0; |
| + | if (strncmp(ls->name, dirent->name, dirent->name_len & 0xFF)) |
| + | return 0; |
| + | } |
| + | if (ls->inode) { |
| + | if (dirent->inode != ls->inode) |
| + | return 0; |
| + | } else { |
| + | if (!dirent->inode) |
| + | return 0; |
| + | } |
| + | |
| + | if (prev) |
| + | prev->rec_len += dirent->rec_len; |
| + | else |
| + | dirent->inode = 0; |
| + | ls->done++; |
| + | return DIRENT_ABORT|DIRENT_CHANGED; |
| + | } |
| + | |
| + | #ifdef __TURBOC__ |
| + | #pragma argsused |
| + | #endif |
| + | errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir, |
| + | const char *name, ext2_ino_t ino, |
| + | int flags EXT2FS_ATTR((unused))) |
| + | { |
| + | errcode_t retval; |
| + | struct link_struct ls; |
| + | |
| + | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
| + | |
| + | if (!name && !ino) |
| + | return EXT2_ET_INVALID_ARGUMENT; |
| + | |
| + | if (!(fs->flags & EXT2_FLAG_RW)) |
| + | return EXT2_ET_RO_FILSYS; |
| + | |
| + | ls.name = name; |
| + | ls.namelen = name ? strlen(name) : 0; |
| + | ls.inode = ino; |
| + | ls.flags = 0; |
| + | ls.done = 0; |
| + | ls.prev = 0; |
| + | |
| + | retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY, |
| + | 0, unlink_proc, &ls); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | return (ls.done) ? 0 : EXT2_ET_DIR_NO_SPACE; |
| + | } |
| + | |
| @@ -0,0 +1,56 @@ | |
| + | /* |
| + | * valid_blk.c --- does the inode have valid blocks? |
| + | * |
| + | * Copyright 1997 by Theodore Ts'o |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | * |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <string.h> |
| + | #include <time.h> |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | /* |
| + | * This function returns 1 if the inode's block entries actually |
| + | * contain block entries. |
| + | */ |
| + | int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode) |
| + | { |
| + | /* |
| + | * Only directories, regular files, and some symbolic links |
| + | * have valid block entries. |
| + | */ |
| + | if (!LINUX_S_ISDIR(inode->i_mode) && !LINUX_S_ISREG(inode->i_mode) && |
| + | !LINUX_S_ISLNK(inode->i_mode)) |
| + | return 0; |
| + | |
| + | /* |
| + | * If the symbolic link is a "fast symlink", then the symlink |
| + | * target is stored in the block entries. |
| + | */ |
| + | if (LINUX_S_ISLNK (inode->i_mode)) { |
| + | if (inode->i_file_acl == 0) { |
| + | /* With no EA block, we can rely on i_blocks */ |
| + | if (inode->i_blocks == 0) |
| + | return 0; |
| + | } else { |
| + | /* With an EA block, life gets more tricky */ |
| + | if (inode->i_size >= EXT2_N_BLOCKS*4) |
| + | return 1; /* definitely using i_block[] */ |
| + | if (inode->i_size > 4 && inode->i_block[1] == 0) |
| + | return 1; /* definitely using i_block[] */ |
| + | return 0; /* Probably a fast symlink */ |
| + | } |
| + | } |
| + | return 1; |
| + | } |
| @@ -0,0 +1,52 @@ | |
| + | /* |
| + | * version.c --- Return the version of the ext2 library |
| + | * |
| + | * Copyright (C) 1997 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #if HAVE_UNISTD_H |
| + | #include <unistd.h> |
| + | #endif |
| + | #include <string.h> |
| + | #include <stdio.h> |
| + | #include <ctype.h> |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | //#include "../../version.h" |
| + | |
| + | static const char *lib_version = E2FSPROGS_VERSION; |
| + | static const char *lib_date = E2FSPROGS_DATE; |
| + | |
| + | int ext2fs_parse_version_string(const char *ver_string) |
| + | { |
| + | const char *cp; |
| + | int version = 0; |
| + | |
| + | for (cp = ver_string; *cp; cp++) { |
| + | if (*cp == '.') |
| + | continue; |
| + | if (!isdigit(*cp)) |
| + | break; |
| + | version = (version * 10) + (*cp - '0'); |
| + | } |
| + | return version; |
| + | } |
| + | |
| + | |
| + | int ext2fs_get_library_version(const char **ver_string, |
| + | const char **date_string) |
| + | { |
| + | if (ver_string) |
| + | *ver_string = lib_version; |
| + | if (date_string) |
| + | *date_string = lib_date; |
| + | |
| + | return ext2fs_parse_version_string(lib_version); |
| + | } |
| @@ -0,0 +1,34 @@ | |
| + | /* |
| + | * write_bb_file.c --- write a list of bad blocks to a FILE * |
| + | * |
| + | * Copyright (C) 1994, 1995 Theodore Ts'o. |
| + | * |
| + | * %Begin-Header% |
| + | * This file may be redistributed under the terms of the GNU Public |
| + | * License. |
| + | * %End-Header% |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | |
| + | #include "ext2_fs.h" |
| + | #include "ext2fs.h" |
| + | |
| + | errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list, |
| + | unsigned int flags EXT2FS_ATTR((unused)), |
| + | FILE *f) |
| + | { |
| + | badblocks_iterate bb_iter; |
| + | blk_t blk; |
| + | errcode_t retval; |
| + | |
| + | retval = ext2fs_badblocks_list_iterate_begin(bb_list, &bb_iter); |
| + | if (retval) |
| + | return retval; |
| + | |
| + | while (ext2fs_badblocks_list_iterate(bb_iter, &blk)) { |
| + | fprintf(f, "%d\n", blk); |
| + | } |
| + | ext2fs_badblocks_list_iterate_end(bb_iter); |
| + | return 0; |
| + | } |