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

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

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

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

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

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

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

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

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

22
задан John Kugelman supports Monica 25 February 2010 в 22:20
поделиться

3 ответа

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

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.

18
ответ дан 29 November 2019 в 05:33
поделиться

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

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

0
ответ дан 29 November 2019 в 05:33
поделиться

Вы можете начать с 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");
}

Спасибо, что отвлекли ;-)

4
ответ дан 29 November 2019 в 05:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: