Frage

Wie kann ich den Mountspunkt für diese Datei festlegen, wenn ich einen Pfad zu einer Datei oder einem Verzeichnis erfassen kann? Zum Beispiel wenn /tmp ist wie a montiert tmpfs Dateisystem anschließend den Dateinamen angegeben /tmp/foo/bar Ich möchte wissen, dass es auf einem gespeichert ist tmpfs verwurzelt bei /tmp.

Dies wird in C ++ sein und ich möchte vermeiden, externe Befehle aufzurufen system(). Der Code sollte robust sein-nicht unbedingt gegen absichtliche Manipulationen, sondern definitiv angesichts verschachtelter Mountpoints, Symlinks usw.

Ich konnte dafür keinen einfachen Systemaufruf finden. Es sieht so aus, als ob ich den Check selbst schreiben muss. Hier ist eine grobe Übersicht darüber, was ich plane.

  1. Kanonisieren Sie den Dateinamen a la die readlink Shell -Befehl. Wie?
  2. Lesen /etc/mtab mit getmntent() & co.
  3. Bestimmen Sie den entsprechenden Mount -Eintrag für die Datei. Wie?

Für #1 gibt es einen einfachen Systemaufruf oder muss ich jede Verzeichniskomponente des Pfades lesen und mit ihnen beheben readlink(2) Wenn sie Symlinks sind? Und Handle . und .. mich selbst? Scheint wie ein Schmerz.

Für #3 habe ich verschiedene Ideen, wie man das macht. Ich bin mir nicht sicher, was am besten ist.

  1. open() die Datei, ihre übergeordnete, die übergeordnete übergeordnete usw. verwendet openat(fd, "..") bis ich einen der von erreiche /etc/mtab Einträge. (Woher weiß ich, wann ich es tue? fstat() sie und vergleichen Sie die Inode -Zahlen?)
  2. Suchen Sie den längsten Verzeichnisnamen in der Mount -Tabelle, die ein Substring meines Dateinamens ist.

Ich neige zu der ersten Option, aber bevor ich alles auf Codierung codiere, möchte ich sicherstellen, dass ich nichts übersehen kann-ideal eine integrierte Funktion, die dies bereits ausführt!

War es hilfreich?

Lösung

Das habe ich mir ausgedacht. Es stellt sich heraus, dass es normalerweise nicht erforderlich ist, durch die Elternverzeichnisse zu iterieren. Alles, was Sie tun müssen, ist die Gerätenummer der Datei zu erhalten und dann den entsprechenden Mount -Eintrag mit derselben Gerätenummer zu finden.

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

Vielen Dank an @richardpennington für die Köpfe auf realpath(), und beim Vergleich von Gerätezahlen anstelle von Inode -Zahlen.

Andere Tipps

Sie können mit RealPath beginnen und sich nach oben arbeiten und jedes Verzeichnis mit STAT überprüfen, um festzustellen, ob es auf demselben Gerät ist. Es scheint, als sollte es einen einfacheren Weg geben.


Bearbeiten:

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

Danke für die Ablenkung ;-)

Dies hat bei OSX für mich funktioniert, was nicht die Mntent -Funktionen liefert. In 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
}

Sie sollten in der Lage sein, in lesen zu können /etc/mtab, Analysieren Sie alle Orte, an denen bereits etwas montiert ist, und prüfen Sie, ob sich eine Ihrer fraglichen Dateien an einem dieser Standorte (oder in ihrer Unterabrechnung) befindet. Dafür sollten Sie keine speziellen Systemfunktionen benötigen. Wenn Sie über die Bergpunkte und Dateipfade als Zeichenfolgen verfügen, können Sie sie mit normalen String -Verarbeitungsfunktionen verarbeiten.

Offensichtlich können Symlinks einen Schraubenschlüssel in diesen ganzen Prozess werfen. Jeder Dateipfad, der einen Symlink enthält, muss vor der Verarbeitung in seinen "tatsächlichen" Pfad konvertiert werden. Hoffentlich gibt es eine Möglichkeit, dies zu tun, ohne eine Datei und jeden seiner Eltern einzeln zu überprüfen, aber Sie können sie immer brutal machen, wenn Sie müssen. Sie werden wahrscheinlich verwenden wollen realpath Symlinks entfernen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top