2008年4月16日水曜日

(FreeBSD) find: -delete: /tmp/ttt: relative path potentially not safe

findコマンドってのも、FreeBSD、Solaris、Linuxの何か等、OSごとに「方言」がきついコマンドの1つですよね。OSごとに、オプションがあったりなかったりすることは、よくあります。
いざとなれば、GNUのfindutilsをインストールすれば、違いを吸収できたりしますが…。



さてさて、FreeBSDのfindコマンドには、-deleteという、ファイルを削除してくれるオプションがあります。「-exec rm {} \;」と書かなくてすむので、いくらか便利かな、みたいなものですが。



しばらく前から気になってたんですが、

find: -delete: /tmp/ttt: relative path potentially not safe

というようなメッセージが表示されることがあります。



実例をあげます。



たとえば、以下のようなお膳立てをしておきます。

cd /tmp
mkdir ttt
mkdir ttt/uuu
mkdir ttt/uuu/vvv
touch ttt/uuu/vvv/www.txt
touch ttt/uuu/vvv/xxx.txt
touch ttt/uuu/vvv/yyy.txt

そのあと、あまり実用的でない、無意味な実行例ですが、「find /tmp/ttt -print -delete」を実行します。



% find /tmp/ttt -print -delete
/tmp/ttt/uuu/vvv/www.txt
/tmp/ttt/uuu/vvv/xxx.txt
/tmp/ttt/uuu/vvv/yyy.txt
/tmp/ttt/uuu/vvv
/tmp/ttt/uuu
/tmp/ttt
find: -delete: /tmp/ttt: relative path potentially not safe



「relative path potentially not safe」がでますね。



そして結局、/tmp/tttというディレクトリは削除されずに残ります。



% ls -lRa /tmp/ttt
total 6
drwxr-xr-x   2 ttt       wheel   512  4 15 22:37 ./
drwxrwxrwt  10 root      wheel  3072  4 15 22:37 ../



ちなみに、以上は、FreeBSD 7.0-RELEASEで実行した結果です。



findコマンドのマニュアルを読んでみますか。



http://www.freebsd.org/cgi/man.cgi?query=find&apropos=0&sektion=0&manpath=FreeBSD+7.0-RELEASE&format=html

-delete
Delete found files and/or directories.  Always returns true.
This executes from the current working directory as find recurses
down the tree.  It will not attempt to delete a filename with a
``/'' character in its pathname relative to ``.'' for security
reasons.  Depth-first traversal processing is implied by this
option.

なるほど。でも、ここの意味

a filename with a ``/'' character in its pathname relative to ``.''

なんか微妙というか、あいまいな気がしません?
/tmp/ttt だったら、relativeじゃなくて、absolute pathじゃないか…って。



わからないことがあったら、ソースコードを確認。



http://www.jp.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/find/function.c?rev=1.60&content-type=text/x-cvsweb-markup



の、この部分ですね。



/*
* -delete functions --
*
*    True always.  Makes its best shot and continues on regardless.
*/
int
f_delete(PLAN *plan __unused, FTSENT *entry)
{
    /* ignore these from fts */
    if (strcmp(entry->fts_accpath, ".") == 0 ||
        strcmp(entry->fts_accpath, "..") == 0)
        return 1;

    /* sanity check */
    if (isdepth == 0 ||            /* depth off */
        (ftsoptions & FTS_NOSTAT) ||    /* not stat()ing */
        !(ftsoptions & FTS_PHYSICAL) ||    /* physical off */
        (ftsoptions & FTS_LOGICAL))        /* or finally, logical on */
        errx(1, "-delete: insecure options got turned on");

    /* Potentially unsafe - do not accept relative paths whatsoever */
    if (strchr(entry->fts_accpath, '/') != NULL)
        errx(1, "-delete: %s: relative path potentially not safe",
            entry->fts_accpath);



strchr(entry->fts_accpath, '/') で判断しているので、“/”が含まれているときはエラー扱いになるようで。
manに書いてある“.”なんてのは関係なくて、directory tree内をtraverseしていって 、降りていったディレクトリをカレントディレクトリとみなして、そこからの相対パスで、というか、ディレクトリ名をまったくつけない(/を含まない)、ファイル名だけが、entry->fts_accpathに入ってるっぽいです。ふつうは。



ただし、findの起点を/tmp/tttのように絶対パスで指定すると、最後の最後で、entry->fts_accpathには、起点の/tmp/tttが、そのまま代入されてしまうみたいで、

find: -delete: /tmp/ttt: relative path potentially not safe

となっていた、というわけですか。



じゃあ、どうすればいいかっていうと、起点を/tmp/tttのような絶対パス指定しなければいいというわけで、正解は「find ttt -print -delete」になるんでしょうね。



% cd /tmp
% find ttt -print -delete
ttt/uuu/vvv/www.txt
ttt/uuu/vvv/xxx.txt
ttt/uuu/vvv/yyy.txt
ttt/uuu/vvv
ttt/uuu
ttt
% ls -ld ttt
ls: ttt: No such file or directory



ただ、起点となるディレクトリは消えて欲しくない、っていう場合もあったりしまして~、実は私のやりたかったことが、まさにそれでして、たまたま結果オーライで、「find /tmp/ttt ...」でうまくいってたのでした。



0 件のコメント:

コメントを投稿