/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * mkjournal.c * * Journal creation for the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 021110-1307, USA. * * Portions of the code from e2fsprogs/lib/ext2fs/mkjournal.c * Copyright (C) 2000 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include #include #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" size_t ocfs2_journal_tag_bytes(journal_superblock_t *jsb) { if (JBD2_HAS_INCOMPAT_FEATURE(jsb, JBD2_FEATURE_INCOMPAT_64BIT)) return JBD2_TAG_SIZE64; else return JBD2_TAG_SIZE32; } uint64_t ocfs2_journal_tag_block(journal_block_tag_t *tag, size_t tag_bytes) { uint64_t blockno = be32_to_cpu(tag->t_blocknr); if (tag_bytes > JBD2_TAG_SIZE32) blockno |= (uint64_t)be32_to_cpu(tag->t_blocknr_high) << 32; return blockno; } /* Returns true if we support these journal features */ static int ocfs2_journal_check_available_features(journal_superblock_t *jsb, ocfs2_fs_options *features) { /* ocfs2 has never supported a superblock other than _V2 */ if (jsb->s_header.h_blocktype != JBD2_SUPERBLOCK_V2) return 0; if ((features->opt_compat & JBD2_KNOWN_COMPAT_FEATURES) != features->opt_compat) return 0; /* * ocfs2 will never use jbd2 checksums. If a jbd2 checksum fails, * the transaction is not replayed. This means that some parts of * the transaction may have checkpointed while other parts have not. * This inconsistent state requires fsck to repair. ocfs2, being * clustered, can't tolerate that situation. Instead, ocfs2 * prefers to replay the journal. This allows the cluster to * remain running. Using the METAECC feature allows ocfs2 to * detect and isolate any corrupted blocks. */ if (features->opt_compat & JBD2_FEATURE_COMPAT_CHECKSUM) return 0; if ((features->opt_ro_compat & JBD2_KNOWN_ROCOMPAT_FEATURES) != features->opt_ro_compat) return 0; if ((features->opt_incompat & JBD2_KNOWN_INCOMPAT_FEATURES) != features->opt_incompat) return 0; return 1; } errcode_t ocfs2_journal_set_features(journal_superblock_t *jsb, ocfs2_fs_options *features) { if (!ocfs2_journal_check_available_features(jsb, features)) return OCFS2_ET_UNSUPP_FEATURE; jsb->s_feature_compat |= features->opt_compat; jsb->s_feature_ro_compat |= features->opt_ro_compat; jsb->s_feature_incompat |= features->opt_incompat; return 0; } errcode_t ocfs2_journal_clear_features(journal_superblock_t *jsb, ocfs2_fs_options *features) { if (!ocfs2_journal_check_available_features(jsb, features)) return OCFS2_ET_UNSUPP_FEATURE; jsb->s_feature_compat &= ~(features->opt_compat); jsb->s_feature_ro_compat &= ~(features->opt_ro_compat); jsb->s_feature_incompat &= ~(features->opt_incompat); return 0; } void ocfs2_swap_journal_superblock(journal_superblock_t *jsb) { if (cpu_is_big_endian) return; jsb->s_header.h_magic = bswap_32(jsb->s_header.h_magic); jsb->s_header.h_blocktype = bswap_32(jsb->s_header.h_blocktype); jsb->s_header.h_sequence = bswap_32(jsb->s_header.h_sequence); jsb->s_blocksize = bswap_32(jsb->s_blocksize); jsb->s_maxlen = bswap_32(jsb->s_maxlen); jsb->s_first = bswap_32(jsb->s_first); jsb->s_sequence = bswap_32(jsb->s_sequence); jsb->s_start = bswap_32(jsb->s_start); jsb->s_errno = bswap_32(jsb->s_errno); jsb->s_feature_compat = bswap_32(jsb->s_feature_compat); jsb->s_feature_incompat = bswap_32(jsb->s_feature_incompat); jsb->s_feature_ro_compat = bswap_32(jsb->s_feature_ro_compat); jsb->s_nr_users = bswap_32(jsb->s_nr_users); jsb->s_dynsuper = bswap_32(jsb->s_dynsuper); jsb->s_max_transaction = bswap_32(jsb->s_max_transaction); jsb->s_max_trans_data = bswap_32(jsb->s_max_trans_data); } errcode_t ocfs2_init_journal_superblock(ocfs2_filesys *fs, char *buf, int buflen, uint32_t jrnl_size_in_blks) { journal_superblock_t *jsb = (journal_superblock_t *)buf; if (buflen < fs->fs_blocksize) return OCFS2_ET_INTERNAL_FAILURE; if (jrnl_size_in_blks < 1024) return OCFS2_ET_JOURNAL_TOO_SMALL; memset(buf, 0, buflen); jsb->s_header.h_magic = JBD2_MAGIC_NUMBER; jsb->s_header.h_blocktype = JBD2_SUPERBLOCK_V2; jsb->s_blocksize = fs->fs_blocksize; jsb->s_maxlen = jrnl_size_in_blks; if (fs->fs_blocksize == 512) jsb->s_first = 2; else jsb->s_first = 1; jsb->s_start = 1; jsb->s_sequence = 1; jsb->s_errno = 0; jsb->s_nr_users = 1; memcpy(jsb->s_uuid, OCFS2_RAW_SB(fs->fs_super)->s_uuid, sizeof(jsb->s_uuid)); return 0; } /* * This function automatically sets up the journal superblock and * returns it as an allocated block. */ static errcode_t ocfs2_create_journal_superblock(ocfs2_filesys *fs, uint32_t size, ocfs2_fs_options *features, char **ret_jsb) { errcode_t retval; char *buf = NULL; journal_superblock_t *jsb; *ret_jsb = NULL; if ((retval = ocfs2_malloc_block(fs->fs_io, &buf))) goto bail; retval = ocfs2_init_journal_superblock(fs, buf, fs->fs_blocksize, size); if (retval) goto bail; jsb = (journal_superblock_t *)buf; retval = ocfs2_journal_set_features(jsb, features); if (retval) goto bail; #if 0 /* Someday */ /* * 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 = 3; else jsb->s_first = 2; } #endif *ret_jsb = buf; bail: if (retval && buf) ocfs2_free(&buf); return retval; } errcode_t ocfs2_read_journal_superblock(ocfs2_filesys *fs, uint64_t blkno, char *jsb_buf) { errcode_t ret; char *blk; journal_superblock_t *disk, *jsb; if ((blkno < OCFS2_SUPER_BLOCK_BLKNO) || (blkno > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) return ret; ret = ocfs2_read_blocks(fs, blkno, 1, blk); if (ret) goto out; disk = (journal_superblock_t *)blk; jsb = (journal_superblock_t *)jsb_buf; if (disk->s_header.h_magic != htonl(JBD2_MAGIC_NUMBER)) { ret = OCFS2_ET_BAD_JOURNAL_SUPERBLOCK_MAGIC; goto out; } memcpy(jsb_buf, blk, fs->fs_blocksize); ocfs2_swap_journal_superblock(jsb); if (JBD2_HAS_INCOMPAT_FEATURE(jsb, ~JBD2_KNOWN_INCOMPAT_FEATURES)) { ret = OCFS2_ET_UNSUPP_FEATURE; goto out; } if (JBD2_HAS_RO_COMPAT_FEATURE(jsb, ~JBD2_KNOWN_ROCOMPAT_FEATURES)) { ret = OCFS2_ET_RO_UNSUPP_FEATURE; goto out; } ret = 0; out: ocfs2_free(&blk); return ret; } errcode_t ocfs2_write_journal_superblock(ocfs2_filesys *fs, uint64_t blkno, char *jsb_buf) { errcode_t ret; char *blk; journal_superblock_t *disk, *jsb; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; if ((blkno < OCFS2_SUPER_BLOCK_BLKNO) || (blkno > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) return ret; disk = (journal_superblock_t *)blk; jsb = (journal_superblock_t *)jsb_buf; memcpy(blk, jsb_buf, fs->fs_blocksize); ocfs2_swap_journal_superblock(disk); ret = io_write_block(fs->fs_io, blkno, 1, blk); if (ret) goto out; fs->fs_flags |= OCFS2_FLAG_CHANGED; ret = 0; out: ocfs2_free(&blk); return ret; } static errcode_t ocfs2_format_journal(ocfs2_filesys *fs, ocfs2_cached_inode *ci, ocfs2_fs_options *features) { errcode_t ret = 0; char *buf = NULL, *jsb_buf = NULL; int bs_bits = OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; uint64_t offset = 0; uint32_t wrote, count, jrnl_blocks; #define BUFLEN 1048576 ret = ocfs2_malloc_blocks(fs->fs_io, (BUFLEN >> bs_bits), &buf); if (ret) goto out; memset(buf, 0, BUFLEN); io_set_nocache(fs->fs_io, true); count = (uint32_t) ci->ci_inode->i_size; while (count) { ret = ocfs2_file_write(ci, buf, ocfs2_min((uint32_t) BUFLEN, count), offset, &wrote); if (ret) break; offset += wrote; count -= wrote; } io_set_nocache(fs->fs_io, false); if (ret) goto out; jrnl_blocks = ocfs2_clusters_to_blocks(fs, ci->ci_inode->i_clusters); ret = ocfs2_create_journal_superblock(fs, jrnl_blocks, features, &jsb_buf); if (ret) goto out; /* re-use offset here for 1st journal block. */ ret = ocfs2_extent_map_get_blocks(ci, 0, 1, &offset, NULL, NULL); if (ret) goto out; ret = ocfs2_write_journal_superblock(fs, offset, jsb_buf); out: if (buf) ocfs2_free(&buf); if (jsb_buf) ocfs2_free(&jsb_buf); return ret; } errcode_t ocfs2_make_journal(ocfs2_filesys *fs, uint64_t blkno, uint32_t clusters, ocfs2_fs_options *features) { errcode_t ret = 0; ocfs2_cached_inode *ci = NULL; struct ocfs2_dinode *di; if ((clusters << OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits) < OCFS2_MIN_JOURNAL_SIZE) { ret = OCFS2_ET_JOURNAL_TOO_SMALL; goto out; } ret = ocfs2_read_cached_inode(fs, blkno, &ci); if (ret) goto out; /* verify it is a journal file */ if (!(ci->ci_inode->i_flags & OCFS2_VALID_FL) || !(ci->ci_inode->i_flags & OCFS2_SYSTEM_FL) || !(ci->ci_inode->i_flags & OCFS2_JOURNAL_FL)) { ret = OCFS2_ET_INTERNAL_FAILURE; goto out; } di = ci->ci_inode; if (clusters > di->i_clusters) { ret = ocfs2_extend_allocation(fs, blkno, (clusters - di->i_clusters)); if (ret) goto out; /* We don't cache in the library right now, so any * work done in extend_allocation won't be reflected * in our now stale copy. */ ocfs2_free_cached_inode(fs, ci); ret = ocfs2_read_cached_inode(fs, blkno, &ci); if (ret) { ci = NULL; goto out; } di = ci->ci_inode; di->i_size = di->i_clusters << OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits; di->i_mtime = time(NULL); ret = ocfs2_write_inode(fs, blkno, (char *)di); if (ret) goto out; } else if (clusters < di->i_clusters) { uint64_t new_size = clusters << OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits; ret = ocfs2_truncate(fs, blkno, new_size); if (ret) goto out; ocfs2_free_cached_inode(fs, ci); ret = ocfs2_read_cached_inode(fs, blkno, &ci); if (ret) { ci = NULL; goto out; } } ret = ocfs2_format_journal(fs, ci, features); out: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; } #ifdef DEBUG_EXE #if 0 static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } static void print_usage(void) { fprintf(stderr, "Usage: mkjournal \n"); } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; uint64_t blkno; char *filename, *buf; ocfs2_filesys *fs; ocfs2_dinode *di; blkno = OCFS2_SUPER_BLOCK_BLKNO; initialize_ocfs_error_table(); if (argc < 2) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[1]; if (argc > 2) { blkno = read_number(argv[2]); if (blkno < OCFS2_SUPER_BLOCK_BLKNO) { fprintf(stderr, "Invalid blockno: %s\n", blkno); print_usage(); return 1; } } ret = ocfs2_open(filename, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { com_err(argv[0], ret, "while allocating inode buffer"); goto out_close; } di = (ocfs2_dinode *)buf; ret = ocfs2_read_inode(fs, blkno, di); if (ret) { com_err(argv[0], ret, "while reading inode %llu", blkno); goto out_free; } fprintf(stdout, "OCFS2 inode %llu on \"%s\"\n", blkno, filename); out_free: ocfs2_free(&buf); out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; } #endif #include int main(int argc, char *argv[]) { fprintf(stdout, "Does nothing for now\n"); return 0; } #endif /* DEBUG_EXE */