Có một thuật toán để quyết định nếu một vòng lặp symlink?


16

Các hệ thống Unix thường chỉ báo lỗi nếu chúng phải đối mặt với một đường dẫn có chứa vòng lặp symlink hoặc chỉ có quá nhiều liên kết tượng trưng, ​​bởi vì chúng có giới hạn về số lượng liên kết tượng trưng mà chúng sẽ đi qua trong một lần tra cứu đường dẫn. Nhưng có cách nào để thực sự quyết định xem một đường dẫn cụ thể có giải quyết được một thứ gì đó hoặc chứa một vòng lặp hay không, ngay cả khi nó chứa nhiều liên kết hơn một unix có sẵn sàng đi theo không? Hay đây là một vấn đề chính thức không thể giải quyết được? Và nếu có thể quyết định, nó có thể được quyết định trong một khoảng thời gian / bộ nhớ hợp lý không (ví dụ: không phải truy cập tất cả các tệp trên hệ thống tệp)?

Vài ví dụ:

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

Chỉnh sửa :

Để làm rõ, tôi không hỏi về việc tìm các vòng lặp trong hệ thống tệp, tôi đang hỏi về một thuật toán quyết định quyết định một đường dẫn cụ thể xem nó có giải quyết được một tệp / thư mục xác định hay không hoặc nó không giải quyết được gì cả. Ví dụ trong hệ thống sau, có một vòng lặp, nhưng đường dẫn đã cho vẫn giải quyết tốt:

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

Cây thư mục này rõ ràng có một chu kỳ, nhưng đường dẫn a/b/b/b/b/bvẫn giải quyết tốt /a.


Công cụ dòng lệnh readlink ...nói gì về các tình huống trên?
slm

1
Bạn đang hỏi nếu chúng ta có thể nói chỉ từ tên đường dẫn nếu có vòng lặp? Hoặc chúng ta có thể làm điều này trong một hệ điều hành thực, sử dụng các công cụ tiêu chuẩn và kiểm tra các thành phần khác nhau của tên đường dẫn giải quyết vấn đề gì?
Mike Diehn

@MikeDiehn Rõ ràng người ta không thể biết chỉ từ một đường dẫn nếu nó giải quyết mà không thực hiện các hoạt động hệ thống tập tin. Nhưng cũng với môi trường HĐH, việc phân biệt một đường dẫn chỉ đơn giản là đi qua rất nhiều liên kết để giải quyết với một đường dẫn không giải quyết được.
JanKanis

Câu trả lời:


10

Tôi không hoàn toàn hiểu những gì bạn đang hỏi. Nếu tôi không biết gì hơn, tôi nghĩ bạn đã hỏi liệu có cách nào để phát hiện ra điều này trong khi đang xử lý một tập tin hay không. Tôi không tin điều này là có thể.

Phương pháp duy nhất tôi có thể hình dung là thực hiện tìm kiếm nơi bạn đặc biệt bắt đầu tìm kiếm thông qua một nhánh cụ thể trong cây thư mục.

Thí dụ

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

5 directories, 1 file

Các findlệnh sẽ phát hiện vòng lặp này, nhưng không thực sự cho bạn biết một toàn bộ rất nhiều về nó.

$ 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

Tôi tùy ý chọn 15 cấp độ để chặn bất kỳ đầu ra nào được hiển thị bởi find. Tuy nhiên, bạn có thể bỏ công tắc đó ( -mindepth) nếu bạn không quan tâm đến cây thư mục đang được hiển thị. Các findlệnh vẫn phát hiện các vòng lặp và điểm dừng:

$ 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

Ngẫu nhiên, nếu bạn muốn ghi đè mặc định MAXSYMLINKSlà 40 trên Linux (phiên bản 3.x mới hơn của kernel), bạn có thể thấy Câu hỏi và trả lời của U & L này có tiêu đề: Làm thế nào để bạn tăng MAXSYMLINKS .

Sử dụng lệnh symlinks

Có một công cụ mà những người duy trì trang FTP có thể sử dụng được gọi là symlinkssẽ giúp phơi bày các vấn đề với các cây dài hoặc treo lủng lẳng do các liên kết tượng trưng gây ra.

