• 作成:

GNU/Linuxにおけるゴミ箱は.Trash/$uid/と.Trash-$uid/のどちらが正しいのか調べてtrash-cliにissueを開いたらSpecを読み間違えていることを教えてもらいました

前半の文章は間違っているので, 正しい情報を得たい人は最後までスクロールするか, Re:GNU/Linuxにおけるゴミ箱ディレクトリは.Trash/$uid/と.Trash-$uid/のどちらが正しいのか - Qiita を参照してください.

通常, GNU/Linux環境, というかFreeDesktop環境での「ゴミ箱」に相当するディレクトリは~/.local/share/Trash/です.

しかし, ルートのファイルシステムに属しないディレクトリ, 例えばUSBストレージなどの上でファイルをゴミ箱に移動する時があります. ゴミ箱の仕様を考えると, 同じファイルシステム上で移動をしないといけないので, その場合, ディレクトリのトップレベルに.Trash-1000/のようなディレクトリが作られて, その中にファイルは移動されます.

問題は, そのゴミ箱のディレクトリに2つの形式のフォーマットが採用されていることです. .Trash/$uid/.Trash-$uid/ですね. $uidというのはユーザのIDのことです. シングルユーザの場合は通常1000でしょう. rootの場合0になります.

Trashを使用していると, この2つのディレクトリが両方作成されて, どちらが正しいのか全くわからなくなったので調べてみることにしました.

まず実験してみることにしました. 既にルートディレクトリには.Trash/1000/.Trash-1000/が存在している環境です. nautilusとandreafrancia/trash-cli: Command line interface to the freedesktop.org trashcan.でファイルをゴミ箱に送ってみました. そうすると, 双方.Trash-1000/にファイルを送信しました.

次はtrash-cliのソースコードを見てみることにしました. trash関数に以下のコメントがありました.

Trash a file in the appropriate trash directory.

If the file belong to the same volume of the trash home directory it will be trashed in the home trash directory.

Otherwise it will be trashed in one of the relevant volume trash directories.

Each volume can have two trash directories, they are

  • $volume/.Trash/$uid
  • $volume/.Trash-$uid

Firstly the software attempt to trash the file in the first directory then try to trash in the second trash directory.

trash-cli/put.py at master · andreafrancia/trash-cli

.Trash/$uidに入れるのを試みて, ダメだったら.Trash-$uidに入れると書いているように見えますね. 実際は.Trash/1000があるのにもかかわらず, .Trash-1000にファイルを移動させたため, コメントと実際の挙動が異なっているように思えます. Pythonにあまり詳しくないのと, Pythonのソースコードはスイスイ読めなくて苦痛なのですが, とりあえずソースコードを読んでみることにしました.

ここでctagsがUniversal Ctagsという名前になってexuberant-ctagsをベースに開発されて, gtags(GNU GLOBAL)を追い越した便利さになっているようなのでEmacsを設定したり大変脱線しました. 私はメインのHaskell開発ではあまりタグジャンプは使わずにhelm-agで全文検索してるのですが, Pythonのような言語だと複数行になってくるので関数定義だけを検索したいときはよくありますね.

人の書いたPythonプログラムは読むのが難しいことがわかってきました. pudbを使って多少デバッグしてみたところ, forループで.Trash/1000/でも実行はしているようですが, _is_trash_dir_securefalseになるので.Trash-1000/で実行されるようになるようですね. これ以上詳しいことを調べる気力は失せました.

これを見て, もしかしたら既存の.Trash/1000/が不正な状態にあるから使えないのではないだろうかと思い, 新規にUSBスティックを空にしてからtrash-putしてみたのですが, やはり.Trash-1000/に移動されることがわかりました. sudo gio trashを直接使っても同じでした. ちなみに昔はgvfs-trashというコマンドだったですがいつの間にかgio trashになっていたようですね.

やはりバグですかね? issueで報告するべきでしょうか?

とりあえずTrashの仕様を読んでみようと思いました. Trash specificationにあります. Trash directoriesの項は, 私には, .Trash/$uid.Trash-$uidより優先されるべきと書いてあるように読めます. 先に.Trash/$uidを試してみてから.Trash-$uidを試すべき, と書いているように読めます.

いやまあ「仕様なんかどうでも良い, デファクトスタンダードであるnautilus(gio trash)の実装に寄せる」というなら納得できます. しかし, 書かれているコメントと, 実際の動作が乖離しているので, これはやはり, issueでバグ報告をしてみるべきでしょうね. ここで書いていても届きませんし.

というわけでissueを開きました. trash-cli prefers $topdir/.Trash-$uid to the trash directory over $topdir/.Trash/$uid · Issue #108 · andreafrancia/trash-cli

またガバガバ英語を披露してしまいました. どう修正すれば良いのかさっぱりわかりません.

今日は風邪を引いていたので, 熱で朦朧として頭が働いていないのを感じます.

追記 yumetodo さんに私のSpecの読解は間違いだと教えてもらいました

issueを開いたらyumetodoさんがissueにコメントをして, trash-cliの動作は正しいと書いてくれました.

日本語訳もQiitaにあります. Re:GNU/Linuxにおけるゴミ箱ディレクトリは.Trash/$uid/と.Trash-$uid/のどちらが正しいのか - Qiita

この指摘のおかげで, 私は複数の間違いをしていることがわかりました. ありがとうございます.

Specの読み間違い

1つはSpecの読み間違いです.

私は, .Trash/が存在しなかったら, 実装は自動的に.Trash/ディレクトリを作成しなければならないと思っていました.

しかし, 本当は, .Trash/が存在して, 要件を満たす時のみ, .Trash/$uid/のディレクトリが存在しなかった場合に自動生成するという文章でした.

単純な誤読でした.

Sticky bitの設定忘れ

私は, ルートディレクトリに.Trash/$uid.Trash-$uidの両方があるのに後者が使われているのはおかしくね? 仕様だと前者が優先されるはずでは?

と思っていたのですが, Specに書いている通り, 実装はディレクトリにSticky bit(誰しも書き込み可能だがrootしかディレクトリを削除できなくするためのフラグ)が設定されていることをチェックする必要があります.

trash-cliではTopTrashDirWriteRulesでそれがチェックされています. 風邪による熱とPythonのデバッグにめげずにもうちょっとステップ実行で深く潜れば良かったですね. そしたらわかったかもしれません.

というかコードを全部読むのが一番良い. ctagsで斜め読みをしようとしたのも一因ですね.

というわけで, 私は.TrashにSticky bitを設定していませんでした. それで.Trashが使われなかったんですね.

設定したら, trash-cliもgioも.Trashを使ってくれました.

なぜこの条件を読み飛ばしていたのか, よくわかりません.