Linux ファイルシステムにおけるタイムスタンプ(mtime、atime、ctime)の違い、確認方法、変更方法

2023-01-13Bash,CentOS,Linux,Ubuntu,Zsh

はじめに

Linux OS 上で行う操作はほぼ全て「ファイル」に対して行われます。

ファイルには「作成された時」や「更新された時」の「日時」の情報が付与されています。

タイトルにある mtimeatimectime というのはファイルに関する日時情報にあたりますが、 なぜ 3 つあるのか?それぞれどういった情報なのか? は即答が難しいです。

これら 3 つの「日時」情報を紹介します。

検証環境

$ uname -moi
x86_64 x86_64 GNU/Linux

$ head -n 2 /etc/os-release
NAME="Ubuntu"
VERSION="21.04 (Hirsute Hippo)"

$ bash -version | head -n 1
GNU bash, バージョン 5.1.4(1)-release (x86_64-pc-linux-gnu)

「タイムスタンプ」はファイル操作したときの日時情報

Linux ファイルシステムにおける日時情報のことを タイムスタンプ と呼びます。

タイムスタンプ はファイルに対して何らかの操作したときに記録されます。

ファイルが作られたタイミングでもタイムスタンプが記録されるので、「タイムスタンプが存在しない」ということはありえません。

「タイムスタンプ」はいくつかの種類がある

一口に「日時情報」といっても、Linux ファイルシステムには以下の 3 つの「タイムスタンプ」があり、 更新されるタイミングが異なります。

  • mtime : 中身(コンテンツ)が変更された日時
  • atime : アクセスされた日時
  • ctime : 属性が変更された日時

mtime は比較的わかりやすいですが、他のものがわかりにくいです。

具体例をもとにそれぞれのタイムスタンプの変化を見ていきましょう。

タイムスタンプを確認するには?

タイムスタンプを確認する方法はいくつかあります。

ls -l コマンド ( mtime を確認 )

ls -l コマンドでファイルの一覧を表示できますが、この際に表示されるタイムスタンプは mtime となります。

# file1.txt というファイルを作成
$ date > file1.txt

$ ls -l
合計 4
-rw-r--r-- 1 root root 43  1月  6 17:23 file1.txt

ls -lu コマンド ( atime を確認 )

ls -l コマンドに更に u オプションを付与すると、 atime タイムタンプが表示されます。

$ ls -lu
合計 4
-rw-r--r-- 1 root root 43  1月  6 17:23 file1.txt

ls -l の実行結果と全く一緒なので、違うタイムスタンプが表示されているかわかりませんね。

試しに atime を変更してみましょう。

先に説明した通り、 atime とは アクセスされた日時 のことでした。

そこで内容は変更せず、ファイルの内容の確認だけを行ってみましょう。

# cat コマンドでファイルの内容を参照する
$ cat file1.txt
2022年  1月  7日 月曜日 17:23:48 JST

# mtime を確認
$ ls -l
合計 4
-rw-r--r-- 1 root root 43  1月  6 17:23 file1.txt

# atime を確認
$ ls -lu
合計 4
-rw-r--r-- 1 root root 43  1月  6 17:26 file1.txt

このように、atime はファイルの内容を読み込んだ場合に更新されます。


atime をもう少し詳しく解説

実は、 atime の挙動はもう少し複雑です。

/etc/fstab にて「ファイルシステムがどのようなオプション付きでマウントされているか」によって挙動が変わります。

# atime はファイルアクセス時に「常に更新される」
$ cat /proc/mounts | grep 'atime'
/dev/root / ext4 rw,strictatime 0 0
# atime はファイルアクセス時に「常に更新されない」
$ cat /proc/mounts | grep 'atime'
/dev/root / ext4 rw,noatime 0 0
# atime はファイルアクセス時に「条件(後述)を満たした場合」に更新される
$ cat /proc/mounts | grep 'atime'
/dev/root / ext4 rw,relatime 0 0

多くの場合、 relatime オプションが有効になっています。
relatime が設定されているファイルシステムでは、以下のいずれかの条件を満たした場合のみ atime が更新されます。

  1. mtime よりも古い
  2. ctime よりも古い
  3. 現在時刻-24 時間 よりも古い

ls コマンドや du コマンドでは更新されません。ファイルサイズもブロックサイズなどから算出しているためだと思います。

vised で更新する場合は mtime だけでなく atime も更新されてしまいますが、これは内部的に更新する前に読み込みを行っているためでしょう。

このように atime は思わぬタイミングで更新される可能性があります。


ついでに mtime も変更してみましょう。
ファイルの末尾に文字列を「追記」してみます。

$ date >> file1.txt

# mtime を確認
$ ls -l
合計 4
-rw-r--r-- 1 root root 86  1月  6 17:28 file1.txt

# atime を確認
$ ls -lu
合計 4
-rw-r--r-- 1 root root 86  1月  6 17:26 file1.txt

ファイルの末尾に文字列を追記しただけの場合は、mtime だけが更新され、atime は更新されません。

また、 ls コマンドでファイルの属性情報(≠ 中身)を参照しただけでは、atime は更新されません。

# file1.txt に対して ls コマンドを実行する(ファイルの「中身」を参照しているわけではない)
$ ls -l file1.txt
-rw-r--r-- 1 root root 86  1月  6 17:28 file1.txt

# atimeは更新されない
$ ls -lu
合計 4
-rw-r--r-- 1 root root 86  1月  6 17:26 file1.txt

ls -lc コマンド ( ctime を確認 )

ls -l コマンドに更に c オプションを付与すると、 ctime タイムタンプが表示されます。

$ ls -lc
合計 4
-rw-r--r-- 1 root root 86  1月  6 17:28 file1.txt

表示されたタイムスタンプは mtime と同じ値となっているので ctime が表示されたのかわかりません。

試しに ctime を変更してみましょう。

先に説明した通り、 ctime とは 属性が変更された日時 のことでした。

そこで、ファイルの「属性」情報を変更してみましょう。
「属性」といえば、パーミッション、パーミッション変更といえば chmod ですね。

chmod 実行前の mtime、 atime、ctime と 実行後の mtime、atime、ctimme を表示してみます。

# ***** chmod 実行前 *****
# mtime を確認
$ ls -l
合計 4
-rw-r--r-- 1 root root 86  1月  6 17:28 file1.txt

# atime を確認
$ ls -lu
合計 4
-rw-r--r-- 1 root root 86  1月  6 17:26 file1.txt

# ctime を確認
$ ls -lc
合計 4
-rw-r--r-- 1 root root 86  1月  6 17:28 file1.txt

$ chmod 666 file1.txt

# ***** chmod 実行後 *****
# mtime を確認
$ ls -l
合計 4
-rw-rw-rw- 1 root root 86  1月  6 17:28 file1.txt

# atime を確認
$ ls -lu
合計 4
-rw-rw-rw- 1 root root 86  1月  6 17:26 file1.txt

# ctime を確認
$ ls -lc
合計 4
-rw-rw-rw- 1 root root 86  1月  6 18:08 file1.txt

chmod で変更されたのは、 ctime だけだったのがわかります。

ファイルの内容を変更する場合は、 mtime が変更されるため、「内容」とともに「属性」情報も変更されます。
そのため、 ctime も同時に変更されます。

$ ls -l
合計 4
-rw-rw-rw- 1 root root 86  1月  6 17:28 file1.txt

$ ls -lc
合計 4
-rw-rw-rw- 1 root root 86  1月  6 18:08 file1.txt

$ echo hello >> file1.txt

$ ls -l
合計 4
-rw-rw-rw- 1 root root 92  1月  6 18:12 file1.txt

$ ls -lc
合計 4
-rw-rw-rw- 1 root root 92  1月  6 18:12 file1.txt

stat コマンド

ここまで ls コマンドでそれぞれのタイムスタンプ情報を確認してきました。

  • mtime : ls -l で確認
  • atime : ls -lu で確認
  • ctime : ls -lc で確認

