The Man Who Fell From The Wrong Side Of The Sky:最新 5 日分

2019/6/18(Tue)

[オレオレN6] CESU-8 and Java Modified UTF-8 support for Citrus iconv

ふと こんなPRをみかけたので、関係ないけどCitrus iconvもCESU-8とかJava Modified UTF-8ってサポート無かったなと思い出したので 実装した

おそらくNなんとかBSDの古いCitrus iconv実装へのバックポートもたいした手間ではない気がするけどあっち今どうなってるか全く知らんし、何年か前に前に__STDC_ISO_10646__化するブランチをみた記憶があるので、libc/citrusの下が大きく変わっててバックポート難しい可能性もあるけど、知ったこっちゃないしサポートはいたしかねます。

コミットした時点ではMySQL utf8mb3もCESU-8と同じものと勘違いしてたのだが(Oracle買収による記憶改竄)、そもそもアレ基本面しかサポートしてないから違うわな。

2019/6/11(Tue)

[音楽] Tycho/Weather

突然 06.11.19 JAPANなんてツイーヨするから来日公演の情報を見落としてたのかと軽くパニクって当日券探しに走ったのだが勘違いであった。

これは明日発売の新アルバム「 Weather」の一曲が「JAPAN」というらしい、お願いなのでポールマッカートニー取調係の菊池案件はやめてねグルーヴ。

前作で Ghostly Internationalの契約が切れたようで Ninja Tuneというインディペンデントに移籍したようだけどちゃんと日本盤も出るようで安心…

…ではなかった、これまでは PLANCHAから出てたけど今回からは BEATNIKに代わったのね、ここ呼び屋としてクッソやる気ねーとこやしこのアルバムのプロモーションは単独来日公演無しでいつものフジロック(笑)だけで終わりっぽくて悲しい、こういう映像も重要なバンドは野外より小屋で観たいのだけどねぇ(まぁ日本の小屋の映像設備だいぶアレやけど)。 音楽が売れねえって自分で首絞めてるだけなんやな。

そいやPLANCHAは1stアルバムの Past Is Prologueの再発したけどただの再プレスなのが残念、このアルバムは元々 Sunrise Projectorというタイトルだったものをレーベル移籍でタイトル変更と曲の入換やっとるのでそこで落ちた曲とか、 セルフリリースEPだった The Science Of Patternsを収録してくれたなら買い直したんだけどね…

あと傑作EPである Coastal BrakeもCD出してくれねえかな、まぁ Bleepでmp3でなくflacで買えるんだけどさ…

内容については前作 Epochの後、アルバム3部作ひとくぎりついたって発言あったけど、音楽性は相変わらず(歌モノも新機軸ってほどでなし)なので契約の話だったんだろうな。

そうそう個人的にはGhostlyから離れたことで Warp Records系の音楽配信サイトBleepでの扱いがなくなってしまうのでウンコmp3しか選択肢無くなるのがつらいねのよね、まぁ聴くだけならシングルやEPはアマゾンプライム会員特典になってるので試聴してどうぞ。

それと 自分とこのショップで過去作品関連のものが全部消えてしまったのも悲しい、欲しいパーカーあったんだよなー。

[追記] Bleepでもアルバムは取り扱うようで24bit flacのハイレゾ配信きてた、ワイはCD買うけどね24bitから16bitへのダウンコンバートめんどくせえし。

2019/6/9(Sun)

[NなんとかBSD] /etc/rc.d/ntpdateの実装とIPv6

タイトルでネットワークプログラミングの話かと鼻息荒くなった皆さん、ここではありません。 前もって言っておくがとてつもなくしょーもない話なので、netinet*の下の話が読みたい奴はブラウザそっ閉じして他のブログ読みに行ってどうぞ。

今年のIPv6普及元年は2001年以来の豊作とか揶揄され続けてはや幾年、スマホの普及により令和元年という時代にIPv6が使えない環境なぞクソクソアンクソ認定していい頃やろ、どうもお疲れ様でした。

ところでdjb先生と落合じゃない方のノビーの前で これとか これをみんなで音読しながら囲う会の開催はしないんですかね?

まあええわIPv6は必要だったし普及にも成功した現在、NGNの例のアレとかISPのAAAAフィルタなんてものは過去の一体あれは何だったんだ案件でテレホマンと同じインターネット老人会とかいう寒いネタに添えられるツマとして生き残るだけだろう。

