Где монтируется файл? - PullRequest
       19

Где монтируется файл?

22 голосов
/ 25 февраля 2010

Учитывая путь к файлу или каталогу, как я могу определить точку монтирования для этого файла? Например, если /tmp смонтирован как файловая система tmpfs, то с учетом имени файла /tmp/foo/bar я хочу знать, что он хранится в tmpfs с корнем в /tmp.

Это будет на C ++, и я бы хотел избежать вызова внешних команд через system(). Код должен быть надежным - не обязательно против преднамеренного взлома, но определенно перед лицом вложенных точек монтирования, символических ссылок и т. Д.

Мне не удалось найти простой системный вызов для этого. Похоже, мне придется самому выписывать чек. Вот примерный план того, что я планирую.

  1. Канонизация имени файла с помощью команды оболочки readlink. Как?
  2. Чтение /etc/mtab с getmntent() & co.
  3. Определите соответствующую запись монтирования для файла. Как?

Для # 1 существует простой системный вызов или мне нужно прочитать каждый компонент каталога пути и разрешить их с помощью readlink(2), если они являются символическими ссылками? И справиться с . и .. сам? Похоже на боль.

Для # 3 у меня есть разные идеи о том, как это сделать. Не уверен, что лучше.

  1. open() файла, его родителя, родителя его родителя и т. Д., Используя openat(fd, ".."), пока я не достигну одной из /etc/mtab записей. ( Как узнать, когда я это сделаю? fstat() их и сравнить номера инодов? )
  2. Найдите самое длинное имя каталога в таблице монтирования, которая является подстрокой моего имени файла.

Я склоняюсь к первому варианту, но прежде чем я все это кодирую, я хочу убедиться, что я ничего не пропускаю - в идеале встроенная функция, которая делает это уже!

Ответы [ 4 ]

18 голосов
/ 26 февраля 2010

Это то, что я придумал. Оказывается, обычно нет необходимости перебирать родительские каталоги. Все, что вам нужно сделать, это получить номер устройства файла и затем найти соответствующую запись монтирования с тем же номером устройства.

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() и сравнение номеров устройств вместо номеров инодов.

4 голосов
/ 25 февраля 2010

Вы можете начать с realpath и продолжить работу, проверяя каждый каталог с помощью stat, чтобы увидеть, находится ли он на том же устройстве. Кажется, должен быть более простой способ.


Edit:
#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");
}

Благодарю за отвлечение; -)

2 голосов
/ 30 мая 2014

Это сработало для меня на OSX, который не предоставляет функции mntent. В 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
}
0 голосов
/ 25 февраля 2010

Вы должны быть в состоянии прочитать в /etc/mtab, разобрать все места, где что-то уже смонтировано, и посмотреть, находятся ли какие-либо из ваших файлов в любом из этих мест (или их подкаталогах). Для этого вам не нужны никакие специальные системные функции, если у вас есть точки монтирования и пути к файлам в виде строк, вы можете обрабатывать их, используя обычные функции обработки строк.

Очевидно, что символические ссылки могут бросить рывок во весь этот процесс. Любой путь к файлу, который содержит символическую ссылку, должен быть преобразован в его «фактический» путь перед его обработкой. Надеемся, что есть способ сделать это без индивидуальной проверки файла и каждого из его родителей, но вы всегда можете переборить его, если потребуется. Возможно, вы захотите использовать realpath для удаления символических ссылок.

...