2009/07/15

なんとなく昔とおった道のような気がする…。

今回使っているのはUbuntu 8.04 LTSのパッケージとして導入している”pdumpfs 1.3”です。 配布されているものと同じです。md5sumの出力は、 55795ac31e83261e2fa068009d3b5085 /usr/bin/pdumpfsです。

pdumpfsを使っていて"--exclude"オプションを使ってみると、突然全体の半分程度の領域しかバックアップが取られていない事に気がつきました。なんとなくgnomeが使っている~/.gvfs/が悪さをしているような気がします。

けれど"--exclude"や"--exclude-by-size"オプションが指定されずにNullMatcherが生成されるような状況下では問題なく終るんですよね…。エラーメッセージはこんな感じです。

pdumpfs: Permission denied - /home/user1/.gvfs

検証用コード

pdumpfsのコードを少し追い掛けてみて、途中のコードを切り出して走らせてわかったのは、"~/.gvfs"に対してFile.lstat()を実行するとエラーになるということでした。

#!/usr/bin/ruby
require 'find'
Find.find("/home/user1/.gvfs") do |s|
  p File.lstat(s)
end

一般ユーザーとroot権限で走らせた場合の違い

自分の権限で走らせる分には"lstat()"が返してくるFile::Statオブジェクトを表示します。

#<File::Stat dev=0x17, ino=1, mode=040500, ...

続いてroot権限でスクリプトを走らせると、失敗してしまいます。

sudo ./test_script.rb

sudoでroot権限で実行します。出力は次のようになります。

ruby_find/run_find.rb:4:in `lstat': Permission denied - /home/user1/.gvfs (Errno::EACCES)

adhockな解決策

fuseを使った環境ではいろいろな事が起こるようです。 root権限でこの手の権限エラーがでるのは、Linux Intrusion Detection System (LIDS) 以来かなぁ。

とりあえず自分の手元で解決できれば良いとわりきって考えてみました。 "--exclude"オプションで指定されたパターンはFile.lstat()の手前で判定するようにしました。

ここを越えても呼び出し元のFind.find()の中でディレクトリの場合は全部を飛ばすためFind.prune()を呼ぶのですが、その判定でFile.lstat(s).directory?が使われていました。

この部分をFileTest.directory?()で置き換えて、ちゃんと動くようになりました。

変更箇所の差分

--- pdumpfs.orig 2009-07-15 20:41:43.000000000 +0900
+++ pdumpfs 2009-07-15 20:43:19.000000000 +0900
@@ -807,12 +807,14 @@
     end
 
     def exclude? (path)
+      if @patterns.find {|pattern| pattern.match(path) }
+        return true
+      end
+      
       stat = File.lstat(path)
 
       if @size >= 0 and stat.file? and stat.size >= @size
         return true
-      elsif @patterns.find {|pattern| pattern.match(path) }
-        return true
       elsif stat.file? and
           @globs.find {|glob| File.fnmatch(glob, File.basename(path)) }
         return true
@@ -1052,7 +1054,7 @@
 
       Find.find(src) do |s|      # path of the source file
         if @matcher.exclude?(s)
-          if File.lstat(s).directory? then Find.prune() else next end
+          if FileTest.directory?(s) then Find.prune() else next end
         end
         r = make_relative_path(s, src)
         l = File.join(latest, r)  # path of the latest  snapshot

手を加えたコードは/usr/local/bin/pdumpfsに入れています。 ”~/.google/"に加えて"--exclude=/home/user1/.gvfs"を入れて、問題のコードを飛ばすようにしています。

さいごに

ちなみにruby 1.9で確認すると、Find.find()自体が原因で見事に失敗します。

paths.collect!{|d| raise Errno::ENOENT unless File.exist?(d); d.dup}

1.8系列ではシンプルにpaths.collect!{|d| d.dup}だけだったんですよね。 この変更の経緯については[ruby-dev:28345]にあるとおりのようです。

調べてみたものの、どうするべきなんでしょう…。 もちろん単純に1.9対応させるだけなら手元のpdumpfsにrescueを追加すれば良いんですが、find.rbを使う場合にこれをどうケアするべきなのか、根は深そうな感じですね…。

fuseに起因するのでrubyとかpdumpfsの問題ではないんですが、Gnome系Ubuntuを使いながらpdumpfsを"--exclude*"オプションと一緒に走らせて"~/"のバックアップを取ろうとするには注意が必要そうです。

0 件のコメント: