Где монтируется файл?
Учитывая путь к файлу или каталогу, как определить точку монтирования для этого файла? Например, если /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()
и сравнить номера индексов?)
- Найдите самое длинное имя каталога в таблице монтирования, которая является подстрокой имени моего файла.
Я склоняюсь к первому варианту, но перед тем, как закодировать его, я хочу убедиться, что я ничего не замечаю - в идеале это встроенная функция, которая делает это уже!
Ответы
Ответ 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
для удаления символических ссылок.