以前、FreeBSDではlongnameフィーチャーは使えないという話をした。
FreeBSDで255バイト超のファイル名でファイルは作れないとして、Linuxで作った256バイト以上のファイル名を持つZFSをFreeBSDにインポートした場合、どうなるのだろうか。インポートできなかったり、クラッシュしたり、データセットが壊れるなんてことは流石にないと思うが、分からんってのが正直なところ。サクッと試してみよう。
まずはLinux側でlongnameフィーチャーが有効なデータセットと、長いファイル名を持つファイルを作る。
環境はこんな感じ。Linuxと言いつつProxmox VEです。
# pveversion
pve-manager/9.1.4/5ac30304265fbd8e (running kernel: 6.17.4-2-pve)
# zpool version
zfs-2.3.4-pve1
zfs-kmod-2.3.4-pve1
# zpool status zlongname
pool: zlongname
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
zlongname ONLINE 0 0 0
zvol/zvm/vm-100-disk-0 ONLINE 0 0 0
errors: No known data errors
データセットの文字コードはUTF-8にした。
# zfs create -o utf8only=on zlongname/longname
UTF-8の場合、日本語1文字はおおむね3~4バイトでエンコードされるので、255バイト=全角85文字となる。255バイトと256バイトのファイル名でファイルを作ってみる。
# touch '123456789~1~123456789~2~1234 56789~3~123456789~4~123456789~5~123456789~6~123456789~7~123456789~8~12345' # touch '123456789~1~123456789~2~1234 56789~3~123456789~4~123456789~5~123456789~6~123456789~7~123456789~8~12345a' touch: cannot touch '123456789~1~123456789~2~123456789~3~123456789~4~123456789~5~123456789~6~123456789~7~123456789~8~12345a': File name too long
256バイトの方はファイル名長すぎィと怒られた。longnameフィーチャーを有効にして再度試してみる。
# zfs set longname=on zlongname/longname # touch '123456789~1~123456789~2~1234 56789~3~123456789~4~123456789~5~123456789~6~123456 789~7~123456789~8~12345a' # ls 123456789~1~123456789~2~123456789~3~123456789~4~123456789~5~123456789~6~123456789~7~123456789~8~12345 123456789~1~123456789~2~123456789~3~123456789~4~123456789~5~123456789~6~123456789~7~123456789~8~12345a
今度は当然ながら問題なく作成できた。このプールをFreeBSDに持っていく。
FreeBSD側は以下のような感じ。ashiftの警告が出てるけど本質じゃないのでスルーで。
$ freebsd-version -uk
14.3-RELEASE-p2
14.3-RELEASE-p2
$ zpool status zlongname
pool: zlongname
state: ONLINE
status: One or more devices are configured to use a non-native block size.
Expect reduced performance.
action: Replace affected devices with devices that support the
configured block size, or migrate data to a properly configured
pool.
config:
NAME STATE READ WRITE CKSUM
zlongname ONLINE 0 0 0
gptid/737d89da-f0e3-6c4d-a86d-426d41479aa8 ONLINE 0 0 0 block size: 512B configured, 4096B native
lsでは普通に255バイト超のファイル名も表示された。ちょっと意外。
$ ls /zlongname/longname/ 123456789~1~123456789~2~123456789~3~123456789~4~123456789~5~123456789~6~123456789~7~123456789~8~12345 123456789~1~123456789~2~123456789~3~123456789~4~123456789~5~123456789~6~123456789~7~123456789~8~12345a
開こうとするとファイル名長すぎィと怒られた。
$ cat '/zlongname/longname/123456789~1~123456789~2~123456789~3~123456789~4~123456789~5~123456789~6~123456789~7~123456789~8~12345a' cat: /zlongname/longname/123456789~1~123456789~2~123456789~3~123456789~4~123456789~5~123456789~6~123456789~7~123456789~8~12345a: File name too long
256バイトのファイル名でファイルを作ろうとしても、やっぱり長すぎィと怒られた。
$ touch '/zlongname/longname/123456789~1~123456789~2~123456789~3~123456789~4~123456789~5~123456789~6~123456789~7~123456789~8~12345b' touch: /zlongname/longname/123456789~1~123456789~2~123456789~3~123456789~4~123456789~5~123456789~6~123456789~7~123456789~8~12345b: File name too long
というわけで、ZFSのlongnameフィーチャーを有効にし256バイト以上のファイル名があるデータセットをFreeBSDに持っていくと、ファイルの存在は確認できるが、ファイル操作は不可というのが現状のようだ。
AinexのAIF-12は、USBを計7ポートを増設可能なPCI Expressカードである。ポートの内訳は下記の通りで、USB 3.0とUSB 2.0の両方のピンヘッダを備えた、なかなかわかっとるじゃないか仕様となっている。
値段も3000円ちょいとお手軽価格。
採用チップの情報は公式サイトに書かれておらず、ネットを彷徨っても発見できず。商品画像からμPD系とGL3523が載ってそうな雰囲気はあるものの、不明瞭で確証が持てなかった。
諸事情で内部USB 2.0ピンヘッダが欲しく、ほかに選択肢もなかったのでダメもとで購入した画像がこれ。
ばっちりμPD720201とGL3523ですな。
パターンを追ってみると、Type-A 3ポートはμPD720201に直結、それ以外はGL3522のハブ経由の模様。まとめると以下のトポロジーになるっぽい。
uPD720201 ├ 外部 3.0 Type-A ├ 外部 3.0 Type-A ├ 内部 3.0 Type-A └ GL3522 ├ 内部 3.0 ピンヘッダ ├ 内部 3.0 ピンヘッダ ├ 内部 2.0 ピンヘッダ └ 内部 2.0 ピンヘッダ
OpenZFS 2.3.0で、個人的に待望のlongnameフィーチャーがリリースされた。
ZFSにおけるファイル名・ディレクトリ名(以後、まとめてファイル名と表記する)の最大長は、UNIX系の習わし(?)に沿って255 bytesとなっているが、longnameフィーチャーを有効にすると、その制約を打ち破って1023 bytesまで使えるようになる。単純に上限が固定的に引き上げられるといったものではなく、255 bytes超の名前があると機能がアクティブとなり、無くなれば非アクティブになるといった、動的な挙動となっているようだ。
この255バイトという伝統的な値はNAME_MAXマクロに由来し、FreeBSDの場合は/usr/src/sus/sys/syslimits.hで定義されている。厳密にいうと、ZFSでの制約は自身が定義している定数由来なのだが、NAME_MAXを意識した値であろうと思われる。なお、ここで注意が必要なのは、ファイルパスの最大長はPATH_MAXと別に定義されており、FreeBSDの場合は1023バイトとなっている。本記事で取り扱うのは、あくまでファイル名の最大長の方である。
現代的なUNIX系のファイルシステムでは、必ずしもこの制限に縛られることはないようだが、有名どころのFSは大抵255バイトが上限となっている。
255バイトもあって何が不満かと言われれば、ファイル名の文字エンコーディングにUTF-8を採用すると、最大文字数がだいぶ心許ないってこと。UTF-8の場合、日本語の文字は大半が3バイト、場合によっては4バイトで表現されるため、いわゆる全角換算で85~63文字に目減りしちゃうんですな。この問題はとりわけWindowsとファイル共有した時に顕在化しやすい。というのも、Windowsの主要FSの最大長はUTF-16で255文字1)なので、つい長い名前つけちゃって、FreeBSD+Sambaな共有フォルダにコピーしようとして失敗、なんてことが年に数回起きる。これが1023バイトならUTF-8でも341~255文字となり、大変都合がいいわけですよ。
そんなわけで、longnameフィーチャーには大変期待していたのだが、どうもFreeBSD 14.3-RELEASE時点では事実上使えないようだ。
ZFS自体の255バイト制限はなくなったものの、FreeBSDのVFSレイヤーに255バイト制限が残っていて、この影響を受ける模様。実際、PortsからOpenZFS 2.3.0入れて試してみたが255バイト制限は突破できなかった…。SambaはNAME_MAX等の直接的な制限は受けないようだし、Linux+Sambaでは問題なく機能しているようだし、やはりFreeBSD側の制限の可能性が大。
VFSの根っこの部分っぽいし、改善されるのかなー?
正直、longnameは今すぐにも使いたい。というか、256バイト以上のファイル名を含むデータセットをFreeBSDに持ってきた時に変なことにならないんだろうか…?ついにLinuxに鞍替えするときが来たのか!?
こちらのページによれば、Western Digital Ultrastar DC SN640 U.2 NVMe SSDシリーズの初期ファームには、まれにSSDがタイムアウトし、機能不全を引き起こす可能性のあるバグがあるらしい。回復手段はSSDのフォーマットで、言わずもがなデータは失われることになる。あな恐ろし。
配布されている修正版ファームウェアはR1110021, R1410004で、手持ちのSN640はR1110012なので発生する可能性がありそう。というわけで更新してみる。
やり方は上記サイトに書いてある通り。Linux環境ならnvmeコマンドでSSDのFWをダウンロードし、適用し、マシンを再起動する。
nvme fw-download /dev/nvme0 --fw=FW.vpkg nvme fw-commit /dev/nvme0 -a 1
こんな感じでR1110021に更新されていることが分かる。
# nvme list | grep WUS4 /dev/nvme2n1 /dev/ng2n1 A06F8XYZ WUS4BB076D7P3E3 1 7.68 TB / 7.68 TB 4 KiB + 0 B R1110021 /dev/nvme1n1 /dev/ng1n1 A066EXYZ WUS4BB076D7P3E3 1 7.68 TB / 7.68 TB 4 KiB + 0 B R1110021 /dev/nvme0n1 /dev/ng0n1 A0647XYZ WUS4BB076D7P3E3 1 7.68 TB / 7.68 TB 4 KiB + 0 B R1110021
なお、このSN640たちは例によって中古で、いずれも2PB以上読み書きされている。データシート上の寿命は11210TBW(4kランダムライト時)であるから、S.M.A.R.T.が示すとおりまだまだ余裕がありそう。
CrystalDiskMarkとCrystalDiskInfoの結果は以下の通り。PCIeパススルーでVMで測定したものなので、値は参考程度に。
シーケンシャルは概ねデータシートどおりだが、ランダムのIOPSが1/2~1/3と振るわないのはパススルーの影響なのかしら?
数年前のある日、突然Nextcloudのクライアントが「ファイルの更新日時が不正」というエラーを吐いてファイルの同期ができなくなった。確認してみると、大量のファイルの更新日時(mtime)が1970年1月1日になってるやんけえええええええ!!!!
どこをどうみてもUNIXエポックです、本当にありがとうございました。デスクトップクライアントv3.4.0でのやらかしらしい?
同期を再開させるだけなら、touchでmtimeを現在日時に更新してやればよい。でもワタクシはファイル探すときに結構mtime使うんですよね、なので極力元に戻したい。幸い、作成日時(ctime)とアクセス日時(atime)のいずれかは無事なようなので、それらからmtimeの復元を試みる。公式でmtime correction tool kitという、名前のとおりの復旧ツールが用意されているが、こちらはLinux環境向けでFreeBSDでそのまま使うことはできない2)ので、これらを参考に手動で直す。
まずは、mtimeが1970/1/1になったファイルを抽出する。Nextcloudのデータフォルダは/mnt/nextcloud/dataとする。
findで「mtimeがUNIX時間の0である」と指定する方法がわからなかったので、「mtimeが40年前(2024年9月時点で1984年9月)より新しい」とした。単位は分であることに注意。で、該当するファイルの作成日時(birthtime)、作成日時(ctime)、更新日時(mtime), 最終アクセス日時(atime), ファイル名をファイルに書き出す。
# cd /mnt/nextcloud/data # find . -mmin +21024000 -print0 | xargs -0 stat -f "%SB,%Sc,%Sm,%Sa,\"%N\"" -t "%s" > /tmp/epoch_time_file_list.csv
CSVをExcelで開き、諸々整形する。UNIX時間→Excel日時の変換数式は=(UNIX時間のセル+32400)/86400+25569で、セル書式をyyyy/mm/dd hh:mm:ssとすればよい。
とりあえず、各種タイムスタンプの中で最も新しい値をmtimeとして採用することにした。どうもNextcloudに突っ込んだ時点でmtime以外の情報は消失しているような気がする3)ので、こんなに頑張っても仕方ないと思いつつ、現在日時にするのはなんか嫌なので。
Excelからタイムスタンプ,ファイルパスのCSVを書き出し、以下のようなスクリプトに食わせればmtime, atimeがそれなりに復旧できる。
#!/usr/local/bin/bash
while IFS=, read datetime file
do
touch -t $datetime "$file"
done < $1
それにしてもだなー、Nextcloudがmtimeしか保持してないっぽいのは結構衝撃。デバイス間の同期が楽で、意識することなく履歴付きバックアップにもなるなーと思って建てたけど、自分の用途には合わないかなぁ…便利なんだけどさー。zipなんかも更新日時しか保持しないし、アクセス日時はともかく、みんな作成日時とか気にしないのかしら…?
他のクラウドストレージはどうなんだろう、と気になってたらDropboxとGoogle Driveを調べてる方がいた。子曰く、どちらも基本的に同期クライアントを使った場合は更新日時は保持されるとのこと。デスヨネー。
結局、ファイル属性含めて保持しておきたいなら、ローカルストレージかSMBでせっせとバックアップするしかないのかねぇ。理想の形はMacのTimeMachine。設定したら知らぬ間にバックアップがとられてて、イザって時に大助かりというやつ。WindowsもVSS(復元ポイント)で似たようなことはできるが、如何せんUIがダメダメすぎる。TimeMachine並にイケててリッチにしろとは言わないが、ファイル/フォルダのプロパティに押し込まれててアクセス性が最悪すぎる。
かといってOneDriveは仕様が素晴らしすぎて全く使う気になれないし。
Windowsのバックアップソリューション、もっとなんとかならんものか。