Como encontro o horário de criação de um arquivo?

64

Preciso encontrar o horário de criação de um arquivo. Quando li alguns artigos sobre esse problema, todos mencionaram que não há solução (como Site1 , Site2 ).

Quando tentei o statcomando, ele afirma Birth: -.

Então, como posso encontrar o horário de criação de um arquivo?

nux
fonte
2
Lembre-se de que não é garantido que o 'horário de criação' de um arquivo seja preciso. Existem várias maneiras de 'falsificar' as datas de criação em um arquivo.
Thomas Ward
11
@ThomasWard Muito mais do que maneiras de falsificar outros dados de arquivo?
Cees Timmerman

Respostas:

67

Existe uma maneira de saber a data de criação de um diretório, basta seguir estas etapas:

  1. Conheça o inode do diretório por ls -icomando (digamos, por exemplo, seu X )

  2. Saiba em qual partição seu diretório é salvo por df -T /pathcomando (digamos que esteja ativado /dev/sda1)

  3. Agora use este comando: sudo debugfs -R 'stat <X>' /dev/sda1

Você verá na saída:

crtime: 0x4e81cacc:966104fc -- mon Sep 27 14:38:28 2013

crtime é a data de criação do seu arquivo.

O que eu testei :

  1. Criou um diretório em um horário específico.
  2. Acessou.
  3. Modificado, criando um arquivo.

  4. Eu tentei o comando e deu um tempo exato.

  5. Então eu modificá-lo e testar novamente, o tempo crtime permaneceu o mesmo, mas o tempo de modificação e acesso mudou.
nux
fonte
Eu postar isso, porque eu gostaria de discutir para que eu possa entender melhor, estou me pergunto por que as pessoas dizem que o Linux não suporta esse recurso
nux
13
Porque o próprio Linux não. O sistema de arquivos ext4 possui essas informações, mas o kernel não fornece uma API para acessá-las. Aparentemente, debugfsextrai-o diretamente do sistema de arquivos para que não precise usar a API do kernel. Veja aqui .
terdon
Eu testei. Funcionou perfeitamente no sistema de arquivos ext4
Fahim Babar Patel
11
Parece que este é ext4 específico? Não funcionou com o XFS para mim.
Quantum7
O kernel, glibc e coreutils agora são statx()
suportados a
54

O @Nux encontrou uma ótima solução para isso, que todos deveriam votar. Decidi escrever uma pequena função que pode ser usada para executar tudo diretamente. Basta adicionar isso ao seu ~/.bashrc.

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 "%s\t%s\n" "${target}" "${crtime}"
    done
}

Agora, você pode executar get_crtimepara imprimir as datas de criação de quantos arquivos ou diretórios desejar:

$ get_crtime foo foo/file 
foo Wed May 21 17:11:08 2014
foo/file    Wed May 21 17:11:27 2014
terdon
fonte
Note-se que a data de criação não é a data de criação do arquivo original se o arquivo é uma cópia (como ele é com a data de modificação). Depois que um arquivo é copiado, a data de modificação é do original, mas a data de criação é da cópia. (Há algum mal-entendido nesta questão: askubuntu.com/questions/529885/... )
Jacob Vlijm
11
@JacobVlijm bem, sim, é claro. Isso não é óbvio? Como poderia ser de outra maneira? Uma cópia é um novo arquivo que, por acaso, possui o mesmo conteúdo que outro. A hora da modificação também muda para uma cópia a propósito. É definido no momento em que a cópia foi criada, a menos que você escolha explicitamente que isso não aconteça usando cp -pou similar.
terdon
Absolutamente, mas ao mesmo tempo, não seria tão lógico se, como o mod. date, em algum lugar do arquivo a data seria armazenada quando originada. Devo admitir que não sabia que não era esse o caso até responder à pergunta vinculada.
Jacob Vlijm
Apenas tentei a propósito, acabei de copiar arquivos no nautilus, a data da modificação permanece como está (era), m. data é anterior à data de criação.
Jacob Vlijm
11
@demongolem sim, a versão do CentOS dfnão parece suportar a --outputopção. Nesse caso, você pode substituir essa linha por fs=$(df foo | awk '{a=$1}END{print a}'e a função também funcionará. Tudo o que estou mostrando nesta resposta é uma maneira de agrupar o comando da resposta aceita de uma maneira que possa ser executada diretamente para destinos de arquivo / diretório.
terdon
11

A incapacidade de statmostrar o tempo de criação é devido à limitação da stat(2)chamada do sistema , cuja estrutura de retorno não incluiu um campo para o tempo de criação. A partir do Linux 4.11 (ou seja, 17.10 e mais recente *), no entanto, a nova statx(2)chamada do sistema está disponível, o que inclui um tempo de criação em sua estrutura de retorno.

* E possivelmente em versões mais antigas do LTS usando os kernels da pilha de habilitação de hardware (HWE). Verifique uname -rse você está usando um kernel pelo menos em 4.11 para confirmar.

Infelizmente, não é fácil fazer chamadas do sistema diretamente em um programa C. Normalmente, a glibc fornece um wrapper que facilita o trabalho, mas a glibc adicionou um wrapper apenas statx(2)em agosto de 2018 (versão 2.28 , disponível em 18.10). Felizmente, o @whotwagner escreveu um programa C de amostra que mostra como usar a statx(2)chamada do sistema nos sistemas x86 e x86-64. Sua saída é do mesmo formato que stato padrão, sem nenhuma opção de formatação, mas é simples modificá-la para imprimir apenas a hora do nascimento.

Primeiro, clone-o:

git clone https://github.com/whotwagner/statx-fun

Você pode compilar o statx.ccódigo ou, se desejar apenas a hora do nascimento, criar um birth.cno diretório clonado com o seguinte código (que é uma versão mínima da statx.cimpressão, apenas o carimbo de data / hora da criação, incluindo precisão de nanossegundos):

#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;
}

