/* 32 bytes in various csum fields */
 #define BTRFS_CSUM_SIZE 32
+
+/* csum types */
+#define BTRFS_CSUM_TYPE_CRC32  0
+
+static int btrfs_csum_sizes[] = { 4, 0 };
+
 /* four bytes for CRC32 */
-#define BTRFS_CRC32_SIZE 4
+//#define BTRFS_CRC32_SIZE 4
 #define BTRFS_EMPTY_DIR_SIZE 0
 
 #define BTRFS_FT_UNKNOWN       0
        __le64 compat_flags;
        __le64 compat_ro_flags;
        __le64 incompat_flags;
+       __le16 csum_type;
        u8 root_level;
        u8 chunk_root_level;
        u8 log_root_level;
                         last_snapshot, 64);
 
 /* struct btrfs_super_block */
+
 BTRFS_SETGET_STACK_FUNCS(super_bytenr, struct btrfs_super_block, bytenr, 64);
 BTRFS_SETGET_STACK_FUNCS(super_flags, struct btrfs_super_block, flags, 64);
 BTRFS_SETGET_STACK_FUNCS(super_generation, struct btrfs_super_block,
                         compat_flags, 64);
 BTRFS_SETGET_STACK_FUNCS(super_incompat_flags, struct btrfs_super_block,
                         incompat_flags, 64);
+BTRFS_SETGET_STACK_FUNCS(super_csum_type, struct btrfs_super_block,
+                        csum_type, 16);
+
+static inline int btrfs_super_csum_size(struct btrfs_super_block *s)
+{
+       int t = btrfs_super_csum_type(s);
+       BUG_ON(t >= ARRAY_SIZE(btrfs_csum_sizes));
+       return btrfs_csum_sizes[t];
+}
 
 static inline unsigned long btrfs_leaf_data(struct extent_buffer *l)
 {
 
 static int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf,
                           int verify)
 {
-       char result[BTRFS_CRC32_SIZE];
+       u16 csum_size =
+               btrfs_super_csum_size(&root->fs_info->super_copy);
+       char *result = NULL;
        unsigned long len;
        unsigned long cur_len;
        unsigned long offset = BTRFS_CSUM_SIZE;
        unsigned long map_len;
        int err;
        u32 crc = ~(u32)0;
+       unsigned long inline_result;
 
        len = buf->len - offset;
        while(len > 0) {
                offset += cur_len;
                unmap_extent_buffer(buf, map_token, KM_USER0);
        }
+       if (csum_size > sizeof(inline_result)) {
+               result = kzalloc(csum_size * sizeof(char), GFP_NOFS);
+               if (!result)
+                       return 1;
+       } else {
+               result = (char *)&inline_result;
+       }
+
        btrfs_csum_final(crc, result);
 
        if (verify) {
                /* FIXME, this is not good */
-               if (memcmp_extent_buffer(buf, result, 0, BTRFS_CRC32_SIZE)) {
+               if (memcmp_extent_buffer(buf, result, 0, csum_size)) {
                        u32 val;
                        u32 found = 0;
-                       memcpy(&found, result, BTRFS_CRC32_SIZE);
+                       memcpy(&found, result, csum_size);
 
-                       read_extent_buffer(buf, &val, 0, BTRFS_CRC32_SIZE);
+                       read_extent_buffer(buf, &val, 0, csum_size);
                        printk("btrfs: %s checksum verify failed on %llu "
                               "wanted %X found %X level %d\n",
                               root->fs_info->sb->s_id,
                               buf->start, val, found, btrfs_header_level(buf));
+                       if (result != (char *)&inline_result)
+                               kfree(result);
                        return 1;
                }
        } else {
-               write_extent_buffer(buf, result, 0, BTRFS_CRC32_SIZE);
+               write_extent_buffer(buf, result, 0, csum_size);
        }
+       if (result != (char *)&inline_result)
+               kfree(result);
        return 0;
 }
 
 
 #include "transaction.h"
 #include "print-tree.h"
 
-#define MAX_CSUM_ITEMS(r) ((((BTRFS_LEAF_DATA_SIZE(r) - \
-                              sizeof(struct btrfs_item) * 2) / \
-                              BTRFS_CRC32_SIZE) - 1))
+#define MAX_CSUM_ITEMS(r,size) ((((BTRFS_LEAF_DATA_SIZE(r) - \
+                                  sizeof(struct btrfs_item) * 2) / \
+                                 size) - 1))
 int btrfs_insert_file_extent(struct btrfs_trans_handle *trans,
                             struct btrfs_root *root,
                             u64 objectid, u64 pos,
        struct btrfs_csum_item *item;
        struct extent_buffer *leaf;
        u64 csum_offset = 0;
+       u16 csum_size =
+               btrfs_super_csum_size(&root->fs_info->super_copy);
        int csums_in_item;
 
        file_key.objectid = objectid;
                csum_offset = (offset - found_key.offset) >>
                                root->fs_info->sb->s_blocksize_bits;
                csums_in_item = btrfs_item_size_nr(leaf, path->slots[0]);
-               csums_in_item /= BTRFS_CRC32_SIZE;
+               csums_in_item /= csum_size;
 
                if (csum_offset >= csums_in_item) {
                        ret = -EFBIG;
        }
        item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item);
        item = (struct btrfs_csum_item *)((unsigned char *)item +
-                                         csum_offset * BTRFS_CRC32_SIZE);
+                                         csum_offset * csum_size);
        return item;
 fail:
        if (ret > 0)
        u64 item_start_offset = 0;
        u64 item_last_offset = 0;
        u32 diff;
