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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Feb 10, 2014 at 05:09:12PM -0600, Mark Tinguely wrote:
> Setting the directory startoff, startblock, and blockcount
> fields are difficult on both big and little endian machines.
> The setting of extentflag was completely broken.
.....
> +	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));

Still whitespace challenged ;)

> 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;

		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';

If you are touching these, the preferred form is

		ostr = strrchr(arg + 1, '\"');
		if (ostr)
			*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++;
			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;

I don't understand why you added this - how can we have input left
that we need to parse after the above loop? @bytes will always be <=
0 at this point in time, which means we have no space in the bit
field left to put values into....

> +	} 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;

That's quite ambiguous - it's not obviously correct as it requires
close attention to determine that the code actually functions
correctly. i.e. I had to look very closely to determine ">>" or ">"
should have been used here. Can you rewrite it as:

	/* Check if the value can fit into the supplied 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));
                                                      ^^
whitespace

Other that that, the comments greatly improve this code. Thanks for
doing that.

Cheers,

Dave.
-- 
Dave Chinner
david@xxxxxxxxxxxxx

_______________________________________________
xfs mailing list
xfs@xxxxxxxxxxx
http://oss.sgi.com/mailman/listinfo/xfs




[Index of Archives]     [Linux XFS Devel]     [Linux Filesystem Development]     [Filesystem Testing]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux