そのファイルには何が記録されているのか?
思考1:テキストファイル
- MacBookでテキストエディット.appを開いて、半角で「ABC」とだけ入力して、保存した。(ファイル名:abc.txt)
- 上記で保存したabc.textの情報を見る(command-I)と、「サイズ:4KB(3 バイト)」と確認できた。
- ハードディスク上は4KBの領域を占有し、その領域の3バイトがファイルの実態であると、これまで理解してきた。
- 3バイトの中身は、おそらく「ABC」の文字コードだろう。
- 半角の「ABC」の文字コードはASCIIコード表で確認できる。
- おそらく、16進数で確認すれば「41、42、43」が並んでいるだろう。
- HexEditor.appで確認してみた。
- 予想どおりの「41、42、43」が確認できた。めでたし、めでたし?
いや、現実は違った...。Finderから見える景色は3バイトだが、OSレベルではもっと多くの情報が記録されているはずである。(作成日・更新日なども表示されるし)
思考2:JPEGファイル
- iPhoneで写真を撮った。
- 今時のカメラは、撮影情報としてGPS座標まで記録される*1。
- そのままweb上に公開すると、撮影場所は丸見えになる。
- 地図と連動して旅行の写真を公開するなどの特定の目的がなければ、GPS座標は削除したい。
ところで、撮影情報って、どこに記録されているんだろう?
mdlsコマンドが表示する情報
- Spotlightが検索対象のディレクトリに、撮影情報のある画像:P1010528.JPGをコピーする。
- mdlsコマンドを実行すると...
$ mdls /Volumes/Macintosh\ Le/Users/zari/Documents/P1010528.JPG kMDItemAcquisitionMake = "OLYMPUS OPTICAL CO.,LTD" kMDItemAcquisitionModel = "C2020Z" kMDItemBitsPerSample = 32 kMDItemColorSpace = "RGB" kMDItemContentCreationDate = 1999-01-01 00:00:00 +0900 kMDItemContentModificationDate = 1999-01-01 00:00:00 +0900 kMDItemContentType = "public.jpeg" kMDItemContentTypeTree = ( "public.jpeg", "public.image", "public.data", "public.item", "public.content" ) kMDItemCreator = "v954-76" kMDItemDisplayName = "P1010528.JPG" kMDItemEXIFVersion = "2.1" kMDItemExposureTimeSeconds = 0.03333334 kMDItemFinderComment = "comment" kMDItemFlashOnOff = 1 kMDItemFNumber = 2 kMDItemFocalLength = 6.6 kMDItemFSContentChangeDate = 1999-01-01 00:00:00 +0900 kMDItemFSCreationDate = 1999-01-01 00:00:00 +0900 kMDItemFSCreatorCode = "" kMDItemFSFinderFlags = 0 kMDItemFSHasCustomIcon = 0 kMDItemFSInvisible = 0 kMDItemFSIsExtensionHidden = 0 kMDItemFSIsStationery = 0 kMDItemFSLabel = 0 kMDItemFSName = "P1010528.JPG" kMDItemFSNodeCount = 0 kMDItemFSOwnerGroupID = 20 kMDItemFSOwnerUserID = 501 kMDItemFSSize = 375701 kMDItemFSTypeCode = "" kMDItemHasAlphaChannel = 0 kMDItemISOSpeed = 100 kMDItemKind = "JPEG イメージ" kMDItemLastUsedDate = 1999-01-01 00:00:00 +0900 kMDItemOrientation = 0 kMDItemPixelCount = 1920000 kMDItemPixelHeight = 1200 kMDItemPixelWidth = 1600 kMDItemProfileName = "sRGB IEC61966-2.1" kMDItemRedEyeOnOff = 0 kMDItemResolutionHeightDPI = 72 kMDItemResolutionWidthDPI = 72 kMDItemUsedDates = ( "1999-01-01 00:00:00 +0900" )
撮影情報やコメントが表示された。
- 次に、Spotlightの検索を除外する場所に指定してみる。
- mdlsコマンドを実行すると...
$ mdls /Volumes/Macintosh\ Le/Users/zari/Documents/P1010528.JPG mdls: could not find /Volumes/Macintosh Le/Users/zari/Documents/P1010528.JPG.
mdlsでは何も見つけられないと警告された。
- 再び、Spotlightの検索対象に戻してみる。
- 戻して直ぐに、mdlsコマンドを実行すると...
$ mdls /Volumes/Macintosh\ Le/Users/zari/Documents/P1010528.JPG
kMDItemFSContentChangeDate = 1999-01-01 00:00:00 +0900
kMDItemFSCreationDate = 1999-01-01 00:00:00 +0900
kMDItemFSCreatorCode = ""
kMDItemFSFinderFlags = 0
kMDItemFSHasCustomIcon = 0
kMDItemFSInvisible = 0
kMDItemFSIsExtensionHidden = 0
kMDItemFSIsStationery = 0
kMDItemFSLabel = 0
kMDItemFSName = "P1010528.JPG"
kMDItemFSNodeCount = 0
kMDItemFSOwnerGroupID = 20
kMDItemFSOwnerUserID = 501
kMDItemFSSize = 375701
kMDItemFSTypeCode = ""
あれあれ、kMDItemFSで始まるファイルの基本情報(サイズ・変更日時・作成日時・ファイル名・ラベル・アクセス権限・タイプ・クリエーターなどの)しか表示されない!
- それでは、検索インデックスを優先して作成してもらうために /Volumes/Macintosh\ Le/Users/zari/Documents/P1010528.JPG を他のディレクトリに移動して、また戻してみる。
- mdlsコマンドを実行すると...
今度は、撮影情報やコメントのメタデータが表示された。(Spotlightの検索対象のディレクトリにコピーした、最初の状態)
Spotlightのインデックスの状態
- Spotlightの検索を除外したボリュームは、インデックスが空っぽになった。
$ ls -a /Volumes/Macintosh\ Le/.Spotlight-V100/Store-V1/Stores
. ..
-
- ゆえに、mdlsコマンドで確認しても「何も見つけられない」と警告されたのだ。
- Spotlightの検索対象であれば、以下のようなインデックスが生成されるのである。
$ ls -a /Volumes/Macintosh\ Le/.Spotlight-V100/Store-V1/Stores/C0514DCD-9DC8-4E5A-9D88-F61BB6688CE6. 0.indexHead indexState live.0.indexDirectory live.0.indexTermIds .. 0.indexIds journalAttr.1 live.0.indexGroups live.0.indexUpdates .store.db 0.indexPositionTable journalExclusion live.0.indexHead store.db 0.indexArrays 0.indexPositions journalLive live.0.indexIds tmp.SnowLeopard 0.indexCompactDirectory 0.indexPostings journalSync live.0.indexPositionTable 0.indexDirectory 0.indexTermIds live.0.indexArrays live.0.indexPositions 0.indexGroups 0.indexUpdates live.0.indexCompactDirectory live.0.indexPostings
-
- Spotlightの検索対象に戻した直後はファイルの基本情報しか表示されなかった。
- Spotlightのインデックス作成が完了すると、撮影情報やコメントまでちゃんと表示されるようになった。
mdlsコマンドは、ファイルの基本情報と、Spotlightのインデックスから取得できる情報を表示する仕組みのようだ。Spotlightのインデックスについては、各ボリュームのルート直下の /.Spotlight-V100 以下に作成されることが分かった。それでは、ファイルの基本情報はどこに保存されているのだろう?
それを知るには、ファイルシステムHFS+の構造を理解する必要があった。
ファイルの基本情報とは?
- 書籍に本文と目次があるように、ハードディスクにもデータ(本文)とインデックス(目次)がある。
- 広大な記憶領域に点在するデータに素早くアクセスできるのは、データの位置を記録しているインデックスのおかげである。
- いかに素早く・正確に・確実に・無駄なく情報を保存・検索できるか、工夫を凝らして、データとインデックスの形式を規定したものが、ファイルシステムだ。
- インデックスにはデータの位置が記録されているのはもちろん、サイズ・変更日時・作成日時・ファイル名・ラベル・アクセス権限・タイプ・クリエーターなどの基本情報も含まれている。
- それらの基本情報が手がかりとなって、目的のデータに素早くアクセスしたり、効率的に管理できるのだ。
つまり、ファイルの基本情報はインデックスにある。
HFS+の構造
- 参考ページ(大変参考になりました。感謝です!)
上記ページと現状の自分の知識で、果たしてどれだけ正確に理解できているか怪しいが、自分の理解を書いてみた。
アロケーションブロック
ボリュームヘッダー
フォーク
エクステント
カタログファイルとエクステントオーバーフローファイル
- カタログファイルでは、1つのフォークに対して、8つのエクステントまで記録できる。
- ファイルを保存する時に、連続した領域を確保できれば、1つのエクステントだけで足りてしまう。(可能な限り1つのエクステントになることを目指す仕様)
- 連続した領域を確保できない場合*3でも、8つの領域までなら、カタログファイルで直接管理される。
- 9つ以上の領域に分けて保存する場合は、エクステントの続きは、エクステントオーバーフローファイルに引き継がれる。
このように、カタログファイル・エクステントオーバーフローファイル・アトリビュートファイルが、一般的なファイルのインデックス情報となっている。
- カタログファイルは以下のように定義されたレコードの集合である。(B-tree、バランスツリーと呼ばれる構造になっている)
- データフォークの格納場所(dataFork)以外にも、様々な属性が付属しているのが分かる。これがファイルの基本情報となっている。
struct HFSPlusCatalogFile { SInt16 recordType; UInt16 flags; UInt32 reserved1; HFSCatalogNodeID fileID; UInt32 createDate; UInt32 contentModDate; UInt32 attributeModDate; UInt32 accessDate; UInt32 backupDate; HFSPlusBSDInfo permissions; FileInfo userInfo; ExtendedFileInfo finderInfo; UInt32 textEncoding; UInt32 reserved2; HFSPlusForkData dataFork; HFSPlusForkData resourceFork; }; typedef struct HFSPlusCatalogFile HFSPlusCatalogFile; struct HFSPlusForkData { UInt64 logicalSize; UInt32 clumpSize; UInt32 totalBlocks; HFSPlusExtentRecord extents; /* HFSPlusExtentDescriptorの配列[8] */ }; typedef struct HFSPlusForkData HFSPlusForkData; typedef HFSPlusExtentDescriptor HFSPlusExtentRecord[8]; struct HFSPlusExtentDescriptor { UInt32 startBlock; /* 先頭のアロケーションブロックの番号 */ UInt32 blockCount; /* 連続するアロケーションブロックの数 */ }; typedef struct HFSPlusExtentDescriptor HFSPlusExtentDescriptor;
拡張属性(EA)を確認する
- mdlsコマンドで表示されるkMDItemFSで始まる属性情報は、カタログファイルの情報が元になっているようだ。
- 一方、ファイルの基本情報以外の属性は、アトリビュートファイルで関連付けられる。
- その属性はSpotlightによって収集され、Spotlightのインデックスに情報が集まる。
- mdlsコマンドはSpotlightのインデックスを見て、基本情報以外の属性も表示してくれるようになるのだ。
- ところで、mdlsコマンドが表示するのはSpotlightのインデックスの状態であり、拡張属性(EA)そのものではない。
- Spotlightがすべての拡張属性(EA)を収集するという保証もない。(Spotlightのインデックスに追加されない属性だってあるかも)
- また、拡張属性(EA)以外の情報も収集している可能性もある。(テキストの内容検索も出来ることだし)
- 拡張属性(EA)の本体は、データフォークと同じように、それぞれのフォークに記録*4されている。
- 拡張属性(EA)は、Spotlightの検索対象である・ないに関係なく、恒久的に保存される。
- 拡張属性(EA)は、xattrコマンドで確認できる。やってみた。
$ xattr -l ~/Desktop/abc.txt com.apple.TextEncoding: utf-8;134217984
- com.apple.TextEncodingという属性に、テキストエンコーディングが保存されていた!
- そういえば、保存する時にテキストエンコーディングを指定する項目があった。それが拡張属性(EA)に保存されていたのだ。
- ちなみに、mdlsコマンドも試してみた。
$ mdls ~/Desktop/abc.txt
kMDItemContentCreationDate = 2010-01-29 16:35:19 +0900
kMDItemContentModificationDate = 2010-01-29 16:35:19 +0900
kMDItemContentType = "public.plain-text"
kMDItemContentTypeTree = (
"public.plain-text",
"public.text",
"public.data",
"public.item",
"public.content"
)
kMDItemDisplayName = "abc.txt"
kMDItemFSContentChangeDate = 2010-01-29 16:35:19 +0900
kMDItemFSCreationDate = 2010-01-29 16:35:19 +0900
kMDItemFSCreatorCode = ""
kMDItemFSFinderFlags = 0
kMDItemFSHasCustomIcon = 0
kMDItemFSInvisible = 0
kMDItemFSIsExtensionHidden = 0
kMDItemFSIsStationery = 0
kMDItemFSLabel = 0
kMDItemFSName = "abc.txt"
kMDItemFSNodeCount = 0
kMDItemFSOwnerGroupID = 20
kMDItemFSOwnerUserID = 501
kMDItemFSSize = 3
kMDItemFSTypeCode = ""
kMDItemKind = "Text Document"
kMDItemLastUsedDate = 2010-01-29 16:35:20 +0900
kMDItemUsedDates = (
"2010-01-29 00:00:00 +0900"
)
- どうやらSpotlightのインデックスには、テキストエンコーディングの情報は収集されないようだ。
- 試しに、abc.txtのSpotlightコメントに「CommentTest」と書き込んでみると...
$ xattr -l ~/Desktop/abc.txt com.apple.TextEncoding: utf-8;134217984 com.apple.metadata:kMDItemFinderComment: 00000000 62 70 6C 69 73 74 30 30 5B 43 6F 6D 6D 65 6E 74 |bplist00[Comment| 00000010 54 65 73 74 08 00 00 00 00 00 00 01 01 00 00 00 |Test............| 00000020 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 |................| 00000030 00 00 00 00 14 |.....| 00000035
- Spotlightコメントは、com.apple.metadata:kMDItemFinderCommentという属性で保存されるようだ。
これでやっと、思考1:テキストファイルの疑問が解決した!
撮影情報はどこ?
- 次に、撮影情報付きのJPEGファイルの拡張属性(EA)を確認してみた。
$ xattr -l /Volumes/Macintosh\ Le/Users/zari/Documents/P1010528.JPG
あれあれ?何も表示されない。予想では、様々な情報が表示されることを期待したのだが...。
Exif
- 調べてみると、撮影情報はExifという規格で、画像ファイルの中に埋め込まれていた。
- つまり、データフォークなのだけど、ルールを決めて撮影情報を区別できるようにしてあるのだ。
つまり、データフォークの中に、HFS+のようなインデックスとフォークの構造を作って、そこに属性情報を埋め込んであるのだ。
検証
- jpegファイルの16進数ダンプ。
- どんな書式か、検証してみた。
ff d8 |
開始マーカー |
ff e1 |
Exif形式の情報 APP1であることを示す |
1c 45 |
APP1のサイズは0x1c45バイト |
45 78 69 66 00 00 |
Exifヘッダー |
49 49 | 2a 00 | 08 00 00 00 |
TIFFヘッダー | TIFFヘッダー | TIFFヘッダー |
Intel形式 | Tagマーカー | オフセット値=8 |
0b 00 |
エントリ数=11個 |
タグ | タイプ | カウント | 値、またはそのオフセット値 | |
---|---|---|---|---|
0e 01 | 02 00 | 20 00 00 00 | 92 00 00 00 | |
画像の説明 | ASCII文字 | 32文字 | 0x92バイト先の「OLYMPUS DIGITAL CAMERA」 | |
0f 01 | 02 00 | 18 00 00 00 | b2 00 00 00 | |
メーカー名 | ASCII文字 | 24文字 | 0xb2バイト先の「OLYMPUS OPTICAL CO.,LTD」 | |
10 01 | 02 00 | 07 00 00 00 | ca 00 00 00 | |
機種名 | ASCII文字 | 7文字 | 0xcaバイト先の「C2020Z」 | |
... | ... | ... | ... |
-
-
- オフセット値は、TIFFヘッダー先頭からの位置
-
タグを調べながら、目で追って行くのはとっても大変...。
exiftoolコマンド
- 実は、そんなに苦労しなくても、exiftoolコマンドが公開されていた。(素晴らしい!)
$ exiftool /Volumes/Macintosh\ Le/Users/zari/Documents/P1010528.JPG ExifTool Version Number : 8.08 File Name : P1010528.JPG Directory : /Users/zari/Desktop File Size : 367 kB File Modification Date/Time : 1999:01:01 00:00:00+09:00 File Permissions : rwxrwxrwx File Type : JPEG MIME Type : image/jpeg Exif Byte Order : Little-endian (Intel, II) Image Description : OLYMPUS DIGITAL CAMERA Make : OLYMPUS OPTICAL CO.,LTD Camera Model Name : C2020Z Orientation : Horizontal (normal) X Resolution : 72 Y Resolution : 72 Resolution Unit : inches Software : v954-76 Modify Date : 0000:00:00 00:00:00 Y Cb Cr Positioning : Co-sited Exposure Time : 1/30 F Number : 2.0 Exposure Program : Program AE ISO : 100 Exif Version : 0210 Date/Time Original : 0000:00:00 00:00:00 Create Date : 0000:00:00 00:00:00 Components Configuration : Y, Cb, Cr, - Compressed Bits Per Pixel : 2 Exposure Compensation : 0 Max Aperture Value : 2.0 Metering Mode : Multi-segment Light Source : Unknown Flash : Fired Focal Length : 6.6 mm Special Mode : Normal, Sequence: 0, Panorama: (none) Quality : HQ (Normal) Macro : Off Black And White Mode : Off Digital Zoom : 0.0 Focal Plane Diagonal : 7.8 mm Lens Distortion Params : -291 -531 -574 -265 -460 -479 Resolution : 2 Camera Type : SR954 Camera ID : OLYMPUS DIGITAL CAMERA Data Dump : (Binary data 122 bytes, use -b option to extract) User Comment : Flashpix Version : 0100 Color Space : sRGB Exif Image Width : 1600 Exif Image Height : 1200 Interoperability Index : R98 - DCF basic file (sRGB) Interoperability Version : 0100 File Source : Digital Camera Scene Type : Directly photographed Compression : JPEG (old-style) Thumbnail Offset : 2048 Thumbnail Length : 4875 Image Width : 1600 Image Height : 1200 Encoding Process : Baseline DCT, Huffman coding Bits Per Sample : 8 Color Components : 3 Y Cb Cr Sub Sampling : YCbCr4:2:2 (2 1) Aperture : 2.0 Image Size : 1600x1200 Scale Factor To 35 mm Equivalent: 5.5 Shutter Speed : 1/30 Thumbnail Image : (Binary data 4875 bytes, use -b option to extract) Circle Of Confusion : 0.005 mm Field Of View : 52.4 deg Focal Length : 6.6 mm (35 mm equivalent: 36.6 mm) Hyperfocal Distance : 4.02 m Light Value : 6.9
かなりの情報量だ!Spotlightは、これらの情報を読み取って、検索インデックスに取り込んでいたのだ。(それがmdlsコマンドで出力された)
exifの削除
# IMG_0439.JPG の GPS情報を削除する $ exiftool -GPSLatitude= -GPSLongitude= -GPSAltitude= -overwrite_original ~/Desktop/IMG_0439.JPG # ...あるいは $ exiftool -geotag= -overwrite_original ~/Desktop/IMG_0439.JPG 1 image files updated # P1010528.JPGから、すべてのExif情報を削除する $ exiftool -all= -overwrite_original ~/Desktop/IMG_0439.JPG 1 image files updated # デスクトップの.jpg画像から、すべてのExif情報を削除する $ exiftool -all= -overwrite_original ~/Desktop/*.JPG 2 image files updated # デスクトップのファイルから、すべてのExif情報を削除する(pdfも対象になった) $ exiftool -all= -overwrite_original ~/Desktop 1 directories scanned 4 image files updated # ちなみに、すべてのExif情報を削除しても、以下の内容だけは確認できた $ exiftool ~/Desktop/IMG_0439.JPG ExifTool Version Number : 8.08 File Name : IMG_0439.JPG Directory : /Users/zari/Desktop File Size : 1585 kB File Modification Date/Time : 2010:01:31 14:03:54+09:00 File Permissions : rw-r--r-- File Type : JPEG MIME Type : image/jpeg Image Width : 2048 Image Height : 1536 Encoding Process : Baseline DCT, Huffman coding Bits Per Sample : 8 Color Components : 3 Y Cb Cr Sub Sampling : YCbCr4:2:0 (2 2) Image Size : 2048x1536
-
-
- exiftool -TagName='abc' で、TagNameに'abc'が書き込まれる。
- exiftool ~/Desktop/IMG_0439.JPGでの出力が正式なタグ名称とは限らない。(例:Camera Model Name...NG → Model...OK)
- exiftool -TagName= のみだと、何も値を指定しないので削除されることになる。(exiftool -TagName='' でも同じ結果になった)
- exiftool -overwrite_originalオプションによって、元ファイルに上書きされる。-overwrite_original無しなら、オリジナルがバックアップされる。
-
以上でやっと、思考2:JPEGファイルで悩んだ撮影情報の場所も分かり、無事削除できたのであった。