* [PATCH v4 04/15] hfsplus: implement functionality for HFSPLUS_JOURNAL_NEED_INIT flag
@ 2014-02-23 15:32 Vyacheslav Dubeyko
0 siblings, 0 replies; only message in thread
From: Vyacheslav Dubeyko @ 2014-02-23 15:32 UTC (permalink / raw)
To: Linux FS devel list
Cc: Al Viro, ChristophHellwig, Hin-Tak Leung, Andrew Morton
From: Vyacheslav Dubeyko <slava@dubeyko.com>
Subject: [PATCH v4 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@dubeyko.com>
CC: Al Viro <viro@zeniv.linux.org.uk>
CC: Christoph Hellwig <hch@infradead.org>
CC: Hin-Tak Leung <htl10@users.sourceforge.net>
---
fs/hfsplus/journal.c | 192 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 190 insertions(+), 2 deletions(-)
diff --git a/fs/hfsplus/journal.c b/fs/hfsplus/journal.c
index 927a762..f0a0815 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 JNL_SWAP16(jnl, value) \
(IS_BE_JOURNAL(jnl) ? swab16(value) : value)
#define JNL_SWAP32(jnl, value) \
@@ -45,6 +62,9 @@
((sector_t)(blk) << \
(HFSPLUS_SB(sb)->alloc_blksz_shift - HFSPLUS_SECTOR_SHIFT))
+#define JHDR_SIZE(jnl) \
+ (le32_to_cpu(JNL_SWAP32(jnl, HFSPLUS_JH(jnl)->jhdr_size)))
+
static inline
u8 hfsplus_check_journal_endianness(struct hfsplus_journal_header *jh)
{
@@ -56,11 +76,179 @@ u8 hfsplus_check_journal_endianness(struct hfsplus_journal_header *jh)
return HFSPLUS_JOURNAL_CORRUPTED;
}
+/*
+ * 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;
+ sector_t sect_count;
+ sector_t secs_in_1gb;
+ sector_t volume_grain;
+ u64 volume_size;
+
+ sect_count = HFSPLUS_SB(sb)->sect_count;
+ secs_in_1gb = ((sector_t)1 << HFSPLUS_1GB_SHIFT) >>
+ HFSPLUS_SECTOR_SHIFT;
+ volume_grain = secs_in_1gb * HFSPLUS_VOLUME_GRAIN;
+
+ jscale = div64_ul(sect_count, 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;
+
+ volume_size = sect_count * HFSPLUS_SECTOR_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)
{
- /* TODO: implement */
- return -EINVAL;
+ 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);
+ u32 journal_size_rem = 0;
+ u64 calculated_journal_size;
+ __le32 calc_cksum;
+
+ 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;
+ }
+
+ div_u64_rem(journal_size, blksz, &journal_size_rem);
+ if (journal_size_rem != 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);
+ }
+
+ if (jnl->endianness == HFSPLUS_JOURNAL_CORRUPTED)
+ jnl->endianness = HFSPLUS_LE_JOURNAL;
+
+ jnl->jh->magic =
+ JNL_SWAP32(jnl, cpu_to_le32(HFSPLUS_JOURNAL_HEADER_MAGIC));
+ jnl->jh->endian =
+ JNL_SWAP32(jnl, 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 =
+ JNL_SWAP32(jnl, cpu_to_le32(HFSPLUS_DEFAULT_JHDR_SIZE));
+ } else {
+ jnl->jh->jhdr_size =
+ JNL_SWAP32(jnl, cpu_to_le32(HFSPLUS_SECTOR_SIZE));
+ }
+
+ if (blksz < HFSPLUS_BLHDR_SIZE_MIN) {
+ jnl->jh->blhdr_size =
+ JNL_SWAP32(jnl, cpu_to_le32(HFSPLUS_BLHDR_SIZE_MIN));
+ } else if (blksz > HFSPLUS_BLHDR_SIZE_MAX) {
+ jnl->jh->blhdr_size =
+ JNL_SWAP32(jnl, cpu_to_le32(HFSPLUS_BLHDR_SIZE_MAX));
+ } else {
+ jnl->jh->blhdr_size =
+ JNL_SWAP32(jnl, cpu_to_le32(sb->s_blocksize));
+ }
+
+ jnl->jh->start = JNL_SWAP64(jnl, cpu_to_le64(JHDR_SIZE(jnl)));
+ jnl->jh->end = JNL_SWAP64(jnl, cpu_to_le64(JHDR_SIZE(jnl)));
+ jnl->jh->size = JNL_SWAP64(jnl, cpu_to_le64(journal_size));
+
+ jnl->jh->checksum = 0;
+ calc_cksum = HFSPLUS_CALC_CHECKSUM((unsigned char *)(jnl->jh),
+ sizeof(*(jnl->jh)));
+ jnl->jh->checksum = JNL_SWAP32(jnl, calc_cksum);
+
+ return 0;
}
/*
--
1.7.9.5
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2014-02-23 15:32 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-02-23 15:32 [PATCH v4 04/15] hfsplus: implement functionality for HFSPLUS_JOURNAL_NEED_INIT flag Vyacheslav Dubeyko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).