2010-02-25 18 views
20

Dada una ruta a un archivo o directorio, ¿cómo puedo determinar el punto de montaje para ese archivo? Por ejemplo, si /tmp está montado como un sistema de archivos tmpfs y dado el nombre de archivo /tmp/foo/bar, quiero saber que está almacenado en tmpfs con raíz en /tmp.¿Dónde se monta un archivo?

Esto estará en C++ y me gustaría evitar invocar comandos externos a través de system(). El código debe ser robusto, no necesariamente en contra de la manipulación deliberada, pero definitivamente frente a los puntos de montaje anidados, enlaces simbólicos, etc.

No he podido encontrar una llamada al sistema simple para hacer esto. Parece que tendré que escribir el cheque yo mismo. Aquí hay un bosquejo de lo que estoy planeando.

  1. Canonicalize el nombre del archivo a la readlink comando de shell. ¿Cómo?
  2. Lea /etc/mtab con getmntent() & co.
  3. Determine la entrada de montaje correspondiente para el archivo. ¿Cómo?

Para # 1 es que hay una simple llamada de sistema o tengo que leer cada componente directorio de la ruta y resolverlos con readlink(2) si son los links? Y manejar yo mismo . y ..? Parece un dolor.

Para # 3 Tengo varias ideas sobre cómo hacer esto. No estoy seguro de cuál es el mejor.

  1. open() el archivo, su padre, padre de su padre, etc. usando openat(fd, "..") hasta llegar a una de las entradas /etc/mtab. (¿Cómo sé cuándo? fstat() y comparo los números de inodo?)
  2. Busque el nombre de directorio más largo en la tabla de montaje que es una subcadena de mi nombre de archivo.

Me estoy inclinando por la primera opción, pero antes de codificarla, quiero asegurarme de que no estoy pasando por alto nada, ¡idealmente una función incorporada que ya lo haga!

+4

¿Por qué votar para cerrar esto? Es una buena pregunta. –

+2

Quien haya votado para cerrarlo probablemente solo haya leído el título y/o las dos primeras líneas. Esta es claramente una pregunta apropiada (e interesante). –

+0

Vea también [Versión de Python] (http://stackoverflow.com/questions/4260116/find-size-and-free-space-of-the-filesystem-containing-a-given-file) y un [comando de shell] (http://stackoverflow.com/questions/3274354/how-to-find-out-mount-partition-a-directory-or-file-is-on-linux-server). –

Respuesta

17

Esto es lo que se me ocurrió. Resulta que generalmente no hay necesidad de iterar a través de los directorios principales. Todo lo que tiene que hacer es obtener el número de dispositivo del archivo y luego buscar la entrada de montaje correspondiente con el mismo número de dispositivo.

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

Gracias a @RichardPennington para el cara a cara en realpath(), y en la comparación de los números de dispositivo en lugar de números de inodo.

+0

Agradable. Mucho mejor que el mío ya que obtienes toda la información que necesitas. –

+0

Es posible que desee utilizar/proc/mounts en lugar de/etc/mtab -/etc/mtab se mantiene mediante el comando mount y se puede mezclar con, mientras que/proc/mounts es una representación de la tabla de montaje. Los usuarios de BSD deben usar getmntinfo() –

+2

Eso debería funcionar incluso en los archivos especiales del dispositivo, sin necesidad de un caso especial al final ('stat.st_dev' es * siempre * el ID del dispositivo que contiene el archivo, incluso para archivos especiales del dispositivo). – caf

4

Puede comenzar con realpath y seguir revisando cada directorio con una estadística para ver si está en el mismo dispositivo. Parece que debería haber una manera más simple.


Editar:

#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 por la distracción ;-)

0

Usted debe ser capaz de leer en /etc/mtab, analizar todos los lugares en los que ya está montado algo, y ver si alguno de los archivos en cuestión se encuentran en cualquiera de esas ubicaciones (o sus subdirectorios). No debe necesitar ninguna función especial del sistema para esto, si tiene los puntos de montaje y las rutas de archivos como cadenas, puede procesarlas usando las funciones normales de procesamiento de cadenas.

Obviamente, los enlaces simbólicos pueden lanzar una llave en todo este proceso. Cualquier ruta de archivo que incluya un enlace simbólico deberá convertirse en su ruta "actual" antes de procesarla. Afortunadamente, hay una manera de hacerlo sin verificar individualmente un archivo y cada uno de sus padres, pero siempre se puede usar la fuerza bruta si es necesario. Es probable que desee utilizar realpath para eliminar enlaces simbólicos.

2

Esto funcionó para mí en OSX, que no proporciona las funciones de mntent. En 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

¿Es su OSX (C++) algún tipo de linux o no? 'getmntinfo' suena como la llamada a la biblioteca FreeBSD/OSX (C++), pero no a Linux ni a POSIX. – osgx

+0

¿Cuál es su punto? –

+0

La pregunta era sobre Linux. – osgx

Cuestiones relacionadas