VFAT,Linux:重新啟動後顯 示的文件時間戳無效
剛剛遇到一個問題:重啟 Linux 系統時,掛載的 VFAT 文件系統中所有文件的時間戳顯示在錯誤的時區中。似乎設備開始認為其本地時間是 UTC,因此它顯示了所有時間戳與班次。
重現步驟:
- 創建一些小的 FAT 格式的圖像:
dd if=/dev/zero of=small.img bs=1M seek=1 count=0
mkfs.vfat small.img
- 在本地掛載此映像:
mount -t vfat -o umask=0022,gid=1001,uid=1001 small.img mnt
- 將時區設置為某個非 UTC 時區;
- 在掛載的文件系統中創建一個文件(即。
touch mnt/newfile
)- 觀察文件修改/更改時間戳:它們是正確的,關於目前設置的:
stat mnt/newfile
File: mnt/newfile Size: 0 Blocks: 0 IO Block: 16384 regular empty file Device: 700h/1792d Inode: 40 Links: 1 Access: (0755/-rwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2021-03-22 12:19:56.000000000 +0100 Modify: 2021-03-22 12:19:56.000000000 +0100 Change: 2021-03-22 12:19:56.000000000 +0100 Birth: -
timedatectl
Local time: Mon 2021-03-22 12:19:07 CET Universal time: Mon 2021-03-22 11:19:07 UTC RTC time: Mon 2021-03-22 11:19:07 Time zone: Europe/Vienna (CET, +0100) System clock synchronized: yes NTP service: active RTC in local TZ: no
- 解除安裝文件系統,檢查重新安裝是否有任何更改:
umount mnt; mount -t vfat -o umask=0022,gid=1001,uid=1001 small.img mnt; stat mnt/newfile
File: mnt/newfile Size: 0 Blocks: 0 IO Block: 16384 regular empty file Device: 700h/1792d Inode: 64 Links: 1 Access: (0755/-rwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2021-03-22 00:00:00.000000000 +0100 Modify: 2021-03-22 12:19:56.000000000 +0100 Change: 2021-03-22 12:19:56.000000000 +0100 Birth: -
- 重啟系統;
- 再次掛載鏡像,查看創建文件的時間戳:
File: mnt/newfile Size: 0 Blocks: 0 IO Block: 16384 regular empty file Device: 700h/1792d Inode: 26 Links: 1 Access: (0755/-rwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2021-03-22 01:00:00.000000000 +0100 Modify: 2021-03-22 13:19:56.000000000 +0100 Change: 2021-03-22 13:19:56.000000000 +0100 Birth: -
可以清楚地觀察到,時間向前移動了 1 小時(12:10 到 13:19),而時區顯示相同 - +0100。看起來
mount
現在認為文件時間戳是以 UTC 記錄的,所以它嘗試用“正確”的轉變來顯示它們。要檢查上一條語句的有效性,讓我們使用
tz=UTC
顯式選項重新掛載相同的文件系統:
mount -t vfat -o umask=0022,gid=1001,uid=1001,tz=UTC small.img mnt; stat mnt/newfile
File: mnt/newfile Size: 0 Blocks: 0 IO Block: 16384 regular empty file Device: 700h/1792d Inode: 50 Links: 1 Access: (0755/-rwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2021-03-22 01:00:00.000000000 +0100 Modify: 2021-03-22 13:19:56.000000000 +0100 Change: 2021-03-22 13:19:56.000000000 +0100 Birth: -
即使系統的時區確實是 CET:
date
Mon Mar 22 12:26:42 CET 2021
PS https://stackoverflow.com/questions/10068855/how-do-i-get-the-correct-modified-datetime-of-a-fat32-file-regardless-of-timezo不是這個問題的答案,既然我無法從中獲取資訊,為什麼在重新啟動機器後立即看到此更改,而不是在重新安裝後?如果 vfat 以本地時間儲存時間戳,為什麼重新啟動後掛載假定時間戳是 UTC 而不是本地時間?
似乎問題出在 Linux 核心本身,因為時區可能(並且通常會)在核心和使用者空間之間有所不同。Linux 核心原始碼樹中的
time.c
文件保存(並導出) ,然後在FAT 時間 <-> UNIX 時間約定中使用。此結構的欄位用於顯示目前時區與 UTC 之間的差異,如果未將選項傳遞給命令,則將其考慮在內。但是,上述欄位預設設置為 0,如此處所述,kernel/time``struct timezone sys_tz``fs/fat/misc.c``tz_minuteswest``tz=UTC``mount.vfat
在 Linux 下,如果在第一次呼叫(啟動後)具有非 NULL tz 參數時,tv 參數為 NULL 且 tz_minuteswest 欄位為非零,則有一些與 settimeofday() 系統呼叫相關的特殊“扭曲時鐘”語義. (在這種情況下 tz_dsttime 欄位應該為零。)在這種情況下,假定 CMOS 時鐘是本地時間,並且它必須增加這個數量才能獲得 UTC 系統時間。毫無疑問,使用此功能是個壞主意。
因此,讓核心(及其驅動程序)始終看到正確時區的唯一方法是呼叫
settimeofday()
withtz
參數,tz.tz_minuteswest
相對於 UTC(即 -60 為CET 等),並tz_dsttime
在每次系統啟動後設置為 0。這可以通過在將系統時區更改為另一個時區(即 UTC)後將系統時區設置為目前時區來實現,因為如果需要時區,命令行工具 sa通常不會執行實際的時區更改等於目前的。創建以下程式碼來證明這個概念:timedatectl
#include <sys/time.h> #include <stdio.h> int main() { struct timeval tv; struct timezone tz; int ret = gettimeofday(&tv, &tz); printf("%d, %dr\n", tz.tz_minuteswest, tz.tz_dsttime); return ret; }
該程式碼執行如下:
$gcc testtz.cpp -o testtz $./testtz minuteswest: 0, dsttime: 0r $timedatectl set-timezone Europe/Vienna $./testtz minuteswest: 0, dsttime: 0r $timedatectl set-timezone UTC $timedatectl set-timezone Europe/Vienna $./testtz minuteswest: -60, dsttime: 0r
這個問題不會很快消失,即使
timezone
結構的使用被認為是過時的。所以對於我的問題,我會考慮使用.time_offset=minutesmount.vfat
選項。