3 つのタイムスタンプを確認するために 3 回もコマンド実行するのは面倒です。

ファイルのメタ情報 (タイムスタンプを含む) をまとめて確認する場合は、 stat コマンドを利用します

$ stat file1.txt
  File: file1.txt
  Size: 92              Blocks: 8          IO Block: 4096   通常ファイル
Device: 100014h/1048596d        Inode: 3036699     Links: 1
Access: (0666/-rw-rw-rw-)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2022-01-06 17:26:04.908323000 +0900
Modify: 2022-01-06 18:12:58.535283000 +0900
Change: 2022-01-06 18:12:58.535283000 +0900
 Birth: 2022-01-06 17:23:48.085993000 +0900

Access が atime、 Modify が mtime、 Change が ctime に該当します。

3 つのタイムスタンプの他に、 Birth 、つまり作成日時も確認できます。
ただし、 Birth が表示されるかどうかは利用しているファイルシステムによって異なり、表示されない場合もあるためあまり期待はできない属性情報です。

まとめ

mtime、atime、ctime の更新タイミングと確認方法は以下の表に表現できます。

タイムスタンプ「内容」を変更「属性」を変更「内容」を参照タイムスタンプを確認
mtime更新stat / ls -l
atime更新(ファイルシステムの設定による)stat / ls -lu
ctime更新更新stat / ls -lc

おまけ : touch コマンドで atimemtime を更新する

touch コマンドを使って、 任意の時刻に atimemtime を更新できます。

実際にファイルを用意して、 atime や mtime を更新してみます。

# まずはファイルを作成
$ touch file2.txt

# ファイルのタイムスタンプを確認
$ stat file2.txt
  File: file2.txt
  Size: 0               Blocks: 0          IO Block: 4096   通常の空ファイル
Device: 100014h/1048596d        Inode: 3036698     Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2022-02-07 19:39:37.176322000 +0900
Modify: 2022-02-07 19:39:37.176322000 +0900
Change: 2022-02-07 19:39:37.176322000 +0900
 Birth: 2022-02-07 19:39:37.176322000 +0900

# before-stat というファイルに書き出しておく(あとで diff してみる)
$ stat file2.txt > before-stat

それでは atime を "2022-02-05 13:00" に更新してみます。

touch コマンド実行時に、 -a-t オプションを指定します。

# atime を更新
$ touch -a -t 02051300 file2.txt

# stat の実行結果と diff する
$ stat file2.txt > after-stat
$ diff before-stat after-stat
5c5
< Access: 2022-02-07 19:39:37.176322000 +0900
---
> Access: 2022-02-05 13:00:00.000000000 +0900
7c7
< Change: 2022-02-07 19:39:37.176322000 +0900
---
> Change: 2022-02-07 19:40:38.784894000 +0900

> で始まる部分が変更後のタイムスタンプです。
atime だけを更新するつもりが、 ctime も同時に更新されました

今度は mtime を "2022-02-05 14:00" に更新してみます。

touch コマンド実行時に、 -m-t オプションを指定します。

# mtime を更新
$ touch -m -t 02051400 file2.txt

$ stat file2.txt > after2-stat
$ diff after-stat > after2-stat
6,7c6,7
< Modify: 2022-02-07 19:39:37.176322000 +0900
< Change: 2022-02-07 19:40:38.784894000 +0900
---
> Modify: 2022-02-05 14:00:00.000000000 +0900
> Change: 2022-02-07 19:44:38.329516000 +0900

mtime だけを更新するつもりが、 ctime も同時に更新されました

mtimeatime ともに、 touch コマンドで改ざんできます。

ただし、 touch コマンドでは ctime を変更できません。

そこで、 なにか変更が行われたのでは?と思ったら、ctime を見ておくのが良さそうです

ひとこと

数年前からいつかブログネタに整理したいと思っていたことがようやく投稿できました。

2023-01-13Bash,CentOS,Linux,Ubuntu,Zsh