Jak znaleźć czas utworzenia pliku?

64

Muszę znaleźć czas utworzenia pliku, kiedy czytam kilka artykułów na ten temat, wszystkie wspominają, że nie ma rozwiązania (jak Site1 , Site2 ).

Kiedy próbowałem statpolecenia, stwierdza Birth: -.

Jak mogę znaleźć czas utworzenia pliku?

nux
źródło
2
Należy pamiętać, że „czas utworzenia” pliku nie gwarantuje dokładności. Istnieje wiele sposobów „fałszowania” dat utworzenia pliku.
Thomas Ward
1
@ThomasWard Wiele więcej niż sposobów fałszowania innych danych plików?
Cees Timmerman,

Odpowiedzi:

67

Istnieje sposób, aby poznać datę utworzenia katalogu, wykonaj następujące kroki:

  1. Poznaj i- węzeł katalogu za pomocą ls -ipolecenia (powiedzmy na przykład, że jego X )

  2. Dowiedz się, na której partycji Twój katalog jest zapisany za pomocą df -T /pathpolecenia (powiedzmy, że jest włączony /dev/sda1)

  3. Teraz użyj tego polecenia: sudo debugfs -R 'stat <X>' /dev/sda1

W wyniku zobaczysz:

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

crtime to data utworzenia pliku.

Co testowałem :

  1. Utworzono katalog w określonym czasie.
  2. Udało mi się.
  3. Zmodyfikowano go, tworząc plik.

  4. Wypróbowałem polecenie i podało dokładny czas.

  5. Potem go modyfikuję i ponownie testuję , crtime pozostał ten sam, ale czas modyfikacji i dostępu się zmienił.
nux
źródło
Publikuję to, ponieważ lubię dyskutować, aby lepiej zrozumieć, zastanawiam się, dlaczego ludzie twierdzą, że Linux nie obsługuje tej funkcji
nux
13
Ponieważ sam Linux nie. System plików ext4 ma te informacje, ale jądro nie udostępnia interfejsu API umożliwiającego dostęp do nich. Najwyraźniej debugfswyodrębnia go bezpośrednio z systemu plików, więc nie musi używać interfejsu API jądra. Zobacz tutaj .
terdon
Przetestowałem to. Działa idealnie na systemie plików ext4
Fahim Babar Patel
1
Wydaje się, że to jest specyficzne dla ext4? Nie działało to dla mnie z XFS.
Quantum7
Jądro, glibc i coreutils obsługują teraz statx()od marca 2019 r.
hippietrail
54

@Nux znalazł świetne rozwiązanie tego problemu, o którym wszyscy powinniście głosować. Postanowiłem napisać małą funkcję, której można użyć do bezpośredniego uruchamiania wszystkiego. Po prostu dodaj to do swojego ~/.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
}

Teraz możesz uruchomić, get_crtimeaby wydrukować daty utworzenia tylu plików lub katalogów, ile chcesz:

