Чи існує алгоритм, щоб вирішити, чи є циклічне посилання?


16

Системи Unix зазвичай просто помиляються, якщо вони стикаються з контуром, який містить цикл символьних посилань або просто занадто багато символьних посилань, оскільки вони мають обмеження кількості символьних посилань, які вони будуть проходити в одному пошуку шляху. Але чи є спосіб насправді вирішити, чи заданий шлях вирішує щось або містить цикл, навіть якщо він містить більше посилань, ніж Unix готовий слідувати? Або це формально нерозв'язна проблема? І якщо це можна вирішити, чи можна прийняти рішення за розумну кількість часу / пам'ять (наприклад, без необхідності відвідувати всі файли у файловій системі)?

Деякі приклади:

a/b/c/d
where a/b is a symlink to ../e
and e is a symlink to f
and f is a symlink to a/b

a/b/c/d
where a/b/c is a symlink to ../c

a/b/c/d
where a/b/c is a symlink to ../c/d

a/b/c/d
where a/b/c is a symlink to /a/b/e
where a/b/e is a symlink to /a/b/f
where a/b/f is a symlink to /a/b/g

Редагувати :

Для уточнення, я не запитую про пошук циклів у файловій системі, я запитую про алгоритм прийняття рішення, який визначає певний шлях, чи відповідає воно певному файлу / каталогу чи він взагалі не вирішується. Наприклад, у наступній системі є цикл, але даний шлях все-таки вирішує добре:

/ -- a -- b
where b is a symlink to /a

У цьому дереві каталогів явно є цикл, але шлях a/b/b/b/b/bвсе-таки добре вирішується /a.


Що говорить інструмент командного рядка readlink ...про вищезазначені ситуації?
slm

1
Ви запитуєте, чи можемо ми дізнатись просто з назви шляху, чи є петлі? Або ми можемо це зробити в реальній операційній системі, використовуючи стандартні інструменти та перевіряючи, на що вирішуються різні компоненти назви шляху?
Майк Дієн

@MikeDiehn Очевидно, що не можна сказати лише з контуру, якщо вона вирішена, не виконуючи операцій з файловою системою. Але також із середовищем ОС нескладно розрізнити шлях, який вимагає просто пройти багато символьних посилань, щоб вирішити той, який взагалі не вирішує.
JanKanis

Відповіді:


10

Я не повністю розумію, про що ви просите. Якщо я не знав нічого кращого, я думаю, ви запитували, чи є спосіб виявити це в розпалі файлу. Я не вірю, що це можливо.

Єдиний метод, який я можу уявити, - це пошук, де ви спеціально починаєте переглядати певну гілку дерева дерева.

Приклад

$ tree 
.
`-- a
    `-- b
        |-- c
        |   `-- d
        |       `-- e -> ../../../../a/b
        `-- e -> e

5 directories, 1 file

findКоманда виявить цей цикл , але на самому ділі не сказати вам багато про нього.

$ find -L . -mindepth 15
find: File system loop detected; `./a/b/c/d/e' is part of the same file system loop as `./a/b'.
find: `./a/b/e': Too many levels of symbolic links

Я довільно вибрав 15 рівнів, щоб заблокувати показ будь-якого виводу find. Однак ви можете скинути цей перемикач ( -mindepth), якщо вам не байдуже зображення дерева, що відображається. findКоманда по- , як і раніше визначає цикл і зупиняється:

$ find -L . 
.
./a
./a/b
./a/b/c
./a/b/c/d
find: File system loop detected; `./a/b/c/d/e' is part of the same file system loop as `./a/b'.
find: `./a/b/e': Too many levels of symbolic links

До речі, якщо ви хочете змінити типовий стандарт, MAXSYMLINKSякий, мабуть, 40 для Linux (новіші 3.x версії ядра), ви можете побачити це запитання U&L під назвою: Як ви збільшуєте MAXSYMLINKS .

Використання команди символьних посилань

Існує інструмент, за допомогою якого сервіси FTP-сайтів могли використовувати виклик, symlinksякий допоможе виявити проблеми із довгими інструментами або звисаючими деревами, які були викликані символічними посиланнями.

У певних випадках цей symlinksінструмент також може використовуватися для видалення порушень, що порушують право.

Приклад

$ symlinks -srv a
lengthy:  /home/saml/tst/99159/a/b/c/d/e -> ../../../../a/b
dangling: /home/saml/tst/99159/a/b/e -> e

Бібліотека glibc

Бібліотека glibc, як видається, пропонує деякі функції C навколо цього, але я не знаю повністю їх ролі або як насправді ними користуватися. Тож я можу лише лише вказати їх на вас.

На сторінці "man" man symlinkвідображається визначення функції для функції, що називається symlink(). Опис виглядає так:

symlink () створює символічне посилання з назвою newpath, яке містить рядковий oldpath.

Одна з помилок говорить, що ця функція повертає:

ELOOP Занадто багато символічних посилань зустрічалося при вирішенні нового шляху.

Я також направлю вас на сторінку man, man path_resolutionде обговорюється, як Unix визначає шляхи до елементів на диску. Зокрема цей параграф.

If  the component is found and is a symbolic link (symlink), we first 
resolve this symbolic link (with the current lookup directory as starting 
lookup directory).  Upon error, that error is returned.  If the result is 
not a directory, an ENOTDIR error is returned.  If the resolution of the 
symlink is successful and returns a directory, we set the current lookup
directory to that directory, and go to the next component.  Note that the 
resolution process here involves recursion.  In order  to  protect  the 
kernel against stack overflow, and also to protect against denial of 
service, there are limits on the maximum recursion depth, and on the maximum 
number of symbolic links followed.  An ELOOP error is returned  when  the
maximum is exceeded ("Too many levels of symbolic links").

Якщо це можливо, я хотів би спосіб виявлення циклу символьної посилання при наданні єдиного шляху і розв'язання символьних посилань вручну в програмі замість того, щоб дозволити ОС це робити. Але мені цікаво, чи це взагалі можливо. Рішення пошуку виглядає цікаво, але чи є у вас ідея / як / find виявляє цикли символьних посилань, і якщо метод, який він використовує, завершений (тобто виявляє всі можливі петлі і не вказує неправильно жодних контурів)?
JanKanis

@Somejan - дивіться мої оновлення до A. Повідомте мене, чи має це сенс.
slm

5

Добре, після ще однієї думки я думаю, що я маю чітке рішення.

Критичне розуміння полягає в тому, що якщо кожна посилання, яка є частиною шляху, до чогось вирішується, тоді вирішується весь шлях. Або навпаки, якщо шлях не вирішується, тоді повинно бути певне символьне посилання, яке вимагає проходження, яке не вирішує.

Раніше роздумуючи над цією проблемою, я раніше використовував алгоритм, який обходив елементи шляху, починаючи від кореня, і коли він натрапив на симпосилання, він замінив цей елемент шляху вмістом симпосилання, а потім продовжив обхід. Оскільки цей підхід не пам’ятає, яке символьне посилання воно в даний час вирішує, він не може виявити, коли він знаходиться в нерозв’язувальному циклі.

Якщо алгоритм відслідковує, яке символьне посилання воно в даний час вирішується (або яке посилається у випадку рекурсивних посилань), він може виявити, чи намагається знову вирішити посилання рекурсивно, яке воно все ще зайняте вирішенням.

Алгоритм:

initialize `location` to the current working directory
initialize `link_contents` to the path we want to resolve
initialize `active_symlinks` to the empty set

def resolve_symlink(location, link_contents, active_symlinks) :
    loop forever:
        next_location = location / [first element of link_contents]
        see if next_location is a symlink.
        if so:
            if next_location in active_symlinks: abort, we have a loop
            location = resolve_symlink(location, readlink(next_location), active_symlinks ∪ {next_location})
        else:
            location = next_location
        strip first element of link_contents
        if link_contents is empty: 
            return location

редагувати :

Я працюю реалізацією цього в python на https://bitbucket.org/JanKanis/python-inotify/src/853ed903e870cbfa283e6ce7a5e41aeffe16d4e7/inotify/pathresolver.py?at=pathwatcher .


3

У Python є функція під назвою networkx.simple_cycles (), яка може бути використана для цього. Але так, потрібно було б прочитати кожен файл у системі.

>>> import networkx as nx
>>> G = nx.DiGraph()
>>> G.add_edge('A', 'B')
>>> G.add_edge('B', 'C')
>>> G.add_edge('C', 'D')
>>> G.add_edge('C', 'A')
>>> nx.simple_cycles(G)
[['A', 'B', 'C', 'A']]

Я також думав про використання якогось алгоритму графіків, але я не впевнений, чи дерево каталогів із посиланнями може бути адекватно представлене у простому графіку. У дереві каталогів abc, де c є символьним посиланням на .., існує цикл, але такі шляхи, як a / b / c / b / c / b, все ще вирішуються, оскільки вони слідують циклу лише кількість разів і не роблять продовжуйте циклічно.
JanKanis

@Somejan: простір імен файлової системи - це графік, а ім'я файлу - шлях, обраний для цього графіка.
ninjalj

@ninjalj: Так, файлова система - це графік, але я не думаю, що ім'я файлу - це просто шлях до цього графіка. Ім’я файлу може розглядатися як набір інструкцій щодо переміщення графіка. Навіть якщо графік містить цикли, що не означає, що ім'я файлу, яке слід за цим циклом, обов'язково не вирішує, дивіться мій приклад у попередньому коментарі.
JanKanis

3

У системі спокою (тобто коли не відбувається змін) так, існує алгоритм. Існує кінцева кількість символьних посилань, тому вони складають скінченний графік, а виявлення циклів - це остаточний процес.

У живій системі немає можливості виявити цикли, тому що символьні посилання можуть змінюватися під час роботи детектора циклів. Читання кожного символьного посилання є атомним, але слідування символічного посилання не є. Якщо деякі посилання продовжують змінюватися, поки ядро ​​робить обхід, воно може опинитися на нескінченному шляху, що включає чіткі посилання.


Існують способи пом’якшити ці зміни, щоб довести їх до 98-99% точності. Ви можете змусити його звернути увагу на часові позначки на файли, і я б не пропонував насправді переходити за посиланнями. Оскільки він є рекурсивним з кореня, він знайде фактичний каталог пізніше.
Back2Basics

1
@ Back2Basics Ці цифри абсолютно безглузді. Це інтерфейс ядра. Якщо вона не працює весь час, вона не працює, періодично.
Жил "ТАК - перестань бути злим"

2

Наскільки я можу сказати, дивлячись на поточні джерела ядра Linux, все ядро ​​- це підрахувати кількість посилань, яке воно виконується, і воно помиляється, якщо воно більше, ніж якесь число. Дивіться рядок 1330 в namei.c для коментаря та nested_symlink()функції. Макрос ELOOP (номер помилки, повернутий з аread(2) системного виклику в цій ситуації) відображається в ряді місць у цьому файлі, тому він може бути не таким простим, як підрахунок посилань, але це точно так, як це виглядає.

Існує ряд алгоритмів пошуку "циклів" у пов'язаних списках ( алгоритм виявлення циклу Флойда ) або у спрямованих графіках . Мені незрозуміло, який саме вам доведеться зробити, щоб виявити фактичний "цикл" або "цикл" на певному шляху. У будь-якому випадку, алгоритми можуть зайняти тривалий час, тому я здогадуюсь, що лише підрахунок кількості символічних посилань, що випливає, отримує 90% шляху до вашої мети.


Для практичного використання просто підрахунок кількості пройдених посилань - це добре, тим більше, що саме так ядро ​​робить, тому навіть якщо ви зіткнулися з правильно розв’язувальним шляхом, який має занадто багато посилань, ви все одно не можете використовувати цей шлях для нічого практичного ( тобто не передбачає розв’язування символьних посилань вручну)
JanKanis
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.