23

ファイルまたはディレクトリへのパスが与えられた場合、そのファイルのマウント ポイントを特定するにはどうすればよいですか? たとえば、がファイルシステム/tmpとしてマウントされている場合tmpfs、ファイル名が指定されている場合、それがルート/tmp/foo/barの に保存されていることを知りたいです。tmpfs/tmp

これは C++ で行われ、system(). コードは堅牢である必要があります。必ずしも意図的な改ざんに対してではなく、ネストされたマウントポイントやシンボリック リンクなどに直面しても確実に対処する必要があります。

これを行うための単純なシステム コールを見つけることができませんでした。自分で小切手を書かなければならないようです。これが私が計画していることの大まかな概要です。

  1. readlinkシェル コマンドでファイル名を正規化します。どのように?
  2. & co/etc/mtabで読むgetmntent()
  3. ファイルに対応するマウント エントリを特定します。どのように?

#1には単純なシステムコールがありますか、それともパスの各ディレクトリコンポーネントを読み取り、readlink(2)シンボリックリンクの場合は解決する必要がありますか? そしてハンドル...自分?痛そうです。

#3については、これを行う方法についてさまざまなアイデアがあります。どれが一番いいかわからない。

  1. open()ファイル、その親、その親の親など、いずれかのエントリopenat(fd, "..")に到達するまで使用します。/etc/mtab(どのようにすればわかりますか?fstat()それらを調べて inode 番号を比較しますか? )
  2. 私のファイル名の部分文字列であるマウントテーブルで最長のディレクトリ名を見つけます。

私は最初のオプションに傾いていますが、すべてをコーディングする前に、何も見落としていないことを確認したいと思います-理想的には、これを既に実行している組み込み関数です!

4

4 に答える 4

19

これが私が思いついたものです。通常、親ディレクトリを反復処理する必要はありません。ファイルのデバイス番号を取得し、同じデバイス番号を持つ対応するマウント エントリを見つけるだけです。

struct mntent *mountpoint(char *filename, struct mntent *mnt, char *buf, size_t buflen)
{
    struct stat s;
    FILE *      fp;
    dev_t       dev;

    if (stat(filename, &s) != 0) {
        return NULL;
    }

    dev = s.st_dev;

    if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
        return NULL;
    }

    while (getmntent_r(fp, mnt, buf, buflen)) {
        if (stat(mnt->mnt_dir, &s) != 0) {
            continue;
        }

        if (s.st_dev == dev) {
            endmntent(fp);
            return mnt;
        }
    }

    endmntent(fp);

    // Should never reach here.
    errno = EINVAL;
    return NULL;
}

@RichardPennington に感謝しrealpath()、inode 番号の代わりにデバイス番号を比較します。

于 2010-02-25T21:26:41.800 に答える
4

realpath から始めて、各ディレクトリを stat でチェックして、同じデバイス上にあるかどうかを確認することができます。もっと簡単な方法があるはずです。


編集:

#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    char *p;
    char path[PATH_MAX];
    struct stat buf;
    dev_t dev;

    if (realpath(argv[1], path) == NULL) {
        fprintf(stderr, "can't find %s\n", argv[1]);
        exit(1);
    }

    if (stat(path, &buf) != 0) {
        fprintf(stderr, "can't statind %s\n", path);
    exit(1);
    }

    dev = buf.st_dev;
    while((p = strrchr(path, '/'))) {
        *p = '\0';
        stat(path, &buf);
        if (buf.st_dev != dev) {
            printf("mount point = %s\n", path);
            exit(0);
        }
   }
    printf("mount point = /\n");
}

気を散らしてくれてありがとう;-)

于 2010-02-25T20:13:55.947 に答える
2

これは、mntent機能を提供しないOSXで機能しました。C++ の場合:

struct stat fileStat;
int result = stat(path, &fileStat);
if (result != 0) {
    // handle error
}    

struct statfs* mounts;
int numMounts = getmntinfo(&mounts, MNT_WAIT);
if (numMounts == 0) {
    // handle error
}    

for (int i = 0; i < numMounts; i++) {
    if (fileStat.st_dev == mounts[i].f_fsid.val[0])
        // mounts[i].f_mntonname is the mount path
}
于 2014-05-29T23:37:20.160 に答える
0

/etc/mtabを読み込んで、何かが既にマウントされているすべての場所を解析し、問題のファイルがそれらの場所 (またはそのサブディレクトリ) のいずれかにあるかどうかを確認できるはずです。これには特別なシステム関数は必要ありません。マウント ポイントとファイル パスが文字列としてある場合は、通常の文字列処理関数を使用して処理できます。

明らかに、シンボリックリンクはこのプロセス全体にレンチを投げ込むことができます. シンボリック リンクを含むファイル パスは、処理する前に「実際の」パスに変換する必要があります。 ファイルとその親を個別にチェックせずにこれを行う方法があることを願っていますが、必要に応じていつでもブルートフォースすることができます. realpathおそらく、シンボリックリンクを削除するため に使用したいと思うでしょう。

于 2010-02-25T20:20:14.940 に答える