Google Chrome 136 byl prohlášen za stabilní. Nejnovější stabilní verze 136.0.7103.59 přináší řadu novinek z hlediska uživatelů i vývojářů. Podrobný přehled v poznámkách k vydání. Opraveno bylo 8 bezpečnostních chyb. Vylepšeny byly také nástroje pro vývojáře.
Homebrew (Wikipedie), správce balíčků pro macOS a od verze 2.0.0 také pro Linux, byl vydán ve verzi 4.5.0. Na stránce Homebrew Formulae lze procházet seznamem balíčků. K dispozici jsou také různé statistiky.
Byl vydán Mozilla Firefox 138.0. Přehled novinek v poznámkách k vydání a poznámkách k vydání pro vývojáře. Řešeny jsou rovněž bezpečnostní chyby. Nový Firefox 138 je již k dispozici také na Flathubu a Snapcraftu.
Šestnáctý ročník ne-konference jOpenSpace se koná 3. – 5. října 2025 v Hotelu Antoň v Telči. Pro účast je potřeba vyplnit registrační formulář. Ne-konference neznamená, že se organizátorům nechce připravovat program, ale naopak dává prostor všem pozvaným, aby si program sami složili z toho nejzajímavějšího, čím se v poslední době zabývají nebo co je oslovilo. Obsah, který vytvářejí všichni účastníci, se skládá z desetiminutových
… více »Richard Stallman přednáší ve středu 7. května od 16:30 na Technické univerzitě v Liberci o vlivu technologií na svobodu. Přednáška je určená jak odborné tak laické veřejnosti.
Jean-Baptiste Mardelle se v příspěvku na blogu rozepsal o novinkám v nejnovější verzi 25.04.0 editoru videa Kdenlive (Wikipedie). Ke stažení také na Flathubu.
TmuxAI (GitHub) je AI asistent pro práci v terminálu. Vyžaduje účet na OpenRouter.
Byla vydána nová verze R14.1.4 desktopového prostředí Trinity Desktop Environment (TDE, fork KDE 3.5, Wikipedie). Přehled novinek i s náhledy v poznámkách k vydání. Podrobný přehled v Changelogu.
Bylo vydáno OpenBSD 7.7. Opět bez písničky.
Zápisky v tomto blogu podléhají licenci Creative Commons Uveďte původ-Zachovejte licenci 4.0 Mezinárodní (CC BY-SA 4.0).
Git repozitář se zdrojovými soubory tohoto blogu v pandoc markdown formátu: marbu/abclinuxu-blog-hromada.
Zarazilo vás někdy, že příkaz stat(1)
z GNU Coreutils na Linuxu vypisuje kromě klasické trojice unixových časových značek access, modify a change navíc také jakési birth, u kterého ale hodnota chybí? Co tu vůbec to prázdné birth dělá? Zajímat o tuto málo známou časovou značku jsem se začal až před pár měsíci při debugování jednoho problému, kdy jsem se snažil chytil čeho se dalo. A i když mi to nakonec přímo nepomohlo, postupně jsem se začal nořit do její historie a budoucnosti, takže tento zápisek je někde na pomezí softwarové archeologie a jaderných novinek, a mj. se v něm dozvíte, kde se tato časová značka vzala, jak s ní dnes na GNU Linuxových distribucích pracovat a jak to s ní vypadá do budoucna.
Pro lepší představu o čem tu píšu. Na následující ukázce je stat zavolaný na souboru uloženém na etx4. To vše na Fedoře 29, s GNU Coreutils 8.30 a jádrem 4.19.8:
$ stat public_html
File: public_html
Size: 4096 Blocks: 8 IO Block: 4096 directory
Device: fd07h/64775d Inode: 7341469 Links: 2
Access: (0755/drwxr-xr-x) Uid: ( 1000/ martin) Gid: ( 1000/ martin)
Context: unconfined_u:object_r:httpd_user_content_t:s0
Access: 2018-12-09 12:59:16.321622899 +0100
Modify: 2018-05-19 21:35:13.813112882 +0200
Change: 2018-12-09 02:51:38.961313721 +0100
Birth: -
$
Unixové souborové časové značky, jak je vrací systémové volání stat, jsou standardizované v normě POSIX takto:
Časová značka | Položka v struct stat |
Význam |
---|---|---|
access | st_atim |
poslední přístup k datům souboru |
modify | st_mtim |
poslední změna dat souboru |
change | st_ctim |
poslední změna statusu souboru (inode) |
To, že se položka pro access time ve struktuře stat
aktuálně jmenuje st_atim
je dáno tím, že v ní je čas počítaný na nanosekundy, zatímco původní a dnes už zastaralé st_atime
počítá čas se sekundovou přesností a je udržováno kvůli zpětné kompatibilitě. V Linuxu je tohle implementováno už od verze 2.5.48, viz stat(2)
. Dál v textu budu místo access time psát prostě atime, místo modify time mtime a pod, bez ohledu na rozdíl mezi st_atim
a st_atime
.
Z pohledu tohoto zápisku je ale důležitější a na první pohled nápadné to, že čas vzniku souboru tu nenajdeme.
Přitom pokud si někdo unixem nezasažený zběžně prohlédne jména časových značek v struktuře stat
(viz tabulka výše), mohl by si myslet, že čas vzniku souboru podporovaný je a že ctime je asi zkratka pro creation time. Další studium man stránky by ale tento omyl rychle vyvrátilo. Podobně pokud si dnes přečtete článek The UNIX Time-Sharing System od tvůrců Unixu z roku 1973, mohlo by vás napadnout, že ctime opravu původně vznik souboru reprezentovalo aby se jeho význam později změnil, protože v tomto článku se mimo jiné píše:
The entry found thereby (the file’s i-node) contains the description of the file: … time of creation, last use, and last modification
Nicméně jak upozorňuje wikipedie, to by byl opět omyl. Několik prvních verzí tzv. Research Unixu sice čas vzniku souboru skutečně podporovalo, jak se můžeme přesvědčit např. v man stránce pro stat(2)
z verze 3 z února roku 1973:
NAME stat -- get file status
SYNOPSIS sys stat; name; buf / stat = 18.
DESCRIPTION name points to a null-terminated string naming a
file; buf is the address of a 34(10) byte buffer into which in-
formation is placed concerning the file. It is unnecessary to
have any permissions at all with respect to the file, but all di-
rectories leading to the file must be readable.
After stat, buf has the following format:
buf, +1 i-number
+2,+3 flags (see below)
+4 number of links
+5 user ID of owner
+6,+7 size in bytes
+8,+9 first indirect block or contents block
+22,+23 eighth indirect block or contents block
+24,+25,+26,+27 creation time
+28,+29,+30,+31 modification time
+32,+33 unused
Btw zajímavé taky je, že příkaz stat z téže verze creation time neuvádí, tedy aspoň dle man stránky stat(1)
, zdroják se mi nepodařilo dohledat:
NAME stat -- get file status
SYNOPSIS stat name1 ...
DESCRIPTION stat gives several kinds of information about one
or more files:
i-number
access mode
number of links
owner
size in bytes
date and time of last modification
name (useful when several files are named)
Ale hned v další verzi (Research) Unixu, tj. verzi 4 z listopadu 1973, již čas vzniku souboru nenajdeme. Za povšimnutí stojí, že V4 byla první verze Unixu napsaná v céčku. Díky tomu tak byly (mimo jiné) poprvé zavedeny názvy položek ve struktuře stat, které se ale trochu liší od dnes používaných. Viz opět manstránka stat(2)
z V4:
stat get file status (stat = 18.)
sys stat; name; buf stat(name, buf)
char *name;
struct inode *buf; points to a null-terminated string naming a
file; is the address of a 36(10) byte buffer into which informa-
tion is placed concerning the file. It is unnecessary to have
any permissions at all with respect to the file, but all directo-
ries leading to the file must be readable. After has the follow-
ing structure (starting offset given in bytes):
struct {
char minor; /* +0: minor device of i-node */
char major; /* +1: major device */
int inumber /* +2 */
int flags; /* +4: see below */
char nlinks; /* +6: number of links to file */
char uid; /* +7: user ID of owner */
char gid; /* +8: group ID of owner */
char size0; /* +9: high byte of 24-bit size */
int size1; /* +10: low word of 24-bit size */
int addr[8]; /* +12: block numbers or device number */
int actime[2]; /* +28: time of last access */
int modtime[2]; /* +32: time of last modification */
};
Časová značka ctime se pak poprvé objevila až ve verzi 7 z ledna 1979, a to hned v dnešním významu change time, takže ctime opravdu creation time nikdy neznamenalo:
struct stat
{
dev_t st_dev;
ino_t st_ino;
unsigned short st_mode;
short st_nlink;
short st_uid;
short st_gid;
dev_t st_rdev;
off_t st_size;
time_t st_atime;
time_t st_mtime;
time_t st_ctime;
};
V této podobě pak časové značky ze struktury stat vydržely až do POSIX standardu a dnešních dní (tedy až na ten výše zmiňovaný detail s nanosekundovou přesností).
<hint> Nějakou dobu se mi pletlo, jaký je rozdíl mezi mtime (modify) a ctime (change) a např. při hledání souborů podle data jsem si to musel čas od času dohledávat. Na základě informací z předchozího textu se ale nabízí pomůcka pro zapamatování rozdílu: ctime je timestamp, co se lidem občas plete s creation time a co nějakou dobu v Unixu vůbec nebyl, takže je z těch dvou to bude ten méně významný, tedy ten co popisuje změnu metadat/inode (což je něco, co mě většinou nezajímá). </hint>
Kdy se poprvé objevila časová značka pro creation time, neboli birth time (btime), je těžké s jistotou říct, protože je možné, že to bylo v rámci nějakého proprietárního Unixového systému. A vzhledem k tomu, že takové systémy jsou v dnešní době většinou mrtvé bez dostupné dokumentace nebo zdrojového kódu pod rozumnou licencí, se mi ani nechce něco takového dohledávat. V rámci unixových systémů s otevřeným zdrojovým kódem je ale toto prvenství jasné: časová značka pro creation time se poprvé objevila až v roce 2003 ve FreeBSD 5.0 s příchodem souborového systému UFS2, a odtud se postupně rozšířila do ostatních BSD systémů, jako jsou NetBSD nebo OpenBSD.
Za povšimnutí stojí, jak podporu pro birth time ve FreeBSD přidali. Díky tomu, že FreeBSD mělo ve struktuře stat nevyužité místo, nebylo přidání další časové značky pojmenované st_birthtime
do této struktury problém z hlediska zpětné kompatibility. Aktuálně se tato časová značka ale jmenuje st_birthtim
, aby její pojmenování a význam odpovídalo konvenci z normy POSIX (viz tabulka výše).
OpenSolaris čas vzniku souboru sice také podporuje, ale strukturu stat
ponechal beze změn. Pro získání hodnoty btime je tak třeba použít volání fgetattr(3C) a ze seznamu vrácených attributů přečíst A_CRTIME
. Tato podpora je zdá se přítomna už v prvním commitu projektu OpenSolaris z roku 2005, takže pravděpodobně pochází ze Solarisu.
Jestli někdo uvažuje o přidání btime do POSIX standardu nevím, nepodařilo se mi o tom nic dohledat. Řekl bych, že to dnes asi už nikomu nestojí za námahu.
Podobně jako jiná unixová jádra, Linux dlouhou dobu btime nepodporoval. Souborový systém ext3 nebo reiserfs btime neukládá a syscall stat(2)
vrací strukturu stejného jména taktéž bez této časové značky. O podpoře btime v Linuxu se sice mluví už nějaký čas, ale na rozdíl od FreeBSD nešlo prostě přidat novou časovou značku někam do volného padding místa stávající struktury stat
, protože Linux tam takové místo nemá. Místo definice nové verze struktury stat
se tak ukázalo schůdnější navrhnout přidání btime do zcela nového volání xstat()
, jehož začlenění do jádra se bohužel na nějakou dobu zadrhlo.
Linuxoví vývojáři nicméně začali přidávat podporu pro btime do nových souborových systémů dávno před tím, než bylo jasné, jak se to nakonec vyřeší. Např. ext4 dostal podporu pro btime již v roce 2007 v rámci patche přidávající podporu pro nanosekundové časové značky (diskový formát ext4 je stabilní od kernelu 2.6.28 z prosince 2008). Btrfs v roce 2012, přičemž jeho disk. formát je stabilní zhruba od listopadu 2013. Do XFS, které původně btime neimplementovalo, se tato podpora přidala v rámci změny přidávající kontrolní součty metadat v roce 2013 a je tak dostupná od jádra 3.10. To znamená, že i na relativně staré distribuci souborový systém dost možná ukládá pro každý soubor btime, i když tato informace není pro uživatele přímo přístupná. Bez podpory v kernelu se k ní lze většinou dostat přes debugging nástroje, které jsou ale pro každý souborový systém jiné, a které pochopitelně vyžadují root oprávnění pro přímý přístup k blokovému zařízení se souborovým systémem.
Nejdříve si (pro účely tohoto blogu) vytvoříme nové oddíly na hraní:
# mkfs.ext4 /dev/vdc
# mount /dev/vdc /mnt/test_ext4
# echo "ext4" > /mnt/test_ext4/testfile
# mkfs.xfs /dev/vdd
# mount /dev/vdd /mnt/test_xfs
# echo "xfs" > /mnt/test_xfs/testfile
Jen pro úplnost: použil systém s Debianem Stretch (aktuální stable), aby byla ukázka blíže realitě. Stretch totiž obsahuje kernel, který už má btime podporu pro ext4 i XFS, ale ještě neumí btime předat do userspace. Postup popsaný níže sice bude fungovat i na novějších distribucích, ale v momentě, kdy máme možnost použít syscall, nemá smysl se s tím takto párat. Jinak zahrnul bych sem i btrfs, ale nepodařilo se mi zjistit, jak z něj btime dostat.
V případě ext4 použijeme debugfs
a jeho příkaz stat
, v jehož výstupu najdeme čas vzniku souboru jako crtime
:
# TZ=CET debugfs -R 'stat testfile' /dev/vdc
debugfs 1.43.4 (31-Jan-2017)
Inode: 12 Type: regular Mode: 0644 Flags: 0x80000
Generation: 1318526178 Version: 0x00000000:00000001
User: 0 Group: 0 Project: 0 Size: 5
File ACL: 0 Directory ACL: 0
Links: 1 Blockcount: 8
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x5c66c5ee:2060896c -- Fri Feb 15 15:00:14 2019
atime: 0x5c66c600:ee5ed49c -- Fri Feb 15 15:00:32 2019
mtime: 0x5c66c5ee:2060896c -- Fri Feb 15 15:00:14 2019
crtime: 0x5c66c5ee:2060896c -- Fri Feb 15 15:00:14 2019
Size of extra inode fields: 32
Inode checksum: 0x0721e8ea
EXTENTS:
(0):32897
Pro soubor na XFS oddílu obdobně použijeme xfs_db
. Nejdřív si však musíme zjistit inode souboru co nás zajímá a pak odpojit (nebo připojit read only) xfs filesystém. Čas vzniku souboru najdeme ve výpisu jako v3.crtime.sec
a v3.crtime.nsec
:
# ls -i /mnt/test_xfs/testfile
99 /mnt/test_xfs/testfile
# umount /mnt/test_xfs
# TZ=CET xfs_db /dev/vdd
xfs_db> inode 99
xfs_db> print
core.magic = 0x494e
core.mode = 0100644
core.version = 3
core.format = 2 (extents)
core.nlinkv2 = 1
core.onlink = 0
core.projid_lo = 0
core.projid_hi = 0
core.uid = 0
core.gid = 0
core.flushiter = 0
core.atime.sec = Fri Feb 15 16:11:36 2019
core.atime.nsec = 155502016
core.mtime.sec = Fri Feb 15 16:11:36 2019
core.mtime.nsec = 155502016
core.ctime.sec = Fri Feb 15 16:11:36 2019
core.ctime.nsec = 155502016
core.size = 0
core.nblocks = 0
core.extsize = 0
core.nextents = 0
core.naextents = 0
core.forkoff = 0
core.aformat = 2 (extents)
core.dmevmask = 0
core.dmstate = 0
core.newrtbm = 0
core.prealloc = 0
core.realtime = 0
core.immutable = 0
core.append = 0
core.sync = 0
core.noatime = 0
core.nodump = 0
core.rtinherit = 0
core.projinherit = 0
core.nosymlinks = 0
core.extsz = 0
core.extszinherit = 0
core.nodefrag = 0
core.filestream = 0
core.gen = 559694043
next_unlinked = null
v3.crc = 0x40d2f493 (correct)
v3.change_count = 3
v3.lsn = 0x100000002
v3.flags2 = 0
v3.cowextsize = 0
v3.crtime.sec = Fri Feb 15 16:11:36 2019
v3.crtime.nsec = 155502016
v3.inumber = 99
v3.uuid = 425730b5-1254-45db-8e31-87f25c75f6cd
v3.reflink = 0
v3.cowextsz = 0
u3 = (empty)
Pozor na to, že příkaz stat -v
z xfs_io
btime neukáže:
# mount /dev/vdd /mnt/test_xfs
# TZ=CET xfs_io -r /mnt/test_xfs/testfile -c 'stat -v'
fd.path = "/mnt/test_xfs/testfile"
fd.flags = non-sync,non-direct,read-only
stat.ino = 99
stat.type = regular file
stat.size = 0
stat.blocks = 0
stat.atime = Fri Feb 15 16:11:36 2019
stat.mtime = Fri Feb 15 16:11:36 2019
stat.ctime = Fri Feb 15 16:11:36 2019
fsxattr.xflags = 0x0 []
fsxattr.projid = 0
fsxattr.extsize = 0
fsxattr.cowextsize = 0
fsxattr.nextents = 0
fsxattr.naextents = 0
dioattr.mem = 0x200
dioattr.miniosz = 512
dioattr.maxiosz = 2147483136
Další potenciální zádrhel u XFS je, že podporu pro btime v XFS nenajdete na RHELu 7, protože jak jsme si řekli před chvílí, XFS umí ukládat btime až od jádra 3.10.
Vtipné je, že pokud připojíte Linuxem vytvořený ext4 filesystém na FreeBSD, tak pomocí nativního stat
příkazu btime pro soubory uložené na tomto Linuxovém oddílu přečtete. A to navzdory tomu, že podpora Linuxových souborových systémů je na FreeBSD pochopitelně omezená, a např. to ext4 lze přes ext2fs
připojit jen pro čtení (případně přes FUSE i pro zápis, ale to jsem nezkoušel).
Takto to dopadne, když se na FreeBSD 12 pokusíme přečíst btime na ext4 oddílu z předchozího pokusu. Pořadí časových značek ve výstupu je atime, mtime, ctime a btime:
# mount -t ext2fs -o ro /dev/vtbd1 /mnt/test_ext4
# cat /mnt/test_ext4/testfile
ext4
# env TZ=CET stat /mnt/test_ext4/testfile
92 12 -rw-r--r-- 1 root wheel 127754 5 "Feb 15 15:00:32 2019" "Feb 15 15:00:14 2019" "Feb 15 15:00:14 2019" "Feb 15 15:00:14 2019" 4096 8 0 /mnt/test_ext4/testfile
Takže podporu btime v Linuxových souborových systémech bychom měli. Ale aby nám to k něčemu bylo, je potřeba mít možnost předat tuto informaci z kernelu do userspace. Jak už jsem zmínil výše, btime mělo být možné získat pomocí volání xstat()
, jehož začlenění se zadrhlo, aby se po několika letech vynořilo v nové podobě jako statx()
, které se nakonec do jádra dostalo v Linuxu 4.11 z dubna 2017. Podpora v glibc existuje od glibc 2.28 ze srpna 2018. To znamená, že např. na Fedoře 29 se to dá už vyzkoušet.
Následující kód ukazuje, jak pomocí statx(2)
přečíst pro daný soubor právě pouze btime. To, že je možné jádru říct o která metadata máme zájem, díky čemuž se jádro nemusí namáhat se zjišťováním hodnot, které stejně nepoužijeme, je mimochodem jedna z hlavních výhod volání statx(2)
oproti stat(2)
.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char**argv)
{
struct statx stx = { 0, };
if (argc != 2) {
printf("Usage: %s FILE\n", argv[0]);
printf("Display btime of a given file in UNIX time format.\n");
return EXIT_SUCCESS;
}
int rc = statx(AT_FDCWD, argv[1], AT_SYMLINK_NOFOLLOW, STATX_BTIME, &stx);
if (rc == 0) {
if (stx.stx_btime.tv_sec != 0) {
printf("@%u.%u\n", stx.stx_btime.tv_sec, stx.stx_btime.tv_nsec);
} else {
printf("-\n");
}
} else {
perror("statx");
}
return rc;
}
Pokud máte na své distribuci glibc starší než 2.28 ale přitom jádro máte alespoň 4.11, musíte zavolat statx(2)
s pomocí syscall(2)
.
Program vypisuje pouze samotnou časovou značku v unixovém formátu, zavináč na začátku je pro zjednodušení dekódování času pomocí nástroje date
:
$ make btime
cc btime.c -o btime
$ ./btime btime
@1550254543.238843517
$ ./btime btime | date -f- --rfc-3339=ns
2019-02-15 19:15:43.238843517+01:00
Když si připojíme ext4 oddíl z předchozích pokusů, dostáváme očekávaný výsledek:
$ ./btime /mnt/test_ext4/testfile | date -f- --rfc-3339=ns
2019-02-15 15:00:14.135799387+01:00
Jak jsem připomněl v úvodu, stat z GNU Coreutils stále vypisuje btime jako “-”. Nabízí se ale otázka, proč se s tím stat vůbec obtěžuje, když tu až do nedávné doby nebyla možnost, jak tuto informaci na Linuxu získat. Bližší pohled však ukáže, že v knihovně gnulib, kterou stat používá, byla podpora pro čtení btime ze struktury stat díky BSD* systémům implementována již v roce 2007. A samotný kód pro zobrazování btime se do stat(1)
přidal už v roce 2010, hádám že v souvislosti s prvním návrhem systémového volání xstat(2)
, které se ale do jádra tehdy nakonec nedostalo. Každopádně díky tomu stat(1)
od GNU Coreutils 8.6 z roku 2010 na Linuxu vypisuje btime s hodnotou “-” (a to bez ohledu na to, co je to za souborový systém), zatímco třeba na BSD systémech nebo Solarisu je schopný tyto hodnoty i zobrazovat, pokud je filesystém podporuje.
Další pohled na zdrojový kód odhalí, že díky hacku řešící podporu btime pro Solaris není až tak těžké tam btime s pomocí statx(2)
volání dotat:
diff --git a/configure.ac b/configure.ac
index 669e9d1f2..081728c96 100644
--- a/configure.ac
+++ b/configure.ac
@@ -318,6 +318,8 @@ if test $ac_cv_func_getattrat = yes; then
AC_SUBST([LIB_NVPAIR])
fi
+AC_CHECK_FUNCS([statx])
+
# SCO-ODT-3.0 is reported to need -los to link programs using initgroups
AC_CHECK_FUNCS([initgroups])
if test $ac_cv_func_initgroups = no; then
diff --git a/src/stat.c b/src/stat.c
index 0a5ef3cb4..189328cab 100644
--- a/src/stat.c
+++ b/src/stat.c
@@ -1007,6 +1007,24 @@ get_birthtime (int fd, char const *filename, struct stat const *st)
}
#endif
+#if HAVE_STATX
+ if (ts.tv_nsec < 0)
+ {
+ struct statx stx = { 0, };
+ if ((fd < 0
+ ? statx(AT_FDCWD, filename, AT_SYMLINK_NOFOLLOW, STATX_BTIME, &stx)
+ : statx(fd, "", AT_EMPTY_PATH, STATX_BTIME, &stx))
+ == 0)
+ {
+ if (stx.stx_btime.tv_sec != 0)
+ {
+ ts.tv_sec = stx.stx_btime.tv_sec;
+ ts.tv_nsec = stx.stx_btime.tv_nsec;
+ }
+ }
+ }
+#endif
+
return ts;
}
Podstata tohoto hacku je v tom, že se volá klasický stat(2)
jako předtím a pak si navíc přes statx(2)
ještě řekneme o btime. Na hraní to stačí:
$ touch ~/tmp/test
$ ./stat ~/tmp/test
File: /home/martin/tmp/test
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: fd07h/64775d Inode: 7377267 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1000/ martin) Gid: ( 1000/ martin)
Access: 2019-02-15 19:52:40.499658659 +0100
Modify: 2019-02-15 19:52:40.499658659 +0100
Change: 2019-02-15 19:52:40.499658659 +0100
Birth: 2019-02-15 19:52:40.499658659 +0100
$ touch ~/tmp/test
$ ./stat ~/tmp/test
File: /home/martin/tmp/test
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: fd07h/64775d Inode: 7377267 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1000/ martin) Gid: ( 1000/ martin)
Access: 2019-02-15 19:52:46.598671520 +0100
Modify: 2019-02-15 19:52:46.598671520 +0100
Change: 2019-02-15 19:52:46.598671520 +0100
Birth: 2019-02-15 19:52:40.499658659 +0100
Tohle “řešení” ale není zcela vhodné na začlenění do coreutils, protože používá zbytečně 2 volání jádra místo jednoho, a celé je to navíc postavená nad jiným hackem. K tomu abych stat upravil nějak rozumně jsem se ale zatím nedostal, a podle toho, že na coreutils listu mi nikdo neodpověděl, bych řekl, že na tom aktuálně nikdo nedělá.
Trochu jsem se zhrozil, když jsem četl release notes pro Bash 5.0 z ledna 2019, kde mezi novinkami je:
- New loadable builtins: rm, stat, fdflags.
Na první pohled by to mohlo vypadat, že než se podaří do stat z coreutils přidat podporu pro btime, bude potřeba tuto práci udělat ještě jednou, protože s příchodem bashe 5 budou všichni používat stat implementovaný přímo v shellu, podobně jako např. v případě time
:
$ type -a time
time is a shell keyword
time is /usr/bin/time
time is /bin/time
Ale ukázalo se, že to není tak horké, protože příslušný zdrojový kód stat.c
se nachází v adresáři examples/loadables/
, tj. jde o tzv. dynamic loadable buildin, a vnitřní funkce umístěné zde, např. cat.c
nebo sleep.c
, nejsou běžně v binárních balíčcích vůbec přítomné (jak si můžete ověřit pomocí výpisu enable -a
). Idea za tímto je taková, že pokud potřebujete zoptimalizovat shell skript kde např. voláte ve smyčce sleep, můžete si zkompilovat příslušný buildin (případně si napsat vlastní) a pomocí enable -f
ho do bashe načíst. Osobně mi sice přijde rozumnější psát takový script třeba v pythonu, ale pokud se není možné bashi vyhnout (např. protože jde o nějaký velký legacy skript), ta možnost tu je.
A jak už jsem naznačil, implementace loadable buildin funkce stat
v bashi s btime pracovat neumí:
$ enable -f ~/projects/bash/examples/loadables/stat stat
$ help stat
stat: stat [-lL] [-A aname] file
Load an associative array with file status information.
Take a filename and load the status information returned by a
stat(2) call on that file into the associative array specified
by the -A option. The default array name is STAT. If the -L
option is supplied, stat does not resolve symbolic links and
reports information about the link itself. The -l option results
in longer-form listings for some of the fields. The exit status is 0
unless the stat fails or assigning the array is unsuccessful.
$ stat ~/tmp/test
$ for i in "${!STAT[@]}"; do echo $i = ${STAT[$i]}; done
nlink = 1
link = /home/martin/tmp/test
perms = 0664
inode = 7377267
blksize = 4096
device = 64775
atime = 1550256766
type = -
blocks = 0
uid = 1000
size = 0
rdev = 0
name = /home/martin/tmp/test
mtime = 1550256766
ctime = 1550256766
gid = 1000
Ale v tomto případě mi přijde, že místo přidání podpory pro btime do této stat
buildin funkce by bylo lepší napsat jinou, která by mohla lépe využívat možností jaderného volání statx(2)
.
Bohužel, podpora btime v základních komponentách GNU Linux distribucí zatím končí u statx(2)
wrapperu v glibc. Stejně jako výše uvedené implementace nástroje stat
, žádný základní nástroj jako např. ls
nebo find
s btime na Linuxu pracovat neumí. Přitom podobně jako v případě stat
, např. find
už základní podporu pro btime má, jen na Linuxu neumí jeho hodnotu zatím přečíst. Na druhou stranu, díky tomu že btime je možné číst pouze pomocí nového volání jádra statx(2)
, nebudou často změny v těchto nástrojích tak přímočaré, jak by se mohlo na první pohled zdát.
Dále také bude záležet na tom, zda se později neobjeví podpora pro změnu btime v jaderných voláních jako utimes(2)
nebo utimensat(2)
. Aktuální stav, kdy není možné libovolně nastavit btime má svou logiku, čas vzniku souboru, pokud má opravdu dostát svému významu, by měl zůstat po zbytek života souboru stejný, ale na druhé straně to také znamená, že není možné např. archivovat soubor včetně btime pomocí cp -a
nebo ho obnovit ze zálohy pomocí rsync
. Z tohoto důvodu bude asi implementace podpory btime v GNU tar trvat trochu déle, protože není jasné, proč by tam někdo přidával podporu pro btime, když by pak tato informace nešla na Linuxu obnovit při rozbalování archivu.
Tady se hodí poznamenat, že FreeBSD možnost měnit btime pomocí utimes(2)
nabízí od začátku, jak je popsané v článku o UFS2.
Co vlastně znamená čas vzniku souboru? Jeden by řekl, že je to jasné, ale tak jednoduché to úplně není. Nejen vzhledem k výše zmíněné nemožnosti btime nastavit jde o low level informaci, o kterou např. při kopírování souboru přijdeme (z pohledu fs jde o nový soubor). Jiný častý případ kdy btime ztratíme je, když aplikace zapisuje do souboru atomicky s pomocí přejmenování dočasného souboru.
Btw nikdy předtím jsem si neuvědomil, že tohle atomické zapisování dělá např. i vim (všiměte si změny v inode a času vzniku souboru):
$ rm ~/tmp/test
$ touch ~/tmp/test
$ stat.hacked ~/tmp/test
File: /home/martin/tmp/test
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: fd07h/64775d Inode: 7377286 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1000/ martin) Gid: ( 1000/ martin)
Access: 2019-02-17 09:51:45.483720811 +0100
Modify: 2019-02-17 09:51:45.483720811 +0100
Change: 2019-02-17 09:51:45.483720811 +0100
Birth: 2019-02-17 09:51:45.483720811 +0100
$ vim ~/tmp/test
$ stat.hacked ~/tmp/test
File: /home/martin/tmp/test
Size: 5 Blocks: 8 IO Block: 4096 regular file
Device: fd07h/64775d Inode: 7377267 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1000/ martin) Gid: ( 1000/ martin)
Access: 2019-02-17 09:52:17.151767057 +0100
Modify: 2019-02-17 09:52:17.151767057 +0100
Change: 2019-02-17 09:52:17.156767065 +0100
Birth: 2019-02-17 09:52:17.151767057 +0100
A tímto se konečně dostáváme k otázce k čemu je to btime vlastně dobré. Jak je vidět z doby, která byla potřeba aby se btime podpora dostala v použitelné podobě do jádra, nikdo tomu nepřipisuje velkou prioritu. To je vidět taky z toho, že změny implementující btime se často objevují v commitech, jejichž hlavní náplní je něco jiného. Ať už v případě ext4, kdy hlavní cíl byl implementovat nanosekundové časové značky. Podobně XFS přidává btime v rámci zavádění kontrolních součtů metadat a syscall statx(2)
nebyl vytvořen jen kvůli čtení btime. Lecos naznačuje i to, za celou 50 letou historii Unixu to nikdo nenavrhl na přidání do POSIX standardu.
Když se podíváme na důvody implementace btime v Linuxu, kromě stručného “UFS2/ZFS to má taky” často vidíme zmínky o Sambě a kompatibilitě s Windows. Bohužel, Samba nemůže Linuxový btime v současné podobně přímo využít, protože Windows umožňuje čas vzniku souboru libovolně měnit. Také NTFS-3G by mohl teoreticky čas vzniku souboru z Windows reportovat na Linuxu pomocí btime. Prakticky se tím ale nikdo nebude zabývat dokud se podpora pro statx(2)
nepřidá do FUSE a alespoň nástroje z coreutils budou umět s btime pracovat. Navíc NTFS-3G už teď umí předat btime pomocí rozšířených atributů, i když možnost použít ls
by byla rozhodně pohodlnější.
Nová časová značka se ale každopádně dá dobře využít při debugování nějakého podivného chování, kdy se každá stopa navíc hodí, ať už je za ním útočník, malware nebo ne zcela fungující software nebo hardware. Mimo těchto “detektivních” případů se btime dá využít i pro opačné účely. Např. by teoreticky šlo do souborových časových značek nepozorovaně ukládat malé množství dat. Paradoxně v obou případech je ale aktuální stav, kdy je btime podpora pouze v kernelu, vlastně výhodný. Pro forenzní analýzu je užitečné, že je vzhledem k menší povědomí o btime pravděpodobnost jeho falšování nižší. A pro opačné případy je zase pěkné, že “zneužívání” btime není tak na očích.
Články k tématu:
Historické zdroje:
Tiskni
Sdílej:
Toto správanie je však možné zmeniť, čím ale dôjde k porušeniu POSIX-u a niektoré aplikácie kvôli tomu nemusia pracovať správne.Kromě věcí typu dump, co jiného nebude fungovat správně, když mám připojený souborový systém s volbou relatime? Vzhledem k tomu, že realtime je default snad všude a existují volby jako noatime, nevím co by se mělo s realtime rozbít.
Ak sa však pozrieme do histórie, hodnota ctime naozaj reprezentovala čas a dátum vytvorenia súboru (z angl. Creation Time). Postupne sa však z tejto hodnoty stala hodnoty času poslednej zmeny vlastností súboru tak, ako ho poznáme dnes.Jinak jak píšu v blogu, tohle není pravda.
3cc1108b usr/sys/ken/pipe.c (Ken Thompson 1974-11-26 18:13:21 -0500 53) rf->f_flag = FREAD|FPIPE; 3cc1108b usr/sys/ken/pipe.c (Ken Thompson 1974-11-26 18:13:21 -0500 54) rf->f_inode = ip; 3cc1108b usr/sys/ken/pipe.c (Ken Thompson 1974-11-26 18:13:21 -0500 55) ip->i_count = 2; [...] 1f183be2 usr/sys/sys/pipe.c (Ken Thompson 1979-01-10 15:19:35 -0500 122) register struct inode *ip; 1f183be2 usr/sys/sys/pipe.c (Ken Thompson 1979-01-10 15:19:35 -0500 123) 1f183be2 usr/sys/sys/pipe.c (Ken Thompson 1979-01-10 15:19:35 -0500 124) ip = fp->f_inode; 1f183be2 usr/sys/sys/pipe.c (Ken Thompson 1979-01-10 15:19:35 -0500 125) c = u.u_count; 1f183be2 usr/sys/sys/pipe.c (Ken Thompson 1979-01-10 15:19:35 -0500 126) 1f183be2 usr/sys/sys/pipe.c (Ken Thompson 1979-01-10 15:19:35 -0500 127) loop: 1f183be2 usr/sys/sys/pipe.c (Ken Thompson 1979-01-10 15:19:35 -0500 128) 1f183be2 usr/sys/sys/pipe.c (Ken Thompson 1979-01-10 15:19:35 -0500 129) /* 9a9f6b22 usr/src/sys/sys/pipe.c (Bill Joy 1980-01-05 05:51:18 -0800 130) * If error or all done, return. 9a9f6b22 usr/src/sys/sys/pipe.c (Bill Joy 1980-01-05 05:51:18 -0800 131) */ 9a9f6b22 usr/src/sys/sys/pipe.c (Bill Joy 1980-01-05 05:51:18 -0800 132) 9a9f6b22 usr/src/sys/sys/pipe.c (Bill Joy 1980-01-05 05:51:18 -0800 133) if (u.u_error) 9a9f6b22 usr/src/sys/sys/pipe.c (Bill Joy 1980-01-05 05:51:18 -0800 134) return; 6d632e85 usr/sys/ken/pipe.c (Ken Thompson 1975-07-17 10:33:37 -0500 135) plock(ip); 6d632e85 usr/sys/ken/pipe.c (Ken Thompson 1975-07-17 10:33:37 -0500 136) if(c == 0) { 6d632e85 usr/sys/ken/pipe.c (Ken Thompson 1975-07-17 10:33:37 -0500 137) prele(ip); 6d632e85 usr/sys/ken/pipe.c (Ken Thompson 1975-07-17 10:33:37 -0500 138) u.u_count = 0; 6d632e85 usr/sys/ken/pipe.c (Ken Thompson 1975-07-17 10:33:37 -0500 139) return; 6d632e85 usr/sys/ken/pipe.c (Ken Thompson 1975-07-17 10:33:37 -0500 140) }
Imho jediný rozumný čas je to, co uvedu přímo v textu toho souboru, ostatní možnosti většinou nedávají dobrý konzistentní smyslSouhlasím, pro uživatele jsou často zajímavější metadata datového formátu (ať už je to ten výše zmíněný git, nebo např. exif data ve fotce) než to, co ukládá souborový systém. To ale neznamená, že by to šlo z něj vyhodit.
kdy ten konkrétní soubor na tom filesystému vzniknul už stejně máme...Moment, to je ten btime, o kterém jsem psal. Nebo jsem nepochopil, jak to myslíš.
Když už FS tu informaci ukládají, tak by to chtělo ji dotáhnout i do uživatelského rozhraní. (případně odmazat z implementací těch FS, třeba tam všude dávat nulu)
Díky za článek.
V anketě jsem dal „naprosto zásadní“ – ono to tedy většinou zásadní není, ale občas by to mohla být pěkná zrada a mohla by tímto kanálem prosáknout informace, která by prosáknout neměla. Z tohoto důvodu doufám, že budou brzo dostupné nástroje pro přepis této informace, aby to člověk nemusel hackovat přímým přepisováním příslušných bajtů na blokovém zařízení.
cp
?
/etc/passwd
a při obnovování ověřit, že na cílovém systému danému UID odpovídá stejný uživatel (resp. username)? Umí to nějaký standardní nástroj?
V rámci jedné organizace bývá obvykle snaha, aby jeden uživatel měl všude stejné UID (je na to např. atribut v LDAPu).
Připojovat někam cizí disk není úplně typický scénář a na výměnných médiích (diskety, USB flash…) býval většinou FAT, u kterého si vlastníka/práva zadáš při připojování. Ale když někam připojíš třeba záložní HDD s Ext4, tak se může stát, že než se rozkoukáš, už jiný uživatel z toho disku čte, protože má náhodou stejné UID jako třeba ty na jiném počítači… to už problém je. Tam musíš dát adresář, kam připojuješ, do adresáře, kam ostatní nevidí, a pak zkontrolovat práva a případně změnit vlastníky.
/etc/passwd
(i k nesystémovým zálohám) a skript na obnovení, který to rovnou diffne proti aktuálnímu a upozorní na nesrovnalosti… To není tolik práce navíc a obnovovací skript (nebo nějaké README) chci mít na záloze asi stejně.
cp -a
, rsync -a
nebo při rozbalování tarballu). Což se běžně děje např. při obnovoání záloh. A ano, majitelem souboru bude uživatel s daným UID bez ohledu na to, že třeba username se liší.
Btw gnu tar tohle nějak řeší, protože ukládá jak UID tak jméno (viz volby --no-same-owner, --numeric-owner, --owner=NAME[:UID], --owner-map=FILE).
Já svých strojích vytvářím uživatele ansible playbookem a zálohuju jen celý /home. Takže postup obnovení je spustit playbook a pak přes rsync zkopírovat celý /home.
$ grep btime /proc/stat btime 1550868084 $ grep btime /proc/stat | awk '{ print "@"$2 }' | date -f- Fri Feb 22 21:51:24 CET 2019
stat
se po několika malých úpravách nakonec dostal do upstreamu.
Viz. sekce Improvements v oznámení o vydání GNU coreutils verze 8.31.