Trong một số trường hợp, symlinkscông cụ này cũng có thể được sử dụng để xóa các liên kết vi phạm.

Thí dụ

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

Thư viện glibc

Thư viện glibc có vẻ cung cấp một số chức năng C xung quanh vấn đề này, nhưng tôi hoàn toàn không biết vai trò của chúng hoặc cách sử dụng chúng thực sự. Vì vậy, tôi chỉ có thể chỉ ra chúng cho bạn.

Trang man, man symlinkhiển thị định nghĩa hàm cho một hàm được gọi symlink(). Mô tả như sau:

symlink () tạo ra một liên kết tượng trưng có tên newpath chứa chuỗi oldpath.

Một trong những lỗi nói rằng hàm này trả về:

ELOOP Quá nhiều liên kết tượng trưng đã gặp phải trong việc giải quyết newpath.

Tôi cũng sẽ hướng dẫn bạn đến trang hướng dẫn, man path_resolutionthảo luận về cách Unix xác định đường dẫn đến các mục trên đĩa. Cụ thể đoạn này.

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").

Nếu có thể, tôi muốn có một cách để phát hiện vòng lặp symlink khi được cung cấp một đường dẫn duy nhất và giải quyết các liên kết tượng trưng theo cách thủ công trong một chương trình thay vì để HĐH thực hiện. Nhưng tôi tự hỏi nếu điều này là có thể ở tất cả. Giải pháp tìm có vẻ thú vị, nhưng bạn có ý tưởng / cách / tìm phát hiện các vòng lặp symlink không và nếu phương thức mà nó sử dụng hoàn thành (nghĩa là phát hiện tất cả các vòng lặp có thể và không xác định nhầm bất kỳ đường dẫn không lặp nào)?
JanKanis

@Somejan - xem các cập nhật của tôi cho A. Hãy cho tôi biết nếu điều đó có ý nghĩa.
slm

5

OK, sau khi suy nghĩ thêm tôi nghĩ rằng tôi có một giải pháp rõ ràng.

Cái nhìn sâu sắc quan trọng là nếu mọi liên kết là một phần của một đường dẫn phân giải thành một cái gì đó, thì toàn bộ đường dẫn sẽ giải quyết. Hoặc theo cách khác, nếu một đường dẫn không giải quyết thì phải có một liên kết tượng trưng cụ thể yêu cầu di chuyển không giải quyết.

Trong khi suy nghĩ về vấn đề này trước đây, tôi đã sử dụng một thuật toán đi qua các phần tử của một đường dẫn bắt đầu từ gốc và khi gặp phải một liên kết tượng trưng, ​​nó đã thay thế phần tử đường dẫn đó bằng nội dung của liên kết tượng trưng và sau đó tiếp tục di chuyển ngang. Vì cách tiếp cận này không nhớ liên kết tượng trưng nào hiện đang giải quyết nên nó không thể phát hiện khi nó ở trong một vòng lặp không giải quyết.

Nếu thuật toán theo dõi liên kết tượng trưng nào hiện đang giải quyết (hoặc liên kết tượng trưng nào trong trường hợp liên kết đệ quy), nó có thể phát hiện nếu nó đang cố gắng giải quyết một liên kết một cách đệ quy mà nó vẫn đang bận giải quyết.

Thuật toán:

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

chỉnh sửa :

Tôi có một triển khai thực hiện điều này trong python tại https://bitbucket.org/JanKanis/python-inotify/src/853ed903e870cbfa283e6ce7a5e41aeffe16d4e7/inotify/pathresolver .


3

Python có một hàm gọi là networkx.simple_c đua () có thể được sử dụng cho việc này. Nhưng vâng, nó sẽ cần phải đọc mọi tập tin trên hệ thống.

>>> 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']]

Tôi cũng đã nghĩ về việc sử dụng một số loại thuật toán đồ thị, nhưng tôi không chắc liệu một cây thư mục có liên kết tượng trưng có thể được biểu diễn đầy đủ trong một biểu đồ đơn giản hay không. Trong cây thư mục abc trong đó c là một liên kết tượng trưng đến .., có một vòng lặp, nhưng các đường dẫn như a / b / c / b / c / b vẫn giải quyết vì chúng chỉ theo vòng lặp một số lần hữu hạn và không cứ lặp đi lặp lại.
JanKanis

