2010-02-25 10 views
20

Cho đường dẫn đến tệp hoặc thư mục, làm thế nào tôi có thể xác định điểm gắn kết cho tệp đó? Ví dụ: nếu /tmp được gắn dưới dạng hệ thống tệp tmpfs thì hãy đặt tên tệp là /tmp/foo/bar Tôi muốn biết rằng tệp được lưu trữ trên số tmpfs bắt nguồn từ số /tmp.Tệp được gắn ở đâu?

Điều này sẽ ở trong C++ và tôi muốn tránh gọi các lệnh bên ngoài qua system(). Mã phải mạnh mẽ - không nhất thiết phải chống lại cố ý giả mạo nhưng chắc chắn đối mặt với các điểm gắn kết lồng nhau, liên kết tượng trưng, ​​v.v.

Tôi chưa thể tìm thấy một cuộc gọi hệ thống đơn giản để thực hiện việc này. Có vẻ như tôi sẽ phải tự mình viết séc. Đây là một phác thảo sơ lược về những gì tôi đang lên kế hoạch.

  1. Canonicalize the name name a la the readlink shell command. Làm cách nào?
  2. Đọc /etc/mtab với getmntent() & co.
  3. Xác định mục nhập gắn kết tương ứng cho tệp. Làm cách nào?

Đối # 1 là có một hệ thống gọi đơn giản hay sao tôi cần phải đọc mỗi thành phần thư mục của con đường và giải quyết chúng với readlink(2) nếu họ liên kết tượng trưng? Và xử lý ... bản thân mình? Có vẻ như là một cơn đau.

Đối với # 3 tôi có nhiều ý tưởng về cách thực hiện việc này. Không chắc cái nào là tốt nhất.

  1. open() file, mẹ, mẹ mẹ, vv sử dụng openat(fd, "..") cho đến khi tôi đạt được một trong những /etc/mtab mục. (Làm cách nào để biết khi nào? fstat() chúng và so sánh các số inode?)
  2. Tìm tên thư mục dài nhất trong bảng gắn kết là một chuỗi con của tên tệp của tôi.

Tôi nghiêng về tùy chọn đầu tiên nhưng trước khi tôi mã hóa tất cả, tôi muốn đảm bảo rằng tôi không nhìn thấy gì cả - lý tưởng là chức năng tích hợp đã thực hiện điều này!

+4

Tại sao lại bỏ phiếu này? Đó là một câu hỏi hay. –

+2

Bất cứ ai đã bỏ phiếu để đóng nó có lẽ chỉ cần đọc tiêu đề và/hoặc hai dòng đầu tiên. Đây rõ ràng là một câu hỏi thích hợp (và thú vị). –

+0

Xem thêm [Phiên bản Python] (http://stackoverflow.com/questions/4260116/find-size-and-free-space-of-the-filesystem-containing-a-given-file) và [lệnh trình bao] (http://stackoverflow.com/questions/3274354/how-to-find-out-mount-partition-a-directory-or-file-is-on-linux-server). –

Trả lời

17

Đây là những gì tôi đã đưa ra. Hóa ra thường không cần phải lặp qua các thư mục cha. Tất cả những gì bạn phải làm là lấy số thiết bị của tập tin và sau đó tìm mục nhập gắn kết tương ứng với cùng số thiết bị.

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; 
} 

Nhờ @RichardPennington cho người đứng đầu lên trên realpath(), và trên so sánh số thiết bị thay vì số inode.

+0

Đẹp. Tốt hơn nhiều so với tôi kể từ khi bạn nhận được tất cả các thông tin bạn cần. –

+0

Bạn có thể muốn sử dụng/proc/mounts thay vì/etc/mtab -/etc/mtab được duy trì bởi lệnh mount và có thể được fudged với, trong khi/proc/mounts là một đại diện của bảng mount. Người dùng BSD nên sử dụng getmntinfo() –

+2

Điều đó thậm chí nên hoạt động trên các tệp đặc biệt của thiết bị mà không cần trường hợp đặc biệt ở cuối ('stat.st_dev' là * luôn * ID của thiết bị chứa tệp, thậm chí cho các tập tin đặc biệt của thiết bị). – caf

4

Bạn có thể bắt đầu với realpath và làm việc theo cách của bạn lên kiểm tra từng thư mục với stat để xem nếu nó trên cùng một thiết bị. Nó có vẻ như nên có một cách đơn giản hơn.


Edit:

#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"); 
} 

Thanksd cho xao lãng ;-)

0

Bạn sẽ có thể đọc trong /etc/mtab, phân tích ra tất cả các địa điểm mà một cái gì đó đã được gắn kết, và xem có bất kỳ tập tin của bạn trong câu hỏi được đặt tại bất kỳ vị trí nào (hoặc các thư mục con của chúng). Bạn không nên cần bất kỳ chức năng hệ thống đặc biệt nào cho điều này, nếu bạn có các điểm gắn kết và đường dẫn tệp dưới dạng chuỗi thì bạn có thể xử lý chúng bằng các hàm xử lý chuỗi bình thường.

Rõ ràng, các liên kết tượng trưng có thể ném cờ lê vào toàn bộ quá trình này. Bất kỳ đường dẫn tệp nào bao gồm một liên kết tượng trưng sẽ phải được chuyển đổi thành đường dẫn "thực tế" của nó trước khi xử lý nó. Hy vọng rằng có một cách để làm điều này mà không cần kiểm tra từng tập tin và từng cha mẹ của nó, nhưng bạn luôn luôn có thể gây khó chịu nếu bạn phải. Có thể bạn sẽ muốn sử dụng realpath để xóa các liên kết tượng trưng.

2

Điều này làm việc cho tôi trên OSX, không cung cấp chức năng mntent. Trong C++:

struct stat fileStat; 
int result = stat(path, &fileStat); 
if (result != 0) { 
    // handle error 
}  

struct statfs* mounts; 
int numMounts = getmntinfo(&mounts, MNT_WAIT); 
if (numMounts == 0) { 
    // handle error 
}  

for (int i = 0; i < numMounts; i++) { 
    if (fileStat.st_dev == mounts[i].f_fsid.val[0]) 
     // mounts[i].f_mntonname is the mount path 
} 
+0

OSX của bạn (C++) có phải là loại Linux nào không? 'getmntinfo' giống như gọi thư viện FreeBSD/OSX (C++), nhưng không phải là linux hoặc POSIX. – osgx

+0

Điểm của bạn là gì? –

+0

Câu hỏi đặt ra là về Linux. – osgx