Birth is empty on ext4

I was just reading up on the Birth section of stat and it appears ext4 should support it, but even a file I just created leaves it empty.

 ~  % touch test                                                       slave-iv
 ~  % stat                                                     slave-iv
  File: ‘’
  Size: 173             Blocks: 8          IO Block: 4096   regular file
Device: 903h/2307d      Inode: 41943086    Links: 1
Access: (0600/-rw-------)  Uid: ( 1000/xenoterracide)   Gid: (  100/   users)
Access: 2012-09-22 18:22:16.924634497 -0500
Modify: 2012-09-22 18:22:16.924634497 -0500
Change: 2012-09-22 18:22:16.947967935 -0500
 Birth: -

 ~  % sudo tune2fs -l /dev/md3 | psp4                                  slave-iv
tune2fs 1.42.5 (29-Jul-2012)
Filesystem volume name:   home
Last mounted on:          /home
Filesystem UUID:          ab2e39fb-acdd-416a-9e10-b501498056de
Filesystem magic number:  0xEF53
Filesystem revision #:    1 (dynamic)
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype needs_recovery extent flex_bg sparse_super large_file huge_file uninit_bg dir_nlink extra_isize
Filesystem flags:         signed_directory_hash 
Default mount options:    journal_data
Filesystem state:         clean
Errors behavior:          Continue
Filesystem OS type:       Linux
Inode count:              59736064
Block count:              238920960
Reserved block count:     11946048
Free blocks:              34486248
Free inodes:              59610013
First block:              0
Block size:               4096
Fragment size:            4096
Reserved GDT blocks:      967
Blocks per group:         32768
Fragments per group:      32768
Inodes per group:         8192
Inode blocks per group:   512
RAID stride:              128
RAID stripe width:        256
Flex block group size:    16
Filesystem created:       Mon May 31 20:36:30 2010
Last mount time:          Sat Oct  6 11:01:01 2012
Last write time:          Sat Oct  6 11:01:01 2012
Mount count:              14
Maximum mount count:      34
Last checked:             Tue Jul 10 08:26:37 2012
Check interval:           15552000 (6 months)
Next check after:         Sun Jan  6 07:26:37 2013
Lifetime writes:          7255 GB
Reserved blocks uid:      0 (user root)
Reserved blocks gid:      0 (group root)
First inode:              11
Inode size:           256
Required extra isize:     28
Desired extra isize:      28
Journal inode:            8
First orphan inode:       55313243
Default directory hash:   half_md4
Directory Hash Seed:      442c66e8-8b67-4a8c-92a6-2e2d0c220044
Journal backup:           inode blocks

Why doesn’t my ext4 partition populate this field?

Asked By: xenoterracide


The field gets populated (see below) only coreutils stat does not display it. Apparently they’re waiting1 for the xstat() interface.

coreutils patches – aug. 2012 – TODO

stat(1) and ls(1) support for birth time. Dependent on xstat() being
provided by the kernel

You can get the creation time via debugfs:

debugfs -R 'stat <inode_number>' DEVICE

e.g. for my /etc/profile which is on /dev/sda2 (see How to find out what device a file is on):

stat -c %i /etc/profile
debugfs -R 'stat <398264>' /dev/sda2
debugfs 1.42.5 (29-Jul-2012)
Inode: 398264   Type: regular    Mode:  0644   Flags: 0x80000
Generation: 2058737571    Version: 0x00000000:00000001
User:     0   Group:     0   Size: 562
File ACL: 0    Directory ACL: 0
Links: 1   Blockcount: 8
Fragment:  Address: 0    Number: 0    Size: 0
 ctime: 0x506b860b:19fa3c34 -- Wed Oct  3 02:25:47 2012
 atime: 0x50476677:dcd84978 -- Wed Sep  5 16:49:27 2012
 mtime: 0x506b860b:19fa3c34 -- Wed Oct  3 02:25:47 2012
