Чтобы определить, находится ли файл F в каталоге D, сначала stat D, чтобы определить номер его устройства и номер inode (члены st_dev и st_ino из struct stat).
Затем stat F, чтобы определить, является ли это каталогом. Если нет, вызовите basename, чтобы определить имя каталога, в котором он находится. Установите G на имя этого каталога. Если F уже был каталогом, установите G = F.
Теперь, F находится в D, если и только если G в D. Затем у нас есть цикл.
while (1) {
if (samefile(d_statinfo.d_dev, d_statinfo.d_ino, G)) {
return 1; // F was within D
} else if (0 == strcmp("/", G) {
return 0; // F was not within D.
}
G = dirname(G);
}
Функция samefile проста:
int samefile(dev_t ddev, ino_t dino, const char *path) {
struct stat st;
if (0 == stat(path, &st)) {
return ddev == st.st_dev && dino == st.st_no;
} else {
throw ...; // or return error value (but also change the caller to detect it)
}
}
Это будет работать на файловых системах POSIX. Но многие файловые системы не POSIX. Проблемы, на которые стоит обратить внимание:
- Файловые системы, в которых устройство / индекс не являются уникальными. Некоторые файловые системы FUSE являются примерами этого; они иногда составляют номера инодов, когда базовые файловые системы их не имеют. Они не должны повторно использовать номера инодов, но в некоторых файловых системах FUSE есть ошибки.
- Сломанные реализации NFS. В некоторых системах все файловые системы NFS имеют одинаковый номер устройства. Если они проходят через номер инода, как он существует на сервере, это может вызвать проблему (хотя я никогда не видел, чтобы это происходило на практике).
- Точки монтирования Linux. Если
/a
- это связывающее монтирование /b
, то /a/1
правильно отображается внутри /a
, но с реализацией выше, /b/1
также представляется внутри /a
. Я думаю, что это, вероятно, правильный ответ. Однако, если это не тот результат, который вы предпочитаете, это легко исправить, изменив регистр return 1
на вызов strcmp()
, чтобы сравнить и пути. Однако, чтобы это работало, вам нужно начать с вызова realpath
на F и D. Вызов realpath
может быть довольно дорогим (поскольку может потребоваться несколько раз ударить по диску).
- Особый путь
//foo/bar
. POSIX позволяет именам путей, начинающимся с //
, быть особенными, что не совсем точно определено. На самом деле я забыл точный уровень гарантии семантики, предоставляемой POSIX. Я думаю, что POSIX позволяет //foo/bar
и //baz/ugh
ссылаться на один и тот же файл. Проверка устройства / индекса должна по-прежнему выполнять правильную функцию, но вы можете обнаружить, что это не так (т. Е. Вы можете обнаружить, что //foo/bar
и //baz/ugh
могут ссылаться на один и тот же файл, но иметь разные номера устройства / индекса).
В этом ответе предполагается, что мы начинаем с абсолютного пути для F и D. Если это не гарантировано, вам может потребоваться выполнить какое-то преобразование, используя realpath()
и getcwd()
. Это будет проблемой, если имя текущего каталога длиннее PATH_MAX
(что, безусловно, может произойти).