svnadminで最新commitを消した後、レポジトリ操作が失敗する

Subversionでマージするときに誤ってsparse checkoutした状態でマージしてしまった。
普通のdepth infinity状態でないとファイルの欠損が起きてしまう…らしい。改善されたというような話を聞いたのだが、とにかく今回はうまくいかず、変なコミットを作ってしまった。

そんなわけで、svnadminでdump、create、load -rで最後のコミットを消したレポジトリを作成し、そこからマージをやり直そうとしたのだが、うまくいかない。

checkoutできない

まず新たにcheckoutしようとしても、空のフォルダを2、3作った状態で止まって進まない。
mod_dav_svn経由なのでWAFがブロックしているのかと思って確認してみるも空振り。
repoサーバ上でローカル限定のsvnserveを起動してcheckoutしてみるとうまくいった。
そこでそれをtarで固めて作業PCに持ってきてからrelocateした。Windows機ではsymlinkの表現形式がLinuxと異なるのでrevert -Rする。

mergeできない

改めてmergeしようとしたり、diffを取ろうとするとエラーが起きる。

  • Malformed svndiff data in representation
  • Reading one svndiff window read beyond the end of the representation

などとメッセージが出る。repoが壊れているときに出るらしいのだが、dump/loadしたばかりのrepoなので、当然verifyしてもエラーは出ない。

原因

svnserve経由だとcheckoutできたところにヒントがあったのだが、httpdをリスタートしたところ問題なくcheckout/mergeできるようになった。
mod_dav_svn (使っているのはsvn 1.11系)がrepoの巻き戻りが起こらないことを前提に何らかのキャッシュをしていたのではないかと疑っている。
新たなrepoを作るときに名前を変えていたら、URL違いでこの問題は発現しなかったのかもしれない(あるいは同じrepo UUIDなので同様なのか)。

名前解決できずSSHサーバに蹴られる

突然サーバにSSH接続できなくなった、と言っても原因はいろいろある。今日あるVPSサーバでscreen内のsshが切れているのを見つけ、再接続しても繋がらない問題が出た(ウェブにはつながる)。
海外の安サーバで最近AlphaRacks*1に買収されたところの一つだ。この新会社は買収に際して突然に収容ホストやIPアドレスを変更しても半日アナウンスせず、こちらが自己解決してから文句付けがてらに問い合わせた後でしれっと告知メールを出すようないい加減なところ*2。なのでまた何かやらかしたんだろうと思い、とりあえずシリアルコンソール経由で接続する。


SolusVMを使っているので本来はブラウザ経由でコンソールにつなげられるはずなのだが、受け側が整っていないのかWebSocketのエラーで接続できない。
なのでVNC接続ページに出る接続先にVNC Viewerを使って接続した。適当に見繕ったVNC Viewerにはポート番号を入力する欄がないので、コロン付きでIPアドレスとポートを区切って入れる。


コンソール接続してログインし、sshdのエラーを確認してみる。

refused connect from x.x.x.x (x.x.x.x)

普通にはじかれているが、カッコ内もIPアドレスのみで逆引きされていない。
もしやと見てみると、予想通り /etc/resolv.conf が空(自動生成コメントのみ)になっていて、/etc/sysconfig/network-scripts/ifcfg-eth0 にもDNS設定が欠落していた。
そうしてサーバが名前解決できなくなり、そして /etc/hosts.{allow,deny} で名前解決に頼ったフィルタをしていたため、接続拒否されていたようだ。


ネットワーク設定を戻す。おまけに何度もsshを試したせいでFail2Banにマークされてしまっていたのでunbanしたら復活した。


/etc/sysconfig/networkも書き換わっていたようで、先頭に"# Generated by SolusVM"とある。
修正日が収容ホスト変更があった日になっているので、ホスト側が指定するネットワーク設定が間違っているのだろう。
そういえばこのサーバは最初のセットアップ時からsshが繋がらない欠陥ノードだった。ネットワークが再設定されてその状態に戻っていたのだ。