crtime: 0x50476677:dcd84978 -- Wed Sep  5 16:49:27 2012
Size of extra inode fields: 28

Time fields meaning:

  • ctime: file change time.
  • atime: file access time.
  • mtime: file modification time.
  • crtime: file creation time.

1 Linus’ reply on LKML thread

Answered By: don_crissti

I combined this into a simple shell function:

get_crtime() {
  for target in "${@}"; do
    inode=$(stat -c %i "${target}")
    fs=$(df  --output=source "${target}"  | tail -1)
    crtime=$(sudo debugfs -R 'stat <'"${inode}"'>' "${fs}" 2>/dev/null | 
    grep -oP 'crtime.*--s*K.*')
    printf "%st%sn" "${target}" "${crtime}"

You can then run it with

$ get_crtime foo foo/file /etc/
foo Wed May 21 17:11:08 2014
foo/file    Wed May 21 17:11:27 2014
/etc/   Wed Aug  1 20:42:03 2012
Answered By: terdon

There’s another case where Birth time will be empty/zero/dash: Ext4’s Inode size has to be at least 256bytes to store crtime. The problem occur if you initially created the filesystem smaller than 512MB ( the default Inode size will be 128 bytes, see /etc/mke2fs.conf and mkfs.ext4 manpage).

stat -c '%n: %w' testfile
testfile: -  


stat -c '%n: %W' testfile
testfile: 0

Now check the filesystem inode (is it big enough to store crtime?):

tune2fs -l $(df . --output=source | grep ^/) | grep "Inode size:"
Inode size:           128

Technical information: On the Ext4 Disk Layout page, note that some attributes of the inode tables are beyond 0x80 (128).

Answered By: Franklin Piat

For what it’s worth I was feeling pedantic so wrote a bash wrapper around stat to silently support crtime using debugfs to fetch it from an underlying ext4 filesystem if available. I hope it’s robust. Find it here.

Note that a fix is ostensibly on the todo list for Linux as documented in that script. So this wrapper has a nominal lifespan only until that is done and is more an exercise in what’s doable.

Answered By: Bernd Wechner

The xstat function never got merged into mainline. However, a new statx call was proposed later on, and was merged in Linux 4.11. The new statx(2) system call does include a creation time in its return struct. A wrapper for statx(2) was added to glibc only in 2.28 (release August 2018). And support for using this wrapper was added in GNU coreutils 8.31 (released March 2019):

stat now prints file creation time when supported by the file system,
on GNU Linux systems with glibc >= 2.28 and kernel >= 4.11.

% stat --version
stat (GNU coreutils) 8.31
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Michael Meskes.
% stat /
  File: /
  Size: 4096            Blocks: 8          IO Block: 4096   directory
Device: b302h/45826d    Inode: 2           Links: 17
Access: (0755/drwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2019-06-06 20:03:12.898725626 +0900
Modify: 2019-05-28 05:15:44.452651395 +0900
Change: 2019-05-28 05:15:44.452651395 +0900
 Birth: 2018-06-07 20:35:54.000000000 +0900

What follows is a demo of statx where userland has yet to catch up (older glibc or coreutils). It’s not easy to call system calls directly in a C program. Typically glibc provides a wrapper that makes the job easy, but Luckily, @whotwagner wrote a sample C program that shows how to use the statx(2) system call on x86 and x86-64 systems. Its output is the same format as stat‘s default, without any formatting options, but it’s simple to modify it to print just the birth time. (If you have a new enough glibc, you won’t need this – you can use statx directly as described in man 2 statx).

First, clone it:

git clone

You can compile the statx.c code, or, if you just want the birth time, create a birth.c in the cloned directory with the following code (which is a minimal version of statx.c printing just the creation timestamp including nanosecond precision):

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include "statx.h"
#include <time.h>
#include <getopt.h>
#include <string.h>

// does not (yet) provide a wrapper for the statx() system call
#include <sys/syscall.h>

/* this code works ony with x86 and x86_64 */
#if __x86_64__
#define __NR_statx 332
#define __NR_statx 383

#define statx(a,b,c,d,e) syscall(__NR_statx,(a),(b),(c),(d),(e))

int main(int argc, char *argv[])
    int dirfd = AT_FDCWD;
    int flags = AT_SYMLINK_NOFOLLOW;
    unsigned int mask = STATX_ALL;
    struct statx stxbuf;
    long ret = 0;

    int opt = 0;

    while(( opt = getopt(argc, argv, "alfd")) != -1)
        switch(opt) {
            case 'a':
                flags |= AT_NO_AUTOMOUNT;
            case 'l':
                flags &= ~AT_SYMLINK_NOFOLLOW;
            case 'f':
                flags &= ~AT_STATX_SYNC_TYPE;
                flags |= AT_STATX_FORCE_SYNC;
            case 'd':
                flags &= ~AT_STATX_SYNC_TYPE;
                flags |= AT_STATX_DONT_SYNC;

    if (optind >= argc) {

    for (; optind < argc; optind++) {
        memset(&stxbuf, 0xbf, sizeof(stxbuf));
        ret = statx(dirfd, argv[optind], flags, mask, &stxbuf);
        if( ret < 0)
            return EXIT_FAILURE;
        printf("%lld.%un", *&stxbuf.stx_btime.tv_sec, *&stxbuf.stx_btime.tv_nsec);
    return EXIT_SUCCESS;


$ make birth
$ ./birth ./birth.c
$ ./birth ./birth.c | xargs -I {} date -d @{}
Mon Nov 27 14:34:51 UTC 2017

In theory this should make the creation time accessible on more filesystems than just the ext* ones (debugfs is a tool for ext2/3/4 filesystems, and unusable on others). It did work for an XFS system, but not for NTFS and exfat. I guess the FUSE filesystems for those didn’t include the creation time.

Answered By: muru

As of version 8.31 of GNU coreutils, stat “now prints file creation time when supported by the file system, on GNU Linux systems with glibc >= 2.28 and kernel >= 4.11.”

My OS (Ubuntu 20.04, which comes with Linux kernel 5.4.0-28 and GLIBC 2.31) only came with GNU coreutils 8.30, so I had to verify by compiling version 8.32 of GNU coreutils from source.

Output of ls -l --time=birth --time-style=full-iso --no-group:

$ ls -l --time=birth --time-style=full-iso --no-group
total 13477610
-rwxr--r-- 1 systemd-coredump 13801071714 2017-06-30 04:53:33.211517792 -0400  file

Output of stat on a btrfs file system:

$ stat file
  File: /mnt/btrfs/file
  Size: 13801071714 Blocks: 26955224   IO Block: 4096   regular file
Device: 33h/51d Inode: 4998        Links: 1
Access: (0744/-rwxr--r--)  Uid: (  999/systemd-coredump)   Gid: (  999/systemd-coredump)
Access: 2020-05-04 12:21:51.640487614 -0400
Modify: 2016-01-19 10:32:19.272000000 -0500
Change: 2017-06-30 04:55:14.910839537 -0400
 Birth: 2017-06-30 04:53:33.211517792 -0400

(Strangely, I was unable to get any birth time to show on Arch Linux using a dummy ext4 file system, despite it having met all the above requirements.)


Well, as of version 8.32 of GNU coreutils (stable release), both stat and ls use the statx call.

ls will show the creation/birth time.

** New Features

ls now supports the –time=birth option to display and sort by
file creation time, where available.

And presumably, stat will too.

** Improvements

stat and ls now use the statx() system call where available, which
can operate more efficiently by only retrieving requested

It’s brand-spanking new, having been posted just March 5, 2020, so it may take a while to trickle down unless you’re using a bleeding-edge distro like Arch Linux. (Arch Linux got version 8.32-1 of the coreutils package on April 1, 2020).

Answered By: Kevin Li
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.