ocfs2-tools-ecc.patch
Index: Config.make.in =================================================================== --- Config.make.in (revision 1204) +++ Config.make.in (revision 1283) @@ -51,6 +51,7 @@ LDFLAGS = @LDFLAGS@ COM_ERR_CFLAGS = @COM_ERR_CFLAGS@ COM_ERR_LIBS = @COM_ERR_LIBS@ UUID_LIBS = @UUID_LIBS@ +ZLIB_LIBS = @ZLIB_LIBS@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_LIBS = @GLIB_LIBS@ Index: libocfs2/newdir.c =================================================================== --- libocfs2/newdir.c (revision 1204) +++ libocfs2/newdir.c (revision 1283) @@ -43,6 +43,7 @@ errcode_t ocfs2_new_dir_block(ocfs2_file uint64_t parent_ino, char **block) { struct ocfs2_dir_entry *dir; + struct ocfs2_dir_check_entry *dc; errcode_t ret; char *buf; @@ -55,6 +56,14 @@ errcode_t ocfs2_new_dir_block(ocfs2_file dir = (struct ocfs2_dir_entry *) buf; dir->rec_len = fs->fs_blocksize; + if (OCFS2_HAS_INCOMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super), + OCFS2_FEATURE_INCOMPAT_BLOCK_CHECK)) { + dc = (struct ocfs2_dir_check_entry *)dir; + memset(dc, 0, sizeof(struct ocfs2_dir_check_entry)); + dc->rec_len = OCFS2_DIR_REC_LEN(sizeof(struct ocfs2_block_check)); + dir = (struct ocfs2_dir_entry *) (((char *)dc) + dc->rec_len); + } + if (dir_ino) { /* * Set up entry for '.' @@ -68,9 +77,9 @@ errcode_t ocfs2_new_dir_block(ocfs2_file /* * Set up entry for '..' */ - dir = (struct ocfs2_dir_entry *) (buf + dir->rec_len); + dir = (struct ocfs2_dir_entry *) (((char *)dir) + dir->rec_len); dir->inode = parent_ino; - dir->rec_len = fs->fs_blocksize - OCFS2_DIR_REC_LEN(1); + dir->rec_len = fs->fs_blocksize - (buf - (char *)dir); dir->name_len = 2; dir->file_type = OCFS2_FT_DIR; dir->name[0] = '.'; Index: libocfs2/Makefile =================================================================== --- libocfs2/Makefile (revision 1204) +++ libocfs2/Makefile (revision 1283) @@ -46,6 +46,7 @@ CFILES = \ alloc.c \ bitmap.c \ bitops.c \ + blockcheck.c \ cached_inode.c \ chain.c \ chainalloc.c \ @@ -85,6 +86,7 @@ CFILES = \ HFILES = \ include/bitmap.h \ include/bitops.h \ + include/blockcheck.h \ include/byteorder.h \ include/dir_iterate.h \ include/dir_util.h \ Index: libocfs2/inode.c =================================================================== --- libocfs2/inode.c (revision 1204) +++ libocfs2/inode.c (revision 1283) @@ -32,6 +32,7 @@ #include <inttypes.h> #include "ocfs2.h" +#include "blockcheck.h" errcode_t ocfs2_check_directory(ocfs2_filesys *fs, uint64_t dir) @@ -234,6 +235,11 @@ errcode_t ocfs2_read_inode(ocfs2_filesys strlen(OCFS2_INODE_SIGNATURE))) goto out; + /* XXX: Someone tell me if I've got the endianess of this right */ + ret = ocfs2_block_check_validate(fs, di, &di->i_check); + if (ret) + goto out; /* XXX: Should we change EIO to a specific et? */ + memcpy(inode_buf, blk, fs->fs_blocksize); di = (struct ocfs2_dinode *) inode_buf; @@ -269,6 +275,7 @@ errcode_t ocfs2_write_inode(ocfs2_filesy di = (struct ocfs2_dinode *)blk; ocfs2_swap_inode_from_cpu(di); + ocfs2_block_check_compute(fs, di, &di->i_check); ret = io_write_block(fs->fs_io, blkno, 1, blk); if (ret) goto out; Index: libocfs2/include/ocfs2.h =================================================================== --- libocfs2/include/ocfs2.h (revision 1204) +++ libocfs2/include/ocfs2.h (revision 1283) @@ -48,6 +48,7 @@ #include "byteorder.h" +#define OCFS2_SB(sb) (sb) #if OCFS2_FLAT_INCLUDES #include "o2dlm.h" Index: libocfs2/include/ocfs2_fs.h =================================================================== --- libocfs2/include/ocfs2_fs.h (revision 1204) +++ libocfs2/include/ocfs2_fs.h (revision 1283) @@ -85,9 +85,8 @@ #define OCFS2_CLEAR_INCOMPAT_FEATURE(sb,mask) \ OCFS2_SB(sb)->s_feature_incompat &= ~(mask) -#define OCFS2_FEATURE_COMPAT_SUPP 0 -#define OCFS2_FEATURE_INCOMPAT_SUPP 0 -#define OCFS2_FEATURE_RO_COMPAT_SUPP 0 +/* 802.3 crc32 + ECC */ +#define OCFS2_FEATURE_INCOMPAT_BLOCK_CHECK 0x0001 /* * Heartbeat-only devices are missing journals and other files. The @@ -97,6 +96,11 @@ #define OCFS2_FEATURE_INCOMPAT_HEARTBEAT_DEV 0x0002 +#define OCFS2_FEATURE_COMPAT_SUPP 0 +#define OCFS2_FEATURE_INCOMPAT_SUPP (OCFS2_FEATURE_INCOMPAT_BLOCK_CHECK) +#define OCFS2_FEATURE_RO_COMPAT_SUPP 0 + + /* * Flags on ocfs2_dinode.i_flags */ @@ -234,6 +238,21 @@ static unsigned char ocfs2_type_by_mode[ #define OCFS2_RAW_SB(dinode) (&((dinode)->id2.i_super)) /* + * Metadata block check structure. If OCFS2_FEATURE_INCOMPAT_BLOCK_CHECK + * is not set, it is all zeros. + */ +struct ocfs2_block_check { +/*00*/ __le32 bc_crc32e; /* 802.3 Ethernet II CRC32 */ + __le16 bc_ecc; /* Single-error-correction parity vector. + This is a simple Hamming code dependant + on the blocksize. OCFS2's maximum + blocksize, 4K, requires 16 parity bits, + so we fit in __le16. */ + __le16 bc_reserved1; +/*08*/ +}; + +/* * On disk extent record for OCFS2 * It describes a range of clusters on disk. */ @@ -245,14 +264,16 @@ struct ocfs2_extent_rec { }; struct ocfs2_chain_rec { - __le32 c_free; /* Number of free bits in this chain. */ +/*00*/ __le32 c_free; /* Number of free bits in this chain. */ __le32 c_total; /* Number of total bits in this chain */ __le64 c_blkno; /* Physical disk offset (blocks) of 1st group */ +/*10*/ }; struct ocfs2_truncate_rec { - __le32 t_start; /* 1st cluster in this log */ +/*00*/ __le32 t_start; /* 1st cluster in this log */ __le32 t_clusters; /* Number of total clusters covered */ +/*08*/ }; /* @@ -306,14 +327,14 @@ struct ocfs2_truncate_log { struct ocfs2_extent_block { /*00*/ __u8 h_signature[8]; /* Signature for verification */ - __le64 h_reserved1; + struct ocfs2_block_check h_check; /* Error checking */ /*10*/ __le16 h_suballoc_slot; /* Slot suballocator this extent_header belongs to */ __le16 h_suballoc_bit; /* Bit offset in suballocator block group */ __le32 h_fs_generation; /* Must match super block */ __le64 h_blkno; /* Offset on disk, in blocks */ -/*20*/ __le64 h_reserved3; +/*20*/ __le64 h_reserved1; __le64 h_next_leaf_blk; /* Offset on disk, in blocks, of next leaf header pointing to data */ @@ -380,7 +401,7 @@ struct ocfs2_dinode { belongs to */ __le16 i_suballoc_bit; /* Bit offset in suballocator block group */ -/*10*/ __le32 i_reserved0; +/*10*/ __le32 i_reserved1; __le32 i_clusters; /* Cluster count */ __le32 i_uid; /* Owner UID */ __le32 i_gid; /* Owning GID */ @@ -399,7 +420,8 @@ struct ocfs2_dinode { __le32 i_atime_nsec; __le32 i_ctime_nsec; __le32 i_mtime_nsec; -/*70*/ __le64 i_reserved1[9]; + struct ocfs2_block_check i_check; /* Error checking */ +/*70*/ __le64 i_reserved2[8]; /*B8*/ union { __le64 i_pad1; /* Generic way to refer to this 64bit union */ @@ -444,6 +466,22 @@ struct ocfs2_dir_entry { } __attribute__ ((packed)); /* + * Filesystems with OCFS2_FEATURE_INCOMPAT_BLOCK_CHECK enabled will + * have this entry as the first part of each dirblock, except the very first + * dirblock, where it follows '.' and '..'. + */ +struct ocfs2_dir_check_entry { +/*00*/ __le64 inode; + __le16 rec_len; + __u8 name_len; + __u8 file_type; + __le32 reserved1; +/*10*/ struct ocfs2_block_check check; +/*18*/ +/* Actual on-disk length specified by rec_len, but it will always be 0x18 */ +}; + +/* * On disk allocator group structure for OCFS2 */ struct ocfs2_group_desc @@ -462,8 +500,10 @@ struct ocfs2_group_desc /*20*/ __le64 bg_parent_dinode; /* dinode which owns me, in blocks */ __le64 bg_blkno; /* Offset on disk, in blocks */ -/*30*/ __le64 bg_reserved2[2]; +/*30*/ struct ocfs2_block_check bg_check; /* Error checking */ + __le64 bg_reserved2; /*40*/ __u8 bg_bitmap[0]; +/* Actual on-disk length is one block */ }; #ifdef __KERNEL__ Index: libocfs2/include/blockcheck.h =================================================================== --- libocfs2/include/blockcheck.h (revision 0) +++ libocfs2/include/blockcheck.h (revision 1283) @@ -0,0 +1,41 @@ +/* -*- mode: c; c-basic-offset: 8; -*- + * vim: noexpandtab sw=8 ts=8 sts=0: + * + * blockcheck.h + * + * Checksum and ECC codes 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. + * + * Authors: Joel Becker + */ + +#ifndef _BLOCKCHECK_H +#define _BLOCKCHECK_H + +extern void ocfs2_hamming_encode(unsigned char *data, unsigned int d, + uint32_t *parity); +extern void ocfs2_hamming_fix(unsigned char *data, unsigned int d, + unsigned int fix); +extern uint32_t crc32_le(uint32_t crc, unsigned char const *p, size_t len); +extern void ocfs2_block_check_compute(ocfs2_filesys *fs, + void *data, + struct ocfs2_block_check *bc); +extern errcode_t ocfs2_block_check_validate(ocfs2_filesys *fs, + void *data, + struct ocfs2_block_check *bc); +#endif Index: libocfs2/chain.c =================================================================== --- libocfs2/chain.c (revision 1204) +++ libocfs2/chain.c (revision 1283) @@ -28,6 +28,7 @@ #include <string.h> #include "ocfs2.h" +#include "blockcheck.h" void ocfs2_swap_group_desc(struct ocfs2_group_desc *gd) { @@ -70,9 +71,14 @@ errcode_t ocfs2_read_group_desc(ocfs2_fi strlen(OCFS2_GROUP_DESC_SIGNATURE))) goto out; + ret = ocfs2_block_check_validate(fs, gd, &gd->bg_check); + if (ret) + goto out; + memcpy(gd_buf, blk, fs->fs_blocksize); gd = (struct ocfs2_group_desc *)gd_buf; + ocfs2_swap_group_desc(gd); ret = 0; @@ -105,6 +111,7 @@ errcode_t ocfs2_write_group_desc(ocfs2_f gd = (struct ocfs2_group_desc *)blk; ocfs2_swap_group_desc(gd); + ocfs2_block_check_compute(fs, gd, &gd->bg_check); ret = io_write_block(fs->fs_io, blkno, 1, blk); if (ret) goto out; Index: libocfs2/blockcheck.c =================================================================== --- libocfs2/blockcheck.c (revision 0) +++ libocfs2/blockcheck.c (revision 1283) @@ -0,0 +1,308 @@ +/* -*- mode: c; c-basic-offset: 8; -*- + * vim: noexpandtab sw=8 ts=8 sts=0: + * + * blockcheck.c + * + * Checksum and ECC codes for the OCFS2 userspace library. + * + * Copyright (C) 2006 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. + * + * The 802.3 CRC32 algorithm is copied from the Linux kernel, lib/crc32.c. + * Code was from the public domain, is now GPL, so no real copyright + * attribution other than "The Linux Kernel". XXX: better text, anyone? + */ + +#define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ +#define _LARGEFILE64_SOURCE + +#include <inttypes.h> + +#include "ocfs2.h" + +#include "bitops.h" +#include "blockcheck.h" + + + +static inline unsigned int hc_hweight32(unsigned int w) +{ + unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); + res = (res & 0x33333333) + ((res >> 2) & 0x33333333); + res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); + res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); + return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); +} + +/* + * We use the following conventions: + * + * d = # data bits + * p = # parity bits + * c = # total code bits (d + p) + */ +static int calc_parity_bits(unsigned int d) +{ + unsigned int p; + + /* + * Bits required for Single Error Correction is as follows: + * + * d + p + 1 <= 2^p + * + * We're restricting ourselves to 31 bits of parity, that should be + * sufficient. + */ + for (p = 1; p < 32; p++) + { + if ((d + p + 1) <= (1 << p)) + return p; + } + + return 0; +} + +/* + * Calculate the bit offset in the hamming code buffer based on the bit's + * offset in the data buffer. Since the hamming code reserves all + * power-of-two bits for parity, the data bit number and the code bit + * number are offest by all the parity bits beforehand. + * + * Recall that bit numbers in hamming code are 1-based. This function + * takes the 0-based data bit from the caller. + * + * An example. Take bit 1 of the data buffer. 1 is a power of two (2^0), + * so it's a parity bit. 2 is a power of two (2^1), so it's a parity bit. + * 3 is not a power of two. So bit 1 of the data buffer ends up as bit 3 + * in the code buffer. + */ +static unsigned int calc_code_bit(unsigned int i) +{ + unsigned int b, p; + + /* + * Data bits are 0-based, but we're talking code bits, which + * are 1-based. + */ + b = i + 1; + + /* + * For every power of two below our bit number, bump our bit. + * + * We compare with (b + 1) becuase we have to compare with what b + * would be _if_ it were bumped up by the parity bit. Capice? + */ + for (p = 0; (1 << p) < (b + 1); p++) + b++; + + return b; +} + +/* XXX: Not endian safe? */ +void ocfs2_hamming_encode(unsigned char *data, unsigned int d, + uint32_t *parity) +{ + unsigned int p = calc_parity_bits(d); + unsigned int i, j, b; + unsigned int plist[p]; /* XXX: We use a simple array to avoid ugly + bitops on *parity. Perhaps this makes us + slower? */ + + if (!p) + abort(); + + *parity = 0; + memset(plist, 0, sizeof(unsigned int) * p); + + /* + * b is the hamming code bit number. Hamming code specifies a + * 1-based array, but C uses 0-based. So 'i' is for C, and 'b' is + * for the algorithm. + * + * The i++ in the for loop is so that the start offset passed + * to ocfs2_find_next_bit_set() is one greater than the previously + * found bit. + */ + for (i = 0; (i = ocfs2_find_next_bit_set(data, d, i)) < d; i++) + { + + b = calc_code_bit(i); + + for (j = 0; j < p; j++) + { + /* + * Data bits in the resultant code are checked by + * parity bits that are part of the bit number + * representation. Huh? + * + * <wikipedia href="http://en.wikipedia.org/wiki/Hamming_code"> + * In other words, the parity bit at position 2^k + * checks bits in positions having bit k set in + * their binary representation. Conversely, for + * instance, bit 13, i.e. 1101(2), is checked by + * bits 1000(2) = 8, 0100(2)=4 and 0001(2) = 1. + * </wikipedia> + * + * Note that 'k' is the _code_ bit number. 'b' in + * our loop. + */ + if (b & (1 << j)) + plist[j] ^= 1; + } + } + + /* Now fill *parity */ + for (j = 0; j < p; j++) + if (plist[j]) + ocfs2_set_bit(j, parity); +} + +void ocfs2_hamming_fix(unsigned char *data, unsigned int d, + unsigned int fix) +{ + unsigned int p = calc_parity_bits(d); + unsigned int i, b; + + if (!p) + abort(); + + /* + * If the bit to fix has an hweight of 1, it's a parity bit. One + * busted parity bit is its own error. Nothing to do here. + */ + if (hc_hweight32(fix) == 1) + return; + + /* See hamming_encode() for a description of 'b' */ + for (i = 0, b = 1; i < d; i++, b++) + { + /* Skip past parity bits */ + while (hc_hweight32(b) == 1) + b++; + + if (b == fix) + { + if (ocfs2_test_bit(i, data)) + ocfs2_clear_bit(i, data); + else + ocfs2_set_bit(i, data); + break; + } + } +} + +/* + * table-less crc32_le() stolen from the kernel. The kernel's slowest + * version :-) + * + * RFC 3385 shows that the 802.3 crc32 (this one) has the same properties + * and probabilities as crc32c (which iSCSI uses) for data blocks < 2^16 + * bits. We fit. + */ + +/* + * There are multiple 16-bit CRC polynomials in common use, but this is + * *the* standard CRC-32 polynomial, first popularized by Ethernet. + * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0 + */ +#define CRCPOLY_LE 0xedb88320 + +/** + * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32 + * @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for + * other uses, or the previous crc32 value if computing incrementally. + * @p - pointer to buffer over which CRC is run + * @len - length of buffer @p + * + */ +uint32_t crc32_le(uint32_t crc, unsigned char const *p, size_t len) +{ + int i; + while (len--) { + crc ^= *p++; + for (i = 0; i < 8; i++) + crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); + } + return crc; +} + +/* + * This function generates check information for a block. + * data is the block to be checked. bc is a pointer to the + * ocfs2_block_check structure describing the crc32 and the ecc. + * + * bc should be a pointer inside data, as the function will + * take care of zeroing it before calculating the check information. If + * bc does not point inside data, the caller must make sure any inline + * ocfs2_block_check structures are zeroed. + */ +void ocfs2_block_check_compute(ocfs2_filesys *fs, void *data, + struct ocfs2_block_check *bc) +{ + uint32_t crc, ecc; + + /* XXX: Caller has already swapped the inode, so this needs + * endian fixup */ + if (!OCFS2_HAS_INCOMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super), + OCFS2_FEATURE_INCOMPAT_BLOCK_CHECK)) + return; + + memset(bc, 0, sizeof(struct ocfs2_block_check)); + + /* XXX: I think crc32_le() might need bitreverse() */ + crc = crc32_le(~0, data, fs->fs_blocksize); + ocfs2_hamming_encode(data, fs->fs_blocksize, &ecc); + + bc->bc_crc32e = crc; + bc->bc_ecc = (uint16_t)ecc; /* We know it's max 16 bits */ +} + +/* + * This function validates existing check information. Like _compute, + * the function will take care of zeroing bc before calculating check codes. + * If bc is not a pointer inside data, the caller must have zeroed any + * inline ocfs2_block_check structures. + */ +errcode_t ocfs2_block_check_validate(ocfs2_filesys *fs, void *data, + struct ocfs2_block_check *bc) +{ + struct ocfs2_block_check check = *bc; + uint32_t crc, ecc; + + /* XXX: Caller has already swapped the inode, so this needs + * endian fixup */ + if (!OCFS2_HAS_INCOMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super), + OCFS2_FEATURE_INCOMPAT_BLOCK_CHECK)) + return 0; + + memset(bc, 0, sizeof(struct ocfs2_block_check)); + + /* Fast path - if the crc32 validates, we're good to go */ + crc = crc32_le(~0, data, fs->fs_blocksize); + if (crc == check.bc_crc32e) + return 0; + + /* Ok, try ECC fixups */ + ocfs2_hamming_encode(data, fs->fs_blocksize, &ecc); + ocfs2_hamming_fix(data, fs->fs_blocksize, ecc ^ check.bc_ecc); + + /* And check the crc32 again */ + crc = crc32_le(~0, data, fs->fs_blocksize); + if (crc == check.bc_crc32e) + return 0; + + return OCFS2_ET_IO; +} Index: libocfs2/dirblock.c =================================================================== --- libocfs2/dirblock.c (revision 1204) +++ libocfs2/dirblock.c (revision 1283) @@ -31,6 +31,7 @@ #include <string.h> #include "ocfs2.h" +#include "blockcheck.h" static void ocfs2_swap_dir_entry(struct ocfs2_dir_entry *dirent) { @@ -87,11 +88,17 @@ errcode_t ocfs2_read_dir_block(ocfs2_fil void *buf) { errcode_t retval; + struct ocfs2_dir_check_entry *dc; retval = io_read_block(fs->fs_io, block, 1, buf); if (retval) return retval; + dc = (struct ocfs2_dir_check_entry *)buf; + retval = ocfs2_block_check_validate(fs, buf, &dc->check); + if (retval) + return retval; + return ocfs2_swap_dir_entries_to_cpu(buf, fs->fs_blocksize); } @@ -100,6 +107,7 @@ errcode_t ocfs2_write_dir_block(ocfs2_fi { errcode_t retval; char *buf = NULL; + struct ocfs2_dir_check_entry *dc; retval = ocfs2_malloc_block(fs->fs_io, &buf); if (retval) @@ -111,6 +119,8 @@ errcode_t ocfs2_write_dir_block(ocfs2_fi if (retval) goto out; + dc = (struct ocfs2_dir_check_entry *) buf; + ocfs2_block_check_compute(fs, buf, &dc->check); retval = io_write_block(fs->fs_io, block, 1, buf); out: ocfs2_free(&buf); Index: libocfs2/extents.c =================================================================== --- libocfs2/extents.c (revision 1204) +++ libocfs2/extents.c (revision 1283) @@ -33,6 +33,7 @@ #include <inttypes.h> #include "ocfs2.h" +#include "blockcheck.h" static void ocfs2_swap_extent_list_primary(struct ocfs2_extent_list *el) { @@ -126,9 +127,16 @@ errcode_t ocfs2_read_extent_block_nochec goto out; } + /* XXX: fsck seems to expect all extent block corruption to be + * ignored, so let's ask zach what to do here */ + ret = ocfs2_block_check_validate(fs, eb, &eb->h_check); + if (ret) + goto out; + memcpy(eb_buf, blk, fs->fs_blocksize); eb = (struct ocfs2_extent_block *) eb_buf; + ocfs2_swap_extent_block_to_cpu(eb); out: @@ -175,6 +183,7 @@ errcode_t ocfs2_write_extent_block(ocfs2 eb = (struct ocfs2_extent_block *) blk; ocfs2_swap_extent_block_from_cpu(eb); + ocfs2_block_check_compute(fs, eb, &eb->h_check); ret = io_write_block(fs->fs_io, blkno, 1, blk); if (ret) goto out; Index: extras/hamming.c =================================================================== --- extras/hamming.c (revision 0) +++ extras/hamming.c (revision 1283) @@ -0,0 +1,214 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <inttypes.h> + +/* Adler32 test */ +#include <zlib.h> + +#include "ocfs2.h" + +#include "bitops.h" +#include "blockcheck.h" + + +static int fill_buf(unsigned char *data, int blocksize) +{ + int fd; + ssize_t ret; + + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) + { + fprintf(stderr, "Unable to open\n"); + return 1; + } + + ret = read(fd, data, blocksize); + close(fd); + if (ret != blocksize) + { + fprintf(stderr, "short read\n"); + return 1; + } + + memset(data + 0xC0, 0, blocksize - 0xC0); + + return 0; +} + +/* So time(1) can tell us we're slow! */ +static int time_encode(unsigned char *data, int blocksize, int count) +{ + int i; + unsigned int d = blocksize * 8; + uint32_t parity; + + for (i = 0; i < count; i++) + ocfs2_hamming_encode(data, d, &parity); + + return 0; +} + +static int time_cpy(unsigned char *data, int blocksize, int count) +{ + int i, j; + char *dest; + + dest = malloc(blocksize); + if (!dest) + return 1; + + for (i = 0; i < count; i++) + /* + * This is purposely inefficient - memcpy(3) is way fast + * ... later ... and still, way fast ... + */ + for (j = 0; j < blocksize; j++) + dest[j] = data[j]; + + free(dest); + + return 0; +} + +static int time_crc32(unsigned char *data, int blocksize, int count) +{ + int i; + unsigned long crc_val; + + for (i = 0; i < count; i++) + { +#if 0 /* zlib crc32 */ + crc_val = crc32(0L, Z_NULL, 0); + crc_val = crc32(crc_val, (unsigned char *)data, blocksize); +#endif + crc_val = crc32_le(~0, data, blocksize); + } + + return 0; +} + +#ifdef HAVE_ZLIB +static int time_adler32(unsigned char *data, int blocksize, int count) +{ + int i; + unsigned long adler_val; + + for (i = 0; i < count; i++) + { + adler_val = adler32(0L, Z_NULL, 0); + adler_val = adler32(adler_val, data, blocksize); + } + + return 0; +} +#endif + + +static int test_buf(unsigned char *data, int blocksize) +{ + int i, rc = 1; + unsigned int d = blocksize * 8; + unsigned char *broken; + uint32_t parity, parity_broken; + + ocfs2_hamming_encode(data, d, &parity); + + broken = malloc(blocksize); + if (!broken) + goto out; + + for (i = 0; i < d; i++) { + fprintf(stdout, "Testing bit %d... ", i); + + memcpy(broken, data, blocksize); + if (ocfs2_test_bit(i, broken)) + ocfs2_clear_bit(i, broken); + else + ocfs2_set_bit(i, broken); + + /* The twiddled bit should break things */ + if (!memcmp(data, broken, blocksize)) + { + fprintf(stdout, "Failed\n"); + fprintf(stderr, "Breaking didn't!\n"); + goto out_free; + } + + ocfs2_hamming_encode(broken, d, &parity_broken); + ocfs2_hamming_fix(broken, d, parity ^ parity_broken); + + /* Now it should work */ + if (memcmp(data, broken, blocksize)) + { + fprintf(stdout, "Failed\n"); + fprintf(stderr, "Fix didn't!\n"); + goto out_free; + } + + fprintf(stdout, "Pass\n"); + } + + rc = 0; + +out_free: + free(broken); + +out: + return rc; +} + + +int main(int argc, char *argv[]) +{ + int rc = 1; + int blocksize = 0; + unsigned char *data; + + if (argc < 2) + goto out; + + blocksize = atoi(argv[1]); + + data = malloc(blocksize); + if (!data) + goto out; + + if (fill_buf(data, blocksize)) + goto out_free; + + if (argc < 3 || !strcmp(argv[2], "test")) + if (test_buf(data, blocksize)) + goto out_free; + + if (argc < 4) + goto out_free; + + if (!strcmp(argv[2], "encode")) + time_encode(data, blocksize, atoi(argv[3])); + else if (!strcmp(argv[2], "cpy")) + time_cpy(data, blocksize, atoi(argv[3])); +#ifdef HAVE_ZLIB + else if (!strcmp(argv[2], "adler32")) + time_adler32(data, blocksize, atoi(argv[3])); +#endif + else if (!strcmp(argv[2], "crc32")) + time_crc32(data, blocksize, atoi(argv[3])); + else + goto out_free; + + + rc = 0; + +out_free: + free(data); + +out: + return rc; +} Index: extras/Makefile =================================================================== --- extras/Makefile (revision 1204) +++ extras/Makefile (revision 1283) @@ -11,7 +11,7 @@ endif CFLAGS = $(OPTS) $(WARNINGS) -UNINST_PROGRAMS = find_hardlinks find_dup_extents find_inode_paths set_random_bits decode_lockres encode_lockres mark_journal_dirty +UNINST_PROGRAMS = find_hardlinks find_dup_extents find_inode_paths set_random_bits decode_lockres encode_lockres mark_journal_dirty hamming INCLUDES = -I../libocfs2/include -I$(TOPDIR)/libo2dlm/include -I$(TOPDIR)/libo2cb/include @@ -28,8 +28,13 @@ SET_RANDOM_BITS_CFILES = set_random_bits DECODE_LOCKRES_CFILES = decode_lockres.c ENCODE_LOCKRES_CFILES = encode_lockres.c MARK_JOURNAL_DIRTY_CFILES = mark_journal_dirty.c +HAMMING_CFILES = hamming.c -DIST_FILES = $(FIND_HARDLINKS_CFILES) $(FIND_DUP_EXTENTS_CFILES) $(FIND_INODE_PATHS_CFILES) $(SET_RANDOM_BITS_CFILES) $(DECODE_LOCKRES_CFILES) $(ENCODE_LOCKRES_CFILES) $(MARK_JOURNAL_DIRTY_CFILES) +ifneq ($(ZLIB_LIBS),) + hamming_CFLAGS = -DHAVE_ZLIB +endif + +DIST_FILES = $(FIND_HARDLINKS_CFILES) $(FIND_DUP_EXTENTS_CFILES) $(FIND_INODE_PATHS_CFILES) $(SET_RANDOM_BITS_CFILES) $(DECODE_LOCKRES_CFILES) $(ENCODE_LOCKRES_CFILES) $(MARK_JOURNAL_DIRTY_CFILES) $(HAMMING_CFILES) FIND_HARDLINKS_OBJS = $(subst .c,.o,$(FIND_HARDLINKS_CFILES)) FIND_DUP_EXTENTS_OBJS = $(subst .c,.o,$(FIND_DUP_EXTENTS_CFILES)) @@ -38,6 +43,7 @@ SET_RANDOM_BITS_OBJS = $(subst .c,.o,$( DECODE_LOCKRES_OBJS = $(subst .c,.o,$(DECODE_LOCKRES_CFILES)) ENCODE_LOCKRES_OBJS = $(subst .c,.o,$(ENCODE_LOCKRES_CFILES)) MARK_JOURNAL_DIRTY_OBJS = $(subst .c,.o,$(MARK_JOURNAL_DIRTY_CFILES)) +HAMMING_OBJS = $(subst .c,.o,$(HAMMING_CFILES)) LIBOCFS2 = ../libocfs2/libocfs2.a EXTRAS_LIBS = $(LIBOCFS2) $(COM_ERR_LIBS) @@ -62,4 +68,7 @@ encode_lockres: $(ENCODE_LOCKRES_OBJS) $ mark_journal_dirty: $(MARK_JOURNAL_DIRTY_OBJS) $(LIBOCFS2) $(LINK) $(EXTRAS_LIBS) +hamming: $(HAMMING_OBJS) $(LIBOCFS2) + $(LINK) $(EXTRAS_LIBS) $(ZLIB_LIBS) + include $(TOPDIR)/Postamble.make Property changes on: extras ___________________________________________________________________ Name: svn:ignore - .*.sw? find_hardlinks find_dup_extents find_inode_paths set_random_bits decode_lockres mark_journal_dirty encode_lockres *.d + .*.sw? find_hardlinks find_dup_extents find_inode_paths set_random_bits decode_lockres mark_journal_dirty encode_lockres hamming *.d Index: tunefs.ocfs2/tunefs.c =================================================================== --- tunefs.ocfs2/tunefs.c (revision 1204) +++ tunefs.ocfs2/tunefs.c (revision 1283) @@ -45,13 +45,14 @@ #include <ctype.h> #include <signal.h> -#include <ocfs2.h> -#include <ocfs2_fs.h> -#include <ocfs1_fs_compat.h> +#include "ocfs2.h" +#include "ocfs2_fs.h" +#include "ocfs1_fs_compat.h" +#include "bitops.h" -#include <jbd.h> +#include "jbd.h" -#include <kernel-list.h> +#include "kernel-list.h" #define SYSTEM_FILE_NAME_MAX 40 @@ -74,6 +75,8 @@ typedef struct _ocfs2_tune_opts { int prompt; time_t tune_time; int fd; + uint32_t feature_incompat; + uint32_t no_feature_incompat; } ocfs2_tune_opts; static ocfs2_tune_opts opts; @@ -82,11 +85,11 @@ static int cluster_locked = 0; static void usage(const char *progname) { - fprintf(stderr, "usage: %s [-N number-of-node-slots] " - "[-L volume-label]\n" - "\t[-J journal-options] [-S volume-size] [-qvV] " - "device\n", - progname); + fprintf(stderr, + "usage: %s [-N number-of-node-slots] [-L volume-label]\n" + " %*s [-J journal-options] [-S volume-size] [-O [^]feature[,...]]\n" + " %*s [-qvV] device \n", + progname, strlen(progname), " ", strlen(progname), " "); exit(0); } @@ -117,12 +120,12 @@ static void handle_signal(int sig) /* Call this with SIG_BLOCK to block and SIG_UNBLOCK to unblock */ static void block_signals(int how) { - sigset_t sigs; + sigset_t sigs; - sigfillset(&sigs); - sigdelset(&sigs, SIGTRAP); - sigdelset(&sigs, SIGSEGV); - sigprocmask(how, &sigs, NULL); + sigfillset(&sigs); + sigdelset(&sigs, SIGTRAP); + sigdelset(&sigs, SIGSEGV); + sigprocmask(how, &sigs, NULL); } static int get_number(char *arg, uint64_t *res) @@ -167,6 +170,51 @@ static int get_number(char *arg, uint64_ return 0; } +/* + * This big switch is so we store the "feature name" -> "feature field" + * mapping just once. + */ +static void set_feature(char *feature, int yes) +{ + uint32_t *incompatp = yes ? &opts.feature_incompat : + &opts.no_feature_incompat; + + if (!strcmp(feature, "check")) + ocfs2_set_bit(OCFS2_FEATURE_INCOMPAT_BLOCK_CHECK, + incompatp); + else { + fprintf(stderr, "%s: Invalid feature: %s\n", + opts.progname, feature); + exit(1); + } +} + +static void parse_features(const char *features) +{ + char *p, *n; + + p = strdup(features); + if (!p) { + fprintf(stderr, "%s: Unable to allocate memory\n", + opts.progname); + exit(1); + } + + while (p) { + n = strchr(p, ','); + if (n) { + *n = '\0'; + n++; + } + if (p[0] == '^') + set_feature(p + 1, 0); + else + set_feature(p, 1); + + p = n; + } +} + /* derived from e2fsprogs */ static void parse_journal_opts(char *progname, const char *opts, uint64_t *journal_size_in_bytes) @@ -241,8 +289,9 @@ static void get_options(int argc, char * { "verbose", 0, 0, 'v' }, { "quiet", 0, 0, 'q' }, { "version", 0, 0, 'V' }, - { "journal-options", 0, 0, 'J'}, - { "volume-size", 0, 0, 'S'}, + { "journal-options", 1, 0, 'J'}, + { "volume-size", 1, 0, 'S'}, + { "feature", 1, 0, 'O'}, { 0, 0, 0, 0} }; @@ -254,7 +303,7 @@ static void get_options(int argc, char * opts.prompt = 1; while (1) { - c = getopt_long(argc, argv, "L:N:J:S:vqVx", long_options, + c = getopt_long(argc, argv, "L:N:J:S:O:vqVx", long_options, NULL); if (c == -1) @@ -303,6 +352,10 @@ static void get_options(int argc, char * opts.vol_size = val; break; + case 'O': + parse_features(optarg); + break; + case 'v': opts.verbose = 1; break; @@ -366,8 +419,8 @@ static errcode_t add_slots(ocfs2_filesys /* create inode for system file */ ret = ocfs2_new_system_inode(fs, &blkno, - ocfs2_system_inodes[i].si_mode, - ocfs2_system_inodes[i].si_iflags); + ocfs2_system_inodes[i].si_mode, + ocfs2_system_inodes[i].si_iflags); if (ret) goto bail; Index: configure.in =================================================================== --- configure.in (revision 1204) +++ configure.in (revision 1283) @@ -97,6 +97,13 @@ AC_CHECK_HEADER(uuid/uuid.h, :, AC_MSG_ERROR([Unable to find uuid headers])) AC_SUBST(UUID_LIBS) +ZLIB_LIBS= +AC_CHECK_LIB(z, adler32, ZLIB_LIBS=-lz) +if test "x$ZLIB_LIBS" != "x"; then + AC_CHECK_HEADER(zlib.h, :, ZLIB_LIBS=) +fi +AC_SUBST(ZLIB_LIBS) + AC_MSG_CHECKING(for debug executables) AC_ARG_ENABLE(debugexe, [ --enable-debugexe=[yes/no] Enable debug executables for library source files [default=no]],,enable_debugexe=no) OCFS2_DEBUG_EXE=