xfs
[Top] [All Lists]

[PATCH v2] xfs_db: fix the setting of unaligned directory fields

To: xfs@xxxxxxxxxxx
Subject: [PATCH v2] xfs_db: fix the setting of unaligned directory fields
From: Mark Tinguely <tinguely@xxxxxxx>
Date: Mon, 10 Feb 2014 17:09:12 -0600
Delivered-to: xfs@xxxxxxxxxxx
User-agent: quilt/0.47-15.17.1
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) {


<Prev in Thread] Current Thread [Next in Thread>