$ get_crtime foo foo/file 
foo Wed May 21 17:11:08 2014
foo/file    Wed May 21 17:11:27 2014
terdon
źródło
Zauważ, że nie jest data utworzenia data utworzenia oryginalnego pliku, jeśli plik jest kopią (tak jak to jest z datą modyfikacji). Po skopiowaniu pliku data modyfikacji pochodzi z oryginału, ale data utworzenia pochodzi z kopii. (istnieje pewne nieporozumienie w tym pytaniu: askubuntu.com/questions/529885/... )
Jacob Vlijm
1
@JacobVlijm no tak, oczywiście. Czy to nie oczywiste? Jak mogłoby być inaczej? Kopia to nowy plik, który akurat ma taką samą zawartość jak inny. Nawiasem mówiąc, czas modyfikacji również zmienia się dla kopii. Jest ustawiony na moment utworzenia kopii, chyba że wyraźnie zdecydujesz, że tak się nie stanie przy użyciu cp -plub podobnie.
terdon
Oczywiście, ale jednocześnie nie byłoby to tak nielogiczne, jak na przykład mod. data, gdzieś w pliku data będzie przechowywana w momencie jej utworzenia. Muszę przyznać, że nie wiedziałem, że tak nie było, dopóki nie odpowiedziałem na powiązane pytanie.
Jacob Vlijm
Po prostu wypróbowałem, właśnie skopiowałem pliki w nautilus, data modyfikacji pozostaje taka, jaka jest (była), m. data jest wcześniejsza niż data utworzenia.
Jacob Vlijm
1
@demongolem tak, wydaje się, że wersja CentOS dfnie obsługuje tej --outputopcji. W takim przypadku możesz zastąpić tę linię fs=$(df foo | awk '{a=$1}END{print a}'i funkcja również będzie działać. W tej odpowiedzi pokazuję tylko sposób na zawinięcie polecenia z zaakceptowanej odpowiedzi w sposób, który można uruchomić bezpośrednio dla obiektów docelowych plików / katalogów.
terdon
11

Niemożność statpokazania czasu utworzenia wynika z ograniczenia stat(2)wywołania systemowego , którego struktura powrotu nie zawierała pola dla czasu utworzenia. Począwszy od Linuksa 4.11 (tj. 17.10 i nowszych *) dostępne jest jednak nowe statx(2)wywołanie systemowe , które zawiera czas utworzenia w strukturze zwracanej.

* I prawdopodobnie w starszych wersjach LTS przy użyciu jądra stosu włączania sprzętu (HWE). Sprawdź, uname -rczy używasz jądra przynajmniej w wersji 4.11 w celu potwierdzenia.

Niestety wywołanie systemowe nie jest łatwe bezpośrednio w programie C. Zazwyczaj glibc zapewnia opakowanie, które ułatwia zadanie, ale glibc dodał tylko opakowanie statx(2)w sierpniu 2018 r. (Wersja 2.28 , dostępna w 18.10). Na szczęście @whotwagner napisał przykładowy program C, który pokazuje, jak używać statx(2)wywołania systemowego w systemach x86 i x86-64. Jego format wyjściowy jest taki sam, jak statdomyślny, bez żadnych opcji formatowania, ale łatwo go zmodyfikować, aby wydrukować tylko datę urodzenia.

Najpierw sklonuj:

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

Możesz skompilować statx.ckod lub, jeśli chcesz tylko czas urodzenia, utworzyć birth.cw sklonowanym katalogu następujący kod (który jest minimalną wersją statx.cdrukowania tylko znacznika czasu utworzenia, w tym precyzji nanosekundowej):

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

Następnie:

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

Teoretycznie powinno to uczynić czas tworzenia bardziej dostępnym:

  • powinno być obsługiwanych więcej systemów plików niż tylko systemy ext * ( debugfsjest to narzędzie do systemów plików ext2 / 3/4 i nie nadaje się do użytku na innych)
  • nie potrzebujesz roota, aby z niego korzystać (oprócz instalowania niektórych wymaganych pakietów, takich jak makei linux-libc-dev).

Testowanie systemu XFS, na przykład:

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

Nie działało to jednak w przypadku systemu plików NTFS i exfat. Wydaje mi się, że systemy plików FUSE dla tych nie zawierały czasu utworzenia.


Jeśli, a raczej kiedy glibc doda obsługę statx(2)wywołania systemowego, statnastąpi wkrótce i będziemy mogli użyć do tego zwykłego starego statpolecenia. Ale nie sądzę, że zostanie to przeniesione do wersji LTS, nawet jeśli otrzymają nowsze jądra. Nie spodziewam statsię więc, że w żadnej bieżącej wersji LTS (14.04, 16.04 lub 18.04) kiedykolwiek wydrukuję czas tworzenia bez ręcznej interwencji.

Jednak w dniu 18.10 można bezpośrednio użyć statxfunkcji opisanej w man 2 statx(zauważ, że strona podręcznika 18.10 nie zgadza się z twierdzeniem, że glibc nie dodał jeszcze opakowania).

muru
źródło
Dzięki za link do github. Szukałem kilka miesięcy temu, kiedy wyszedł 4.11 i nic nie znalazłem, a potem zapomniałem o tym.
WinEunuuchs2Unix
@ WinEunuuchs2unix wybaczyć pingując ale to byłoby mądre, aby zapytać o meta miejscu dlaczego muru za konto ma rep po prostu 1?
George Udosen
@GeorgeUdosen To szokujące! Mam przeczucie, dlaczego ...
WinEunuuchs2Unix,
@GeorgeUdosen Pojawiło się ostatnie meta pytanie dotyczące zawieszeń w ogóle i nie będą one adresowane do konkretnego użytkownika: meta.askubuntu.com/questions/18341/ ... Idę teraz do pokoju czatu, aby móc tam kontynuować rozmowę, jeśli życzenie.
WinEunuuchs2Unix,
Czy funkcja jest już dostępna, czy wiesz, jak zmodyfikować to pole? Mogę spróbować utworzyć opakowanie typu ctypes, aby zrobić to w Pythonie. Dzięki.
Gringo Suave
3

TL; DR: Po prostu uruchom: sudo debugfs -R 'stat /path/to/your/file' /dev/<your fs>

(Aby dowiedzieć się, jaki jest twój fs, uruchom df -T /path/to/your/file, najprawdopodobniej tak będzie /dev/sda1).

Długa wersja:

Uruchomimy dwa polecenia:

  1. Znajdź nazwę partycji dla swojego pliku.

    df -T /path/to/your/file

    Dane wyjściowe będą wyglądać następująco (nazwa partycji jest pierwsza):

    Filesystem     Type 1K-blocks    Used Available Use% Mounted on
    /dev/<your fs> ext4   7251432 3481272   3509836  50% /
    
  2. Dowiedz się czas utworzenia tego pliku.

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

    W wyniku wyszukaj ctime.

Łukasz Czerwiński
źródło