+       u16 csum_size =
+               btrfs_super_csum_size(&root->fs_info->super_copy);
        int ret;
        struct btrfs_path *path;
        struct btrfs_csum_item *item = NULL;
                        item_size = btrfs_item_size_nr(path->nodes[0],
                                                       path->slots[0]);
                        item_last_offset = item_start_offset +
-                               (item_size / BTRFS_CRC32_SIZE) *
+                               (item_size / csum_size) *
                                root->sectorsize;
                        item = btrfs_item_ptr(path->nodes[0], path->slots[0],
                                              struct btrfs_csum_item);
                 */
                diff = offset - item_start_offset;
                diff = diff / root->sectorsize;
-               diff = diff * BTRFS_CRC32_SIZE;
+               diff = diff * csum_size;
 
                read_extent_buffer(path->nodes[0], &sum,
                                   ((unsigned long)item) + diff,
-                                  BTRFS_CRC32_SIZE);
+                                  csum_size);
 found:
                set_state_private(io_tree, offset, sum);
                bio_index++;
        char *eb_token;
        unsigned long map_len;
        unsigned long map_start;
+       u16 csum_size =
+               btrfs_super_csum_size(&root->fs_info->super_copy);
 
        path = btrfs_alloc_path();
        BUG_ON(!path);
                /* we found one, but it isn't big enough yet */
                leaf = path->nodes[0];
                item_size = btrfs_item_size_nr(leaf, path->slots[0]);
