L'incapacité de stat
pour montrer que le temps de création est dû à la limitation de la stat(2)
appel système dont la structure de retour ne comprend pas de champ pour le temps de création. À partir de Linux 4.11 (c'est-à-dire 17.10 et plus récents*), cependant, le nouveau statx(2)
appel système est disponible, qui inclut un temps de création dans sa structure de retour.
* Et éventuellement sur les anciennes versions LTS utilisant les noyaux HWE (Hardware enablement stack). Consultez <code>uname -r</code> pour voir si vous utilisez un noyau au moins à 4.11 pour confirmer.
Malheureusement, il n'est pas facile d'appeler les appels système directement dans un programme C. En général, la glibc fournit un wrapper qui facilite le travail, mais la glibc n'a ajouté qu'un wrapper pour statx(2)
en août 2018 (version 2.28 disponible en 18.10). Le site stat
Le commandement lui-même a obtenu le soutien de statx(2)
uniquement dans GNU coreutils 8.31 (publié en mars 2019) Cependant, même Ubuntu 20.04 ne dispose que de coreutils 8.30 .
Mais je ne pense pas que cela sera reporté sur les versions LTS même si elles obtiennent, ou sont déjà sur, des noyaux ou des glibcs plus récents. Donc, je ne m'attends pas à ce que stat
sur toute version LTS actuelle (16.04, 18.04 ou 20.04) pour toujours imprimer l'heure de création sans intervention manuelle.
Sous la version 18.10 et les versions plus récentes, vous pouvez utiliser directement la commande statx
comme décrit dans man 2 statx
(notez que la page de manuel de la version 18.10 est incorrecte en affirmant que la glibc n'a pas encore ajouté le wrapper).
Et dans Ubuntu 20.10, vous pourrez utiliser stat
directement :
# stat --version
stat (GNU coreutils) 8.32
Copyright (C) 2020 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: 88h/136d Inode: 57279593 Links: 1
Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2020-08-18 06:57:46.912243164 +0000
Modify: 2020-08-18 06:57:06.768492334 +0000
Change: 2020-08-18 06:57:59.136165661 +0000
Birth: 2020-08-18 06:57:06.768492334 +0000
Pour les systèmes plus anciens, heureusement, @whotwagner a écrit un exemple de programme C qui montre comment utiliser le statx(2)
sur les systèmes x86 et x86-64. Sa sortie a le même format que stat
L'heure de naissance est la valeur par défaut, sans aucune option de formatage, mais il est facile de la modifier pour n'imprimer que l'heure de naissance.
D'abord, clonez-le :
git clone https://github.com/whotwagner/statx-fun
Vous pouvez compiler le statx.c
ou, si vous souhaitez uniquement connaître l'heure de naissance, créez un code birth.c
dans le répertoire cloné avec le code suivant (qui est une version minimale de statx.c
en imprimant uniquement l'horodatage de création, avec une précision de l'ordre de la nanoseconde) :
#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.%u\n", *&stxbuf.stx_btime.tv_sec, *&stxbuf.stx_btime.tv_nsec);
}
return EXIT_SUCCESS;
}
Entonces:
$ make birth
$ ./birth ./birth.c
1511793291.254337149
$ ./birth ./birth.c | xargs -I {} date -d @{}
Mon Nov 27 14:34:51 UTC 2017
En théorie, cela devrait rendre le temps de création plus accessible :
- plus de systèmes de fichiers devraient être supportés que les seuls ext* (
debugfs
est un outil pour les systèmes de fichiers ext2/3/4, et inutilisable sur les autres)
- vous n'avez pas besoin de root pour l'utiliser (sauf pour installer certains paquets nécessaires, comme
make
y linux-libc-dev
).
Tester un système xfs, par exemple :
$ truncate -s 1G temp; mkfs -t xfs temp; mkdir foo; sudo mount temp foo; sudo chown $USER foo
$ touch foo/bar
$ # some time later
$ echo > foo/bar
$ chmod og-w foo/bar
$ ./birth foo/bar | xargs -I {} date -d @{}
Mon Nov 27 14:43:21 UTC 2017
$ stat foo/bar
File: foo/bar
Size: 1 Blocks: 8 IO Block: 4096 regular file
Device: 700h/1792d Inode: 99 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1000/ muru) Gid: ( 1000/ muru)
Access: 2017-11-27 14:43:32.845579010 +0000
Modify: 2017-11-27 14:44:38.809696644 +0000
Change: 2017-11-27 14:44:45.536112317 +0000
Birth: -
Cependant, cela n'a pas fonctionné pour NTFS et exfat. Je suppose que les systèmes de fichiers FUSE pour ceux-ci n'incluent pas le temps de création.