日記/2007-06-23/ファイルシステムとタイムスタンプ
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
] [
リンク元
]
開始行:
* ファイルシステムとタイムスタンプ [#e41c69af]
** そもそもの疑問 [#y52839a4]
Linux で FAT フォーマットの SD カードを mount し,ls -l ...
> 何で Linux 上で FAT filesystem のタイムスタンプが正常に...
と,すごく気になりだしたのである.
というわけで追っかけてみた.
** epoch と timezone [#abe51c4f]
「何でそんなこと気にするんだ」という話のとっかかりとして...
まず,epoch とは「1970 年 1 月 1 日 00:00:00 UTC からの経...
UTC とは協定世界時のことで,サマータイムが適用されていな...
UNIX では,カーネルが持っている時計の現在時刻を time(2) ...
次に timezone について.
timezone とは要するに「現地時間の UTC からの時差」である.
たとえば日本時間は,UTC に対して 9 時間進んだ時刻である.
** ls -l コマンドを実行すると… [#meee1fa6]
まず,ディレクトリ内のファイル名・ディレクトリ名の一覧を...
そして,各ファイル・ディレクトリに対して stat(2) システム...
stat(2) が返す構造体の形式は以下のとおりである.
struct stat {
dev_t st_dev; /* ファイルがあるデバ...
ino_t st_ino; /* inode 番号 */
mode_t st_mode; /* アクセス保護 */
nlink_t st_nlink; /* ハードリンクの数 */
uid_t st_uid; /* 所有者のユーザ ID */
gid_t st_gid; /* 所有者のグループ I...
dev_t st_rdev; /* デバイス ID (特殊...
off_t st_size; /* 全体のサイズ (バイ...
blksize_t st_blksize; /* ファイルシステム I...
ブロックサイズ */
blkcnt_t st_blocks; /* 割り当てられたブロ...
time_t st_atime; /* 最終アクセス時刻 */
time_t st_mtime; /* 最終修正時刻 */
time_t st_ctime; /* 最終状態変更時刻 */
};
ファイルのタイムスタンプは time_t 型であり,つまり epoch ...
この epoch 形式を現地時間の「年月日時分秒」に変換するのは...
libc は /etc/localtime ファイルにある時差情報に基づいて e...
かくして ls -l のタイムスタンプは現地時間で表示されるわけ...
あ,そうそう,ext3 などの UNIX / Linux native のファイル...
ここらへんの仕組みをまとめると,下図のようになる.
HDD
epoch
|
|
--------|----------
kernel |
|
--------|---------- stat(2)
user |
+<--- /etc/localtime
#
#
V
yy/mm/dd hh:mm:ss JST
** FAT の場合 [#mea1bd8a]
FAT filesystem の場合,少々話が違ってくる.
「[http://www.geocities.co.jp/SiliconValley-PaloAlto/2038...
つまり「どこの現地時間か UTC かは知らないけど,そういう時...
しかし,strace をかけてみても ls -l ではやはり stat(2) で...
ということは,
> カーネル内部で「年月日時分秒」形式を現地時間として epoc...
ということになる.
ところが,これには timezone 情報が必要であるのだが,timez...
/etc/* のファイルは,ユーザランドプログラムやライブラリが...
ということで冒頭の疑問に戻るわけである.
** FAT のファイルシステムドライバ [#ndf9d9f2]
この疑問を解決するには,やはり
> Linux カーネルのソースを見る
のが一番だろう.
Linux カーネルはこのソースに従って動作しているのだから.
というわけで,FAT のファイルシステムドライバでタイムスタ...
ファイルシステム回りは linux-x.y.z.w/fs/ の下にまとまって...
linux-2.6.17.1/fs/fat/misc.c にこんな記述を見つける.
extern struct timezone sys_tz;
…(略)…
/* Convert a MS-DOS time/date pair to a UNIX date (secon...
int date_dos2unix(unsigned short time, unsigned short da...
{
int month, year, secs;
/*
* first subtract and mask after that... Otherwi...
* date == 0, bad things happen
*/
month = ((date >> 5) - 1) & 15;
year = date >> 9;
secs = (time & 31)*2+60*((time >> 5) & 63)+(time...
((date & 31)-1+day_n[month]+(year/4)+year*36...
month < 2 ? 1 : 0)+3653);
/* days since 1.1.70 plus 80's l...
secs += sys_tz.tz_minuteswest*60;
return secs;
}
カーネル内部に timezone 構造体の sys_tz が保持されいて,...
この sys_tz であるが,どこで定義されているのか探してみる...
struct timezone sys_tz;
EXPORT_SYMBOL(sys_tz);
…(略)…
int do_sys_settimeofday(struct timespec *tv, struct time...
{
static int firsttime = 1;
int error = 0;
if (tv && !timespec_valid(tv))
return -EINVAL;
error = security_settime(tv, tz);
if (error)
return error;
if (tz) {
/* SMP safe, global irq locking makes it...
sys_tz = *tz;
if (firsttime) {
firsttime = 0;
if (!tv)
warp_clock();
}
}
if (tv)
{
/* SMP safe, again the code in arch/foo/...
* globally block out interrupts when it...
*/
return do_settimeofday(tv);
}
return 0;
}
このソースで sys_tz を定義し,カーネル全体のグローバル変...
で,do_sys_settimeofday() という関数で sys_tz へ値を代入...
この関数,名前を見て気づいた人も多いと思うが,システムコ...
このソースファイルでは settimeofday(2) と兄弟の gettimeof...
もちろん,gettimeofday(2) ではカーネルの sys_tz の値を取...
FAT でのタイムスタンプを読み取る仕組みをまとめると,下図...
HDD
yy/mm/dd hh:mm:ss
#
#
--------#----------
kernel # (assume localtime)
#
+<------------------------------- sys_tz (timezo...
| (epoch) ^ |
| | |
--------|---------- stat(2) ----|--|--- getti...
user | | |
+<---- /etc/localtime | V
#
#
V
yy/mm/dd hh:mm:ss JST
** じっけん [#b3b271e1]
以下のようなプログラムを書く.
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
int main ( int argc, char *argv[] )
{
struct timezone tz;
struct timeval tv;
int t, s, h, m;
if ( argc > 1 ) {
gettimeofday ( NULL, &tz );
tz.tz_minuteswest = atoi ( argv[1] );
settimeofday ( NULL, &tz );
}
gettimeofday ( NULL, &tz );
printf ( "minuteswest = %d, dsttime = %d\n",
tz.tz_minuteswest, tz.tz_dsttime );
return 0;
}
コンパイルし,timezone という実行プログラムを作る.
gcc timezone.c -o timezone
このプログラムは
# ./timezone
と,引数なしで呼び出されたときは単に
minuteswest = -540, dsttime = 0
と,timezone 構造体の中身を出力し,
# ./timezone 0
と,引数を付けた場合は settimeofday(2) で timezone を設定...
次に FAT フォーマットの SD カードを用意する.
windows でフォーマットし,適当なファイルを入れておく.
J:\>dir
ドライブ J のボリューム ラベルがありません。
ボリューム シリアル番号は 98D0-E398 です
J:\ のディレクトリ
2007/06/23 22:10 0 textfile.txt
1 個のファイル 0 バイト
0 個のディレクトリ 30,854,656 バイト...
この SD カードを Linux 上で mount し,中を見ると
# ls -l
合計 0
-rwxr-xr-x 1 root root 0 2007-06-23 22:10 textfile.txt
と,見える.
このとき,カーネルが保持している timezone は
# ~/timezone
minuteswest = -540, dsttime = 0
であり,JST である.
ここで,minuteswest の値がマイナスとなっているので「おや...
minuteswest は,その名前のとおりグリニッジから西方向をを...
日本はグリニッジから東側に9時間なので,-540 分となるので...
単に「UTC を基準とした時差」でないところが妙なところでも...
まぁ,ここらへんは歴史的なしがらみだと思うので「こんなも...
この場合,FAT filesystem 上でのタイムスタンプは下図のよう...
HDD
2007/06/23 22:10
#
#
--------#----------
kernel # (assume localtime)
#
+<------------------------------- sys_tz (timezo...
| (epoch = 2007/06/23 22:10 JST) ^ |
| | |
--------|---------- stat(2) ----|--|--- getti...
user | | |
+<---- /etc/localtime = JST | V
#
#
V
2007/06/23 22:10 JST
ここで,
# ~/timezone 0
minuteswest = 0, dsttime = 0
とし,カーネルが保持している timezone を 0 に設定する.
そして,SD カードを umount し,mount しなおすと…
# ls -l
合計 0
-rwxr-xr-x 1 root root 0 2007-06-24 07:10 textfile.txt
予想通り,表示される時刻が狂う.
が,タイムスタンプ取扱いモデルと照らしあわせると,このよ...
HDD
2007/06/23 22:10
#
#
--------#----------
kernel # (assume localtime)
#
+<------------------------------- sys_tz (timezo...
| (epoch = 2007/06/23 22:10 UTC) ^ |
| | |
--------|---------- stat(2) ----|--|--- getti...
user | | |
+<---- /etc/localtime = JST | V
#
#
V
2007/06/24 07:10 JST
** まとめ [#k0011530]
というわけで,まとめてみると
- FAT filesystem でのタイムスタンプは,Linux カーネルが保...
- ユーザプロセスでは /etc/localtime の timezone に従って...
というあたりかな.
** gettimeofday(2) / settimeofday(2) の man [#x53d42fa]
蛇足として.
[http://www.linux.or.jp/JM/html/LDP_man-pages/man2/gettim...
> timezone 構造体を使うのは時代遅れ (obsolete) である: tz...
とあり,tz が後方互換性だけのための引数のように書かれてい...
FAT filesystem (や,timezone の概念の無い他の filesystem...
終了行:
* ファイルシステムとタイムスタンプ [#e41c69af]
** そもそもの疑問 [#y52839a4]
Linux で FAT フォーマットの SD カードを mount し,ls -l ...
> 何で Linux 上で FAT filesystem のタイムスタンプが正常に...
と,すごく気になりだしたのである.
というわけで追っかけてみた.
** epoch と timezone [#abe51c4f]
「何でそんなこと気にするんだ」という話のとっかかりとして...
まず,epoch とは「1970 年 1 月 1 日 00:00:00 UTC からの経...
UTC とは協定世界時のことで,サマータイムが適用されていな...
UNIX では,カーネルが持っている時計の現在時刻を time(2) ...
次に timezone について.
timezone とは要するに「現地時間の UTC からの時差」である.
たとえば日本時間は,UTC に対して 9 時間進んだ時刻である.
** ls -l コマンドを実行すると… [#meee1fa6]
まず,ディレクトリ内のファイル名・ディレクトリ名の一覧を...
そして,各ファイル・ディレクトリに対して stat(2) システム...
stat(2) が返す構造体の形式は以下のとおりである.
struct stat {
dev_t st_dev; /* ファイルがあるデバ...
ino_t st_ino; /* inode 番号 */
mode_t st_mode; /* アクセス保護 */
nlink_t st_nlink; /* ハードリンクの数 */
uid_t st_uid; /* 所有者のユーザ ID */
gid_t st_gid; /* 所有者のグループ I...
dev_t st_rdev; /* デバイス ID (特殊...
off_t st_size; /* 全体のサイズ (バイ...
blksize_t st_blksize; /* ファイルシステム I...
ブロックサイズ */
blkcnt_t st_blocks; /* 割り当てられたブロ...
time_t st_atime; /* 最終アクセス時刻 */
time_t st_mtime; /* 最終修正時刻 */
time_t st_ctime; /* 最終状態変更時刻 */
};
ファイルのタイムスタンプは time_t 型であり,つまり epoch ...
この epoch 形式を現地時間の「年月日時分秒」に変換するのは...
libc は /etc/localtime ファイルにある時差情報に基づいて e...
かくして ls -l のタイムスタンプは現地時間で表示されるわけ...
あ,そうそう,ext3 などの UNIX / Linux native のファイル...
ここらへんの仕組みをまとめると,下図のようになる.
HDD
epoch
|
|
--------|----------
kernel |
|
--------|---------- stat(2)
user |
+<--- /etc/localtime
#
#
V
yy/mm/dd hh:mm:ss JST
** FAT の場合 [#mea1bd8a]
FAT filesystem の場合,少々話が違ってくる.
「[http://www.geocities.co.jp/SiliconValley-PaloAlto/2038...
つまり「どこの現地時間か UTC かは知らないけど,そういう時...
しかし,strace をかけてみても ls -l ではやはり stat(2) で...
ということは,
> カーネル内部で「年月日時分秒」形式を現地時間として epoc...
ということになる.
ところが,これには timezone 情報が必要であるのだが,timez...
/etc/* のファイルは,ユーザランドプログラムやライブラリが...
ということで冒頭の疑問に戻るわけである.
** FAT のファイルシステムドライバ [#ndf9d9f2]
この疑問を解決するには,やはり
> Linux カーネルのソースを見る
のが一番だろう.
Linux カーネルはこのソースに従って動作しているのだから.
というわけで,FAT のファイルシステムドライバでタイムスタ...
ファイルシステム回りは linux-x.y.z.w/fs/ の下にまとまって...
linux-2.6.17.1/fs/fat/misc.c にこんな記述を見つける.
extern struct timezone sys_tz;
…(略)…
/* Convert a MS-DOS time/date pair to a UNIX date (secon...
int date_dos2unix(unsigned short time, unsigned short da...
{
int month, year, secs;
/*
* first subtract and mask after that... Otherwi...
* date == 0, bad things happen
*/
month = ((date >> 5) - 1) & 15;
year = date >> 9;
secs = (time & 31)*2+60*((time >> 5) & 63)+(time...
((date & 31)-1+day_n[month]+(year/4)+year*36...
month < 2 ? 1 : 0)+3653);
/* days since 1.1.70 plus 80's l...
secs += sys_tz.tz_minuteswest*60;
return secs;
}
カーネル内部に timezone 構造体の sys_tz が保持されいて,...
この sys_tz であるが,どこで定義されているのか探してみる...
struct timezone sys_tz;
EXPORT_SYMBOL(sys_tz);
…(略)…
int do_sys_settimeofday(struct timespec *tv, struct time...
{
static int firsttime = 1;
int error = 0;
if (tv && !timespec_valid(tv))
return -EINVAL;
error = security_settime(tv, tz);
if (error)
return error;
if (tz) {
/* SMP safe, global irq locking makes it...
sys_tz = *tz;
if (firsttime) {
firsttime = 0;
if (!tv)
warp_clock();
}
}
if (tv)
{
/* SMP safe, again the code in arch/foo/...
* globally block out interrupts when it...
*/
return do_settimeofday(tv);
}
return 0;
}
このソースで sys_tz を定義し,カーネル全体のグローバル変...
で,do_sys_settimeofday() という関数で sys_tz へ値を代入...
この関数,名前を見て気づいた人も多いと思うが,システムコ...
このソースファイルでは settimeofday(2) と兄弟の gettimeof...
もちろん,gettimeofday(2) ではカーネルの sys_tz の値を取...
FAT でのタイムスタンプを読み取る仕組みをまとめると,下図...
HDD
yy/mm/dd hh:mm:ss
#
#
--------#----------
kernel # (assume localtime)
#
+<------------------------------- sys_tz (timezo...
| (epoch) ^ |
| | |
--------|---------- stat(2) ----|--|--- getti...
user | | |
+<---- /etc/localtime | V
#
#
V
yy/mm/dd hh:mm:ss JST
** じっけん [#b3b271e1]
以下のようなプログラムを書く.
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
int main ( int argc, char *argv[] )
{
struct timezone tz;
struct timeval tv;
int t, s, h, m;
if ( argc > 1 ) {
gettimeofday ( NULL, &tz );
tz.tz_minuteswest = atoi ( argv[1] );
settimeofday ( NULL, &tz );
}
gettimeofday ( NULL, &tz );
printf ( "minuteswest = %d, dsttime = %d\n",
tz.tz_minuteswest, tz.tz_dsttime );
return 0;
}
コンパイルし,timezone という実行プログラムを作る.
gcc timezone.c -o timezone
このプログラムは
# ./timezone
と,引数なしで呼び出されたときは単に
minuteswest = -540, dsttime = 0
と,timezone 構造体の中身を出力し,
# ./timezone 0
と,引数を付けた場合は settimeofday(2) で timezone を設定...
次に FAT フォーマットの SD カードを用意する.
windows でフォーマットし,適当なファイルを入れておく.
J:\>dir
ドライブ J のボリューム ラベルがありません。
ボリューム シリアル番号は 98D0-E398 です
J:\ のディレクトリ
2007/06/23 22:10 0 textfile.txt
1 個のファイル 0 バイト
0 個のディレクトリ 30,854,656 バイト...
この SD カードを Linux 上で mount し,中を見ると
# ls -l
合計 0
-rwxr-xr-x 1 root root 0 2007-06-23 22:10 textfile.txt
と,見える.
このとき,カーネルが保持している timezone は
# ~/timezone
minuteswest = -540, dsttime = 0
であり,JST である.
ここで,minuteswest の値がマイナスとなっているので「おや...
minuteswest は,その名前のとおりグリニッジから西方向をを...
日本はグリニッジから東側に9時間なので,-540 分となるので...
単に「UTC を基準とした時差」でないところが妙なところでも...
まぁ,ここらへんは歴史的なしがらみだと思うので「こんなも...
この場合,FAT filesystem 上でのタイムスタンプは下図のよう...
HDD
2007/06/23 22:10
#
#
--------#----------
kernel # (assume localtime)
#
+<------------------------------- sys_tz (timezo...
| (epoch = 2007/06/23 22:10 JST) ^ |
| | |
--------|---------- stat(2) ----|--|--- getti...
user | | |
+<---- /etc/localtime = JST | V
#
#
V
2007/06/23 22:10 JST
ここで,
# ~/timezone 0
minuteswest = 0, dsttime = 0
とし,カーネルが保持している timezone を 0 に設定する.
そして,SD カードを umount し,mount しなおすと…
# ls -l
合計 0
-rwxr-xr-x 1 root root 0 2007-06-24 07:10 textfile.txt
予想通り,表示される時刻が狂う.
が,タイムスタンプ取扱いモデルと照らしあわせると,このよ...
HDD
2007/06/23 22:10
#
#
--------#----------
kernel # (assume localtime)
#
+<------------------------------- sys_tz (timezo...
| (epoch = 2007/06/23 22:10 UTC) ^ |
| | |
--------|---------- stat(2) ----|--|--- getti...
user | | |
+<---- /etc/localtime = JST | V
#
#
V
2007/06/24 07:10 JST
** まとめ [#k0011530]
というわけで,まとめてみると
- FAT filesystem でのタイムスタンプは,Linux カーネルが保...
- ユーザプロセスでは /etc/localtime の timezone に従って...
というあたりかな.
** gettimeofday(2) / settimeofday(2) の man [#x53d42fa]
蛇足として.
[http://www.linux.or.jp/JM/html/LDP_man-pages/man2/gettim...
> timezone 構造体を使うのは時代遅れ (obsolete) である: tz...
とあり,tz が後方互換性だけのための引数のように書かれてい...
FAT filesystem (や,timezone の概念の無い他の filesystem...
ページ名:
-->