Подтвердить что ты не робот

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

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

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

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

  • Канонизировать имя файла a la the readlink shell. Как?
  • Прочитайте /etc/mtab с помощью getmntent() и co.
  • Определите соответствующую запись монтирования для файла. Как?

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

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

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

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

4b9b3361

Ответ 1

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

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.

Ответ 2

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

Спасибо за отвлечение; -)

Ответ 3

Это работало для меня на OSX, который не предоставляет функции mntent. В С++:

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
}

Ответ 4

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

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