• Filipe Manana's avatar
    Btrfs: don't initialize a space info as full to prevent ENOSPC · 6af3e3ad
    Filipe Manana authored
    Commit 2e6e5183 ("Btrfs: fix block group ->space_info null pointer
    dereference") accidently marked a space info as full when initializing
    it with a value of 0 total bytes. This introduces an ENOSPC problem when
    writing file data if we mount a filesystem that has no data block groups
    allocated, because the data space info is initialized with 0 total bytes,
    marked as full, and it never gets its total bytes incremented by a
    (positive) value to unmark it as full (because there are no data block
    groups loaded when the fs is mounted).
    For metadata and system spaces this issue can never happen since we always
    have at least one metadata block group and one system block group (even
    for an empty filesystem).
    
    So fix this by just not initializing a space info as full, reverting the
    offending part of the commit mentioned above.
    
    The following test case for fstests reproduces the issue:
    
      seq=`basename $0`
      seqres=$RESULT_DIR/$seq
      echo "QA output created by $seq"
      tmp=/tmp/$$
      status=1	# failure is the default!
      trap "_cleanup; exit \$status" 0 1 2 3 15
    
      _cleanup()
      {
          rm -f $tmp.*
      }
    
      # get standard environment, filters and checks
      . ./common/rc
      . ./common/filter
    
      # real QA test starts here
      _need_to_be_root
      _supported_fs btrfs
      _supported_os Linux
      _require_scratch
    
      rm -f $seqres.full
    
      _scratch_mkfs >>$seqres.full 2>&1
    
      # Mount our filesystem without space caches enabled so that we do not
      # get any space used from the initial data block group that mkfs creates
      # (space caches used space from data block groups).
      _scratch_mount "-o nospace_cache"
    
      # Need an fs with at least 2Gb to make sure mkfs.btrfs does not create
      # an fs using mixed block groups (used both for data and metadata). We
      # really need to have dedicated block groups for data to reproduce the
      # issue and mkfs.btrfs defaults to mixed block groups only for small
      # filesystems (up to 1Gb).
      _require_fs_space $SCRATCH_MNT $((2 * 1024 * 1024))
    
      # Run balance with the purpose of deleting the unused data block group
      # that mkfs created. We could also wait for the background kthread to
      # automatically delete the unused block group, but we do not have a way
      # to make it run and wait for it to complete, so just do a balance
      # instead of some unreliable sleep
      _run_btrfs_util_prog balance start -dusage=0 $SCRATCH_MNT
    
      # Now unmount the filesystem, mount it again (either with or with space
      # caches enabled, it does not matter to trigger the problem) and attempt
      # to create a file with some data - this used to fail with ENOSPC
      # because there were no data block groups when the filesystem was
      # mounted and the data space info object was marked as full when
      # initialized (because it had 0 total bytes), which prevented the file
      # write path from attempting to allocate a data block group and fail
      # immediately with ENOSPC.
      _scratch_remount
      echo "hello world" > $SCRATCH_MNT/foobar
    
      echo "Silence is golden"
      status=0
      exit
    Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
    6af3e3ad
extent-tree.c 278 KB