strace is awesome
Your periodic reminder that strace is awesome..
On Windows you basically know which partition a file is hosted on by looking at the drive letter.
On Linux there’s just a global namespace and it is more difficult to know the partition of a given file (although I think the indirection in the Unix design is better).
I remembered that $ df
can show me this info:
$ df /home/vagrant
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/vagrant--vg-root 64440148 16413800 44729888 27% /
Ok sure enough, /dev/mapper/vagrant--vg-root
is just what my program needs to fetch - the
partition the directory is on (mine happens to be at root mount, but it always isn’t
the case).
How does it do this? Hmm, just prefix the df
command with strace
. I filtered out
non-interesting lines and was left with this:
$ strace df /home/vagrant
open("/home/vagrant", O_RDONLY|O_NOCTTY) = 3
fstat(3, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
close(3) = 0
open("/proc/self/mountinfo", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
read(3, "18 23 0:17 / /sys rw,nosuid,node"..., 1024) = 1024
read(3, "ystemd/systemd-cgroups-agent,nam"..., 1024) = 1024
read(3, "io rw,nosuid,nodev,noexec,relati"..., 1024) = 1024
read(3, "1 - overlay overlay rw,lowerdir="..., 1024) = 1024
read(3, "S63JLQIBVW7YZFJWUO55ZHIU:/var/li"..., 1024) = 1024
read(3, " 0:3 net:[4026532217] /run/docke"..., 1024) = 1024
read(3, "3 / /var/lib/docker/overlay2/a2c"..., 1024) = 1024
read(3, "67ed5b6ad8a07382c15873226/merged"..., 1024) = 1024
read(3, "rlay overlay rw,lowerdir=/var/li"..., 1024) = 1024
read(3, "NQDRB346OHVJV6PBKG,upperdir=/var"..., 1024) = 1024
read(3, "ork\n223 23 0:69 / /var/lib/docke"..., 1024) = 1024
read(3, "[4026532841] /run/docker/netns/1"..., 1024) = 1024
read(3, "6a84e38acf4b5f39f321c257c4f2c178"..., 1024) = 1024
read(3, " rw,nosuid,nodev,noexec,relatime"..., 1024) = 157
read(3, "", 1024) = 0
lseek(3, 0, SEEK_CUR) = 13469
close(3) = 0
lstat("/home", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/home/vagrant", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
uname({sysname="Linux", nodename="vagrant", ...}) = 0
statfs("/home/vagrant", {f_type="EXT2_SUPER_MAGIC", f_bsize=4096, f_blocks=16110037, f_bfree=12006561, f_bavail=11182446, f_files=4104192, f_ffree=3738210, f_fsid={-812717494, -1339855268}, f_namelen=255, f_frsize=4096, f_flags=4128}) = 0
open("/usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=26258, ...}) = 0
mmap(NULL, 26258, PROT_READ, MAP_SHARED, 3, 0) = 0x7f600b2bb000
close(3) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
write(1, "Filesystem 1K-"..., 74) = 74
write(1, "/dev/mapper/vagrant--vg-root 64"..., 65) = 65
close(1) = 0
close(2) = 0
+++ exited with 0 +++
$ df
is using fstat(), lstat(), stat() and statfs(). All but statfs() return struct like
this, so there’s no partition location there.
statfs()
sounded really promising, but there’s
no partition info there. It has “Filesystem ID” which
I though I could correlate with data from some other API, but it seemed not be the case.
Ok moving on, $ df
also accesses /proc/self/mountinfo
, let’s try it:
$ cat /proc/self/mountinfo
... (snipped)
23 0 252:0 / / rw,relatime shared:1 - ext4 /dev/mapper/vagrant--vg-root rw,errors=remount-ro,data=ordered
... (snipped)
Ok there’s basically all we need. The format for this output is documented here.
So all we need to do to resolve /home/vagrant
Β΄s partition is grab the longest match of
mount point from output of /proc/self/mountinfo
.
E.g., given this entire mountinfo
.. (some numbers just randomized, they’re probably wrong):
23 0 252:0 / / rw,relatime shared:1 - ext4 /dev/mapper/vagrant--vg-root rw,errors=remount-ro,data=ordered
24 0 0:139 / /home rw,relatime shared:1 - ext4 /dev/mapper/vagrant--vg-homes rw,errors=remount-ro,data=ordered
.. a few mount point resolve examples:
/home/vagrant
would be hosted on partition/dev/mapper/vagrant--vg-homes
(longest match is/home
)/var/www
would be hosted on/dev/mapper/vagrant--vg-root
(longest match is/
)
My intuition about this “longest match” seems to be confirmed by coreutils' df
source code.
There you go, today I learned how to access mount info, quite easily with the help of strace!
p.s. I’m using Go in my program, and I found a great library for accessing procfs output.