From: Vyacheslav Dubeyko <slava@xxxxxxxxxxx> Subject: [PATCH v2 04/15] hfsplus: implement functionality for HFSPLUS_JOURNAL_NEED_INIT flag This patch implements functionality of creation of journal header, journal buffer and initialization of them. Signed-off-by: Vyacheslav Dubeyko <slava@xxxxxxxxxxx> CC: Al Viro <viro@xxxxxxxxxxxxxxxxxx> CC: Christoph Hellwig <hch@xxxxxxxxxxxxx> CC: Hin-Tak Leung <htl10@xxxxxxxxxxxxxxxxxxxxx> --- fs/hfsplus/journal.c | 168 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 167 insertions(+), 1 deletion(-) diff --git a/fs/hfsplus/journal.c b/fs/hfsplus/journal.c index d748fd4..e086979 100644 --- a/fs/hfsplus/journal.c +++ b/fs/hfsplus/journal.c @@ -8,6 +8,23 @@ #include "hfsplus_fs.h" +#define HFSPLUS_1KB_SHIFT 10 +#define HFSPLUS_1MB_SHIFT 20 +#define HFSPLUS_1GB_SHIFT 30 +#define HFSPLUS_1TB_SHIFT 40 + +#define HFSPLUS_JOURNAL_MIN_SIZE (512 * 1024) +#define HFSPLUS_JOURNAL_DEFAULT_SIZE (8 * (1 << HFSPLUS_1MB_SHIFT)) +#define HFSPLUS_JOURNAL_MAX_SIZE (512 * (1 << HFSPLUS_1MB_SHIFT)) + +#define HFSPLUS_VOLUME_GRAIN 100 /* 100 GB*/ +#define HFSPLUS_JSCALE_MAX 64 + +#define HFSPLUS_VOLUME_MIN_THRESHOLD (128 * (1 << HFSPLUS_1MB_SHIFT)) +#define HFSPLUS_DEFAULT_JHDR_SIZE (4 * 1024) +#define HFSPLUS_BLHDR_SIZE_MIN (4 * 1024) +#define HFSPLUS_BLHDR_SIZE_MAX (16 * 1024) + #define HFSPLUS_HAS_JOURNAL(sb) \ (HFSPLUS_SB(sb)->s_vhdr->attributes & \ cpu_to_be32(HFSPLUS_VOL_JOURNALED)) @@ -25,8 +42,157 @@ ((sector_t)(blk) << \ (HFSPLUS_SB(sb)->alloc_blksz_shift - HFSPLUS_SECTOR_SHIFT)) +/* + * We want at least 8 megs of journal for each 100 gigs of + * disk space. We cap the size at 512 megs (64x default), unless + * the allocation block size is larger, in which case we use one + * allocation block. + * + * Volumes that are 128 megs or less in size have such + * a small bitmap (one 4k-block) and inherhently such + * a small btree that we can get by with a much smaller + * journal. Even in a worst case scenario of a catalog + * filled with very long korean file names we should + * never touch more than 256k of meta-data for a single + * transaction. therefore we'll make the journal 512k + * which is safe and doesn't waste much space. + */ +static u64 calc_journal_size(struct super_block *sb) +{ + u64 journal_size; + u64 jscale; + u64 volume_size = (u64)HFSPLUS_SB(sb)->sect_count * + HFSPLUS_SECTOR_SIZE; + u64 volume_grain = (u64)HFSPLUS_VOLUME_GRAIN * + ((u64)1 << HFSPLUS_1GB_SHIFT); + + jscale = volume_size / volume_grain; + + if (jscale > HFSPLUS_JSCALE_MAX) + jscale = HFSPLUS_JSCALE_MAX; + + journal_size = HFSPLUS_JOURNAL_DEFAULT_SIZE * (jscale + 1); + + if (journal_size > HFSPLUS_JOURNAL_MAX_SIZE) + journal_size = HFSPLUS_JOURNAL_MAX_SIZE; + + if (volume_size < HFSPLUS_VOLUME_MIN_THRESHOLD) + journal_size = HFSPLUS_JOURNAL_MIN_SIZE; + + return journal_size; +} + +/* + * Technical Note TN1150 + * + * Checksums can be verified as part of a basic consistency check. + * To verify the checksum, temporarily set the checksum field to zero + * and then call the calc_checksum routine with the address and size of + * the header being checksummed. The function result should equal the + * original value of the checksum field. + * + * static int + * calc_checksum(unsigned char *ptr, int len) + * { + * int i, cksum=0; + * + * for(i=0; i < len; i++, ptr++) { + * cksum = (cksum << 8) ^ (cksum + *ptr); + * } + * + * return (~cksum); + * } +*/ +static u32 calc_internal_checksum(u32 seed, unsigned char *ptr, int len) +{ + int i; + u32 chksum = seed; + + for (i = 0; i < len; i++, ptr++) + chksum = (chksum << 8) ^ (chksum + *ptr); + + return chksum; +} + +#define HFSPLUS_CHECKSUM(internal_checksum) \ + (cpu_to_le32(~(u32)(internal_checksum))) + +#define HFSPLUS_CALC_CHECKSUM(ptr, len) \ + HFSPLUS_CHECKSUM(calc_internal_checksum(0, ptr, len)) + +/* + * hfsplus_create_journal - create journal + * + * @sb: superblock + * + * Create journal header, journal buffer and initialize them. + * It is assumed that presence of journal is already verified. + */ static int hfsplus_create_journal(struct super_block *sb, - struct hfsplus_journal *jnl); + struct hfsplus_journal *jnl) +{ + u64 offset = be64_to_cpu(jnl->jib->offset); + int blksz_bits = HFSPLUS_SB(sb)->alloc_blksz_shift; + u32 blksz = HFSPLUS_SB(sb)->alloc_blksz; + u64 journal_size = be64_to_cpu(jnl->jib->size); + u64 calculated_journal_size; + + hfs_dbg(JOURNAL, "create HFS+ journal: blocksize %u, jib_hdr->offset %llu\n", + blksz, offset); + + if (((offset >> blksz_bits) << blksz_bits) != offset) { + pr_err("journal size is not aligned\n"); + return -EIO; + } + + if (journal_size < HFSPLUS_JOURNAL_MIN_SIZE || + journal_size > HFSPLUS_JOURNAL_MAX_SIZE) { + pr_err("journal size %llu looks bogus\n", journal_size); + return -EIO; + } + + if ((journal_size % blksz) != 0) { + pr_err("journal size %llu is not an even multiple of block size %u\n", + journal_size, blksz); + return -EIO; + } + + calculated_journal_size = calc_journal_size(sb); + if (journal_size != calculated_journal_size) { + pr_warn("journal size %llu is different from calculated journal size %llu\n", + journal_size, calculated_journal_size); + } + + jnl->jh->magic = cpu_to_le32(HFSPLUS_JOURNAL_HEADER_MAGIC); + jnl->jh->endian = cpu_to_le32(HFSPLUS_JOURNAL_HEADER_ENDIAN); + + /* + * The jhdr_size is a 512 byte for volumes are less than 1TB. + * Otherwise, jhdr_size is 4096 bytes for 1+TB volumes. + */ + if (HFSPLUS_SB(sb)->sect_count > + (((u64)1 << HFSPLUS_1TB_SHIFT) >> HFSPLUS_SECTOR_SHIFT)) + jnl->jh->jhdr_size = cpu_to_le32(HFSPLUS_DEFAULT_JHDR_SIZE); + else + jnl->jh->jhdr_size = cpu_to_le32(HFSPLUS_SECTOR_SIZE); + + if (blksz < HFSPLUS_BLHDR_SIZE_MIN) + jnl->jh->blhdr_size = cpu_to_le32(HFSPLUS_BLHDR_SIZE_MIN); + else if (blksz > HFSPLUS_BLHDR_SIZE_MAX) + jnl->jh->blhdr_size = cpu_to_le32(HFSPLUS_BLHDR_SIZE_MAX); + else + jnl->jh->blhdr_size = cpu_to_le32(sb->s_blocksize); + + jnl->jh->start = cpu_to_le64(JHDR_SIZE(jnl->jh)); + jnl->jh->end = cpu_to_le64(JHDR_SIZE(jnl->jh)); + jnl->jh->size = cpu_to_le64(journal_size); + + jnl->jh->checksum = 0; + jnl->jh->checksum = HFSPLUS_CALC_CHECKSUM((unsigned char *)(jnl->jh), + sizeof(*(jnl->jh))); + + return 0; +} /* * hfsplus_init_journal - initialize journal object -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html