Учитывая путь к файлу или каталогу, как я могу определить точку монтирования для того файла? Например, если /tmp
смонтирован как a tmpfs
файловая система тогда, учитывая имя файла /tmp/foo/bar
Я хочу знать, что это хранится на a tmpfs
базированный в /tmp
.
Это будет в C++, и я хотел бы постараться не вызывать внешние команды через system()
. Код должен быть устойчивым - не обязательно против преднамеренного вмешательства, но определенно перед лицом вложенных точек монтирования, символьных ссылок, и т.д.
Я не был в состоянии найти, что простой системный вызов делает это. Похоже, что я должен буду выписать чек сам. Вот грубая схема того, что я планирую.
readlink
команда оболочки. Как?/etc/mtab
с getmntent()
И co.Поскольку № 1 является там простым системным вызовом, или сделайте я должен считать каждый компонент каталога пути и разрешить их с readlink(2)
если они - символьные ссылки? И дескриптор .
и ..
самостоятельно? Походит на боль.
Для № 3 у меня есть различные идеи о том, как сделать это. Не уверенный, который является лучшим.
open()
файл, его родитель, родитель его родителя, и т.д. с помощью openat(fd, "..")
пока я не достигаю одного из /etc/mtab
записи. (Как я знаю, когда я делаю? fstat()
их и сравнивают inode числа?)Я склоняюсь к первой опции, но прежде чем я кодирую все это, я хочу удостовериться, что я ничего не пропускаю - идеально встроенная функция, которая уже делает это!
Вот что я придумал. Оказывается, обычно нет необходимости в итерации по родительским каталогам. Достаточно получить номер устройства файла, а затем найти соответствующую запись монтирования с тем же номером устройства.
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.
У вас должна быть возможность прочитать в / etc / mtab
, проанализировать все места, где что-то уже установлено, и посмотреть, любые из ваших файлов, о которых идет речь, находятся в любом из этих мест (или их подкаталогов). Для этого вам не нужны какие-либо специальные системные функции, если у вас есть точки монтирования и пути к файлам в виде строк, вы можете обрабатывать их, используя обычные функции обработки строк.
Очевидно, что символические ссылки могут помешать всему этому процессу. Любой путь к файлу, который включает символическую ссылку, должен быть преобразован в его «фактический» путь перед его обработкой. Будем надеяться, что есть способ сделать это без индивидуальной проверки файла и каждого из его родителей, но вы всегда можете подобрать его, если потребуется. Вы, вероятно, захотите использовать realpath
для удаления символических ссылок.
Вы можете начать с 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");
}
Спасибо, что отвлекли ;-)