All of lore.kernel.org
 help / color / mirror / Atom feed
From: Eryu Guan <guan@eryu.me>
To: Filipe Manana <fdmanana@gmail.com>
Cc: ethanwu <ethanwu@synology.com>, fstests <fstests@vger.kernel.org>,
	linux-btrfs <linux-btrfs@vger.kernel.org>
Subject: Re: [PATCH v2] btrfs: test if rename handles dir item collision correctly
Date: Sun, 20 Dec 2020 23:31:10 +0800	[thread overview]
Message-ID: <20201220153110.GB3853@desktop> (raw)
In-Reply-To: <CAL3q7H412cHxC4p7Nx+qLFYJCUzdt5CP4JGw0JtM3jDqycnrog@mail.gmail.com>

On Thu, Dec 17, 2020 at 11:47:49AM +0000, Filipe Manana wrote:
> On Tue, Dec 15, 2020 at 4:16 AM ethanwu <ethanwu@synology.com> wrote:
> >
> > This is a regression test for the issue fixed by the kernel commit titled
> > "btrfs: correctly calculate item size used when item key collision happens"
> >
> > In this case, we'll simply rename many forged filename that cause collision
> > under a directory to see if rename failed and filesystem is forced readonly.
> >
> > Signed-off-by: ethanwu <ethanwu@synology.com>
> > ---
> > v2:
> > - Add a python script to generate the forged name at run-time rather than
> > from hardcoded names
> > - Fix , Btrfs->btrfs, and typo mentioned in v1
> >
> >  src/btrfs_crc32c_forged_name.py | 92 +++++++++++++++++++++++++++++++++
> >  tests/btrfs/228                 | 72 ++++++++++++++++++++++++++
> >  tests/btrfs/228.out             |  2 +
> >  tests/btrfs/group               |  1 +
> >  4 files changed, 167 insertions(+)
> >  create mode 100755 src/btrfs_crc32c_forged_name.py
> >  create mode 100755 tests/btrfs/228
> >  create mode 100644 tests/btrfs/228.out
> >
> > diff --git a/src/btrfs_crc32c_forged_name.py b/src/btrfs_crc32c_forged_name.py
> > new file mode 100755
> > index 00000000..d8abedde
> > --- /dev/null
> > +++ b/src/btrfs_crc32c_forged_name.py
> > @@ -0,0 +1,92 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +import struct
> > +import sys
> > +import os
> > +import argparse
> > +
> > +class CRC32(object):
> > +  """A class to calculate and manipulate CRC32."""
> > +  def __init__(self):
> > +    self.polynom = 0x82F63B78
> > +    self.table, self.reverse = [0]*256, [0]*256
> > +    self._build_tables()
> > +
> > +  def _build_tables(self):
> > +    for i in range(256):
> > +      fwd = i
> > +      rev = i << 24
> > +      for j in range(8, 0, -1):
> > +        # build normal table
> > +        if (fwd & 1) == 1:
> > +          fwd = (fwd >> 1) ^ self.polynom
> > +        else:
> > +          fwd >>= 1
> > +        self.table[i] = fwd & 0xffffffff
> > +        # build reverse table =)
> > +        if rev & 0x80000000 == 0x80000000:
> > +          rev = ((rev ^ self.polynom) << 1) | 1
> > +        else:
> > +          rev <<= 1
> > +        rev &= 0xffffffff
> > +        self.reverse[i] = rev
> > +
> > +  def calc(self, s):
> > +    """Calculate crc32 of a string.
> > +       Same crc32 as in (binascii.crc32)&0xffffffff.
> > +    """
> > +    crc = 0xffffffff
> > +    for c in s:
> > +      crc = (crc >> 8) ^ self.table[(crc ^ ord(c)) & 0xff]
> > +    return crc^0xffffffff
> > +
> > +  def forge(self, wanted_crc, s, pos=None):
> > +    """Forge crc32 of a string by adding 4 bytes at position pos."""
> > +    if pos is None:
> > +      pos = len(s)
> > +
> > +    # forward calculation of CRC up to pos, sets current forward CRC state
> > +    fwd_crc = 0xffffffff
> > +    for c in s[:pos]:
> > +      fwd_crc = (fwd_crc >> 8) ^ self.table[(fwd_crc ^ ord(c)) & 0xff]
> > +
> > +    # backward calculation of CRC up to pos, sets wanted backward CRC state
> > +    bkd_crc = wanted_crc^0xffffffff
> > +    for c in s[pos:][::-1]:
> > +      bkd_crc = ((bkd_crc << 8) & 0xffffffff) ^ self.reverse[bkd_crc >> 24]
> > +      bkd_crc ^= ord(c)
> > +
> > +    # deduce the 4 bytes we need to insert
> > +    for c in struct.pack('<L',fwd_crc)[::-1]:
> > +      bkd_crc = ((bkd_crc << 8) & 0xffffffff) ^ self.reverse[bkd_crc >> 24]
> > +      bkd_crc ^= ord(c)
> > +
> > +    res = s[:pos] + struct.pack('<L', bkd_crc) + s[pos:]
> > +    return res
> > +
> > +  def parse_args(self):
> > +    parser = argparse.ArgumentParser()
> > +    parser.add_argument("-d", default=os.getcwd(), dest='dir',
> > +                        help="directory to generate forged names")
> > +    parser.add_argument("-c", default=1, type=int, dest='count',
> > +                        help="number of forged names to create")
> > +    return parser.parse_args()
> > +
> > +if __name__=='__main__':
> > +
> > +  crc = CRC32()
> > +  wanted_crc = 0x00000000
> > +  count = 0
> > +  args = crc.parse_args()
> > +  dirpath=args.dir
> > +  while count < args.count :
> > +    origname = os.urandom (89).encode ("hex")[:-1].strip ("\x00")
> > +    forgename = crc.forge(wanted_crc, origname, 4)
> > +    if ("/" not in forgename) and ("\x00" not in forgename):
> > +      srcpath=dirpath + '/' + str(count)
> > +      dstpath=dirpath + '/' + forgename
> > +      file (srcpath, 'a').close()
> > +      os.rename(srcpath, dstpath)
> > +      os.system('btrfs fi sync %s' % (dirpath))
> > +      count+=1;
> > +
> > diff --git a/tests/btrfs/228 b/tests/btrfs/228
> > new file mode 100755
> > index 00000000..e38da19b
> > --- /dev/null
> > +++ b/tests/btrfs/228
> > @@ -0,0 +1,72 @@
> > +#! /bin/bash
> > +# SPDX-License-Identifier: GPL-2.0
> > +# Copyright (c) 2020 Synology.  All Rights Reserved.
> > +#
> > +# FS QA Test 228
> > +#
> > +# Test if btrfs rename handle dir item collision correctly
> > +# Without patch fix, rename will fail with EOVERFLOW, and filesystem
> > +# is forced readonly.
> > +#
> > +# This bug is going to be fixed by a patch for kernel titled
> > +# "btrfs: correctly calculate item size used when item key collision happens"
> > +#
> > +seq=`basename $0`
> > +seqres=$RESULT_DIR/$seq
> > +echo "QA output created by $seq"
> > +
> > +here=`pwd`
> > +tmp=/tmp/$$
> > +status=1       # failure is the default!
> > +trap "_cleanup; exit \$status" 0 1 2 3 15
> > +
> > +_cleanup()
> > +{
> > +       cd /
> > +       rm -f $tmp.*
> > +}
> > +
> > +# get standard environment, filters and checks
> > +. ./common/rc
> > +. ./common/filter
> > +
> > +# real QA test starts here
> > +
> > +_supported_fs btrfs
> > +_require_scratch
> > +_require_command $PYTHON2_PROG python2
> > +
> > +rm -f $seqres.full
> > +
> > +# Currently in btrfs the node/leaf size can not be smaller than the page
> > +# size (but it can be greater than the page size). So use the largest
> > +# supported node/leaf size (64Kb) so that the test can run on any platform
> > +# that Linux supports.
> > +_scratch_mkfs "--nodesize 65536" >>$seqres.full 2>&1
> > +_scratch_mount
> > +
> > +#
> > +# In the following for loop, we'll create a leaf fully occupied by
> > +# only one dir item with many forged collision names in it.
> > +#
> > +# leaf 22544384 items 1 free space 0 generation 6 owner FS_TREE
> > +# leaf 22544384 flags 0x1(WRITTEN) backref revision 1
> > +# fs uuid 9064ba52-3d2c-4840-8e26-35db08fa17d7
> > +# chunk uuid 9ba39317-3159-46c9-a75a-965ab1e94267
> > +#    item 0 key (256 DIR_ITEM 3737737011) itemoff 25 itemsize 65410
> > +#    ...
> > +#
> > +
> > +$PYTHON2_PROG $here/src/btrfs_crc32c_forged_name.py -d $SCRATCH_MNT -c 310
> > +
> > +ISRW=$(_fs_options $SCRATCH_DEV | grep -w "rw")
> > +if [ -n "$ISRW" ]; then
> > +       echo "FS is Read-Write Test OK"
> > +else
> > +       echo "FS is Read-Only. Test Failed"
> > +       status=1
> > +       exit
> > +fi
> 
> You don't need to print these messages. In case the test fails:
> 
> 1) There's a warning stack trace in dmesg, that alone makes the test
> fail since fstests checks for warnings in dmesg and reports the test
> as failed is any exist;
> 2) The test script results in python dumping a stack trace, which
> causes a mismatch with the golden output, therefore making the test
> fail:
> 
> Traceback (most recent call last):
>   File "/home/fdmanana/git/hub/xfstests/src/btrfs_crc32c_forged_name.py",
> line 89, in <module>
>     os.rename(srcpath, dstpath)
> OSError: [Errno 75] Value too large for defined data type
> 
> Anyway, it's a minor thing, if Eryu doesn't like it, I suppose we can
> remove that if-then-else and just replace it with "echo Silence is
> golden".

