Komfortabler Dateimanager mit vielen Funktionen

Wurzelproblem im Netzwerk

By Sven on 28.01.2005 - 16:40 in Entwicklung

Ein Betatester machte mich gestern auf ein fehlerhaftes Verhalten von SpeedCommander in Zusammenhang mit gesperrten Tabs und UNC-Namen aufmerksam. Wenn man sich ein Tab mit einem UNC-Namen als Basisordner definiert und den Laufwerkswechsel sperrt, dann aktiviert SpeedCommander unter Umständen auch ein freies Tab, wenn man in einen Ordner innerhalb der gleichen Freigabe wechselt.

Im Debugger war zu erkennen, dass die Funktion PathIsSameRoot anscheinend falsche Ergebnisse liefert. SpeedCommander prüft mit dieser Funktion, ob sich zwei Ordner auf dem gleichen Laufwerk befinden:

// Laufwerkswechsel ist gesperrt
if (pTabLayoutFolder->m_dwFlags & FOLDERTAB_LOCKDRIVES)
{
    // Kein Basisordner oder ungleiches Laufwerk
    if (!Lwl_PathIsSame(pTabLayoutFolder->m_strBaseFolder, strNewFolder) && 
        !PathIsSameRoot(strCurrentFolder, strNewFolder))
    {
        // Wechsel zum freien Tab
        if (-1 != iFirstUnlockedTab || fCreateNewTab)
            goto DisplayFolder;

        // Fehlermeldung und weg
        ShowInfoTip(IDS_TAB_INFOTIP_LOCKDRIVES);
        return FALSE;
    }

	...
}

Wenn sich die beiden Ordner auf unterschiedlichen Laufwerken befinden, dann wird bei gesperrtem Laufwerkswechsel entweder auf das erste nicht gesperrte Tab gewechselt oder ein InfoTip angezeigt und der Wechsel verhindert.

PathIsSameRoot bekommt die zu testenden Ordner als Parameter übergeben und gibt TRUE zurück, wenn sich beide Ordner auf das gleiche Laufwerk beziehen. Bei den Ordnern C:\Windows und C:\Programme wird TRUE zurückgegeben, während bei C:\Windows und D:\Programme FALSE zurückgegeben wird.

Bei UNC-Pfaden ist die Freigabeebene die Root, also z.B. \\Server\Daten. PathIsSameRoot prüft bei UNC-Pfaden also, ob sich beide übergebenen Ordner auf die gleiche Freigabe beziehen:

\\Server1\Daten\Test und \\Server1\Daten\Test2
—> Gleiche Root

\\Server1\Daten\Test und \\Server1\Ablage\Test
—> Ungleiche Root

\\Server1\Daten\Test und \\Server2\Daten\Test2
—> Ungleiche Root

\\Server\Daten\Test und \\Server\Daten
–> Gleiche Root

\\Server\Daten und \\Server\Daten\Test
—> Ungleiche Root (Falsch!)

Das letzte Beispiel zeigt, dass die Funktion PathIsSameRoot offensichtlich Probleme hat, wenn der erste Parameter einen Freigabeordner (also eine Root) darstellt. Die beiden Parameter sind die gleichen wie im Beispiel davor, nur die Reihenfolge ist geändert. Doch warum gibt die Funktion hier unterschiedliche Ergebnisse zurück?

Schauen wir uns einfach einmal die WINE-Implementation der Funktion an, die etwas anschaulicher ist als der Assembler-Quelltext aus dem Debugger:

BOOL WINAPI PathIsSameRootA(LPCSTR lpszPath1, LPCSTR lpszPath2)
{
    LPCSTR lpszStart;
    int dwLen;

    TRACE("(%s,%s)\n", debugstr_a(lpszPath1), debugstr_a(lpszPath2));

    if (!lpszPath1 || !lpszPath2 || !(lpszStart = PathSkipRootA(lpszPath1)))
        return FALSE;

    dwLen = PathCommonPrefixA(lpszPath1, lpszPath2, NULL) + 1;
    if (lpszStart - lpszPath1 > dwLen)
        return FALSE; /* Paths not common up to length of the root */

    return TRUE;
}

Die Funktion prüft, ob lpszPath1 und lpszPath2 gültige Zeiger sind. PathSkipRoot liefert einen Zeiger auf das erste Zeichen, das der Root folgt oder NULL, wenn keine Root ermittelt werden konnte. Mit PathCommonPrefix wird die Anzahl der gleichen Zeichen ermittelt, mit denen beide Ordnernamen anfangen. Anschließend wird geprüft, ob die Anzahl der gemeinsamen Zeichen mindestens so groß ist wie die Länge der ermittelten Root von lpszPath1. Wenn ja, dann zeigen beide Pfade auf die gleiche Root.

Die Schwachstelle scheint hier PathSkipRoot zu sein. Wenn man die Funktion einmal jeweils direkt mit C:\ und \\Server\Freigabe aufruft, dann gibt PathSkipRoot bei C:\ einen Zeiger auf das abschließende NULL-Zeichen zurück, bei \\Server\Freigabe jedoch einen NULL-Zeiger. Das veranlasst PathIsSameRoot, die Abarbeitung zu beenden und FALSE zurückzugeben.

Doch selbst wenn Microsoft diesen Fehler im nächsten Service Pack fixen sollte, so wird es immer noch eine große Anzahl von Windows-Systemen geben, bei denen eine SHLWAPI.DLL mit einer nicht korrekten Implementierung von PathIsSameRoot Verwendung findet. Die einzige Möglichkeit, den Fehler in der eigenen Anwendung dauerhaft zu beheben, ist daher die Umgehung von PathIsSameRoot und die Implementation einer eigenen Funktion:

BOOL Lwl_PathIsSameRoot(LPCTSTR pszPath1, LPCTSTR pszPath2)
{
    // Pruefen auf Gueltigkeit
    if (NULL == pszPath1 || NULL == pszPath2)
        return FALSE;

    // Temporaere Kopien erstellen
    TCHAR szPath1[MAX_PATH+1], szPath2[MAX_PATH+1]; 
    StringCchCopy(szPath1, countof(szPath1), pszPath1);
    StringCchCopy(szPath2, countof(szPath2), pszPath2);

    // Beide Pfade bis auf die Root kuerzen
    if (!PathStripToRoot(szPath1) || !PathStripToRoot(szPath2))
        return FALSE;

    // Root von beiden Pfaden vergleichen
    return (0 == lstrcmpi(szPath1, szPath2)) ? TRUE : FALSE;
}

Das gleiche gilt leider auch für PathSkipRoot und alle Funktionen, die PathSkipRoot intern verwenden. Hier steht man nun vor der Entscheidung, generell auf die doch sehr komfortablen Pfadfunktionen zu verzichten oder darauf zu hoffen, dass es keine negativen Auswirkungen von PathSkipRoot auf die anderen Funktionen gibt.

Es können keine Kommentare abgegeben werden.

Top