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 test.pl                                                     slave-iv
  File: ‘test.pl’
  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
398264
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
EXTENTS:
(0):3308774

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}"
  done
    }

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: -  

and/or

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 <https://gnu.org/licenses/gpl.html>.
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 https://github.com/whotwagner/statx-fun

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
#define _ATFILE_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
#else
#define __NR_statx 383
#endif

#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;
                break;
            case 'l':
                flags &= ~AT_SYMLINK_NOFOLLOW;
                break;
            case 'f':
                flags &= ~AT_STATX_SYNC_TYPE;
                flags |= AT_STATX_FORCE_SYNC;
                break;
            case 'd':
                flags &= ~AT_STATX_SYNC_TYPE;
                flags |= AT_STATX_DONT_SYNC;
                break;
            default:
                exit(EXIT_SUCCESS);
                break;
        }
    }

    if (optind >= argc) {
        exit(EXIT_FAILURE);
    }

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

Then:

$ make birth
$ ./birth ./birth.c
1511793291.254337149
$ ./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.)


OLD ANSWER:

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
attributes.

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.