02 July 2011

Feedback regarding my "stat or ls" post

Hi all

Several people are kind enough to share her/his thoughts about my "stat or ls" post. One of them even share this forum post. Quite neat I must say!

Basically they said that both "ls" and "stat" output are correct. One even compare it with "du" output (by default, "du" is using block size unit when showing file size).

What I might failed to stress was, the tests done in my last post was done on top of SELinux enabled-ext3 filesystem. "So what?" you might ask. Briefly maybe none. But my friend pointed that stat was accouting extra blocks that might (I say "might" because my friend is not so sure) contain metadata such as SELinux and ACL.

So far, I find it consistent that the used blocks reported in "ls -ls" is always half of one reported by "stat". It must be something related to return value of function I stated in my previous post. 1KiB? hmmmm......

PS: Further info regarding by block device and filesystem. Thanks to Justin Cook who pointed me to this neat tool:

# blockdev --getbsz /dev/sda3
# blockdev --getss /dev/sda3
The first is my fs block size, the latter is my disk sector size.


Mulyadi Santosa.

01 July 2011

stat or ls? which one showing correct number of used block?

The answer: stat!

(credits to Manish Katiyar who helped me tinkering with the codes.... click here to find out more about him)

Why? OK, suppose we create a test file like this:
dd if=/dev/zero of=doang.img bs=1K count=3

And then we run both ls and stat against it:
$ stat doang.img
  File: `doang.img'
  Size: 3072            Blocks: 16         IO Block: 4096   regular file

$ ls -ls doang.img
8 -rw-rw-r-- 1 mulyadi mulyadi 3072 Jun 30 12:36 doang.img

"what the @#$%?" Yeah I suppose you would say that. stat is saying the file is using 16 blocks while ls says 8 (leftmost one). Both couldn't be true, right?

Strace comes to rescue. Stracing both of them would yield something like this:

lstat64("/home/mulyadi/doang.img", {st_dev=makedev(8, 5), st_ino=1304650, st_mode=S_IFREG|0664, st_nlink=1, st_uid=500, st_gid=500, st_blksize=4096, st_blocks=16, st_size=3072, st_atime=2011/06/30-12:36:40, st_mtime=2011/06/30-12:36:40, st_ctime=2011/06/30-12:36:40}) = 0

lstat/fstat/stat is glibc function which fetch inode information about a file. In turn it will call stat syscall.

According to "man 2 stat", st_blocks denotes the number of blocks allocated for target file. So, we could directly say stat shows correct number.

But wait... why does ls show us 8? To find out, you need debuginfo of coreutils package or download coreutils package and compile by yourself. I assume you pick the latter. By default, compilation will leave debugging-ready binary in its source directory.

To make story short:
$ gdb src/ls
(gdb) b default_block_size
Breakpoint 1 at 0x80553e6: file human.c, line 407.
(gdb) r -ls ~/doang.img
Starting program: /home/mulyadi/Download/SOURCE/coreutils-8.12/src/ls -ls ~/doang.img
[Thread debugging using libthread_db enabled]

Breakpoint 1, default_block_size () at human.c:407
407       return getenv ("POSIXLY_CORRECT") ? 512 : DEFAULT_BLOCK_SIZE;

So, the above function would yield the "correct" block size which will be used for further computation. Guess what it returns?

(gdb) finish
Run till exit from #0  default_block_size () at human.c:407
0x0805553c in humblock (spec=0x0, opts=0x805feb0, block_size=0x805feb8)
    at human.c:419
419         *block_size = default_block_size ();
Value returned is $1 = 1024

Tada............. 1024 bye a.k.a 1 KiB ladies and gentlemen! That's why you see 8. It's 16*512/1024 = 16/2 = 8! Solved!

PS: this means my system, in this case ( a CentOS 5.x ) is not POSIX-ly correct. Not sure if that's a bad news or good news... :)

Conclusion: take any command's output with a grain of salt. Trusting them blindly sometimes might mislead yourself into wrong information. You have been warned.


Mulyadi Santosa.