Index: trunk/src/emx/src/lib/sys/fs.c
===================================================================
--- trunk/src/emx/src/lib/sys/fs.c	(revision 1518)
+++ trunk/src/emx/src/lib/sys/fs.c	(revision 1518)
@@ -0,0 +1,985 @@
+/* $Id$ */
+/** @file
+ *
+ * LIBC SYS Backend - file system stuff.
+ *
+ * Copyright (c) 2004 knut st. osmundsen <bird@innotek.de>
+ *
+ *
+ * This file is part of InnoTek LIBC.
+ *
+ * InnoTek LIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * InnoTek LIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with InnoTek LIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include "libc-alias.h"
+#include "fs.h"
+#define INCL_FSMACROS
+#define INCL_BASE
+#define INCL_ERRORS
+#include <os2emx.h>
+#define __USE_GNU
+#include <string.h>
+#include <errno.h>
+#include <386/builtin.h>
+#include <sys/fmutex.h>
+#include <emx/startup.h>
+#include "syscalls.h"
+#include <InnoTekLIBC/sharedpm.h>
+#include <InnoTekLIBC/pathrewrite.h>
+#include <InnoTekLIBC/libc.h>
+#define __LIBC_LOG_GROUP __LIBC_LOG_GRP_BACK_IO
+#include <InnoTekLIBC/logstrict.h>
+
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+/** Mutex protecting all the FS globals. */
+static _fmutex __libc_gmtxFS;
+
+/** Indicator whether or not we're in the unix tree. */
+int  __libc_gfInUnixTree = 0;
+/** Length of the unix root if the unix root is official. */
+int  __libc_gcchUnixRoot = 0;
+/** The current unix root. */
+char __libc_gszUnixRoot[PATH_MAX];
+
+
+#define EA_SYMLINK  "LIBC.SYMLINK"
+/** Symlink ea name. */
+const char __libc_gszSymlinkEA[] = EA_SYMLINK;
+/* GEA2LIST Structure, just a bit more conveniently laid out.. */
+static const struct
+{
+    ULONG   cbList;
+    ULONG   oNextEntryOffset;
+    BYTE    cbName;
+    CHAR    szName[sizeof(EA_SYMLINK)];
+} gSymlinkGEA2List =
+{
+    sizeof(gSymlinkGEA2List),
+    0,
+    sizeof(EA_SYMLINK) - 1,
+    EA_SYMLINK
+};
+
+/**
+ * The special /@unixroot rewrite rule.
+ */
+static __LIBC_PATHREWRITE   gaRewriteRule =
+{
+    __LIBC_PRWF_CASE_SENSITIVE | __LIBC_PRWF_TYPE_DIR,      "/@unixroot",  10, __libc_gszUnixRoot, 0
+};
+
+
+
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+static int fsInit(void);
+static int fsResolveUnix(const char *pszUserPath, int fVerifyLast, char *pszNativePath, size_t cchNativePath, int *pfInUnixTree);
+
+
+
+_CRT_INIT1(fsInit)
+
+/**
+ * Init the file system stuff.
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ */
+static int fsInit(void)
+{
+    LIBCLOG_ENTER("\n");
+
+    /*
+     * Only once.
+     */
+    static void *pvCalled = (void *)fsInit;
+    if (!pvCalled)
+        LIBCLOG_RETURN_INT(0);
+    pvCalled = NULL;
+
+
+    /*
+     * Create the mutex.
+     */
+    int rc = _fmutex_create(&__libc_gmtxFS, 0);
+    if (rc)
+        abort();
+
+    /*
+     * Inherit stuff.
+     */
+    __LIBC_PSPMINHERIT  pInherit = __libc_spmInheritRequest();
+    __LIBC_PSPMINHFS    pFS;
+    if (pInherit && (pFS = pInherit->pFS) != NULL && pFS->cchUnixRoot)
+    {
+        LIBCLOG_MSG("Inherited unixroot=%s\n", pFS->szUnixRoot);
+        __libc_gfInUnixTree = pFS->fInUnix;
+        __libc_gcchUnixRoot = pFS->cchUnixRoot;
+        memcpy(&__libc_gszUnixRoot[0], &pFS->szUnixRoot[0], pFS->cchUnixRoot + 1);
+
+        __libc_spmInheritRelease();
+
+        /* Register rewrite rule. */
+        gaRewriteRule.pszTo = "/";
+        gaRewriteRule.cchTo = 1;
+        if (__libc_PathRewriteAdd(&gaRewriteRule, 1))
+        {
+            abort();
+            LIBCLOG_RETURN_INT(-1);
+        }
+    }
+    else
+    {
+        /*
+         * Nothing to inherit.
+         */
+        if (pInherit)
+            __libc_spmInheritRelease();
+        if (!__libc_gfNoUnix)
+        {
+            /*
+             * Setup unofficial unixroot.
+             */
+            const char *psz = getenv("UNIXROOT");
+            if (psz)
+            {
+                LIBCLOG_MSG("Unofficial unixroot=%s\n", psz);
+                size_t cch = strlen(psz);
+                if (cch >= PATH_MAX - 32)
+                {
+                    LIBC_ASSERTM_FAILED("The UNIXROOT environment variable is too long! cch=%d maxlength=%d\n", cch, PATH_MAX - 32);
+                    abort();
+                    LIBCLOG_RETURN_INT(-1);
+                }
+                memcpy(&__libc_gszUnixRoot[0], psz, cch + 1);
+
+                /* Register the rewrite rule. */
+                gaRewriteRule.cchTo = cch;
+                if (__libc_PathRewriteAdd(&gaRewriteRule, 1))
+                {
+                    abort();
+                    LIBCLOG_RETURN_INT(-1);
+                }
+            }
+        }
+    }
+
+    LIBCLOG_RETURN_INT(0);
+}
+
+
+int __libc_back_fsMutexRequest(void)
+{
+    int rc = _fmutex_request(&__libc_gmtxFS, 0);
+    if (!rc)
+        return 0;
+    _sys_set_errno(rc);
+    return -1;
+}
+
+
+void __libc_back_fsMutexRelease(void)
+{
+    int rc = _fmutex_release(&__libc_gmtxFS);
+    if (!rc)
+        return;
+    LIBC_ASSERTM_FAILED("_fmutex_release(gmtxFS) -> rc=%d\n", rc);
+}
+
+
+/**
+ * Reads a symbolic link.
+ *
+ * @returns 0 on success.
+ * @returns -1 and errno on failure.
+ * @param   pszPath     The symlink to read.
+ * @param   pszBuf      Where to read to.
+ * @param   cchBuf      Size of the buffer.
+ */
+int __libc_Back_fsSymlinkRead(const char *pszPath, char *pszBuf, size_t cchBuf)
+{
+    LIBCLOG_ENTER("pszPath=%p:{%s} pszBuf=%p cchBuf=%d\n", (void *)pszPath, pszPath, (void *)pszBuf, cchBuf);
+
+    /*
+     * Query the symlink EA value.
+     */
+    char    achBuffer[PATH_MAX + 97];
+    EAOP2   EaOp;
+    EaOp.fpGEA2List = (PGEA2LIST)&gSymlinkGEA2List;
+    EaOp.fpFEA2List = (PFEA2LIST)achBuffer;
+    EaOp.oError     = 0;
+    EaOp.fpFEA2List->cbList = sizeof(achBuffer) - 1;
+    LIBC_ASSERT(!((uintptr_t)&achBuffer[0] & 4));
+    int rc = DosQueryPathInfo((PCSZ)pszPath, FIL_QUERYEASFROMLIST, &EaOp, sizeof(EaOp));
+    if (!rc)
+    {
+        /*
+         * Validate the EA.
+         */
+        PUSHORT pusType = (PUSHORT)((char *)&EaOp.fpFEA2List->list[1] + EaOp.fpFEA2List->list[0].cbName);
+        char   *pszSymlink = (char *)&pusType[2];
+        if (    pusType[0] == EAT_ASCII
+            &&  pusType[1] < EaOp.fpFEA2List->list[0].cbValue
+            &&  pusType[1]
+            &&  *pszSymlink)
+        {
+            /*
+             * Check the length, copy it to the buffer and return.
+             */
+            int cchSymlink = strnlen(pszSymlink, pusType[1]);
+            pszSymlink[cchSymlink] = '\0';
+            if (cchSymlink < cchBuf)
+            {
+                memcpy(pszBuf, pszSymlink, cchSymlink + 1);
+                LIBCLOG_RETURN_INT(0);
+            }
+            else
+            {
+                LIBC_ASSERTM_FAILED("Buffer to small. %d bytes required, %d bytes available. pszSymlink='%s' pszPath='%s'\n",
+                                    cchSymlink + 1, cchBuf, pszSymlink, pszPath);
+                errno = ENAMETOOLONG;
+                LIBCLOG_RETURN_INT(-1);
+            }
+        }
+        else
+        {
+            LIBC_ASSERTM_FAILED("Invalid symlink EA! type=%x len=%d cbValue=%d *pszSymlink=%c pszPath='%s'\n",
+                                pusType[0], pusType[1], EaOp.fpFEA2List->list[0].cbValue, *pszSymlink, pszPath);
+            errno = EFTYPE;
+            LIBCLOG_RETURN_INT(-1);
+        }
+    }
+
+    /* failure */
+    _sys_set_errno(rc);
+    LIBCLOG_RETURN_INT(-1);
+}
+
+#if 0 //just testing, not useful.
+/**
+ * Updates a symlink.
+ *
+ * @returns 0 on success.
+ * @returns -1 and errno on failure.
+ * @param   pszTarget       The symlink target.
+ * @param   pszSymlink      The symlink, the one to be updated.
+ */
+int __libc_back_fsSymlinkWrite(const char *pszTarget, const char *pszSymlink)
+{
+    LIBCLOG_ENTER("pszTarget=%s pszSymlink=%s\n", pszTarget, pszSymlink);
+
+    /*
+     * Validate input.
+     */
+    int cchTarget = strlen(pszTarget);
+    if (cchTarget >= PATH_MAX)
+    {
+        errno = ENAMETOOLONG;
+        LIBC_ASSERTM_FAILED("Target is too long, %d bytes. (%s)\n", cchTarget, pszTarget);
+        LIBCLOG_RETURN_INT(-1);
+    }
+
+    /*
+     * Allocate FEA buffer.
+     */
+    EAOP2   EaOp = {0,0,0};
+    int     cb = cchTarget + sizeof(USHORT) * 2 + sizeof(EA_SYMLINK) + sizeof(FEALIST);
+    EaOp.fpFEA2List = alloca(cb);
+    if (!EaOp.fpFEA2List)
+    {
+        errno = ENOMEM;
+        LIBC_ASSERTM_FAILED("Out of stack! alloca(%d) failed\n", cb);
+        LIBCLOG_RETURN_INT(-1);
+    }
+
+    /*
+     * Setup the EAOP structure.
+     */
+    EaOp.fpFEA2List->cbList = cb;
+    PFEA2   pFEA = &EaOp.fpFEA2List->list[0];
+    pFEA->oNextEntryOffset  = 0;
+    pFEA->cbName            = sizeof(EA_SYMLINK) - 1;
+    pFEA->cbValue           = cchTarget + sizeof(USHORT) * 2;
+    pFEA->fEA               = FEA_NEEDEA;
+    memcpy(pFEA->szName, EA_SYMLINK, sizeof(EA_SYMLINK));
+    PUSHORT pus = (PUSHORT)&pFEA->szName[sizeof(EA_SYMLINK)];
+    pus[0]                  = EAT_ASCII;
+    pus[1]                  = cchTarget;
+    memcpy(&pus[2], pszTarget, cchTarget);
+    int rc = DosSetPathInfo((PCSZ)pszTarget, FIL_QUERYEASIZE, &EaOp, sizeof(EAOP2), 0);
+    if (!rc)
+        LIBCLOG_RETURN_INT(0);
+
+    _sys_set_errno(rc);
+    LIBCLOG_RETURN_INT(-1);
+}
+#endif
+
+
+/**
+ * Creates a symlink.
+ *
+ * @returns 0 on success.
+ * @returns -1 and errno on failure.
+ * @param   pszTarget       The symlink target.
+ * @param   pszSymlink      The symlink, the one to be updated.
+ */
+int __libc_Back_fsSymlinkCreate(const char *pszTarget, const char *pszSymlink)
+{
+    LIBCLOG_ENTER("pszTarget=%s pszSymlink=%s\n", pszTarget, pszSymlink);
+
+    /*
+     * Validate input.
+     */
+    int cchTarget = strlen(pszTarget);
+    if (cchTarget >= PATH_MAX)
+    {
+        errno = ENAMETOOLONG;
+        LIBC_ASSERTM_FAILED("Target is too long, %d bytes. (%s)\n", cchTarget, pszTarget);
+        LIBCLOG_RETURN_INT(-1);
+    }
+
+    /*
+     * Allocate FEA buffer.
+     */
+    EAOP2   EaOp = {0,0,0};
+    int     cb = cchTarget + sizeof(USHORT) * 2 + sizeof(EA_SYMLINK) + sizeof(FEALIST);
+    EaOp.fpFEA2List = alloca(cb);
+    if (!EaOp.fpFEA2List)
+    {
+        errno = ENOMEM;
+        LIBC_ASSERTM_FAILED("Out of stack! alloca(%d) failed\n", cb);
+        LIBCLOG_RETURN_INT(-1);
+    }
+
+    /*
+     * Setup the EAOP structure.
+     */
+    EaOp.fpFEA2List->cbList = cb;
+    PFEA2   pFEA = &EaOp.fpFEA2List->list[0];
+    pFEA->oNextEntryOffset  = 0;
+    pFEA->cbName            = sizeof(EA_SYMLINK) - 1;
+    pFEA->cbValue           = cchTarget + sizeof(USHORT) * 2;
+    pFEA->fEA               = FEA_NEEDEA;
+    memcpy(pFEA->szName, EA_SYMLINK, sizeof(EA_SYMLINK));
+    PUSHORT pus = (PUSHORT)&pFEA->szName[sizeof(EA_SYMLINK)];
+    pus[0]                  = EAT_ASCII;
+    pus[1]                  = cchTarget;
+    memcpy(&pus[2], pszTarget, cchTarget);
+    HFILE   hf = -1;
+    ULONG   ul = 0;
+    int rc = DosOpen((PCSZ)pszSymlink, &hf, &ul, cchTarget + 1, FILE_NORMAL,
+                     OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_FAIL_IF_EXISTS,
+                     OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE,
+                     &EaOp);
+    if (!rc)
+    {
+        DosWrite(hf, EA_SYMLINK "=", sizeof(EA_SYMLINK "=") - 1, &ul);
+        DosWrite(hf, pszTarget, cchTarget + 1, &ul);
+        DosClose(hf);
+        LIBCLOG_RETURN_INT(0);
+    }
+
+    _sys_set_errno(rc);
+    LIBCLOG_RETURN_INT(-1);
+}
+
+
+
+/**
+ * Updates the global unix root stuff.
+ * Assumes caller have locked the fs stuff.
+ *
+ * @param   pszUnixRoot     The new unix root. Fully resolved and existing.
+ */
+void __libc_back_fsUpdateUnixRoot(const char *pszUnixRoot)
+{
+    gaRewriteRule.pszTo = "/";
+    gaRewriteRule.cchTo = 1;
+
+    int cch = strlen(pszUnixRoot);
+    memcpy(__libc_gszUnixRoot, pszUnixRoot, cch + 1);
+    __libc_gcchUnixRoot = cch;
+}
+
+
+/**
+ * Cleans up a path specifier a little bit.
+ * This includes removing duplicate slashes, uncessary single dots, and
+ * trailing slashes.
+ *
+ * @returns Number of bytes in the clean path.
+ * @param   pszPath     The path to cleanup.
+ */
+static int fsCleanPath(char *pszPath)
+{
+    /*
+     * Change to '/' and remove duplicates.
+     */
+    int     fUnc = 0;
+    char   *pszSrc = pszPath;
+    char   *pszTrg = pszPath;
+    if (    (pszPath[0] == '\\' || pszPath[0] == '/')
+        &&  (pszPath[1] == '\\' || pszPath[1] == '/'))
+    {   /* Skip first slash in a unc path. */
+        pszSrc++;
+        *pszTrg++ = '/';
+        fUnc = 1;
+    }
+
+    for (;;)
+    {
+        char ch = *pszSrc++;
+        if (ch == '/' || ch == '\\')
+        {
+            *pszTrg++ = '/';
+            for (;;)
+            {
+                do  ch = *pszSrc++;
+                while (ch == '/' || ch == '\\');
+
+                /* Remove '/./' and '/.'. */
+                if (ch != '.' || (*pszSrc && *pszSrc != '/' && *pszSrc != '\\'))
+                    break;
+            }
+        }
+        *pszTrg = ch;
+        if (!ch)
+            break;
+        pszTrg++;
+    }
+
+    /*
+     * Remove trailing slash.
+     */
+    int cch = pszTrg - pszPath;
+    if (cch > 1 && pszTrg[-1] == '/' && pszTrg[-2] != ':' && pszTrg[-2] != '/')
+        pszPath[--cch] = '\0';
+
+    return cch;
+}
+
+
+/**
+ * Resolves and verifies the user path to a native path.
+ *
+ * @returns 0 on success.
+ * @returns -1 and errno on failure.
+ * @param   pszUserPath     The user path.
+ * @param   pszNativePath   Where to store the native path.
+ * @param   cchNativePath   Size of the buffer for the native path.
+ * @param   fVerifyLast     Whether or not to verify the last component of the path.
+ * @param   pfInUnixTree    Whether or not the path is in the unix tree. Optional.
+ *
+ * @remark  For correct operation the caller must lock the FS stuff.
+ */
+int __libc_back_fsResolve(const char *pszUserPath, int fVerifyLast, char *pszNativePath, size_t cchNativePath, int *pfInUnixTree)
+{
+    if (!__libc_gfNoUnix)
+        return fsResolveUnix(pszUserPath, fVerifyLast, pszNativePath, cchNativePath, pfInUnixTree);
+
+    /* __libc_back_fsResolveOS2(): */
+
+    LIBCLOG_ENTER("pszUserPath=%p:{%s} fVerifyLast=%d pszNativePath=%p cchNativePath=%d pfInUnixTree=%p\n",
+                  (void *)pszUserPath, pszUserPath, fVerifyLast, (void *)pszNativePath, cchNativePath, (void *)pfInUnixTree);
+
+    /*
+     * Apply rewrite rule.
+     */
+    int cch = __libc_PathRewrite(pszUserPath, pszNativePath, sizeof(pszNativePath));
+    if (cch < 0)
+    {
+        errno = EINVAL;
+        LIBCLOG_RETURN_INT(-1);
+    }
+    if (cch == 0)
+    {
+        cch = strlen(pszUserPath);
+        if (cch >= cchNativePath)
+        {
+            errno = EINVAL;
+            LIBCLOG_RETURN_INT(-1);
+        }
+        memcpy(pszNativePath, pszUserPath, cch + 1);
+    }
+
+    /*
+     * Convert slashes.
+     */
+    char *psz = strchr(pszNativePath, '/');
+    while (psz)
+    {
+        *psz++ = '\\';
+        psz = strchr(psz, '/');
+    }
+
+    /** @todo Validate the path? hopefully not necessary. */
+
+    if (pfInUnixTree)
+        *pfInUnixTree = 0;
+
+    LIBCLOG_RETURN_INT(0);
+}
+
+
+/**
+ * Resolves and verifies the user path to a native path.
+ *
+ * @returns 0 on success.
+ * @returns -1 and errno on failure.
+ * @param   pszUserPath     The user path.
+ * @param   pszNativePath   Where to store the native path.
+ * @param   cchNativePath   Size of the buffer for the native path.
+ * @param   fVerifyLast     Whether or not to verify the last component of the path.
+ * @param   pfInUnixTree    Whether or not the path is in the unix tree. Optional.
+ *
+ * @remark  For correct operation the caller must lock the FS stuff.
+ */
+static int fsResolveUnix(const char *pszUserPath, int fVerifyLast, char *pszNativePath, size_t cchNativePath, int *pfInUnixTree)
+{
+    LIBCLOG_ENTER("pszUserPath=%p:{%s} fVerifyLast=%d pszNativePath=%p cchNativePath=%d *pfInUnixTree=%p\n",
+                  (void *)pszUserPath, pszUserPath, fVerifyLast, (void *)pszNativePath, cchNativePath, (void *)pfInUnixTree);
+    const char *pszUserPathIn = pszUserPath;
+    char        szTmp[PATH_MAX];
+    char        achBuffer[PATH_MAX + 96]; /* Assumes 4 bytes aligned on stack! */
+    unsigned    cLoopsLeft = 8;
+    int         fInUnixTree = __libc_gfInUnixTree;
+    int         rcRet = ENAMETOOLONG;
+    FS_VAR()
+    FS_SAVE_LOAD();
+    while (cLoopsLeft-- > 0)
+    {
+        /*
+         * Determin root slash position.
+         */
+        int iRoot;
+        if (pszUserPath[0] == '/' || pszUserPath[0] == '\\')
+        {
+            iRoot = 0;
+            fInUnixTree = __libc_gfInUnixTree;
+        }
+        else if (pszUserPath[0] && pszUserPath[1] == ':'
+                 && (pszUserPath[2] == '/' || pszUserPath[2] == '\\'))
+        {
+            iRoot = 2;
+            fInUnixTree = 0;
+        }
+        /*
+         * No root slash? Make one!
+         * This can only happen in the first pass (unless something BAD happens of course).
+         */
+        else
+        {
+            ULONG   ul;
+            int     rc;
+            if (pszUserPath[1] != ':')
+            {
+                /*
+                 * Current drive.
+                 */
+                ULONG ulDisk;
+                rc = DosQueryCurrentDisk(&ulDisk, &ul);
+                szTmp[0] = ulDisk + 'A' - 1;
+                ul = sizeof(szTmp) - 2;
+                if (!rc)
+                    rc = DosQueryCurrentDir(0, (PSZ)&szTmp[3], &ul);
+                iRoot = __libc_gfInUnixTree ? __libc_gcchUnixRoot : 2;
+                /* fInUnixTree remains whatever it is */
+            }
+            else
+            {
+                /*
+                 * Drive letter but no root slash.
+                 */
+                szTmp[0] = pszUserPath[0];
+                ul = sizeof(szTmp) - 2;
+                rc = DosQueryCurrentDir(pszUserPath[0] - (pszUserPath[0] >= 'A' && pszUserPath[0] <= 'Z' ? 'A' : 'a') + 1, (PSZ)&szTmp[3], &ul);
+                pszUserPath += 2;
+                iRoot = 2;
+                fInUnixTree = 0;
+            }
+            /* failed? */
+            if (rc)
+            {
+                rcRet = __libc_native2errno(rc);
+                break;
+            }
+
+            /*
+             * Add the path stuff from the user.
+             */
+            szTmp[1] = ':';
+            szTmp[2] = '/';
+            int cch = strlen(szTmp);
+            szTmp[cch++] = '/';
+            int cchUserPath = strlen(pszUserPath) + 1;
+            if (cch + cchUserPath > PATH_MAX)
+                break;
+            memcpy(&szTmp[cch], pszUserPath, cchUserPath);
+            pszUserPath = memcpy(achBuffer, szTmp, cch + cchUserPath);
+        }
+
+        /*
+         * Verify any drive specifier.
+         */
+        if (    pszUserPath[1] == ':'
+            &&  !(  (*pszUserPath >= 'A' && *pszUserPath <= 'Z')
+                  ||(*pszUserPath >= 'a' && *pszUserPath <= 'z')) )
+        {
+            rcRet = ENOENT;
+            break;
+        }
+
+        /*
+         * Apply rewrite rules.
+         * Path now goes to szTmp buffer.
+         */
+        int cchTmp = __libc_PathRewrite(pszUserPath, szTmp, sizeof(szTmp));
+        if (cchTmp)
+        {
+            /*
+             * Redetermin root because of rewrite.
+             * Assume qualified path from rewrite!
+             */
+            iRoot = szTmp[1] == ':' ? 2 : 1;
+        }
+        else if (!cchTmp)
+        {
+            cchTmp = strlen(pszUserPath);
+            if (cchTmp + 2 > sizeof(szTmp))
+                break;
+            memcpy(szTmp, pszUserPath, cchTmp + 1);
+        }
+        else
+        {
+            rcRet = EINVAL;
+            break;
+        }
+
+        /*
+         * Check if special OS/2 name.
+         */
+        if (pszUserPath[0] == '/' || pszUserPath[0] == '\\')
+        {
+            int fDone = 0;
+            if (    (pszUserPath[1] == 'p' || pszUserPath[1] == 'P')
+                &&  (pszUserPath[2] == 'i' || pszUserPath[2] == 'I')
+                &&  (pszUserPath[3] == 'p' || pszUserPath[3] == 'P')
+                &&  (pszUserPath[4] == 'e' || pszUserPath[4] == 'E')
+                &&  (pszUserPath[5] == '/' || pszUserPath[5] == '\\'))
+                fDone = 1;
+            else if ((pszUserPath[1]== 'd' || pszUserPath[1] == 'D')
+                &&  (pszUserPath[2] == 'e' || pszUserPath[2] == 'E')
+                &&  (pszUserPath[3] == 'v' || pszUserPath[3] == 'V')
+                &&  (pszUserPath[4] == '/' || pszUserPath[4] == '\\')
+                )
+            {
+                PFSQBUFFER2 pfsqb = (PFSQBUFFER2)&szTmp[0];
+                ULONG       cb = sizeof(szTmp);
+                fDone = !DosQueryFSAttach((PCSZ)pszUserPath, 0, FSAIL_QUERYNAME, pfsqb, &cb);
+            }
+
+            /* If it was, copy path to szTmp and go to exit code. */
+            if (fDone)
+            {
+                int cch = strlen(szTmp) + 1;
+                if (cch <= PATH_MAX)
+                {
+                    memcpy(szTmp, pszUserPath, cchTmp + 1);
+                    fsCleanPath(&szTmp[0]);
+                    rcRet = 0;
+                }
+                break;
+            }
+        }
+
+        /*
+         * Remove excessive slashing and convert all slashes to '/'.
+         */
+        cchTmp = fsCleanPath(&szTmp[0]);
+
+        /*
+         * Expand unix root.
+         */
+        if (szTmp[0] == '/' && szTmp[1] != '/' && __libc_gcchUnixRoot)
+        {
+            iRoot = __libc_gcchUnixRoot;
+            if (cchTmp + iRoot >= PATH_MAX)
+                break;
+            memcpy(achBuffer, szTmp, cchTmp + 1);
+            memcpy(szTmp, __libc_gszUnixRoot, iRoot);
+            memcpy(&szTmp[iRoot], szTmp, cchTmp + 1);
+            fInUnixTree = 1;
+        }
+
+
+        /*
+         * Check all directory components.
+         *
+         * We actually don't bother checking if they're directories, only that
+         * they exists. This shouldn't matter much since any operation on the
+         * next/final component will assume the previous one being a directory.
+         *
+         * While walking we will clean up all use of '.' and '..' so we'll end
+         * up with an optimal path in the end.
+         */
+
+        /*
+         * Find the end of the first component (start and end + 1).
+         */
+        char *pszPrev;
+        char *psz;
+        if (szTmp[0] == '/' && szTmp[1] == '/')
+        {
+            /* UNC - skip past the server name. */
+            psz = strchr(&szTmp[2], '/');
+            if (!psz)
+            {
+                rcRet = ENOENT;
+                break;
+            }
+            pszPrev = ++psz;
+            iRoot = psz - &szTmp[0];    /* Unix root cannot be UNC. */
+            psz = strchr(psz, '/');
+        }
+        else
+        {
+            if (szTmp[0] == '/')
+                pszPrev = &szTmp[1];
+            else
+            {
+                pszPrev = &szTmp[3];
+                LIBC_ASSERT(szTmp[1] == ':' && szTmp[2] == '/');
+            }
+            psz = strchr(pszPrev, '/');
+        }
+        LIBC_ASSERT(iRoot >= pszPrev - &szTmp[0]);
+        /* If only one component, we'll check if the fVerifyLast was requested. */
+        if (!psz && fVerifyLast)
+            psz = strchr(szTmp, '\0');
+
+        /*
+         * Walking loop.
+         */
+        int fDone = 1;
+        while (psz)
+        {
+            char chSlash = *psz;
+            *psz = '\0';
+
+            /*
+             * Kill . and .. specs.
+             */
+            if (    pszPrev[0] == '.'
+                &&  (   pszPrev[1] == '\0'
+                     || (pszPrev[1] == '.' && pszPrev[2] == '\0') ) )
+            {
+                if (    pszPrev[1] != '\0'
+                    &&  (uintptr_t)(pszPrev - &szTmp[0]) != iRoot + 1)
+                {
+                    for (pszPrev -= 2; *pszPrev != '/'; pszPrev--)
+                        /* noop */;
+                    pszPrev++;
+                }
+                *psz = chSlash;
+                memmove(pszPrev - 1, psz, cchTmp - (psz - &szTmp[0]) + 1);
+                cchTmp -= psz - pszPrev - 1;
+
+                /*
+                 * Next path component and restart loop.
+                 */
+                if (!chSlash)
+                {
+                    rcRet = 0;
+                    break;
+                }
+                psz = pszPrev;
+                while (*psz != '/')
+                {
+                    if (!*psz)
+                    {
+                        if (!fVerifyLast)
+                        {
+                            rcRet = 0;
+                            psz = NULL;
+                        }
+                        break;
+                    }
+                    psz++;
+                }
+                continue;
+            }
+
+            /*
+             * Query the symlink EA value and at the same time verifying the path
+             * component.
+             */
+            EAOP2   EaOp;
+            EaOp.fpGEA2List = (PGEA2LIST)&gSymlinkGEA2List;
+            EaOp.fpFEA2List = (PFEA2LIST)achBuffer;
+            EaOp.oError     = 0;
+            EaOp.fpFEA2List->cbList = sizeof(achBuffer);
+            LIBC_ASSERT(!((uintptr_t)&achBuffer[0] & 4));
+            int rc = DosQueryPathInfo((PCSZ)&szTmp[0], FIL_QUERYEASFROMLIST, &EaOp, sizeof(EaOp));
+            if (rc)
+            {
+                LIBCLOG_MSG("DosQueryPathInfo(%s,,,) -> %d resolving %s\n", szTmp, rc, pszUserPathIn);
+                rcRet = rc == ERROR_FILE_NOT_FOUND && chSlash ? ENOTDIR : __libc_native2errno(rc);
+                break;
+            }
+
+            /*
+             * Did we find any symlink EA?
+             */
+            if (EaOp.fpFEA2List->list[0].cbValue)
+            {
+                /* Validate the EA. */
+                PUSHORT pusType = (PUSHORT)((char *)&EaOp.fpFEA2List->list[1] + EaOp.fpFEA2List->list[0].cbName);
+                char   *pszSymlink = (char *)&pusType[2];
+                if (    pusType[0] != EAT_ASCII
+                    ||  pusType[1] > EaOp.fpFEA2List->list[0].cbValue
+                    ||  !pusType[1]
+                    ||  !*pszSymlink)
+                {
+                    LIBC_ASSERTM_FAILED("Invalid symlink EA! type=%x len=%d cbValue=%d *pszSymlink=%c\n",
+                                        pusType[0], pusType[1], EaOp.fpFEA2List->list[0].cbValue, *pszSymlink);
+                    rcRet = EFTYPE;
+                    break;
+                }
+
+                /* Cleanup the symlink and find it's length. */
+                pszSymlink[pusType[1]] = '\0';
+                int cchSymlink = fsCleanPath(pszSymlink);
+
+                /* Merge symlink with the path. */
+                int cchLeft = cchTmp - (psz - &szTmp[0]);
+                if (*pszSymlink == '/' || *pszSymlink == '\\' || pszSymlink[1] == ':')
+                {
+                    /*
+                     * Replace the path up to the current point with the symlink,
+                     */
+                    if (cchSymlink + cchLeft + 2 >= PATH_MAX)
+                        break;
+                    if (cchLeft)
+                    {
+                        if (pszSymlink[cchSymlink - 1] != '/')
+                            pszSymlink[cchSymlink++] = '/';
+                        memcpy(&pszSymlink[cchSymlink], psz + 1, cchLeft + 1);
+                    }
+
+                    /* restart the whole shebang. */
+                    pszUserPath = pszSymlink;
+                    fDone = 0;
+                    break;
+                }
+                else
+                {
+                    /*
+                     * Squeeze the symlink in instead of the current path component.
+                     */
+                    if (cchSymlink + cchTmp + 2 >= PATH_MAX)
+                        break;
+                    if (cchLeft)
+                    {
+                        pszSymlink[cchSymlink++] = '/';
+                        memcpy(&pszSymlink[cchSymlink], psz + 1, cchLeft + 1);
+                        cchSymlink += cchLeft;
+                    }
+                    memcpy(pszPrev, pszSymlink, cchSymlink + 1);
+
+                    psz = pszPrev;
+                    while (*psz != '/')
+                    {
+                        if (!*psz)
+                        {
+                            if (!fVerifyLast)
+                            {
+                                rcRet = 0;
+                                psz = NULL;
+                            }
+                            break;
+                        }
+                        psz++;
+                    }
+                    continue;
+                }
+            }
+
+            /*
+             * Next path component.
+             */
+            *psz++ = chSlash;
+            if (!chSlash)
+            {
+                rcRet = 0;
+                break;
+            }
+            pszPrev = psz;
+            while (*psz != '/')
+            {
+                if (!*psz)
+                {
+                    if (!fVerifyLast)
+                    {
+                        rcRet = 0;
+                        psz = NULL;
+                    }
+                    break;
+                }
+                psz++;
+            }
+        } /* inner loop */
+
+        if (fDone)
+            break;
+    } /* outer loop */
+
+
+    /*
+     * Copy the resolved path the the caller buffer.
+     */
+    if (!rcRet)
+    {
+        int cch = strlen(szTmp) + 1;
+        if (cch == 1 || (cch == 3 && szTmp[1] == ':'))
+        {
+            szTmp[cch - 1] = '/';
+            szTmp[cch++] = '\0';
+        }
+        if (cch <= cchNativePath)
+        {
+            memcpy(pszNativePath, szTmp, cch);
+            if (pfInUnixTree)
+                *pfInUnixTree = fInUnixTree;
+            LIBCLOG_RETURN_INT(0);
+        }
+        rcRet = ENAMETOOLONG;
+    }
+
+    /* failure */
+    errno = rcRet;
+    LIBCLOG_RETURN_INT(-1);
+    pszUserPathIn = pszUserPathIn;
+}
Index: trunk/src/emx/src/lib/sys/fs.h
===================================================================
--- trunk/src/emx/src/lib/sys/fs.h	(revision 1518)
+++ trunk/src/emx/src/lib/sys/fs.h	(revision 1518)
@@ -0,0 +1,106 @@
+/* $Id$ */
+/** @file
+ *
+ * LIBC SYS Backend - file system stuff.
+ *
+ * Copyright (c) 2004 knut st. osmundsen <bird@innotek.de>
+ *
+ *
+ * This file is part of InnoTek LIBC.
+ *
+ * InnoTek LIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * InnoTek LIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with InnoTek LIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef __fs_h__
+#define __fs_h__
+
+#include <sys/cdefs.h>
+#include <sys/syslimits.h>
+#include <sys/types.h>
+
+
+__BEGIN_DECLS
+
+/** Indicator whether or not we're in the unix tree. */
+extern int  __libc_gfInUnixTree;
+/** Length of the unix root if the unix root is official. */
+extern int  __libc_gcchUnixRoot;
+/** The current unix root. */
+extern char __libc_gszUnixRoot[PATH_MAX];
+
+
+/**
+ * Request the owner ship of the FS mutex.
+ *
+ * @returns 0 on success.
+ * @returns -1 and errno on failure.
+ */
+int __libc_back_fsMutexRequest(void);
+
+/**
+ * Releases the owner ship of the FS mutex obtained by __libc_back_fsMutexRelease().
+ */
+void __libc_back_fsMutexRelease(void);
+
+/**
+ * Updates the global unix root stuff.
+ * Assumes caller have locked the fs stuff.
+ *
+ * @param   pszUnixRoot     The new unix root. Fully resolved and existing.
+ */
+void __libc_back_fsUpdateUnixRoot(const char *pszUnixRoot);
+
+/**
+ * Resolves and verifies the user path to a native path.
+ *
+ * @returns 0 on success.
+ * @returns -1 and errno on failure.
+ * @param   pszUserPath     The user path.
+ * @param   pszNativePath   Where to store the native path.
+ * @param   cchNativePath   Size of the buffer for the native path.
+ * @param   fVerify         Whether or not to verify that the path exists.
+ * @param   pfDrvSpec       Where to store the drive spec indicator. Optional.
+ *                          *pfDrvSpec will be set if the pszUserPath or any symbolic link
+ *                          contained pszUserPath referenced contained a drive letter specifier.
+ */
+int __libc_back_fsResolve(const char *pszUserPath, int fVerify, char *pszNativePath, size_t cchNativePath, int *pfDrvSpec);
+
+/**
+ * Creates a symlink.
+ *
+ * @returns 0 on success.
+ * @returns -1 and errno on failure.
+ * @param   pszTarget       The symlink target.
+ * @param   pszSymlink      The symlink, the one to be updated.
+ */
+int __libc_Back_fsSymlinkCreate(const char *pszTarget, const char *pszSymlink);
+
+/**
+ * Reads a symbolic link.
+ *
+ * @returns 0 on success.
+ * @returns -1 and errno on failure.
+ * @param   pszPath     The symlink to read.
+ * @param   pszBuf      Where to read to.
+ * @param   cchBuf      Size of the buffer.
+ */
+int __libc_Back_fsSymlinkRead(const char *pszPath, char *pszBuf, size_t cchBuf);
+
+
+
+__END_DECLS
+
+#endif