Então:

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

Em teoria, isso deve tornar o tempo de criação mais acessível:

  • mais sistemas de arquivos devem ser suportados do que apenas os ext * ( debugfsé uma ferramenta para sistemas de arquivos ext2 / 3/4 e inutilizável em outros)
  • você não precisa do root para usá-lo (exceto para instalar alguns pacotes necessários, como makee linux-libc-dev).

Testando um sistema xfs, por exemplo:

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

No entanto, isso não funcionou para NTFS e exfat. Eu acho que os sistemas de arquivos FUSE para aqueles não incluíram o tempo de criação.


Se, ou melhor, quando, o glibc adicionar suporte à statx(2)chamada do sistema, statseguirá em breve e poderemos usar o statcomando antigo simples para isso. Mas acho que isso não será suportado nos lançamentos do LTS, mesmo que eles tenham novos kernels. Portanto, não espero que statem nenhuma versão atual do LTS (14.04, 16.04 ou 18.04) imprima o tempo de criação sem intervenção manual.

No 18.10, no entanto, você pode usar diretamente a statxfunção conforme descrito em man 2 statx(observe que a página de manual 18.10 está incorreta ao afirmar que a glibc ainda não adicionou o wrapper).

muru
fonte
Obrigado por vincular ao github. Pesquisei alguns meses atrás, quando o 4.11 foi lançado e não encontrei nada e esqueci.
WinEunuuchs2Unix
@ WinEunuuchs2unix perdoe pelo ping, mas seria sensato perguntar no meta site por que a conta de muru tem um representante de apenas 1?
George Udosen
@GeorgeUdosen Isso é chocante! Eu tenho um palpite por que ...
WinEunuuchs2Unix 23/12
@GeorgeUdosen Há uma recente questão meta sobre suspensões em geral e elas não abordam um usuário específico: meta.askubuntu.com/questions/18341/… Estou entrando na sala de bate-papo agora para que você possa continuar conversando lá, se desejar desejo.
WinEunuuchs2Unix
Agora que o recurso está disponível, você saberia como modificar esse campo? Eu posso tentar criar um wrapper ctypes para fazê-lo em python. Obrigado.
Gringo Suave
3

TL; DR: Basta executar: sudo debugfs -R 'stat /path/to/your/file' /dev/<your fs>

(Para descobrir seu fs, corra df -T /path/to/your/file, provavelmente será /dev/sda1).

Versão longa:

Vamos executar dois comandos:

  1. Descubra o nome do nome da partição para o seu arquivo.

    df -T /path/to/your/file

    A saída ficará assim (o nome da partição é o primeiro):

    Filesystem     Type 1K-blocks    Used Available Use% Mounted on
    /dev/<your fs> ext4   7251432 3481272   3509836  50% /
    
  2. Descubra o tempo de criação para esse arquivo.

    sudo debugfs -R 'stat /path/to/your/file' /dev/<your fs>
    

    Na saída, procure ctime.

Lukasz Czerwinski
fonte