From: Mark Tinguely Date: Wed, 19 Feb 2014 00:16:09 +0000 (+1100) Subject: xfs_db: fix the setting of unaligned directory fields X-Git-Tag: v3.2.0-rc1~69 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dc1513283c3e831a6ecd8350373982045120215c;p=thirdparty%2Fxfsprogs-dev.git xfs_db: fix the setting of unaligned directory fields Setting the directory startoff, startblock, and blockcount fields are difficult on both big and little endian machines. The setting of extentflag was completely broken. big endian test: xfs_db> write u.bmx[0].startblock 12 u.bmx[0].startblock = 0 xfs_db> write u.bmx[0].startblock 0xc0000 u.bmx[0].startblock = 192 little endian test: xfs_db> write u.bmx[0].startblock 12 u.bmx[0].startblock = 211106232532992 xfs_db> write u.bmx[0].startblock 0xc0000 u.bmx[0].startblock = 3221225472 Since the output fields and the lengths are not aligned to a byte, setbitval requires them to be entered in big endian and properly byte/nibble shifted. The extentflag output was aligned to a byte but was not shifted correctly. Convert the input to big endian on little endian machines and nibble/byte shift on all platforms so setbitval can set the bits correctly. Clean some whitespace while in the setbitbal() function. [dchinner: cleaned up remaining bad whitespace and comments] Signed-off-by: Mark Tinguely Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner --- diff --git a/db/bit.c b/db/bit.c index ca57d3156..e8adab376 100644 --- a/db/bit.c +++ b/db/bit.c @@ -128,57 +128,41 @@ getbitval( return rval; } +/* + * The input data can be 8, 16, 32, and 64 sized numeric values + * aligned on a byte boundry, or odd sized numbers stored on odd + * aligned offset (for example the bmbt fields). + * + * The input data sent to this routine has been converted to big endian + * and has been adjusted in the array so that the first input bit is to + * be written in the first bit in the output. + * + * If the field length and the output buffer are byte aligned, then use + * memcpy from the input to the output, but if either entries are not byte + * aligned, then loop over the entire bit range reading the input value + * and set/clear the matching bit in the output. + * + * example when ibuf is not multiple of a byte in length: + * + * ibuf: | BBBBBBBB | bbbxxxxx | + * \\\\\\\\--\\\\ + * obuf+bitoff: | xBBBBBBB | Bbbbxxxx | + * + */ void setbitval( - void *obuf, /* buffer to write into */ - int bitoff, /* bit offset of where to write */ - int nbits, /* number of bits to write */ - void *ibuf) /* source bits */ + void *obuf, /* start of buffer to write into */ + int bitoff, /* bit offset into the output buffer */ + int nbits, /* number of bits to write */ + void *ibuf) /* source bits */ { - char *in = (char *)ibuf; - char *out = (char *)obuf; - - int bit; - -#if BYTE_ORDER == LITTLE_ENDIAN - int big = 0; -#else - int big = 1; -#endif - - /* only need to swap LE integers */ - if (big || (nbits!=16 && nbits!=32 && nbits!=64) ) { - /* We don't have type info, so we can only assume - * that 2,4 & 8 byte values are integers. sigh. - */ - - /* byte aligned ? */ - if (bitoff%NBBY) { - /* no - bit copy */ - for (bit=0; bit= '0' || *(ostr+1) <= '7') { - ret = convert_oct(ostr+1, &octval); + if (*(ostr + 1) >= '0' || *(ostr + 1) <= '7') { + ret = convert_oct(ostr + 1, &octval); *rbuf++ = octval; - ostr += ret+1; + ostr += ret + 1; continue; } } *rbuf++ = *ostr++; } - return buf; - } else if (arg[0] == '#' || ((arg[0] != '-') && strchr(arg,'-'))) { + } + + if (arg[0] == '#' || ((arg[0] != '-') && strchr(arg,'-'))) { /* * handle hex blocks ie * #00112233445566778899aabbccddeeff @@ -496,48 +519,79 @@ convert_arg( * * (but if it starts with "-" assume it's just an integer) */ - int bytes=bit_length/8; + int bytes = bit_length / NBBY; + + /* is this an array of hec numbers? */ + if (bit_length % NBBY) + return NULL; /* skip leading hash */ - if (*arg=='#') arg++; + if (*arg == '#') + arg++; while (*arg && bytes--) { - /* skip hypens */ - while (*arg=='-') arg++; - - /* get first nybble */ - if (!isxdigit((int)*arg)) return NULL; - *rbuf=NYBBLE((int)*arg)<<4; - arg++; - - /* skip more hyphens */ - while (*arg=='-') arg++; - - /* get second nybble */ - if (!isxdigit((int)*arg)) return NULL; - *rbuf++|=NYBBLE((int)*arg); - arg++; + /* skip hypens */ + while (*arg == '-') + arg++; + + /* get first nybble */ + if (!isxdigit((int)*arg)) + return NULL; + *rbuf = NYBBLE((int)*arg) << 4; + arg++; + + /* skip more hyphens */ + while (*arg == '-') + arg++; + + /* get second nybble */ + if (!isxdigit((int)*arg)) + return NULL; + *rbuf++ |= NYBBLE((int)*arg); + arg++; } - if (bytes<0&&*arg) return NULL; + if (bytes < 0 && *arg) + return NULL; + return buf; - } else { - /* - * handle integers - */ - *value = strtoll(arg, NULL, 0); - -#if __BYTE_ORDER == BIG_ENDIAN - /* hackery for big endian */ - if (bit_length <= 8) { - rbuf += 7; - } else if (bit_length <= 16) { - rbuf += 6; - } else if (bit_length <= 32) { - rbuf += 4; - } -#endif - return rbuf; } + + /* handle decimal / hexadecimal integers */ + val = strtoll(arg, &endp, 0); + /* return if not a clean number */ + if (*endp != '\0') + return NULL; + + /* Does the value fit into the range of the destination bitfield? */ + if ((val >> bit_length) > 0) + return NULL; + /* + * If the length of the field is not a multiple of a byte, push + * the bits up in the field, so the most signicant field bit is + * the most significant bit in the byte: + * + * before: + * val |----|----|----|----|----|--MM|mmmm|llll| + * after + * val |----|----|----|----|----|MMmm|mmll|ll00| + */ + offset = bit_length % NBBY; + if (offset) + val <<= (NBBY - offset); + + /* + * convert to big endian and copy into the array + * rbuf |----|----|----|----|----|MMmm|mmll|ll00| + */ + *value = cpu_to_be64(val); + + /* + * Align the array to point to the field in the array. + * rbuf = |MMmm|mmll|ll00| + */ + offset = sizeof(__be64) - 1 - ((bit_length - 1) / sizeof(__be64)); + rbuf += offset; + return rbuf; } @@ -550,9 +604,9 @@ write_struct( { const ftattr_t *fa; flist_t *fl; - flist_t *sfl; - int bit_length; - char *buf; + flist_t *sfl; + int bit_length; + char *buf; int parentoffset; if (argc != 2) {