-               if ((item_size / BTRFS_CRC32_SIZE) >= MAX_CSUM_ITEMS(root)) {
+               if ((item_size / csum_size) >=
+                   MAX_CSUM_ITEMS(root, csum_size)) {
                        /* already at max size, make a new one */
                        goto insert;
                }
         */
        btrfs_release_path(root, path);
        ret = btrfs_search_slot(trans, root, &file_key, path,
-                               BTRFS_CRC32_SIZE, 1);
+                               csum_size, 1);
        if (ret < 0)
                goto fail_unlock;
        if (ret == 0) {
                        root->fs_info->sb->s_blocksize_bits;
        if (btrfs_key_type(&found_key) != BTRFS_CSUM_ITEM_KEY ||
            found_key.objectid != objectid ||
-           csum_offset >= MAX_CSUM_ITEMS(root)) {
+           csum_offset >= MAX_CSUM_ITEMS(root, csum_size)) {
                goto insert;
        }
        if (csum_offset >= btrfs_item_size_nr(leaf, path->slots[0]) /
-           BTRFS_CRC32_SIZE) {
-               u32 diff = (csum_offset + 1) * BTRFS_CRC32_SIZE;
+           csum_size) {
+               u32 diff = (csum_offset + 1) * csum_size;
                diff = diff - btrfs_item_size_nr(leaf, path->slots[0]);
-               if (diff != BTRFS_CRC32_SIZE)
+               if (diff != csum_size)
                        goto insert;
                ret = btrfs_extend_item(trans, root, path, diff);
                BUG_ON(ret);
                tmp -= offset & ~((u64)root->sectorsize -1);
                tmp >>= root->fs_info->sb->s_blocksize_bits;
                tmp = max((u64)1, tmp);
-               tmp = min(tmp, (u64)MAX_CSUM_ITEMS(root));
-               ins_size = BTRFS_CRC32_SIZE * tmp;
+               tmp = min(tmp, (u64)MAX_CSUM_ITEMS(root, csum_size));
+               ins_size = csum_size * tmp;
        } else {
-               ins_size = BTRFS_CRC32_SIZE;
+               ins_size = csum_size;
        }
        ret = btrfs_insert_empty_item(trans, root, path, &file_key,
                                      ins_size);
        item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item);
        ret = 0;
        item = (struct btrfs_csum_item *)((unsigned char *)item +
-                                         csum_offset * BTRFS_CRC32_SIZE);
+                                         csum_offset * csum_size);
 found:
        item_end = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item);
        item_end = (struct btrfs_csum_item *)((unsigned char *)item_end +
 next_sector:
 
        if (!eb_token ||
-          (unsigned long)item  + BTRFS_CRC32_SIZE >= map_start + map_len) {
+          (unsigned long)item + csum_size >= map_start + map_len) {
                int err;
 
                if (eb_token)
                        unmap_extent_buffer(leaf, eb_token, KM_USER1);
                eb_token = NULL;
                err = map_private_extent_buffer(leaf, (unsigned long)item,
-                                               BTRFS_CRC32_SIZE,
+                                               csum_size,
                                                &eb_token, &eb_map,
                                                &map_start, &map_len, KM_USER1);
                if (err)
        }
        if (eb_token) {
                memcpy(eb_token + ((unsigned long)item & (PAGE_CACHE_SIZE - 1)),
-                      §or_sum->sum, BTRFS_CRC32_SIZE);
+                      §or_sum->sum, csum_size);
        } else {
                write_extent_buffer(leaf, §or_sum->sum,
-                                   (unsigned long)item, BTRFS_CRC32_SIZE);
+                                   (unsigned long)item, csum_size);
        }
 
        total_bytes += root->sectorsize;
        sector_sum++;
        if (total_bytes < sums->len) {
                item = (struct btrfs_csum_item *)((char *)item +
-                                                 BTRFS_CRC32_SIZE);
+                                                 csum_size);
                if (item < item_end && offset + PAGE_CACHE_SIZE ==
                    sector_sum->offset) {
                            offset = sector_sum->offset;
        new_item_span = isize - key.offset;
        blocks = (new_item_span + root->sectorsize - 1) >>
                root->fs_info->sb->s_blocksize_bits;
-       new_item_size = blocks * BTRFS_CRC32_SIZE;
+       new_item_size = blocks *
+               btrfs_super_csum_size(&root->fs_info->super_copy);
        if (new_item_size >= btrfs_item_size_nr(leaf, slot))
                return 0;
        ret = btrfs_truncate_item(trans, root, path, new_item_size, 1);