しかしまだv4でしか通信できないケースも地雷のように残ってる、例を挙げればワイの環境だとVMware NetworkのNAT越えとかドコモ版iPhone 5sでのテザリングがどっちも対応しましたといいながらバグってんのかまともに動かへんのやな、もうどっちも捨ててえなあ… *1。 つってもインターネットブラウザとかならどうせv4にフォールバックするからタイムアウト待ち分の遅延が無視できりゃ問題ないし、なんならv4優先する設定にすりゃいいだけだ。

そんでもntpd(8)はアカンのだ、DNSがAAAAレコード返してきた場合はv6を優先するのだがv4にはフォールバックしてくれんようで

     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
 ntp-a3.nict.go. .INIT.          16 u    -   64    0    0.000    0.000   0.000

とstratumが16(接続失敗)のまま時刻同期できなくなってしまうのだ、よって回避策が必要になる。

それはとても消極的な方法ではあるけれど、ntp.confの中で

server -4 ntp.nict.jp

と-4を指定しプロトコルバージョンを強制すりゃええだけではある。

ちなみにntpdate(1)の方は古い4.8.4あたりの実装だと

# ntpdate ntp.nict.jp

を実行した時に

 9 Jun 21:09:59 ntpdate[334]: sendto(ntp-a3.nict.go.jp): Undefined error: 0
transmit(2001:df0:232:eea0::fff4)
...
2001:df0:232:eea0::fff4: Server dropped: no data
...
 9 Jun 21:10:03 ntpdate[334]: no server suitable for synchronization found

と、こちらもv6からv4にはフォールバックせずにエラーになる(最新の安定板の4.8.12はだいじょうぶ)。

そんでこっからが本題のほんとしょーも無い話。

実はntp.confを参照するのはntpd(8)だけではない、NなんとかBSDは/etc/rc.confで

ntpdate=YES

が指定されている場合、起動時に/etc/rc.d/ntpdateというスクリプトが実行され、ntp.confに指定されてる一番最初のserver or peerに対してntpdate(1)を実行するという機能がある。 ユースケースとしては

というパターンですかね。

ところが実際にコードをみれば一目瞭然なんだけど、ntp.conf中の-4や-6オプションはこのスクリプトは読み飛ばすだけでntpdate(1)には教えてくれないのだ。