@Somejan: không gian tên hệ thống tập tin một biểu đồ và tên tệp là một đường dẫn được chọn trên biểu đồ đó.
ninjalj

@ninjalj: Có, một hệ thống tập tin là một biểu đồ, nhưng tôi không nghĩ rằng tên tệp chỉ đơn giản là một đường dẫn trên biểu đồ đó. Tên tệp có thể được xem như một bộ hướng dẫn về cách duyệt qua biểu đồ. Ngay cả khi biểu đồ chứa các chu kỳ không có nghĩa là tên tệp theo chu kỳ đó nhất thiết không giải quyết được, hãy xem ví dụ của tôi trong nhận xét trước đây của tôi.
JanKanis

3

Trên một hệ thống hoạt động (tức là khi không có thay đổi nào diễn ra), vâng, có một thuật toán. Có một số hữu hạn các liên kết tượng trưng, ​​vì vậy chúng tạo thành một biểu đồ hữu hạn và phát hiện các chu kỳ là một quá trình hữu hạn.

Trên một hệ thống trực tiếp, không có cách nào để phát hiện các chu kỳ, bởi vì các liên kết tượng trưng có thể thay đổi trong khi trình phát hiện chu kỳ đang chạy. Đọc mỗi liên kết tượng trưng là nguyên tử, nhưng theo một liên kết tượng trưng thì không. Nếu một số liên kết tượng trưng liên tục thay đổi trong khi kernel đang thực hiện truyền tải, nó có thể kết thúc trên một đường dẫn vô tận liên quan đến các liên kết riêng biệt.


Có nhiều cách để giảm thiểu những thay đổi đó để mang lại độ chính xác lên tới 98-99%. Bạn có thể làm cho nó chú ý đến dấu thời gian trên các tệp và tôi sẽ không đề xuất thực sự theo các liên kết. Vì nó được đệ quy từ thư mục gốc nên nó sẽ tìm thấy thư mục thực tế sau này.
Back2Basics

1
@ Back2Basics Những con số này là hoàn toàn vô nghĩa. Đây là một giao diện kernel. Nếu nó không hoạt động mọi lúc, nó không hoạt động, thời gian.
Gilles 'SO- ngừng trở nên xấu xa'

2

Gần như tôi có thể biết khi nhìn vào các nguồn nhân Linux hiện tại, tất cả các nhân làm được là giữ số lượng liên kết mà nó theo dõi và nó sẽ lỗi nếu số đó lớn hơn một số. Xem dòng 1330 trong namei.c để biết nhận xét và nested_symlink()chức năng. Macro ELOOP (số lỗi được trả về từ một read(2)cuộc gọi hệ thống cho tình huống này) hiển thị ở một số vị trí trong tệp đó, vì vậy nó có thể không đơn giản như đếm các liên kết theo sau, nhưng chắc chắn nó trông như thế nào.

Có một số thuật toán để tìm "chu kỳ" trong danh sách được liên kết ( thuật toán phát hiện chu kỳ của Floyd ) hoặc trong các biểu đồ có hướng . Tôi không rõ bạn phải làm gì để phát hiện một "vòng lặp" hoặc "chu kỳ" thực tế trong một đường dẫn cụ thể. Trong mọi trường hợp, các thuật toán có thể mất nhiều thời gian để chạy, vì vậy tôi đoán rằng chỉ cần đếm số lượng liên kết tượng trưng theo sau sẽ giúp bạn đạt được 90% mục tiêu.


Đối với sử dụng thực tế, chỉ cần đếm số lượng liên kết đi qua là tốt, đặc biệt vì đó là những gì hạt nhân làm, vì vậy ngay cả khi bạn gặp một đường dẫn giải quyết chính xác có quá nhiều liên kết tượng trưng, ​​bạn vẫn không thể sử dụng đường dẫn đó cho bất kỳ điều gì thực tế ( tức là điều đó không liên quan đến việc giải quyết các liên kết tượng trưng bằng tay)
JanKanis
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.