From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from relay.sgi.com (relay2.corp.sgi.com [137.38.102.29]) by oss.sgi.com (Postfix) with ESMTP id E6F7F7F3F for ; Mon, 10 Feb 2014 17:10:04 -0600 (CST) Received: from eagdhcp-232-208.americas.sgi.com (eagdhcp-232-208.americas.sgi.com [128.162.232.208]) by relay2.corp.sgi.com (Postfix) with ESMTP id C7473304039 for ; Mon, 10 Feb 2014 15:10:01 -0800 (PST) Received: from eagdhcp-232-208.americas.sgi.com (localhost [127.0.0.1]) by eagdhcp-232-208.americas.sgi.com (8.14.5/8.14.5) with ESMTP id s1ANA4xA003105 for ; Mon, 10 Feb 2014 17:10:04 -0600 (CST) (envelope-from tinguely@eagdhcp-232-208.americas.sgi.com) Message-Id: <20140210230923.268327906@sgi.com> Date: Mon, 10 Feb 2014 17:09:12 -0600 From: Mark Tinguely Subject: [PATCH v2] xfs_db: fix the setting of unaligned directory fields Content-Disposition: inline; filename=xfs_db-fix-dir-settings.patch List-Id: XFS Filesystem from SGI List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: xfs-bounces@oss.sgi.com Sender: xfs-bounces@oss.sgi.com To: xfs@oss.sgi.com 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. Signed-off-by: Mark Tinguely --- v2: Ignore extra characters in input. Fix hash input if still used as an integer input. It was broken on big endian, but someone may use this input in a little endian script. Add documentation. Did more clean up. db/bit.c | 84 +++++++++++------------------ db/write.c | 173 +++++++++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 152 insertions(+), 105 deletions(-) Index: b/db/bit.c =================================================================== --- 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 @@ -495,49 +518,89 @@ convert_arg( * 1122334455667788-99aa-bbcc-ddee-ff00112233445566778899 * * (but if it starts with "-" assume it's just an integer) + * + * Treat all requests 64 bits or smaller as numbers and + * requests larger than 64 bits as hex blocks arrays. */ - int bytes=bit_length/8; + int bytes = bit_length / NBBY; /* skip leading hash */ - if (*arg=='#') arg++; + if (*arg == '#') arg++; while (*arg && bytes--) { - /* skip hypens */ - while (*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++; + /* get first nybble */ + if (!isxdigit((int)*arg)) + return NULL; + if (bit_length <= 64) + val = (val << 4) + NYBBLE((int)*arg); + else + *rbuf = NYBBLE((int)*arg) << 4; + arg++; + + /* skip more hyphens */ + while (*arg == '-') + arg++; + + /* get second nybble */ + if (!isxdigit((int)*arg)) + return NULL; + if (bit_length <= 64) + val = (val << 4) + NYBBLE((int)*arg); + else + *rbuf++ |= NYBBLE((int)*arg); + arg++; } - if (bytes<0&&*arg) return NULL; - return buf; - } else { + if (bytes < 0 && *arg) + return NULL; + /* - * handle integers + * Values larger than 64 bits are array of hex digits that + * already in the desired ordering (example UUID). */ - *value = strtoll(arg, NULL, 0); + if (bit_length > 64) + return buf; + } else { + /* handle decimal / hexadecimal integers */ -#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; + val = strtoll(arg, &endp, 0); + /* return if not a clean number */ + if (*endp != '\0') + return NULL; } + + /* Is this value valid for the bit length? */ + if (val >> bit_length) + 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 +613,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) { _______________________________________________ xfs mailing list xfs@oss.sgi.com http://oss.sgi.com/mailman/listinfo/xfs