Skip to content

Instantly share code, notes, and snippets.

@dgdavid
Last active January 30, 2019 13:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dgdavid/54880e42ae6578c805ae0ad2bdc49843 to your computer and use it in GitHub Desktop.
Save dgdavid/54880e42ae6578c805ae0ad2bdc49843 to your computer and use it in GitHub Desktop.
Check if the given path is in a btrfs filesystem
require 'yast'
require 'yast2/execute'
require 'pathname'
# Returns if the given path is placed in a btrfs
#
# @param path [String, Pathname]
#
# @return [Boolean]
def btrfs_available_for?(path)
mounted_btrfs = Yast::Execute.locally!.stdout(
["df", "-t", "btrfs", "--output=target"],
["tail", "-n", "+2"]
).split.uniq
not_mounted_btrfs = Yast::Execute.locally!.stdout(
["df", "-x", "btrfs", "--output=target"],
["tail", "-n", "+2"]
).split.uniq
candidate_paths = Pathname.new(path).descend.map(&:to_s)
return false if mounted_btrfs.empty?
return false if (not_mounted_btrfs & candidate_paths).any?
(mounted_btrfs & candidate_paths).any?
end
# Simply for testing (manually, through irb) the scenario
# > for a system with "/" in btrfs and a separate "/home" with ext4
#
def fake_btrfs_available_for?(path)
candidate_paths = Pathname.new(path).descend.map(&:to_s)
return false if btrfs_targets.empty?
return false if (not_btrfs_targets & candidate_paths).any?
(btrfs_targets & candidate_paths).any?
end
def btrfs_targets
["/"]
end
def not_btrfs_targets
["/home"]
end
@dgdavid
Copy link
Author

dgdavid commented Jan 30, 2019

In a system with below df output

ytm@linux-9x6v:~/suse/src> df -t btrfs
Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/vda2       10476524 6597212   3644836  65% /
/dev/vda2       10476524 6597212   3644836  65% /tmp
/dev/vda2       10476524 6597212   3644836  65% /srv
/dev/vda2       10476524 6597212   3644836  65% /var
/dev/vda2       10476524 6597212   3644836  65% /opt
/dev/vda2       10476524 6597212   3644836  65% /usr/local
/dev/vda2       10476524 6597212   3644836  65% /home
/dev/vda2       10476524 6597212   3644836  65% /boot/grub2/i386-pc
/dev/vda2       10476524 6597212   3644836  65% /boot/grub2/x86_64-efi
/dev/vda2       10476524 6597212   3644836  65% /root

it will returns true

irb(main):001:0> require_relative './btrfs_check_poc.rb'
=> true
irb(main):002:0> btrfs_available_for?('/home/ytm_tester')
=> true
irb(main):003:0>

@dgdavid
Copy link
Author

dgdavid commented Jan 30, 2019

In a system with below df output

❯ df -t btrfs
df: no file systems processed

it will returns false

irb(main):001:0> require_relative './btrfs_check_poc.rb'
=> true
irb(main):002:0> btrfs_available_for?('/home/ytm_tester')
=> false
irb(main):003:0> 

@dgdavid
Copy link
Author

dgdavid commented Jan 30, 2019

for a system with "/" in btrfs and a separate "/home" with ext4

irb(main):001:0> require_relative './btrfs_check_poc.rb'
=> true
irb(main):002:0> btrfs_available_for?('/home/ytm_tester')
=> false
irb(main):003:0> btrfs_available_for?('/alternative_home/ytm_tester')
=> true

@wfeldt
Copy link

wfeldt commented Jan 30, 2019

I would have gone for something like df --output=fstype /home simply to avoid path operations and symlink complications.

What if /home is a symlink to a different file system?

@dgdavid
Copy link
Author

dgdavid commented Jan 30, 2019

Thank you @wfeldt

So,

def btrfs?(path)
  dirname = Pathname.new(path).dirname
  fstype = Yast::Execute.locally!.stdout(
    ["df", "--output=fstype", dirname],
    ["tail", "-n", "+2"]
  ).chomp

  fstype == "btrfs"
end

will be enough :)

@lslezak
Copy link

lslezak commented Jan 30, 2019

Related to the recent security hardening, we should call /usr/bin/df (with full path).

But I found an easier way:

# /usr/bin/stat -f --format '%T' /home
xfs

No need for removing the df header from the output...

@lslezak
Copy link

lslezak commented Jan 30, 2019

Ah, you might need to add the --dereference to follow symlinks...

@dgdavid
Copy link
Author

dgdavid commented Jan 30, 2019

@lslezak,

Pretty nice! Thank you!

@dgdavid
Copy link
Author

dgdavid commented Jan 30, 2019

BTW,

Related to the recent security hardening

Thank you for the reminder! :) I was using the Yast::Execute.locally! because of this, but I forgot to use the full path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment