Lấy tên tệp từ trình mô tả tệp trong C


105

Có thể lấy tên tệp của bộ mô tả tệp (Linux) trong C không?


Tôi đoán, câu trả lời đã chọn nên được trao cho zneak vì giải pháp của anh ấy có tính di động tốt hơn và không có vấn đề truy cập nào được lưu ý.
Sergei

Nó không được hỗ trợ trên Ubuntu 14.04 (kernel 3.16.0-76-chung). Tôi đoán nó không được hỗ trợ trên Linux.
felipou

Đối với macOS, hãy xem câu trả lời này cho một câu hỏi khác của D.Nathanael .
Jonathan Leffler

Câu trả lời:


120

Bạn có thể sử dụng readlinktrên /proc/self/fd/NNNnơi NNN là mô tả tập tin. Điều này sẽ cung cấp cho bạn tên của tệp như khi nó được mở - tuy nhiên, nếu tệp đã bị di chuyển hoặc bị xóa kể từ đó, nó có thể không còn chính xác nữa (mặc dù Linux có thể theo dõi tên trong một số trường hợp). Để xác minh, stattên tệp đã cho và fstatfd bạn có, đồng thời đảm bảo st_devst_inogiống nhau.

Tất nhiên, không phải tất cả các trình mô tả tệp đều tham chiếu đến tệp và đối với những tệp đó, bạn sẽ thấy một số chuỗi văn bản kỳ lạ, chẳng hạn như pipe:[1538488]. Vì tất cả các tên tệp thực sẽ là đường dẫn tuyệt đối, bạn có thể dễ dàng xác định chúng là đủ. Hơn nữa, như những người khác đã lưu ý, các tệp có thể có nhiều liên kết cứng trỏ đến chúng - điều này sẽ chỉ báo cáo tệp được mở bằng. Nếu bạn muốn tìm tất cả các tên cho một tệp nhất định, bạn sẽ phải duyệt qua toàn bộ hệ thống tệp.


9
Miễn là tệp gốc vẫn còn tham chiếu đến nó (tệp mở fdsẽ là tham chiếu như vậy), số inode không thể được sử dụng lại. Bất kỳ phần mềm nào sử dụng số inode sau khi đóng tệp hoặc trước khi mở tệp vốn dĩ phải tuân theo các điều kiện chủng tộc.
R .. GitHub NGỪNG TRỢ GIÚP TỪ

3
Nguy hiểm, Will Robinson! Điều này không phải lúc nào cũng hoạt động --- nếu bạn thực hiện các setuid()thủ thuật, có thể /proc/self/fdquy trình của bạn không thể truy cập được. Xem: permalink.gmane.org/gmane.linux.kernel/1302546
David Given

2
@bdonlan: và trong trường hợp / proc không được gắn kết?
dùng2284570

1
@ user2284570, câu trả lời này dành riêng cho Linux. Tôi không biết liệu NetBSD có hỗ trợ procfs hay không - nếu máy chủ chia sẻ của bạn không cung cấp nó, có thể là do NetBSD không hỗ trợ nó và thay vào đó sử dụng cơ chế khác. Bạn có thể muốn gửi một câu hỏi khác với trọng tâm là NetBSD để xem có ai biết cách NetBSD lộ thông tin này (bạn có thể muốn cũng cố gắng zneak của câu trả lời dưới đây, OS X cũng tương tự hơn để BSD hơn Linux)
bdonlan

1
@bdonlan: Hỗ trợ NetBSD / proc nhưng không bắt buộc phải gắn nó. Mỗi lần tôi đề cập, câu trả lời trở thành "chuyển sang nhà cung cấp chi phí cao hơn và bạn sẽ nhận được / proc". Vì vậy, tôi đang tìm kiếm một giải pháp bất ngờ.
dùng2284570

90

Tôi gặp sự cố này trên Mac OS X. Chúng tôi không có /prochệ thống tệp ảo, vì vậy giải pháp được chấp nhận không thể hoạt động.

Thay vào đó, chúng tôi có một F_GETPATHlệnh cho fcntl:

 F_GETPATH          Get the path of the file descriptor Fildes.  The argu-
                    ment must be a buffer of size MAXPATHLEN or greater.

Vì vậy, để có được tệp được liên kết với trình mô tả tệp, bạn có thể sử dụng đoạn mã này:

#include <sys/syslimits.h>
#include <fcntl.h>

char filePath[PATH_MAX];
if (fcntl(fd, F_GETPATH, filePath) != -1)
{
    // do something with the file path
}

Vì tôi không bao giờ nhớ nơi MAXPATHLENđược xác định, tôi nghĩ PATH_MAXtừ syslimits sẽ ổn.


@uchuugaka, chắc là không. Sử dụng getsockname.
zneak

2
Bạn mong chờ điều gì? Trừ khi đó là ổ cắm UNIX, nó không có tệp nào được liên kết.
zneak

2
@uchuugaka Đúng, mọi thứ đều là tệp, nhưng không phải mọi thứ đều là mục nhập thư mục có tên và vị trí bên trong cây hệ thống tệp. Một tệp được đại diện bởi một inode, nó có thể tồn tại mà không cần bất kỳ mục nhập thư mục nào đề cập đến nó.
lgeorget

9
Trong <sys / param.h>: #define MAXPATHLEN PATH_MAX
geowar

1
Tôi vừa kiểm tra điều này và nó vẫn đúng nếu tệp được di chuyển và bạn gọi lại nó (nghĩa là: bạn nhận được đường dẫn mới của tệp). Tuy nhiên điều này không được hỗ trợ trên linux (được thử nghiệm trên Ubuntu 14.04 - F_GETPATH ​​không được định nghĩa).
felipou


15

Như Tyler đã chỉ ra, không có cách nào để thực hiện những gì bạn yêu cầu "trực tiếp và đáng tin cậy", vì FD nhất định có thể tương ứng với 0 tên tệp (trong các trường hợp khác nhau) hoặc> 1 (nhiều "liên kết cứng" là cách tình huống thứ hai thường được mô tả ). Nếu bạn vẫn cần chức năng với tất cả các giới hạn (về tốc độ VÀ khả năng nhận được kết quả 0, 2, ... thay vì 1), đây là cách bạn có thể làm điều đó: trước tiên, hãy FD - điều này cho bạn biết , kết quả là struct statthiết bị của tệp, có bao nhiêu liên kết cứng, đó có phải là tệp đặc biệt hay không, v.v. Điều này có thể đã trả lời câu hỏi của bạn - ví dụ: nếu 0 liên kết cứng, bạn sẽ BIẾT thực tế không có tên tệp tương ứng trên đĩa.

Nếu số liệu thống kê mang lại cho bạn hy vọng, thì bạn phải "đi dạo trên cây" các thư mục trên thiết bị có liên quan cho đến khi bạn tìm thấy tất cả các liên kết cứng (hoặc chỉ liên kết đầu tiên, nếu bạn không cần nhiều hơn một và bất kỳ liên kết nào sẽ làm được ). Với mục đích đó, bạn sử dụng readdir (và tất nhiên là opendir & c) mở một cách đệ quy các thư mục con cho đến khi bạn tìm thấy trong một thư mục con struct direntdo đó nhận được cùng một số inode mà bạn có trong bản gốc struct stat(tại thời điểm đó nếu bạn muốn toàn bộ đường dẫn, thay vì chỉ tên, bạn sẽ cần phải đi ngược lại chuỗi thư mục để xây dựng lại nó).

Nếu cách tiếp cận chung này có thể chấp nhận được, nhưng bạn cần mã C chi tiết hơn, hãy cho chúng tôi biết, nó sẽ không khó viết (mặc dù tôi không muốn viết nó nếu nó vô dụng, tức là bạn không thể chịu được hiệu suất chậm chắc chắn hoặc khả năng nhận được! = 1 kết quả cho các mục đích của ứng dụng của bạn ;-).


9

Trước khi viết tắt điều này, tôi khuyên bạn nên xem mã nguồn của lệnh lsof .

Có thể có những hạn chế nhưng lsof dường như có khả năng xác định trình mô tả tệp và tên tệp. Thông tin này tồn tại trong hệ thống tệp / proc nên có thể lấy từ chương trình của bạn.


6

Bạn có thể sử dụng fstat () để lấy inode của tệp theo struct stat. Sau đó, sử dụng readdir (), bạn có thể so sánh inode bạn tìm thấy với những inode tồn tại (struct dirent) trong một thư mục (giả sử rằng bạn biết thư mục, nếu không bạn sẽ phải tìm kiếm toàn bộ hệ thống tệp) và tìm tên tệp tương ứng. Bẩn thỉu?


2

Không thể nào. Một bộ mô tả tệp có thể có nhiều tên trong hệ thống tệp hoặc nó có thể không có tên nào cả.

Chỉnh sửa: Giả sử bạn đang nói về một hệ thống POSIX cũ thuần túy, không có bất kỳ API dành riêng cho hệ điều hành nào, vì bạn không chỉ định một hệ điều hành.


4
thì câu trả lời của tôi được áp dụng. Linux không có cơ sở để làm điều này. Bộ mô tả tệp Linux (POSIX) không nhất thiết phải tham chiếu đến tệp và ngay cả khi chúng đề cập đến inodes chứ không phải tên tệp. Một bộ mô tả có thể trỏ đến một tệp đã xóa (do đó không có tên, đây là cách phổ biến để tạo tệp tạm thời) hoặc nó có thể trỏ đến một inode có nhiều tên (liên kết cứng).
Tyler McHenry,

3
Hãy thử xem mã nguồn lsof. :) Đó là những gì tôi đã làm khi tôi có cùng câu hỏi này một thời gian trước. lsof hoạt động trên ma thuật đen và dê hiến tế - bạn không thể hy vọng sao chép hành vi của nó. Cụ thể hơn, lsof được kết hợp chặt chẽ với hạt nhân linux và không làm những gì nó làm bằng bất kỳ API nào có sẵn cho mã người dùng.
Tyler McHenry

27
Linux có một API proc không di động cho việc này. Thực sự có những hạn chế, nhưng nói rằng nó không thể chỉ là sai.
bdonlan

1
@Tyler - lsof chạy trong không gian người dùng. Do đó, có một API cho bất cứ điều gì nó có sẵn để mã Userland :)
bdonlan

1
@ Duck, tính di động có lẽ là lý do tại sao nguồn của lsof có quá nhiều ma thuật đen; mỗi biến thể UNIX làm điều đó khác nhau. Các giao diện proc linux không quá tệ, thực sự, được ghi chép khá thưa thớt.
bdonlan 27-07-09
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.