*1:AlphaRacksはその後まもなく夜逃げした。「買収」からの一連の動きがexit scamの一環だろうという見方がある。

*2:元の値段が値段なのでサポートには金の処理とサーバの提供以外は何も期待していないが、IPアドレスが変わるのに事前告知しないところは初めてだ。

DOMDocument->loadHTML()でパースすると文字化けする

大体HTMLに原因があるのでそこを見ればよい。

  • 原因1: CP932なのにmeta charset=Shift_JISになっている
  • 原因2: meta charsetは正しいが、それより前にマルチバイト文字がある
  • 原因3: そもそもmeta charsetがない

ブラウザが「いい感じ」に処理してくれるのもダメHTMLがいまだに駆逐されない原因の一つじゃないだろうか。

正しく処理するには、HTMLの頭に正しいmeta charset指定を補うのが一番速いだろうと思う。
ただ(3)のmeta charsetがない場合、mb_detect_encoding()で見当をつけるにも言語を絞り込まないと限界があるので、結構難しい。

ちなみにコンストラクタで文字コードを指定できるが、HTML処理では全く使われない。

$doc = new DOMDocument(null, 'UTF-8');

$doc->loadHTMLFile() の場合PHPはlibxmlの htmlCreateFileParserCtxt()エンコーディングを渡しておらず、$doc->loadHTML() の場合 htmlCreateMemoryParserCtxt() にはエンコーディングを指定できないようになっている。

$doc->saveHTML() で出力する

パースしたHTMLを変形して出力することもあると思うが、libxml2 2.9.8 時点では <meta charset="..."> で指定していると保存するべきエンコーディングを発見できず、出力がHTMLエンティティだらけになる。
そのため<meta http-equiv="Content-Type" content="text/html; charset=..."> で指定する必要がある。
そもそもHTML4のパースと言っているので、HTML5の記法には対応していないということだろうか。読み込みでは認識しているようにも見えるが深く追っていない。

RawGitからjsDelivrに移行した

今月末以後にサービス終了とのことなのでjsDelivrに移行した。
アナウンスから終了まで割と短いが、jsDelivrに専用のガイドページが用意されている。jsDelivrはRawGitのようにGitHubの参照もできるが、npmパッケージがある場合はそちらの方が利用率は高くなる傾向があるとか。

続きを読む

Subversion 1.10で認可され(authorize)ない

1.9から1.10にサーバをバージョンアップしたら問題が発生した。
svn up なり svn list なりを行うと、

svn: E175013: Access to '/path/to/hoge/trunk' forbidden

と出てアクセスできない。HTTP(S)経由なので、LogLevel debug としてログを追ってみると最後に

Access denied: 'admin' OPTIONS hoge:/trunk

となっていて、認証(authentication)まではうまくいっている。

認可用ファイルは以下の通り

[/]
admin = rw

[hoge:/]
user = rw

こうしておくと、今までは admin ユーザは全リポジトリにアクセスできていた。
どうも1.10から一番詳しく一致するレコードのみ見るようになった風に思える。確かにその方がより細かくアクセス制御はできるのだろうが、リリースノートには認可周りの修正と強化をしたとあるだけで、そのような非互換の変更があったとは書いていない(か、気付かなかった)。

対策1 新機能の :glob:を使う

[/][:glob:/*] としたらアクセスできるようになった。
より深いパスの指定が他にある場合は、それより優先させるために [:glob:/**] とする必要があるはず。
一番単純で admin としての意図に近いのでこの方法にした。大量にファイルを舐める時にパフォーマンス的には悪いかもしれない。

対策2 各レコードにいちいち権限を付ける

[hoge:/] 以下にも admin = rw を加えても、アクセスできるようになった。
これはリポジトリが多い場合、記述が冗長になりすぎる。

対策2.1 グループを使う

ユーザをすべてグループ定義に変えて、各グループに admin を加えれば、それなりにコンパクトになる

[groups]
user = user, admin

[hoge:/]
@user = rw

ただ、グループ名にユーザ名を使うにしろリポジトリ名を使うにしろ、本来のグループの使い方とずれているので、後で混乱しそうだ。