From: Mark Tinguely <tinguely@sgi.com>
To: xfs@oss.sgi.com
Subject: [PATCH v2] xfs_db: fix the setting of unaligned directory fields
Date: Mon, 10 Feb 2014 17:09:12 -0600 [thread overview]
Message-ID: <20140210230923.268327906@sgi.com> (raw)
[-- Attachment #1: xfs_db-fix-dir-settings.patch --]
[-- Type: text/plain, Size: 9914 bytes --]
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@sgi.com>
---
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@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
next reply other threads:[~2014-02-10 23:10 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-02-10 23:09 Mark Tinguely [this message]
2014-02-11 1:31 ` [PATCH v2] xfs_db: fix the setting of unaligned directory fields Dave Chinner
2014-02-11 14:18 ` Mark Tinguely
2014-02-12 0:22 ` Dave Chinner
2014-02-12 21:37 ` Mark Tinguely
2014-02-13 20:25 ` [PATCH v3] " Mark Tinguely
2014-02-18 10:34 ` Dave Chinner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20140210230923.268327906@sgi.com \
--to=tinguely@sgi.com \
--cc=xfs@oss.sgi.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.