Why does GNU Core Utilities stat (including du and ls) report the following sizes (ext4 in GNU/Linux)

coreutilsdisk-usagefilesystemslsstat

If I have the following contents in a directory:

  • empty_dir: Empty directory.
  • empty_file: Empty file.
  • one_char: File consisting of one character.
  • several_blocks: File consisting of several blocks (but not "too" large or "sparse").

Then, ls will display the following †:

$ ls -Gghs
total 152K
8,0K drwxr-xr-x 2 4,0K dec 21 23:34 empty_dir
4,0K -rw-r--r-- 1    0 dec 21 23:21 empty_file
8,0K -rw-r--r-- 1    1 dec 21 23:22 one_char
132K -rw-r--r-- 1 127K dec 22 00:14 several_blocks

Secondly, stat displays the following:

$ stat empty_dir/
  File: empty_dir/
  Size: 4096            Blocks: 16         IO Block: 4096   directory
  ...

$ stat empty_file 
  File: empty_file
  Size: 0               Blocks: 8          IO Block: 4096   regular empty file
  ...

$ stat one_char 
  File: one_char
  Size: 1               Blocks: 16         IO Block: 4096   regular file
  ...

$ stat several_blocks 
  File: several_blocks
  Size: 129760          Blocks: 264        IO Block: 4096   regular file
  ...

Thirdly, du displays the following:

$ du -h empty_dir/
8,0K    empty_dir/

$ du -h empty_file 
4,0K    empty_file

$ du -h one_char 
8,0K    one_char

$ du -h several_blocks 
132K    several_blocks

Lastly:

$ tune2fs /dev/nvme0n1p2 -l
...
Block size:               4096
...
Inode size:               256
...

The size of the blocks reported by stat is 512 B, which means that the output between stat, ls, and du is consistent:

  • empty_dir: 16 * 512 / 1024 = 4096 + 4096 = 8 KiB.
  • empty_file: 8 * 512 / 1024 = 0 + 4096 = 4 KiB.
  • one_char: 16 * 512 / 1024 = 4096 + 4096 = 8 KiB.
  • several_blocks: 264 * 512 / 1024 = 129760 + 5408 = 129760 + 1312 + 4096 = 131072 + 4096 = 32 * 4096 + 4096 = 132 KiB.

Questions

  1. Why is the allocated size for empty_dir and one_char two blocks (of size 4096 B) and not one?
  2. Why is the allocated size for empty_file one block and not zero?
  3. Why is the allocated size for several_blocks (and larger files in general) more than one block larger than the apparent size ((264 * 512) – 129760 = 5408 > 4096)?

I suspect the additional block is the one containing the inode, like this questioner asks (but goes unanswered). Similarly this questioner has observed the double size, but it is incorrectly formulated in the question and receives an answer to the other part of the question. However, this answer to a different question, suggests that there should be no additional blocks (which was my intuition).

  1. Are our systems incorrectly configured?
  2. Assuming the block containing the inode is counted: When using du on multiple files, does it compensate for counting the inode block several times, should multiple inodes be in the same block (since one block can contain 16 inodes (4096 / 256 = 16))?

Appendix

@WumpusQ.Wumbley speculated that it could be extended attributes and this turned out to be the case!

getfattr returns user.com.dropbox.attributes. Turns out the testing directory was a subdirectory deep down in a directory that was symbolically linked into my Dropbox folder. See the accepted answer below.


This uses GNU Core Utilities 8.30 on GNU/Linux with kernel 4.19.1 (Manjaro) on ext4 on a NVME SSD.

Best Answer

@WumpusQ.Wumbley pointed out the cause in a comment: extended attributes.

For completeness sake the answers are presented below.

Extended attributes, in this case applied by Dropbox (getfattr returns user.com.dropbox.attributes), uses additional blocks for storage. Without these extended attributes ls (and the other commands) returns:

$ ls -Gghs
total 136K
4,0K drwxr-xr-x 2 4,0K dec 22 20:11 empty_dir
   0 -rw-r--r-- 1    0 dec 22 20:11 empty_file
4,0K -rw-r--r-- 1    1 dec 22 20:12 one_char
128K -rw-r--r-- 1 127K dec 22 20:13 several_blocks

As expected.

In addition, stat for the only interesting case of several_blocks returns:

$ stat several_blocks 
  File: several_blocks
  Size: 129760          Blocks: 256        IO Block: 4096   regular file
  ...

Which is also as expected, since 256 * 512 - 129760 = 1312 < 4096, i.e., no extra block used.

  1. Due to extended attributes.
  2. Due to extended attributes.
  3. Due to extended attributes.
  4. No, but be aware of extended attributes added by applications.
  5. Incorrect assumption.
Related Question