2

I'm trying to read the contents of a file using the file's inode.

This works fine:

echo "First line" > data.txt
sync
sudo debugfs -R "cat <$(stat -c %i data.txt)>" /dev/sda3

debugfs tells me the file contents are "First line". This part of the command gets data.txt's inode number: $(stat -c %i data.txt).

Things go awry when adding a second line:

echo "Second line" >> data.txt
sync
sudo debugfs -R "cat <$(stat -c %i data.txt)>" /dev/sda3

I still only get "First line" from debugfs. This doesn't change after adding more lines, running sync again, or retrying a couple of days later.

Why doesn't debugfs show the remainder of the file contents? Am I using debugfs the wrong way?

I can reproduce this behavior reliably with other files.


I noticed that when overwriting existing file contents with echo "New content" > data.txt, debugfs does show the new content. But adding a second line to it, will, as described above, show only the first line.


I'm on Arch Linux 5.12.3 using debugfs 1.46.2. The file system on /dev/sda3 is ext4. Calling debugfs -R "stat ..." produces the following, which seems unsuspicious to me:

Inode: 16515371   Type: regular    Mode:  0644   Flags: 0x80000
Generation: 3923658711    Version: 0x00000000:00000001
User:  1000   Group:  1000   Project:     0   Size: 34
File ACL: 0
Links: 1   Blockcount: 8
Fragment:  Address: 0    Number: 0    Size: 0
ctime: 0x60b639e5:71315fa0 -- Tue Jun  1 15:45:09 2021
atime: 0x60b63988:b7c456cc -- Tue Jun  1 15:43:36 2021
mtime: 0x60b639e5:71315fa0 -- Tue Jun  1 15:45:09 2021
crtime: 0x60b63988:b7c456cc -- Tue Jun  1 15:43:36 2021
Size of extra inode fields: 32
Inode checksum: 0xbfa4390e
EXTENTS:
(0):66095479
0
2

It is due to caching. You have at least two options.

  1. Use the -D flag:

    -D  Causes debugfs to open the device using Direct I/O, bypassing the buf‐
    fer  cache.  Note that some Linux devices, notably device mapper as of
    this writing, do not support Direct I/O.
    

  2. Drop buffer cache:

    echo 1 | sudo tee /proc/sys/vm/drop_caches
    

See for example:


If you do not pass the -D flag you can still see some action by piping the result to for example xxd:

sudo debugfs -R "cat <$(stat --printf %i data.txt)>" /dev/sda3 | xxd -a -c 32

You will see that the cat'ed file is filled with zero bytes, and sometimes data (if enough has been written).

For example, after echo A >data.txt

00000000: 410a                A.

Then after for i in {1..7}; do echo A >>data.txt; done:

00000000: 410a 0000 0000 0000 0000 0000 0000 0000  A...............

You can also monitor by using something like this:

Usage: sudo ./script file_to_monitor

It launches watch with an awk script that prints statistics for the device from /sys/block in addition to print the result of cat <inode> for the file.

#!/bin/sh

if [ "$1" = "-h" ]; then
    printf '%s FILE\n' "$0"
    exit 1
fi

file="$1"
inode=$(stat --printf %i "$file")
dev_path="$(df -P -- "$file" | awk 'END{print $1}')"
dev_name="$(lsblk -no NAME  "$dev_path")"
dev_core="$(lsblk -no PKNAME  "$dev_path")"

if [ "$dev_core" = "loop" ]; then
    fn_stat=/sys/block/$dev_name/stat
else
    fn_stat=/sys/block/$dev_core/$dev_name/stat
fi

printf 'File : %s\n' "$file"
printf 'Inode: %s\n' "$inode"
printf 'Stat : %s\n' "$fn_stat"
printf 'Dev  : %s\n' "$dev_path"

printf "Continue? [yN] " >&2
read -r ans
if ! [ "$ans" = "y" ] && ! [ "$ans" = "Y" ]; then
    exit
fi

watch -n 0.2 'awk \
    -v inode="'$inode'" \
    -v dev_path="'$dev_path'" \
"{
    rs = \$3 * 512
    rsk = rs / 1024
    rsm = rsk / 1024
    ws = \$7 * 512
    wsk = ws / 1024
    wsm = wsk / 1024

    printf \" 1: Reads  Completed   : %9d\n\", \$1
    printf \" 2: Reads  Merged      : %9d\n\", \$2
    printf \" 3: Read   Sectors     : %9d %6d MiB %9d KiB %d bytes\n\",
    \$3, rsm, rsk, rs
    printf \" 4: Read   ms          : %9d\n\", \$4
    printf \" 5: Writes Completed   : %9d\n\", \$5
    printf \" 6: Writes Merged      : %9d\n\", \$6
    printf \" 7: Write  Sectors     : %9d %6d MiB %9d KiB %d bytes\n\",
    \$7, wsm, wsk, rs
    printf \" 8: Write  ms          : %9d\n\", \$8
    printf \" 9: I/Os   in progress : %9d\n\", \$9
    printf \"10: I/O    ms          : %9d\n\", \$10
    printf \"11: I/O    ms weighted : %9d\n\", \$11

    printf \"\n\nFILE <%d> %s:\n\", inode, dev_path

    system(\"sudo debugfs -R '\''cat <\"inode\">'\'' \"dev_path\" | xxd -a -c 32\")
}
"' "$fn_stat"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.