Updated the patch as you suggested.

> 
> Reviewed-by: Filipe Manana <fdmanana@suse.com>
> 
> Thank you very much for writing the test case Ethan!

Thanks for the test and review!

Eryu

> 
> > +
> > +# success, all done
> > +status=0; exit
> > diff --git a/tests/btrfs/228.out b/tests/btrfs/228.out
> > new file mode 100644
> > index 00000000..eae514f0
> > --- /dev/null
> > +++ b/tests/btrfs/228.out
> > @@ -0,0 +1,2 @@
> > +QA output created by 228
> > +FS is Read-Write Test OK
> > diff --git a/tests/btrfs/group b/tests/btrfs/group
> > index d18450c7..f8021668 100644
> > --- a/tests/btrfs/group
> > +++ b/tests/btrfs/group
> > @@ -228,3 +228,4 @@
> >  224 auto quick qgroup
> >  225 auto quick volume seed
> >  226 auto quick rw snapshot clone prealloc punch
> > +228 auto quick
> > --
> > 2.25.1
> >
> 
> 
> -- 
> Filipe David Manana,
> 
> “Whether you think you can, or you think you can't — you're right.”

      reply	other threads:[~2020-12-20 15:32 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-15  3:59 [PATCH v2] btrfs: test if rename handles dir item collision correctly ethanwu
2020-12-17 11:47 ` Filipe Manana
2020-12-20 15:31   ` Eryu Guan [this message]

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=20201220153110.GB3853@desktop \
    --to=guan@eryu.me \
    --cc=ethanwu@synology.com \
    --cc=fdmanana@gmail.com \
    --cc=fstests@vger.kernel.org \
    --cc=linux-btrfs@vger.kernel.org \
    /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.