/* * orphan.c --- utility function to handle orphan file * * Copyright (C) 2015 Jan Kara. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include "config.h" #include #include "ext2_fs.h" #include "ext2fsP.h" errcode_t ext2fs_truncate_orphan_file(ext2_filsys fs) { struct ext2_inode inode; errcode_t err; ext2_ino_t ino = fs->super->s_orphan_file_inum; err = ext2fs_read_inode(fs, ino, &inode); if (err) return err; err = ext2fs_punch(fs, ino, &inode, NULL, 0, ~0ULL); if (err) return err; fs->flags &= ~EXT2_FLAG_SUPER_ONLY; memset(&inode, 0, sizeof(struct ext2_inode)); err = ext2fs_write_inode(fs, ino, &inode); ext2fs_clear_feature_orphan_file(fs->super); ext2fs_clear_feature_orphan_present(fs->super); ext2fs_mark_super_dirty(fs); /* Need to update group descriptors as well */ fs->flags &= ~EXT2_FLAG_SUPER_ONLY; return err; } __u32 ext2fs_do_orphan_file_block_csum(ext2_filsys fs, ext2_ino_t ino, __u32 gen, blk64_t blk, char *buf) { int inodes_per_ob = ext2fs_inodes_per_orphan_block(fs); __u32 crc; ino = ext2fs_cpu_to_le32(ino); gen = ext2fs_cpu_to_le32(gen); blk = ext2fs_cpu_to_le64(blk); crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&ino, sizeof(ino)); crc = ext2fs_crc32c_le(crc, (unsigned char *)&gen, sizeof(gen)); crc = ext2fs_crc32c_le(crc, (unsigned char *)&blk, sizeof(blk)); crc = ext2fs_crc32c_le(crc, (unsigned char *)buf, inodes_per_ob * sizeof(__u32)); return ext2fs_cpu_to_le32(crc); } struct mkorphan_info { char *buf; char *zerobuf; blk_t num_blocks; blk_t alloc_blocks; blk64_t last_blk; errcode_t err; ext2_ino_t ino; __u32 generation; }; static int mkorphan_proc(ext2_filsys fs, blk64_t *blocknr, e2_blkcnt_t blockcnt, blk64_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct mkorphan_info *oi = (struct mkorphan_info *)priv_data; blk64_t new_blk; errcode_t err; /* Can we just continue in currently allocated cluster? */ if (blockcnt && EXT2FS_B2C(fs, oi->last_blk) == EXT2FS_B2C(fs, oi->last_blk + 1)) { new_blk = oi->last_blk + 1; } else { err = ext2fs_new_block2(fs, oi->last_blk, 0, &new_blk); if (err) { oi->err = err; return BLOCK_ABORT; } ext2fs_block_alloc_stats2(fs, new_blk, +1); oi->alloc_blocks++; } if (blockcnt >= 0) { if (ext2fs_has_feature_metadata_csum(fs->super)) { struct ext4_orphan_block_tail *tail; tail = ext2fs_orphan_block_tail(fs, oi->buf); tail->ob_checksum = ext2fs_do_orphan_file_block_csum(fs, oi->ino, oi->generation, new_blk, oi->buf); } err = io_channel_write_blk64(fs->io, new_blk, 1, oi->buf); } else /* zerobuf is used to initialize new indirect blocks... */ err = io_channel_write_blk64(fs->io, new_blk, 1, oi->zerobuf); if (err) { oi->err = err; return BLOCK_ABORT; } oi->last_blk = new_blk; *blocknr = new_blk; if (blockcnt >= 0 && --oi->num_blocks == 0) return BLOCK_CHANGED | BLOCK_ABORT; return BLOCK_CHANGED; } errcode_t ext2fs_create_orphan_file(ext2_filsys fs, blk_t num_blocks) { struct ext2_inode inode; ext2_ino_t ino = fs->super->s_orphan_file_inum; errcode_t err; char *buf = NULL, *zerobuf = NULL; struct mkorphan_info oi; struct ext4_orphan_block_tail *ob_tail; if (!ino) { err = ext2fs_new_inode(fs, EXT2_ROOT_INO, LINUX_S_IFREG | 0600, 0, &ino); if (err) return err; ext2fs_inode_alloc_stats2(fs, ino, +1, 0); ext2fs_mark_ib_dirty(fs); } err = ext2fs_read_inode(fs, ino, &inode); if (err) return err; if (EXT2_I_SIZE(&inode)) { err = ext2fs_truncate_orphan_file(fs); if (err) return err; } memset(&inode, 0, sizeof(struct ext2_inode)); if (ext2fs_has_feature_extents(fs->super)) { inode.i_flags |= EXT4_EXTENTS_FL; err = ext2fs_write_inode(fs, ino, &inode); if (err) return err; } err = ext2fs_get_mem(fs->blocksize, &buf); if (err) return err; err = ext2fs_get_mem(fs->blocksize, &zerobuf); if (err) goto out; memset(buf, 0, fs->blocksize); memset(zerobuf, 0, fs->blocksize); ob_tail = ext2fs_orphan_block_tail(fs, buf); ob_tail->ob_magic = ext2fs_cpu_to_le32(EXT4_ORPHAN_BLOCK_MAGIC); oi.num_blocks = num_blocks; oi.alloc_blocks = 0; oi.last_blk = 0; oi.generation = inode.i_generation; oi.ino = ino; oi.buf = buf; oi.zerobuf = zerobuf; oi.err = 0; err = ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_APPEND, 0, mkorphan_proc, &oi); if (err) goto out; if (oi.err) { err = oi.err; goto out; } /* Reread inode after blocks were allocated */ err = ext2fs_read_inode(fs, ino, &inode); if (err) goto out; ext2fs_iblk_set(fs, &inode, 0); inode.i_atime = inode.i_mtime = inode.i_ctime = fs->now ? fs->now : time(0); inode.i_links_count = 1; inode.i_mode = LINUX_S_IFREG | 0600; ext2fs_iblk_add_blocks(fs, &inode, oi.alloc_blocks); err = ext2fs_inode_size_set(fs, &inode, (unsigned long long)fs->blocksize * num_blocks); if (err) goto out; err = ext2fs_write_new_inode(fs, ino, &inode); if (err) goto out; fs->super->s_orphan_file_inum = ino; ext2fs_set_feature_orphan_file(fs->super); ext2fs_mark_super_dirty(fs); /* Need to update group descriptors as well */ fs->flags &= ~EXT2_FLAG_SUPER_ONLY; out: if (buf) ext2fs_free_mem(&buf); if (zerobuf) ext2fs_free_mem(&zerobuf); return err; } /* * Find reasonable size for orphan file. We choose orphan file size to be * between 32 and 512 filesystem blocks and not more than 1/4096 of the * filesystem unless it is really small. */ e2_blkcnt_t ext2fs_default_orphan_file_blocks(ext2_filsys fs) { __u64 num_blocks = ext2fs_blocks_count(fs->super); e2_blkcnt_t blks = 512; if (num_blocks < 128 * 1024) blks = 32; else if (num_blocks < 2 * 1024 * 1024) blks = num_blocks / 4096; return (blks + EXT2FS_CLUSTER_MASK(fs)) & ~EXT2FS_CLUSTER_MASK(fs); } static errcode_t ext2fs_orphan_file_block_csum(ext2_filsys fs, ext2_ino_t ino, blk64_t blk, char *buf, __u32 *crcp) { struct ext2_inode inode; errcode_t retval; retval = ext2fs_read_inode(fs, ino, &inode); if (retval) return retval; *crcp = ext2fs_do_orphan_file_block_csum(fs, ino, inode.i_generation, blk, buf); return 0; } errcode_t ext2fs_orphan_file_block_csum_set(ext2_filsys fs, ext2_ino_t ino, blk64_t blk, char *buf) { struct ext4_orphan_block_tail *tail; if (!ext2fs_has_feature_metadata_csum(fs->super)) return 0; tail = ext2fs_orphan_block_tail(fs, buf); return ext2fs_orphan_file_block_csum(fs, ino, blk, buf, &tail->ob_checksum); } int ext2fs_orphan_file_block_csum_verify(ext2_filsys fs, ext2_ino_t ino, blk64_t blk, char *buf) { struct ext4_orphan_block_tail *tail; __u32 crc; errcode_t retval; if (!ext2fs_has_feature_metadata_csum(fs->super)) return 1; retval = ext2fs_orphan_file_block_csum(fs, ino, blk, buf, &crc); if (retval) return 0; tail = ext2fs_orphan_block_tail(fs, buf); return ext2fs_le32_to_cpu(tail->ob_checksum) == crc; }