18 ntpdate_start()
19 {
20         if [ -z "$ntpdate_hosts" ]; then
21                 ntpdate_hosts=$(awk '
22                         /^#/                            { next }
23                         /^(server|peer)[ \t]*127.127/   { next }
24                         /^(server|peer)/                { if ($2 ~ /^-[46]/)
25                                                             print $3
26                                                           else
27                                                             print $2 }
28                 ' </etc/ntp.conf)
29         fi
30         if [ -n "$ntpdate_hosts"  ]; then
31                 echo "Setting date via ntp."
32                 $command $rc_flags $ntpdate_hosts
33         fi
34 }

よってrc.conf(あるいはrc.conf.d/ntpdateの中)で

ntpdate_flags="-4 -b -s"

と改めて-4オプションを指定するというクソダサなkludge入れる必要がある、まぁN5以降は4.8.12なのでv4にフォールバックするからもはやどうでもいい気がしないでもないが。

むしろこっちの方が問題かな、NTPサーバが負荷分散にDNSラウンドロビンしてるような場合

server ntp.nict.jp
server ntp.nict.jp
server ntp.nict.jp
server ntp.nict.jp

と同じDNS名を複数回書くなんて記事を書く人がようけおるのだが、これは今時のntp.confでは

pool ntp.nict.jp

と書けるんですわ。

あっ(察し)、お気づきでしょうさっきの/etc/rc.d/ntpdateのスクリプトはntp.conf中のserverかpeerかしかキーワードをひっかけてないので、ナウいpoolを使うとこいつを認識せず時刻同期が実行されないのだ、これN6どころかHEADでもそのまんまなんだな…ダサ過ぎる。

スクリプトの修正はたいした手間ではないけど、本当はこれntpdate(1)の方にntp.confを読むオプションつけて貰った方がいいよね案件な気がしないでもない。

[追記] オレオレN6は 雑に直した

*1:なおドコモの3G終了あるいはワイが死ぬかクックが改心してSIMフリーで実売2万前後のiPhone SEXを出すまではiPhone 5s使い続けるもよう、もうセキュリティとかどーでもいいや。

2019/6/5(Wed)

[Windows] MoveFileEx vs ReplaceFile

うーん ReplaceFileって本当にAtomicかすげー怪しいと思ってたんですが、なんで自分は手堅く MoveFileEx使ってますかね…

これReplaceFileはAtomicだよ派のソースを調べると このPDF

Under UNIX, rename() is guaranteed to
atomically overwrite the old version of the file. Under
Windows, the ReplaceFile() call is used to atomically
replace one file with another. 

の一文、そしてReFS導入も影響しての TxF廃止に伴う代替案にReplaceFileが挙げられてることあたりか、じゃあ大丈夫なのかねぇでもどうやって実現しとるのやら。

ちなみにJava7で導入された java.nio.file.Path::move()とATOMIC_MOVEフラグの実装、Windowsの場合は sun.nio.fs.WindowsFileCopyで、Windows APIのMoveFileExを使っている。

266     static void move(WindowsPath source, WindowsPath target, CopyOption... options)
267         throws IOException
268     {
269         // map options
270         boolean atomicMove = false;
271         boolean replaceExisting = false;
272         for (CopyOption option: options) {
273             if (option == StandardCopyOption.ATOMIC_MOVE) {
274                 atomicMove = true;
275                 continue;
276             }
...
298         // atomic case
299         if (atomicMove) {
300             try {
301                 MoveFileEx(sourcePath, targetPath, MOVEFILE_REPLACE_EXISTING);
302             } catch (WindowsException x) {
303                 if (x.lastError() == ERROR_NOT_SAME_DEVICE) {
304                     throw new AtomicMoveNotSupportedException(
305                         source.getPathForExceptionMessage(),
306                         target.getPathForExceptionMessage(),
307                         x.errorString());
308                 }
309                 x.rethrowAsIOException(source, target);
310             }
311             return;
312         }

これはGolangのos.Renameも一緒、該当コードは src/internal/syscall/windows/syscall_windows.goにある。

134 func Rename(oldpath, newpath string) error {
135         from, err := syscall.UTF16PtrFromString(oldpath)
136         if err != nil {
137                 return err
138         }
139         to, err := syscall.UTF16PtrFromString(newpath)
140         if err != nil {
141                 return err
142         }
143         return MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING)
144 }

そしてPythonの os.rename()の実装である POSIX moduleもやっぱりMoveFileEx()を使ってる。

4043 static PyObject *
4044 internal_rename(path_t *src, path_t *dst, int src_dir_fd, int dst_dir_fd, int is_replace)
4045 {
...
4049 #ifdef MS_WINDOWS
4050     BOOL result;
4051     int flags = is_replace ? MOVEFILE_REPLACE_EXISTING : 0;
...
4065 #ifdef MS_WINDOWS
4066     Py_BEGIN_ALLOW_THREADS
4067     result = MoveFileExW(src->wide, dst->wide, flags);
4068     Py_END_ALLOW_THREADS
...

あとlibuvもファイルシステム系システムコールラッパー( src/win/fs.c)ではMoveFileEx使ってる。

1314 static void fs__rename(uv_fs_t* req) {
...
1375     if (MoveFileExW(src, dst, MOVEFILE_REPLACE_EXISTING) != 0) {
1376       SET_REQ_RESULT(req, 0);
1377       return;
1378     }
...

そしてCygwinのrename(2) syscall emulationはもっとパラノイアックにKERNEL32でなくNTDLL叩いてますな。

2241 static int
2242 rename2 (const char *oldpath, const char *newpath, unsigned int at2flags)
2243 {
...
2713       if (use_posix_semantics)
2714         pfri->Flags = noreplace ? 0
2715                                 : (FILE_RENAME_REPLACE_IF_EXISTS
2716                                    | FILE_RENAME_POSIX_SEMANTICS
2717                                    | FILE_RENAME_IGNORE_READONLY_ATTRIBUTE);
...
2728       status = NtSetInformationFile (fh, &io, pfri,
2729                                      sizeof *pfri + pfri->FileNameLength,
2730                                      use_posix_semantics
2731                                      ? FileRenameInformationEx
2732                                      : FileRenameInformation);

ちゃんとWindows10で導入されたFILE_RENAME_POSIX_SEMANTICSとFileRenameInformationEx使ってるのさすがなんやな。

さて本日のオチ担当は

これMOVEFILE_COPY_ALLOWED指定してるからAtomicちゃうよね…直すべきだと思うんだけど多分直すと世界中で動かなくなるコードが大量発生するパターンだろうか。

そもそもgit annotateからコミットメッセージ読んだら「ドライブ跨いだリネーム」云々書いてあるのでそもそもアトミックなんぞ一切保障しない POSIX renameとは何の関係も無いAPIだといわれりゃそれまでではある。

というかPOSIX.1-2017出てたのね、2008の改定だからそう大きく変更無いと思うけどここ何年もまったく動向追ってねえからさっぱりわからん…

2019/6/3(Mon)

[Windows] PowerShellで生活するために - Move-Itemコマンドレット編(その1)

前回の続き、別の問題に気づいたのでちょっとだけ脱線するよ。

@ Move-Itemや[System.IO.File]::Move()を使ってのファイル移動はアトミックではない

こいつら使ってファイルを移動する時

  • 単なるリネームで済む場合(これはアトミック)
  • ファイルシステムを跨いだ場合など、ファイルコピーを伴う場合(もちろん非アトミック)

でエラーにするか否か挙動を変更できないのねこれ、Move-Itemコマンドレットには最初から期待してなかったけど.Net Frameworkもダメなんやな。

 9 internal partial class Interop
10 {
11     internal partial class Kernel32
12     {
13         const uint MOVEFILE_REPLACE_EXISTING = 0x01;
14         const uint MOVEFILE_COPY_ALLOWED = 0x02;
           ...
19         [DllImport(Libraries.Kernel32, EntryPoint = "MoveFileExW", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)]
20         private static extern bool MoveFileExPrivate(string src, string dst, uint flags);
           ...
29         internal static bool MoveFile(string src, string dst, bool overwrite)
30         {
31             src = PathInternal.EnsureExtendedPrefixIfNeeded(src);
32             dst = PathInternal.EnsureExtendedPrefixIfNeeded(dst);
33
34             uint flags = MOVEFILE_COPY_ALLOWED;
35             if (overwrite)
36             {
37                 flags |= MOVEFILE_REPLACE_EXISTING;
38             }
39
40             return MoveFileExPrivate(src, dst, flags);
41         }
42     }
43 }

ちなみにWindows APIのMoveFileEx()ならフラグにMOVEFILE_COPY_ALLOWEDを渡さない限りはエラーになってくれるんだが、[System.IO.File]::Move()のコードを追っていくと、上記のコードの通り常時おったててるしまっとる。

これまでUNIXでよく訓練されたプログラマなら、そもそも「移動」ではなく「名前変更」つまりRename-Itemコマンドレットを使うケースなのでは…と考えるとこでしょう、ワイもそう思いました。

しかしだな、Rename-Itemは-ForceオプションをつけようがMOVEFILE_REPLACE_EXISTINGは建たないようでファイルの置換ができないという別の問題があるのですわ…

New-Item -ItemType File 'unko1'|Out-Null
Copy-Item 'unko1' 'unko2'
Rename-Item 'unko2' 'unko1'

Rename-Item : 既に存在するファイルを作成することはできません。
At line:3 char:1
+ Rename-Item 'unko2' 'unko1'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : WriteError: (C:\Users\tnozaki\unko2:String) [Rename-Item], IOException
    + FullyQualifiedErrorId : RenameItemIOError,Microsoft.PowerShell.Commands.RenameItemCommand

うーんこの。

まぁNew-Itemなんかも既にファイルが存在する場合は上書きせずエラーになったりするし、見かけ倒しの安全性にふってるんだろうな、そのかわりまともなセキュアコーディングができねえとも考えずにな…

これどんな時に困るかちゅーと

  • ファイルロックによる排他を行わずにファイルの内容更新したい
  • でも更新中のファイル内容を他には絶対に見せたくない

なんて時に

  • 一時ファイル作って元ファイルの内容をコピー
  • 一時ファイル側を更新
  • 一時ファイルを元ファイルに上書きリネーム

するというテクニックが広く使われているのだけど *1、移動にコピーが必要となる場合(別ドライブを跨ぐ時など)には失敗してくれんと、コピー途中の中途半端な状態のファイルが他に見えちゃうのだよな。

まーこれ一時ファイルと元ファイルが同じファイルシステムにあることをチェックすれば事足りるし、UNIXでもシェルスクリプトからじゃrename(2)使えないからmv(1)でお茶濁すのでPowerShellと事情は一緒だから、新規にコマンドレット書くほどの問題かというのは微妙ではある。

*1:例として挙げるならpasswd(1)のコードが最も有名だろう、 過去回でTOCTTOUとシンボリックリンク攻撃を説明の時にとりあげてるのでそっち参照のこと。