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 <tinguely@xxxxxxx> --- 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<nbits; bit++) - setbit(out, bit+bitoff, getbit(in, bit)); - } else { - /* yes - byte copy */ - memcpy(out+byteize(bitoff), in, byteize(nbits)); - } - - } else { - int ibit; - int obit; - - /* we need to endian swap this value */ - - out+=byteize(bitoff); - obit=bitoffs(bitoff); - - ibit=nbits-NBBY; - - for (bit=0; bit<nbits; bit++) { - setbit(out, bit+obit, getbit(in, ibit)); - if (ibit%NBBY==NBBY-1) - ibit-=NBBY*2-1; - else - ibit++; - } - } + char *in = ibuf; + char *out = obuf; + int bit; + + if (bitoff % NBBY || nbits % NBBY) { + for (bit=0; bit<nbits; bit++) + setbit(out, bit+bitoff, getbit(in, bit)); + } else + memcpy(out+byteize(bitoff), in, byteize(nbits)); } Index: b/db/write.c =================================================================== --- a/db/write.c +++ b/db/write.c @@ -439,55 +439,78 @@ convert_oct( #define NYBBLE(x) (isdigit(x)?(x-'0'):(tolower(x)-'a'+0xa)) +/* + * convert_arg allow input in the following forms: + * A string ("ABTB") whose ASCII value is placed in an array in the order + * matching the input. + * + * An even number of hex numbers. If the length is greater than + * 64 bits, then the output is an array of bytes whose top nibble + * is the first hex digit in the input, the lower nibble is the second + * hex digit in the input. UUID entries are entered in this manner. + * If the length is 64 bits or less, then treat the hex input characters + * as a number used with setbitval(). + * + * A decimal or hexadecimal integer to be used with setbitval(). + * --- + * Numbers that will use setbitval() are in big endian format and + * are adjusted in the array so that the first input bit is to be + * be written to the first bit in the output. + */ static char * convert_arg( - char *arg, - int bit_length) + char *arg, + int bit_length) { - int i; - static char *buf = NULL; - char *rbuf; - long long *value; - int alloc_size; - char *ostr; - int octval, ret; + int i; + int alloc_size; + int octval; + int offset; + int ret; + static char *buf = NULL; + char *endp; + char *rbuf; + char *ostr; + __u64 *value; + __u64 val = 0; if (bit_length <= 64) alloc_size = 8; else - alloc_size = (bit_length+7)/8; + alloc_size = (bit_length + 7)/8; buf = xrealloc(buf, alloc_size); memset(buf, 0, alloc_size); - value = (long long *)buf; + value = (__u64 *)buf; rbuf = buf; if (*arg == '\"') { - /* handle strings */ + /* input a string and output ASCII array of characters */ /* zap closing quote if there is one */ - if ((ostr = strrchr(arg+1, '\"')) != NULL) + if ((ostr = strrchr(arg + 1, '\"')) != NULL) *ostr = '\0'; - ostr = arg+1; + ostr = arg + 1; for (i = 0; i < alloc_size; i++) { if (!*ostr) break; - /* do octal */ + /* do octal conversion */ if (*ostr == '\\') { - if (*(ostr+1) >= '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@xxxxxxxxxxx http://oss.sgi.com/mailman/listinfo/xfs