/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



#define UNICODE
#define _UNICODE
#define _WIN32_WINNT_0x0500
#include "systools/win32/uwinapi.h"

#include "osl/file.h"

#include "file_url.h"
#include "file_error.h"

#include "path_helper.hxx"

#include "osl/diagnose.h"
#include "osl/time.h"
#include "rtl/alloc.h"
#include "rtl/ustring.hxx"

#include <tchar.h>
#ifdef __MINGW32__
#include <ctype.h>
#endif 

//#####################################################
#define ELEMENTS_OF_ARRAY(arr) (sizeof(arr)/(sizeof((arr)[0])))

static const wchar_t UNC_PREFIX[] = L"\\\\";
static const wchar_t BACKSLASH = '\\';
static const wchar_t SLASH = '/';

//#####################################################
extern "C" BOOL TimeValueToFileTime(const TimeValue *cpTimeVal, FILETIME *pFTime)
{
	SYSTEMTIME	BaseSysTime;
	FILETIME	BaseFileTime;
	FILETIME	FTime;
	__int64		localTime;
	BOOL		fSuccess = FALSE;

	BaseSysTime.wYear         = 1970;
	BaseSysTime.wMonth        = 1;
    BaseSysTime.wDayOfWeek    = 0;
    BaseSysTime.wDay          = 1;
    BaseSysTime.wHour         = 0;
    BaseSysTime.wMinute       = 0;
    BaseSysTime.wSecond       = 0;
    BaseSysTime.wMilliseconds = 0;

	if (cpTimeVal==NULL)
		return fSuccess;

	if ( SystemTimeToFileTime(&BaseSysTime, &BaseFileTime) )
	{
		__int64 timeValue;
		localTime=cpTimeVal->Seconds*(__int64)10000000+cpTimeVal->Nanosec/100;
		*(__int64 *)&FTime=localTime;
		fSuccess = 0 <= (timeValue= *((__int64 *)&BaseFileTime) + *((__int64 *) &FTime));
		if (fSuccess)
			*(__int64 *)pFTime=timeValue;
	}
	return fSuccess;
}

//#####################################################
extern "C" BOOL FileTimeToTimeValue(const FILETIME *cpFTime, TimeValue *pTimeVal)
{
	SYSTEMTIME	BaseSysTime;
	FILETIME	BaseFileTime;
	BOOL		fSuccess = FALSE;	/* Assume failure */

	BaseSysTime.wYear         = 1970;
	BaseSysTime.wMonth        = 1;
    BaseSysTime.wDayOfWeek    = 0;
    BaseSysTime.wDay          = 1;
    BaseSysTime.wHour         = 0;
    BaseSysTime.wMinute       = 0;
    BaseSysTime.wSecond       = 0;
    BaseSysTime.wMilliseconds = 0;

	if ( SystemTimeToFileTime(&BaseSysTime, &BaseFileTime) )
	{
		__int64		Value;

		fSuccess = 0 <= (Value = *((__int64 *)cpFTime) - *((__int64 *)&BaseFileTime));

		if ( fSuccess )
		{
			pTimeVal->Seconds  = (unsigned long) (Value / 10000000L);
			pTimeVal->Nanosec  = (unsigned long)((Value % 10000000L) * 100);
		}
	}
	return fSuccess;
}

//#####################################################
namespace /* private */
{       
    //#####################################################
	struct Component
    {       
        Component() : 
            begin_(0), end_(0)
        {}

        bool isPresent() const
        { return (static_cast<sal_IntPtr>(end_ - begin_) > 0); }        
        
        const sal_Unicode* begin_;
        const sal_Unicode* end_;
    };
                    
    //#####################################################
    struct UNCComponents
    {                        
        Component server_;
        Component share_;
        Component resource_;
    };
    
    //#####################################################
    inline bool is_UNC_path(const sal_Unicode* path)
    { return (0 == wcsncmp(UNC_PREFIX, reinterpret_cast<LPCWSTR>(path), ELEMENTS_OF_ARRAY(UNC_PREFIX) - 1)); }   
    
    //#####################################################
    inline bool is_UNC_path(const rtl::OUString& path)
    { return is_UNC_path(path.getStr()); }
    
    //#####################################################
    void parse_UNC_path(const sal_Unicode* path, UNCComponents* puncc)
    {                
        OSL_PRECOND(is_UNC_path(path), "Precondition violated: No UNC path");
        OSL_PRECOND(rtl_ustr_indexOfChar(path, SLASH) == -1, "Path must not contain slashes");
                        
        const sal_Unicode* pend = path + rtl_ustr_getLength(path);
        const sal_Unicode* ppos = path + 2;
                
        puncc->server_.begin_ = ppos;                                    
        while ((ppos < pend) && (*ppos != BACKSLASH))
            ppos++;
        
        puncc->server_.end_ = ppos;
        
        if (BACKSLASH == *ppos)    
        {                                                
            puncc->share_.begin_ = ++ppos;                                
            while ((ppos < pend) && (*ppos != BACKSLASH))
                ppos++;
                
            puncc->share_.end_ = ppos;
                                        
            if (BACKSLASH == *ppos)
            {                                                                                                                                                                               
                puncc->resource_.begin_ = ++ppos;                                                        
                while (ppos < pend)
                    ppos++;
                
                puncc->resource_.end_ = ppos;
            }                                 
        }     
        
        OSL_POSTCOND(puncc->server_.isPresent() && puncc->share_.isPresent(), \
        "Postcondition violated: Invalid UNC path detected");   
    }
    
    //#####################################################
    void parse_UNC_path(const rtl::OUString& path, UNCComponents* puncc)
    { parse_UNC_path(path.getStr(), puncc); }
    
                                   
    //#####################################################
    bool has_path_parent(const sal_Unicode* path)
    {                
		// Has the given path a parent or are we already there, 
		// e.g. 'c:\' or '\\server\share\'?

        bool has_parent = false;
        if (is_UNC_path(path))
        {
            UNCComponents unc_comp;
            parse_UNC_path(path, &unc_comp);                        
            has_parent = unc_comp.resource_.isPresent();
        }
        else 
        {
            has_parent = !osl::systemPathIsLogicalDrivePattern(path);
        }
        return has_parent;
    }
    
    //#####################################################
    inline bool has_path_parent(const rtl::OUString& path)
    { return has_path_parent(path.getStr()); }
    
} // end namespace private 

//#####################################################
// volume handling functions
//#####################################################

//#####################################################
oslFileError SAL_CALL osl_unmountVolumeDevice( oslVolumeDeviceHandle Handle )
{
	if ( Handle )
		return osl_File_E_None;
	else
		return osl_File_E_INVAL;
}

//#####################################################
oslFileError SAL_CALL osl_automountVolumeDevice( oslVolumeDeviceHandle Handle )
{
	if ( Handle )
		return osl_File_E_None;
	else
		return osl_File_E_INVAL;
}

//#####################################################
oslFileError SAL_CALL osl_acquireVolumeDeviceHandle( oslVolumeDeviceHandle Handle )
{
	if ( Handle )
	{
		rtl_uString_acquire( (rtl_uString *)Handle );
		return osl_File_E_None;
	}
	else
		return osl_File_E_INVAL;
}

//#####################################################
oslFileError SAL_CALL osl_releaseVolumeDeviceHandle( oslVolumeDeviceHandle Handle )
{
	if ( Handle )
	{
		rtl_uString_release( (rtl_uString *)Handle );
		return osl_File_E_None;
	}
	else
		return osl_File_E_INVAL;
}

//#####################################################
oslFileError SAL_CALL osl_getVolumeDeviceMountPath( oslVolumeDeviceHandle Handle, rtl_uString **pstrPath )
{
	if ( Handle && pstrPath )
	{
		rtl_uString_assign( pstrPath, (rtl_uString *)Handle );
		return osl_File_E_None;
	}
	else
		return osl_File_E_INVAL;
}

//##################################################################
// directory handling functions
//##################################################################

#define DIRECTORYITEM_DRIVE		0
#define DIRECTORYITEM_FILE		1
#define DIRECTORYITEM_SERVER	2

struct DirectoryItem_Impl
{
	UINT uType;
	union {
		WIN32_FIND_DATA	FindData;
		TCHAR			cDriveString[MAX_PATH];
	};
    rtl_uString*    m_pFullPath;
	BOOL			bFullPathNormalized;
	int				nRefCount;
};

//#####################################################

#define	DIRECTORYTYPE_LOCALROOT	    0
#define	DIRECTORYTYPE_NETROOT		1
#define	DIRECTORYTYPE_NETRESORCE	2
#define	DIRECTORYTYPE_FILESYSTEM	3

struct Directory_Impl
{
	UINT uType;
	union {
		HANDLE	hDirectory;
		HANDLE	hEnumDrives;
	};
    rtl_uString*    m_pDirectoryPath;
};

//##################################################### 

typedef struct tagDRIVEENUM
{
	LPCTSTR	lpIdent;
	TCHAR	cBuffer[/*('Z' - 'A' + 1) * sizeof("A:\\") + 1*/256];
	LPCTSTR	lpCurrent;
} DRIVEENUM, * PDRIVEENUM, FAR * LPDRIVEENUM;

//##################################################### 

static HANDLE WINAPI OpenLogicalDrivesEnum(void)
{
	LPDRIVEENUM	pEnum = (LPDRIVEENUM)HeapAlloc( GetProcessHeap(), 0, sizeof(DRIVEENUM) );
	if ( pEnum )
	{
		DWORD dwNumCopied = GetLogicalDriveStrings( (sizeof(pEnum->cBuffer) - 1) / sizeof(TCHAR), pEnum->cBuffer );

		if ( dwNumCopied && dwNumCopied < sizeof(pEnum->cBuffer) / sizeof(TCHAR) )
		{
			pEnum->lpCurrent = pEnum->cBuffer;
			pEnum->lpIdent = L"tagDRIVEENUM";
		}
		else
		{
			HeapFree( GetProcessHeap(), 0, pEnum );
			pEnum = NULL;
		}
	}
	return pEnum ? (HANDLE)pEnum : INVALID_HANDLE_VALUE;
}

//##################################################### 
static BOOL WINAPI EnumLogicalDrives(HANDLE hEnum, LPTSTR lpBuffer)
{
	BOOL		fSuccess = FALSE;
	LPDRIVEENUM	pEnum = (LPDRIVEENUM)hEnum;

	if ( pEnum )
	{
		int	nLen = _tcslen( pEnum->lpCurrent );

		if ( nLen )
		{
			CopyMemory( lpBuffer, pEnum->lpCurrent, (nLen + 1) * sizeof(TCHAR) );
			pEnum->lpCurrent += nLen + 1;
			fSuccess = TRUE;
		}
		else
			SetLastError( ERROR_NO_MORE_FILES );
	}
	else
		SetLastError( ERROR_INVALID_HANDLE );

	return fSuccess;
}

//##################################################### 
static BOOL WINAPI CloseLogicalDrivesEnum(HANDLE hEnum)
{
	BOOL		fSuccess = FALSE;
	LPDRIVEENUM	pEnum = (LPDRIVEENUM)hEnum;

	if ( pEnum )
	{
		HeapFree( GetProcessHeap(), 0, pEnum );
		fSuccess = TRUE;
	}
	else
		SetLastError( ERROR_INVALID_HANDLE );

	return fSuccess;
}

//#####################################################
typedef struct tagDIRECTORY
{
	HANDLE          hFind;
	WIN32_FIND_DATA aFirstData;
} DIRECTORY, *PDIRECTORY, FAR *LPDIRECTORY;

//#####################################################
static HANDLE WINAPI OpenDirectory( rtl_uString* pPath)
{
	LPDIRECTORY	pDirectory = NULL;

	if ( pPath )
	{
        sal_uInt32 nLen = rtl_uString_getLength( pPath );
        if ( nLen )
        {
            TCHAR* pSuffix = 0;
            sal_uInt32 nSuffLen = 0;

            if ( pPath->buffer[nLen - 1] != L'\\' )
            {
                pSuffix = L"\\*.*";
                nSuffLen = 4;
            }
            else
            {
                pSuffix = L"*.*";
                nSuffLen = 3;
            }

            TCHAR* szFileMask = reinterpret_cast< TCHAR* >( rtl_allocateMemory( sizeof( TCHAR ) * ( nLen + nSuffLen + 1 ) ) );

		    _tcscpy( szFileMask, reinterpret_cast<LPCTSTR>( rtl_uString_getStr( pPath ) ) );
            _tcscat( szFileMask, pSuffix );

            pDirectory = (LPDIRECTORY)HeapAlloc(GetProcessHeap(), 0, sizeof(DIRECTORY));
            pDirectory->hFind = FindFirstFile(szFileMask, &pDirectory->aFirstData);

            if (!IsValidHandle(pDirectory->hFind))		
            {
                if ( GetLastError() != ERROR_NO_MORE_FILES )
                {
                    HeapFree(GetProcessHeap(), 0, pDirectory);
                    pDirectory = NULL;
                }
            }
        }
	}

	return (HANDLE)pDirectory;
}

//#####################################################
BOOL WINAPI EnumDirectory(HANDLE hDirectory, LPWIN32_FIND_DATA pFindData)
{
	BOOL		fSuccess = FALSE;
	LPDIRECTORY	pDirectory = (LPDIRECTORY)hDirectory;

	if ( pDirectory )
	{
		BOOL	fValid;

		do
		{
			if ( pDirectory->aFirstData.cFileName[0] )
			{
				*pFindData = pDirectory->aFirstData;
				fSuccess = TRUE;
				pDirectory->aFirstData.cFileName[0] = 0;
			}
			else if ( IsValidHandle( pDirectory->hFind ) )
				fSuccess = FindNextFile( pDirectory->hFind, pFindData );
			else
			{
				fSuccess = FALSE;
				SetLastError( ERROR_NO_MORE_FILES );
			}

			fValid = fSuccess && _tcscmp( TEXT("."), pFindData->cFileName ) != 0 && _tcscmp( TEXT(".."), pFindData->cFileName ) != 0;

		} while( fSuccess && !fValid );
	}
	else
		SetLastError( ERROR_INVALID_HANDLE );

	return fSuccess;
}

//#####################################################
static BOOL WINAPI CloseDirectory(HANDLE hDirectory)
{
	BOOL		fSuccess = FALSE;
	LPDIRECTORY	pDirectory = (LPDIRECTORY)hDirectory;

	if (pDirectory)
	{
		if (IsValidHandle(pDirectory->hFind))
			fSuccess = FindClose(pDirectory->hFind);

		fSuccess = HeapFree(GetProcessHeap(), 0, pDirectory) && fSuccess;
	}
	else
		SetLastError(ERROR_INVALID_HANDLE);

	return fSuccess;
}

//#####################################################
static oslFileError osl_openLocalRoot(
    rtl_uString *strDirectoryPath, oslDirectory *pDirectory)
{
    rtl_uString		*strSysPath = NULL;
    oslFileError	error;
	    
    if ( !pDirectory )
        return osl_File_E_INVAL;

    *pDirectory = NULL;

    error = _osl_getSystemPathFromFileURL( strDirectoryPath, &strSysPath, sal_False );
    if ( osl_File_E_None == error )
    {
        Directory_Impl	*pDirImpl;

        pDirImpl = reinterpret_cast<Directory_Impl*>(rtl_allocateMemory( sizeof(Directory_Impl)));
        ZeroMemory( pDirImpl, sizeof(Directory_Impl) );
        rtl_uString_newFromString( &pDirImpl->m_pDirectoryPath, strSysPath );

        /* Append backslash if neccessary */

        /* @@@ToDo
           use function ensure backslash
        */
        sal_uInt32 nLen = rtl_uString_getLength( pDirImpl->m_pDirectoryPath );
        if ( nLen && pDirImpl->m_pDirectoryPath->buffer[nLen - 1] != L'\\' )
        {
            rtl_uString* pCurDir = 0;
            rtl_uString* pBackSlash = 0;

            rtl_uString_assign( &pCurDir, pDirImpl->m_pDirectoryPath );
            rtl_uString_newFromAscii( &pBackSlash, "\\" );
            rtl_uString_newConcat( &pDirImpl->m_pDirectoryPath, pCurDir, pBackSlash );
            rtl_uString_release( pBackSlash );
            rtl_uString_release( pCurDir );
        }

        pDirImpl->uType = DIRECTORYTYPE_LOCALROOT;
        pDirImpl->hEnumDrives = OpenLogicalDrivesEnum();

        /* @@@ToDo
           Use IsValidHandle(...)
        */
        if ( pDirImpl->hEnumDrives != INVALID_HANDLE_VALUE )
        {
            *pDirectory = (oslDirectory)pDirImpl;
            error = osl_File_E_None;
        }
        else
        {
            if ( pDirImpl )
            {
                if ( pDirImpl->m_pDirectoryPath )
                {
                    rtl_uString_release( pDirImpl->m_pDirectoryPath );
                    pDirImpl->m_pDirectoryPath = 0;
                }

                rtl_freeMemory(pDirImpl);
                pDirImpl = 0;
            }

            error = oslTranslateFileError( GetLastError() );
        }

        rtl_uString_release( strSysPath );
    }
    return error;
}

//#####################################################
static oslFileError SAL_CALL osl_openFileDirectory(
	rtl_uString *strDirectoryPath, oslDirectory *pDirectory)
{
	oslFileError error = osl_File_E_None;
	    
	if ( !pDirectory )
		return osl_File_E_INVAL;
	*pDirectory = NULL;

	Directory_Impl *pDirImpl = reinterpret_cast<Directory_Impl*>(rtl_allocateMemory(sizeof(Directory_Impl)));
    ZeroMemory( pDirImpl, sizeof(Directory_Impl) );
    rtl_uString_newFromString( &pDirImpl->m_pDirectoryPath, strDirectoryPath );

	/* Append backslash if neccessary */

	/* @@@ToDo
	   use function ensure backslash
	*/
    sal_uInt32 nLen = rtl_uString_getLength( pDirImpl->m_pDirectoryPath );
    if ( nLen && pDirImpl->m_pDirectoryPath->buffer[nLen - 1] != L'\\' )
    {
        rtl_uString* pCurDir = 0;
        rtl_uString* pBackSlash = 0;

        rtl_uString_assign( &pCurDir, pDirImpl->m_pDirectoryPath );
        rtl_uString_newFromAscii( &pBackSlash, "\\" );
        rtl_uString_newConcat( &pDirImpl->m_pDirectoryPath, pCurDir, pBackSlash );
        rtl_uString_release( pBackSlash );
        rtl_uString_release( pCurDir );
    }


	pDirImpl->uType = DIRECTORYTYPE_FILESYSTEM;
	pDirImpl->hDirectory = OpenDirectory( pDirImpl->m_pDirectoryPath );

	if ( !pDirImpl->hDirectory )
	{
		error = oslTranslateFileError( GetLastError() );

        if ( pDirImpl->m_pDirectoryPath )
        {
            rtl_uString_release( pDirImpl->m_pDirectoryPath );
            pDirImpl->m_pDirectoryPath = 0;
        }

		rtl_freeMemory(pDirImpl), pDirImpl = 0;
	}

	*pDirectory = (oslDirectory)(pDirImpl);
	return error;
}

//#####################################################
static oslFileError SAL_CALL osl_openNetworkServer(
	rtl_uString *strSysDirPath, oslDirectory *pDirectory)
{
	NETRESOURCEW	aNetResource;
	HANDLE			hEnum;
	DWORD			dwError;

	ZeroMemory( &aNetResource, sizeof(aNetResource) );

	aNetResource.lpRemoteName = reinterpret_cast<LPWSTR>(strSysDirPath->buffer);

	dwError = WNetOpenEnumW( 
		RESOURCE_GLOBALNET, 
		RESOURCETYPE_DISK, 
		RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER, 
		&aNetResource, 
		&hEnum );

	if ( ERROR_SUCCESS == dwError )
	{
		Directory_Impl	*pDirImpl;

		pDirImpl = reinterpret_cast<Directory_Impl*>(rtl_allocateMemory(sizeof(Directory_Impl)));
        ZeroMemory( pDirImpl, sizeof(Directory_Impl) );
		pDirImpl->uType = DIRECTORYTYPE_NETROOT;
		pDirImpl->hDirectory = hEnum;	
		*pDirectory = (oslDirectory)pDirImpl;
	}
	return oslTranslateFileError( dwError );
}

//#############################################
static DWORD create_dir_with_callback(
    rtl_uString * dir_path,
    oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc, 
    void* pData)
{
	// Create the specified directory and call the 
	// user specified callback function. On success
	// the function returns ERROR_SUCCESS else a Win32 error code.

    BOOL bCreated = FALSE;

    bCreated = CreateDirectoryW( reinterpret_cast<LPCWSTR>(rtl_uString_getStr( dir_path )), NULL );

    if ( bCreated )
    {
        if (aDirectoryCreationCallbackFunc)
        {
            rtl::OUString url;
            _osl_getFileURLFromSystemPath(dir_path, &(url.pData));
            aDirectoryCreationCallbackFunc(pData, url.pData);
        }
        return ERROR_SUCCESS;
    }
    return GetLastError();
}

//#############################################
static int path_make_parent(sal_Unicode* path)
{    
    /*  Cut off the last part of the given path to 
    get the parent only, e.g. 'c:\dir\subdir' -> 
    'c:\dir' or '\\share\sub\dir' -> '\\share\sub'
    @return The position where the path has been cut 
    off (this is the posistion of the last backslash).
    If there are no more parents 0 will be returned, 
    e.g. 'c:\' or '\\Share' have no more parents */        

	OSL_PRECOND(rtl_ustr_indexOfChar(path, SLASH) == -1, "Path must not contain slashes");
	OSL_PRECOND(has_path_parent(path), "Path must have a parent");                
                    
	sal_Unicode* pos_last_backslash = path + rtl_ustr_lastIndexOfChar(path, BACKSLASH);
	*pos_last_backslash = 0; 
	return (pos_last_backslash - path);        
}
    
//#############################################
static DWORD create_dir_recursively_(
    rtl_uString * dir_path,
    oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc, 
    void* pData)
{
    OSL_PRECOND(
		rtl_ustr_lastIndexOfChar_WithLength(dir_path->buffer, dir_path->length, BACKSLASH) != dir_path->length,
		"Path must not end with a backslash");
        
    DWORD w32_error = create_dir_with_callback(
        dir_path, aDirectoryCreationCallbackFunc, pData);
    if (w32_error == ERROR_SUCCESS)
        return ERROR_SUCCESS;   
                                
    if ((w32_error != ERROR_PATH_NOT_FOUND) || !has_path_parent(dir_path->buffer))
        return w32_error;

    int pos = path_make_parent(dir_path->buffer); // dir_path->buffer[pos] = 0, restore below
                            
    w32_error = create_dir_recursively_(
        dir_path, aDirectoryCreationCallbackFunc, pData);

    dir_path->buffer[pos] = BACKSLASH; // restore

    if (ERROR_SUCCESS != w32_error)
        return w32_error;

    return create_dir_recursively_(dir_path, aDirectoryCreationCallbackFunc, pData);
}

//#############################################
oslFileError SAL_CALL osl_createDirectoryPath(
    rtl_uString* aDirectoryUrl, 
    oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc, 
    void* pData)
{   
    if (aDirectoryUrl == NULL)
        return osl_File_E_INVAL;
        
    rtl::OUString sys_path;         
    oslFileError osl_error = 
        _osl_getSystemPathFromFileURL(aDirectoryUrl, &sys_path.pData, sal_False);        
    
    if (osl_error != osl_File_E_None)
        return osl_error;
                                                                
    osl::systemPathRemoveSeparator(sys_path);
    
    // const_cast because sys_path is a local copy
    // which we want to modify inplace instead of 
    // coyp it into another buffer on the heap again
    return oslTranslateFileError(create_dir_recursively_(
        sys_path.pData, aDirectoryCreationCallbackFunc, pData));                     
}

//#####################################################
oslFileError SAL_CALL osl_createDirectory(rtl_uString* strPath)
{
	rtl_uString	*strSysPath = NULL;
	oslFileError	error = _osl_getSystemPathFromFileURL( strPath, &strSysPath, sal_False );

	if ( osl_File_E_None == error )
	{
        BOOL bCreated = FALSE;

        bCreated = CreateDirectoryW( reinterpret_cast<LPCWSTR>(rtl_uString_getStr( strSysPath )), NULL );
		
        if ( !bCreated )
		{
            /*@@@ToDo
              The following case is a hack because the ucb or the webtop had some 
              problems with the error code that CreateDirectory returns in
              case the path is only a logical drive, should be removed!
            */			

			const sal_Unicode	*pBuffer = rtl_uString_getStr( strSysPath );
			sal_Int32			nLen = rtl_uString_getLength( strSysPath );

			if (
				( ( pBuffer[0] >= 'A' && pBuffer[0] <= 'Z' ) ||
				  ( pBuffer[0] >= 'a' && pBuffer[0] <= 'z' ) ) &&
				pBuffer[1] == ':' && ( nLen ==2 || ( nLen == 3 && pBuffer[2] == '\\' ) )
				)
				SetLastError( ERROR_ALREADY_EXISTS );

			error = oslTranslateFileError( GetLastError() );
		}

		rtl_uString_release( strSysPath );
	}
	return error;
}

//#####################################################
oslFileError SAL_CALL osl_removeDirectory(rtl_uString* strPath)
{
	rtl_uString	*strSysPath = NULL;
	oslFileError	error = _osl_getSystemPathFromFileURL( strPath, &strSysPath, sal_False );

	if ( osl_File_E_None == error )
	{
		if ( RemoveDirectory( reinterpret_cast<LPCTSTR>(rtl_uString_getStr( strSysPath )) ) )
			error = osl_File_E_None;
		else
			error = oslTranslateFileError( GetLastError() );

		rtl_uString_release( strSysPath );
	}
	return error;
}

//#####################################################
oslFileError SAL_CALL osl_openDirectory(rtl_uString *strDirectoryPath, oslDirectory *pDirectory)
{
	oslFileError	error;			

	if ( 0 == rtl_ustr_ascii_compareIgnoreAsciiCase( strDirectoryPath->buffer, "file:///" ) )
		error = osl_openLocalRoot( strDirectoryPath, pDirectory );
	else
	{
		rtl_uString	*strSysDirectoryPath = NULL;
		DWORD		dwPathType;

		error = _osl_getSystemPathFromFileURL( strDirectoryPath, &strSysDirectoryPath, sal_False );

		if ( osl_File_E_None != error )
				return error;

		dwPathType = IsValidFilePath( strSysDirectoryPath, NULL, VALIDATEPATH_NORMAL, NULL );

		if ( dwPathType & PATHTYPE_IS_SERVER )
		{
			error = osl_openNetworkServer( strSysDirectoryPath, pDirectory );
		}
		else
			error = osl_openFileDirectory( strSysDirectoryPath, pDirectory );

		rtl_uString_release( strSysDirectoryPath );
	}
	return error;
}

//#####################################################
static oslFileError SAL_CALL osl_getNextNetResource( 
	oslDirectory Directory, oslDirectoryItem *pItem, sal_uInt32 uHint )
{
	Directory_Impl		*pDirImpl = (Directory_Impl *)Directory;
	DirectoryItem_Impl	*pItemImpl = NULL;
	BYTE				buffer[16384];
	LPNETRESOURCEW		lpNetResource = (LPNETRESOURCEW)buffer;
	DWORD				dwError, dwCount, dwBufSize;

	uHint = uHint; /* to get no warning */        	    

	if ( !pItem )
		return osl_File_E_INVAL;
	*pItem = NULL;

	if ( !pDirImpl )
		return osl_File_E_INVAL;

	dwCount = 1;
	dwBufSize = sizeof(buffer);
	dwError = WNetEnumResource( pDirImpl->hDirectory, &dwCount, lpNetResource, &dwBufSize );

	switch ( dwError )
	{
	    case NO_ERROR:
	    case ERROR_MORE_DATA:
		{
			pItemImpl = reinterpret_cast<DirectoryItem_Impl*>(rtl_allocateMemory(sizeof(DirectoryItem_Impl)));
			if ( !pItemImpl )
				return osl_File_E_NOMEM;

			ZeroMemory( pItemImpl, sizeof(DirectoryItem_Impl) );
			pItemImpl->uType = DIRECTORYITEM_DRIVE;
			osl_acquireDirectoryItem( (oslDirectoryItem)pItemImpl );

			wcscpy( pItemImpl->cDriveString, lpNetResource->lpRemoteName );

			*pItem = pItemImpl;
		}
		return osl_File_E_None;
	    case ERROR_NO_MORE_ITEMS:
		    return osl_File_E_NOENT;
	    default:
		    return oslTranslateFileError( dwError );
	}
}

//##################################################### 
static oslFileError SAL_CALL osl_getNextDrive( 
	oslDirectory Directory, oslDirectoryItem *pItem, sal_uInt32 uHint )
{
	Directory_Impl		*pDirImpl = (Directory_Impl *)Directory;
	DirectoryItem_Impl	*pItemImpl = NULL;
	BOOL				fSuccess;

	uHint = uHint; /* avoid warnings */
        	    
	if ( !pItem )
		return osl_File_E_INVAL;
	*pItem = NULL;

	if ( !pDirImpl )
		return osl_File_E_INVAL;

	pItemImpl = reinterpret_cast<DirectoryItem_Impl*>(rtl_allocateMemory(sizeof(DirectoryItem_Impl)));
	if ( !pItemImpl )
		return osl_File_E_NOMEM;

	ZeroMemory( pItemImpl, sizeof(DirectoryItem_Impl) );
	pItemImpl->uType = DIRECTORYITEM_DRIVE;
	osl_acquireDirectoryItem( (oslDirectoryItem)pItemImpl );
	fSuccess = EnumLogicalDrives( pDirImpl->hEnumDrives, pItemImpl->cDriveString );

	if ( fSuccess )
	{
		*pItem = pItemImpl;
		return osl_File_E_None;
	}
	else
	{
        if ( pItemImpl->m_pFullPath )
        {
            rtl_uString_release( pItemImpl->m_pFullPath );
            pItemImpl->m_pFullPath = 0;
        }

		rtl_freeMemory( pItemImpl );
		return oslTranslateFileError( GetLastError() );
	}
}

//##################################################### 
static oslFileError SAL_CALL osl_getNextFileItem( 
	oslDirectory Directory, oslDirectoryItem *pItem, sal_uInt32 uHint)
{
	Directory_Impl		*pDirImpl = (Directory_Impl *)Directory;
	DirectoryItem_Impl	*pItemImpl = NULL;
	BOOL				fFound;

	uHint = uHint; /* avoid warnings */
        	    
	if ( !pItem )
		return osl_File_E_INVAL;
	*pItem = NULL;

	if ( !pDirImpl )
		return osl_File_E_INVAL;

	pItemImpl = reinterpret_cast<DirectoryItem_Impl*>(rtl_allocateMemory(sizeof(DirectoryItem_Impl)));
	if ( !pItemImpl )
		return osl_File_E_NOMEM;

	memset( pItemImpl, 0, sizeof(DirectoryItem_Impl) );
	fFound = EnumDirectory( pDirImpl->hDirectory, &pItemImpl->FindData );

	if ( fFound )
	{
		pItemImpl->uType = DIRECTORYITEM_FILE;
		pItemImpl->nRefCount = 1;

        rtl_uString* pTmpFileName = 0;
        rtl_uString_newFromStr( &pTmpFileName,  reinterpret_cast<const sal_Unicode *>(pItemImpl->FindData.cFileName) );
        rtl_uString_newConcat( &pItemImpl->m_pFullPath, pDirImpl->m_pDirectoryPath, pTmpFileName );
        rtl_uString_release( pTmpFileName );

		pItemImpl->bFullPathNormalized = FALSE;
		*pItem = (oslDirectoryItem)pItemImpl;
		return osl_File_E_None;
	}
	else
	{
        if ( pItemImpl->m_pFullPath )
        {
            rtl_uString_release( pItemImpl->m_pFullPath );
            pItemImpl->m_pFullPath = 0;
        }

		rtl_freeMemory( pItemImpl );
		return oslTranslateFileError( GetLastError() );
	}
}

//##################################################### 
oslFileError SAL_CALL osl_getNextDirectoryItem(
	oslDirectory Directory, oslDirectoryItem *pItem, sal_uInt32 uHint)
{
	Directory_Impl		*pDirImpl = (Directory_Impl *)Directory;
	
	/* Assume failure */

	if ( !pItem )
		return osl_File_E_INVAL;
	*pItem = NULL;

	if ( !pDirImpl )
		return osl_File_E_INVAL;

	switch ( pDirImpl->uType )
	{
	case DIRECTORYTYPE_LOCALROOT:
		return osl_getNextDrive( Directory, pItem, uHint );
	case DIRECTORYTYPE_NETROOT:
		return osl_getNextNetResource( Directory, pItem, uHint );
	case DIRECTORYTYPE_FILESYSTEM:
		return osl_getNextFileItem( Directory, pItem, uHint );
	default:
		return osl_File_E_INVAL;
	}
}

//#####################################################
oslFileError SAL_CALL osl_closeDirectory(oslDirectory Directory)
{
	Directory_Impl	*pDirImpl = (Directory_Impl *)Directory;
	oslFileError	eError = osl_File_E_INVAL;

	if ( pDirImpl )
	{
		switch ( pDirImpl->uType )
		{
		case DIRECTORYTYPE_FILESYSTEM:
			eError = CloseDirectory( pDirImpl->hDirectory ) ? osl_File_E_None : oslTranslateFileError( GetLastError() );
			break;
		case DIRECTORYTYPE_LOCALROOT:
			eError = CloseLogicalDrivesEnum( pDirImpl->hEnumDrives ) ? osl_File_E_None : oslTranslateFileError( GetLastError() );
			break;
		case DIRECTORYTYPE_NETROOT:
		    {
		        DWORD err = WNetCloseEnum(pDirImpl->hDirectory);
			    eError = (err == NO_ERROR) ? osl_File_E_None : oslTranslateFileError(err);
			}
			break;
		default:
			OSL_ENSURE( 0, "Invalid directory type" );
			break;
		}

        if ( pDirImpl->m_pDirectoryPath )
        {
            rtl_uString_release( pDirImpl->m_pDirectoryPath );
            pDirImpl->m_pDirectoryPath = 0;
        }

		rtl_freeMemory(pDirImpl);
	}	
	return eError;
}

//#####################################################
/* Different types of paths */
typedef enum _PATHTYPE
{
	PATHTYPE_SYNTAXERROR = 0,
	PATHTYPE_NETROOT,
	PATHTYPE_NETSERVER,
	PATHTYPE_VOLUME,
	PATHTYPE_FILE
} PATHTYPE;

oslFileError SAL_CALL osl_getDirectoryItem(rtl_uString *strFilePath, oslDirectoryItem *pItem)
{
	oslFileError	error = osl_File_E_None;
	rtl_uString*	strSysFilePath = NULL;
	PATHTYPE		type = PATHTYPE_FILE;
	DWORD			dwPathType;

	/* Assume failure */

	if ( !pItem )
		return osl_File_E_INVAL;

	*pItem = NULL;


	error = _osl_getSystemPathFromFileURL( strFilePath, &strSysFilePath, sal_False );

	if ( osl_File_E_None != error )
			return error;

	dwPathType = IsValidFilePath( strSysFilePath, NULL, VALIDATEPATH_NORMAL, NULL );

	if ( dwPathType & PATHTYPE_IS_VOLUME )
		type = PATHTYPE_VOLUME;
	else if ( dwPathType & PATHTYPE_IS_SERVER )
		type = PATHTYPE_NETSERVER;
	else
		type = PATHTYPE_FILE;

	switch ( type )
	{
	case PATHTYPE_NETSERVER:
		{
			DirectoryItem_Impl*	pItemImpl = 
			    reinterpret_cast<DirectoryItem_Impl*>(rtl_allocateMemory(sizeof(DirectoryItem_Impl)));

			if ( !pItemImpl )
				error = osl_File_E_NOMEM;

			if ( osl_File_E_None == error )
			{
				ZeroMemory( pItemImpl, sizeof(DirectoryItem_Impl) );
				pItemImpl->uType = DIRECTORYITEM_SERVER;

				osl_acquireDirectoryItem( (oslDirectoryItem)pItemImpl );
                rtl_uString_newFromString( &pItemImpl->m_pFullPath, strSysFilePath );

				// Assign a title anyway
				{
					int iSrc = 2;
					int	iDst = 0;

					while( iSrc < strSysFilePath->length && strSysFilePath->buffer[iSrc] && strSysFilePath->buffer[iSrc] != '\\' )
					{
						pItemImpl->FindData.cFileName[iDst++] = strSysFilePath->buffer[iSrc++];
					}
				}

				*pItem = pItemImpl;
			}
		}
		break;
	case PATHTYPE_VOLUME:
		{
			DirectoryItem_Impl*	pItemImpl = 
			    reinterpret_cast<DirectoryItem_Impl*>(rtl_allocateMemory(sizeof(DirectoryItem_Impl)));

			if ( !pItemImpl )
				error = osl_File_E_NOMEM;

			if ( osl_File_E_None == error )
			{
				ZeroMemory( pItemImpl, sizeof(DirectoryItem_Impl) );
				pItemImpl->uType = DIRECTORYITEM_DRIVE;

				osl_acquireDirectoryItem( (oslDirectoryItem)pItemImpl );

				_tcscpy( pItemImpl->cDriveString, reinterpret_cast<LPCTSTR>(strSysFilePath->buffer) );
				pItemImpl->cDriveString[0] = _toupper( pItemImpl->cDriveString[0] );

				if ( pItemImpl->cDriveString[_tcslen(pItemImpl->cDriveString) - 1] != '\\' )
					_tcscat( pItemImpl->cDriveString, TEXT( "\\" ) );

				*pItem = pItemImpl;
			}
		}
		break;
    case PATHTYPE_SYNTAXERROR:
    case PATHTYPE_NETROOT:
	case PATHTYPE_FILE:
		{
			HANDLE				hFind;
			WIN32_FIND_DATA		aFindData;

			if ( strSysFilePath->length > 0 && strSysFilePath->buffer[strSysFilePath->length - 1] == '\\' )
				rtl_uString_newFromStr_WithLength( &strSysFilePath, strSysFilePath->buffer, strSysFilePath->length - 1 );

			hFind = FindFirstFile( reinterpret_cast<LPCTSTR>(rtl_uString_getStr(strSysFilePath)), &aFindData );

			if ( hFind != INVALID_HANDLE_VALUE )
			{
				DirectoryItem_Impl	*pItemImpl = 
				    reinterpret_cast<DirectoryItem_Impl*>(rtl_allocateMemory(sizeof(DirectoryItem_Impl)));

				ZeroMemory( pItemImpl, sizeof(DirectoryItem_Impl) );
				osl_acquireDirectoryItem( (oslDirectoryItem)pItemImpl );

				CopyMemory( &pItemImpl->FindData, &aFindData, sizeof(WIN32_FIND_DATA) );
                rtl_uString_newFromString( &pItemImpl->m_pFullPath, strSysFilePath );

				// MT: This costs 600ms startup time on fast v60x!
				// GetCaseCorrectPathName( pItemImpl->szFullPath, pItemImpl->szFullPath, sizeof(pItemImpl->szFullPath) );

				pItemImpl->uType = DIRECTORYITEM_FILE;
				*pItem = pItemImpl;
				FindClose( hFind );
			}
			else
				error = oslTranslateFileError( GetLastError() );
		}
		break;
	}

	if ( strSysFilePath )
		rtl_uString_release( strSysFilePath );

	return error;
}

//#####################################################
oslFileError SAL_CALL osl_acquireDirectoryItem( oslDirectoryItem Item )
{
	DirectoryItem_Impl	*pItemImpl = (DirectoryItem_Impl *)Item;

	if ( !pItemImpl )
		return osl_File_E_INVAL;

	pItemImpl->nRefCount++;
	return osl_File_E_None;
}

//#####################################################
oslFileError SAL_CALL osl_releaseDirectoryItem( oslDirectoryItem Item )
{
	DirectoryItem_Impl	*pItemImpl = (DirectoryItem_Impl *)Item;

	if ( !pItemImpl )
		return osl_File_E_INVAL;

	if ( ! --pItemImpl->nRefCount )
    {
        if ( pItemImpl->m_pFullPath )
        {
            rtl_uString_release( pItemImpl->m_pFullPath );
            pItemImpl->m_pFullPath = 0;
        }

		rtl_freeMemory( pItemImpl );
    }

	return osl_File_E_None;
}

//#####################################################
// volume / file info handling functions
//#####################################################

//#####################################################
static inline bool is_floppy_A_present()
{ return (GetLogicalDrives() & 1); }
    
//#####################################################
static inline bool is_floppy_B_present()
{ return (GetLogicalDrives() & 2); }
    
//#####################################################
bool is_floppy_volume_mount_point(const rtl::OUString& path)
{                   
    // determines if a volume mount point shows to a floppy
    // disk by comparing the unique volume names           
    static const LPCWSTR FLOPPY_A = L"A:\\";
    static const LPCWSTR FLOPPY_B = L"B:\\";

	rtl::OUString p(path);
	osl::systemPathEnsureSeparator(p);
        
	TCHAR vn[51];
	if (GetVolumeNameForVolumeMountPoint(reinterpret_cast<LPCTSTR>(p.getStr()), vn, ELEMENTS_OF_ARRAY(vn)))
	{       
		TCHAR vnfloppy[51];
		if (is_floppy_A_present() && 
			GetVolumeNameForVolumeMountPoint(FLOPPY_A, vnfloppy, ELEMENTS_OF_ARRAY(vnfloppy)) &&
			(0 == wcscmp(vn, vnfloppy)))
			return true;
                    
		if (is_floppy_B_present() &&
			GetVolumeNameForVolumeMountPoint(FLOPPY_B, vnfloppy, ELEMENTS_OF_ARRAY(vnfloppy)) &&
			(0 == wcscmp(vn, vnfloppy)))
			return true;
	}
	return false;
}
    
//################################################
static bool is_floppy_drive(const rtl::OUString& path)
{
	static const LPCWSTR FLOPPY_DRV_LETTERS = TEXT("AaBb"); 

    // we must take into account that even a floppy 
    // drive may be mounted to a directory so checking
    // for the drive letter alone is not sufficient
    // we must compare the unique volume name with 
    // that of the available floppy disks
 
	const sal_Unicode* pszPath = path.getStr();
	return ((wcschr(FLOPPY_DRV_LETTERS, pszPath[0]) && (L':' == pszPath[1])) || is_floppy_volume_mount_point(path));
}
    
//#####################################################
static bool is_volume_mount_point(const rtl::OUString& path)
{
	rtl::OUString p(path);        
	osl::systemPathRemoveSeparator(p);
            
	bool  is_volume_root = false;
        
	if (!is_floppy_drive(p))
	{
		DWORD fattr = GetFileAttributes(reinterpret_cast<LPCTSTR>(p.getStr()));
            
		if ((INVALID_FILE_ATTRIBUTES != fattr) && 
			(FILE_ATTRIBUTE_REPARSE_POINT & fattr))
		{
			WIN32_FIND_DATA find_data;
			HANDLE h_find = FindFirstFile(reinterpret_cast<LPCTSTR>(p.getStr()), &find_data);    
                           
			if (IsValidHandle(h_find) &&
				(FILE_ATTRIBUTE_REPARSE_POINT & find_data.dwFileAttributes) && 
				(IO_REPARSE_TAG_MOUNT_POINT == find_data.dwReserved0))
			{            
				is_volume_root = true;
			}        
			if (IsValidHandle(h_find))
				FindClose(h_find);
		}
	}
	return is_volume_root;
}

//#############################################
static UINT get_volume_mount_point_drive_type(const rtl::OUString& path)
{
	if (0 == path.getLength())
		return GetDriveType(NULL);
        
	rtl::OUString p(path);
	osl::systemPathEnsureSeparator(p);
        
	TCHAR vn[51];  
	if (GetVolumeNameForVolumeMountPoint(reinterpret_cast<LPCTSTR>(p.getStr()), vn, ELEMENTS_OF_ARRAY(vn)))
		return GetDriveType(vn);
        
	return DRIVE_NO_ROOT_DIR;  
}

//#############################################
static inline bool is_drivetype_request(sal_uInt32 field_mask)
{
	return (field_mask & osl_VolumeInfo_Mask_Attributes);
}

//#############################################
static oslFileError osl_get_drive_type(
	const rtl::OUString& path, oslVolumeInfo* pInfo)
{
	// GetDriveType fails on empty volume mount points
	// see Knowledge Base Q244089
	UINT drive_type;
	if (is_volume_mount_point(path))
		drive_type = get_volume_mount_point_drive_type(path);
	else
		drive_type = GetDriveType(reinterpret_cast<LPCTSTR>(path.getStr()));
    	
	if (DRIVE_NO_ROOT_DIR == drive_type)
		return oslTranslateFileError(ERROR_INVALID_DRIVE);
    		    
	pInfo->uValidFields |= osl_VolumeInfo_Mask_Attributes;		

	switch (drive_type)
	{
		case DRIVE_CDROM:
			pInfo->uAttributes |= osl_Volume_Attribute_CompactDisc | osl_Volume_Attribute_Removeable;
			break;
		case DRIVE_REMOVABLE:
			pInfo->uAttributes |= osl_Volume_Attribute_Removeable;
			if (is_floppy_drive(path))
				pInfo->uAttributes |= osl_Volume_Attribute_FloppyDisk;
			break;
		case DRIVE_FIXED:
			pInfo->uAttributes |= osl_Volume_Attribute_FixedDisk;
			break;
		case DRIVE_RAMDISK:
			pInfo->uAttributes |= osl_Volume_Attribute_RAMDisk;
			break;
		case DRIVE_REMOTE:
			pInfo->uAttributes |= osl_Volume_Attribute_Remote;
			break;
		case DRIVE_UNKNOWN:
			pInfo->uAttributes = 0;
			break;		
		default:
			pInfo->uValidFields &= ~osl_VolumeInfo_Mask_Attributes;
			pInfo->uAttributes = 0;
			break;
	}
	return osl_File_E_None;    
}

//#############################################
static inline bool is_volume_space_info_request(sal_uInt32 field_mask)
{
	return (field_mask & 
			(osl_VolumeInfo_Mask_TotalSpace | 
			 osl_VolumeInfo_Mask_UsedSpace  | 
			 osl_VolumeInfo_Mask_FreeSpace));
}
    
//#############################################
static void get_volume_space_information(
	const rtl::OUString& path, oslVolumeInfo *pInfo)
{
	BOOL ret = GetDiskFreeSpaceEx(
		reinterpret_cast<LPCTSTR>(path.getStr()), 
		(PULARGE_INTEGER)&(pInfo->uFreeSpace),
		(PULARGE_INTEGER)&(pInfo->uTotalSpace),
		NULL);
            
	if (ret)
	{			    			    
		pInfo->uUsedSpace    = pInfo->uTotalSpace - pInfo->uFreeSpace;
		pInfo->uValidFields |= osl_VolumeInfo_Mask_TotalSpace | 
			osl_VolumeInfo_Mask_UsedSpace | 
			osl_VolumeInfo_Mask_FreeSpace;
	}
}
    
//#############################################
static inline bool is_filesystem_attributes_request(sal_uInt32 field_mask)
{
	return (field_mask & 
			(osl_VolumeInfo_Mask_MaxNameLength | 
			 osl_VolumeInfo_Mask_MaxPathLength | 
			 osl_VolumeInfo_Mask_FileSystemName | 
			 osl_VolumeInfo_Mask_FileSystemCaseHandling));
}
    
//#############################################
static oslFileError get_filesystem_attributes(
	const rtl::OUString& path, sal_uInt32 field_mask, oslVolumeInfo* pInfo)
{
	pInfo->uAttributes = 0;
        
	// osl_get_drive_type must be called first because
	// this function resets osl_VolumeInfo_Mask_Attributes
	// on failure
	if (is_drivetype_request(field_mask))
	{
		oslFileError osl_error = osl_get_drive_type(path, pInfo);
        if (osl_File_E_None != osl_error)
			return osl_error;
	}
	if (is_filesystem_attributes_request(field_mask))
	{
        /* the following two parameters can not be longer than MAX_PATH+1 */
		WCHAR vn[MAX_PATH+1];
		WCHAR fsn[MAX_PATH+1];

		DWORD serial;
		DWORD mcl;
		DWORD flags;
        	
		LPCTSTR pszPath = reinterpret_cast<LPCTSTR>(path.getStr());
		if (GetVolumeInformation(pszPath, vn, MAX_PATH+1, &serial, &mcl, &flags, fsn, MAX_PATH+1))
		{		
            // Currently sal does not use this value, instead MAX_PATH is used
			pInfo->uValidFields   |= osl_VolumeInfo_Mask_MaxNameLength;
			pInfo->uMaxNameLength  = mcl;
        	
            // Should the uMaxPathLength be set to 32767, "\\?\" prefix allowes it
			pInfo->uValidFields   |= osl_VolumeInfo_Mask_MaxPathLength;
			pInfo->uMaxPathLength  = MAX_PATH;

			pInfo->uValidFields   |= osl_VolumeInfo_Mask_FileSystemName;
			rtl_uString_newFromStr(&pInfo->ustrFileSystemName, reinterpret_cast<const sal_Unicode*>(fsn));
        		
			// volumes (even NTFS) will always be considered case
			// insensitive because the Win32 API is not able to
			// deal with case sensitive volumes see M$ Knowledge Base
			// article 100625 that's why we never set the attribute
			// osl_Volume_Attribute_Case_Sensitive
        		
			if (flags & FS_CASE_IS_PRESERVED)
				pInfo->uAttributes |= osl_Volume_Attribute_Case_Is_Preserved;
		            
			pInfo->uValidFields |= osl_VolumeInfo_Mask_Attributes;
		}
	}
	return osl_File_E_None;
}
    
//#####################################################
static bool path_get_parent(rtl::OUString& path)
{   
	OSL_PRECOND(path.lastIndexOf(SLASH) == -1, "Path must not have slashes");
        
	if (!has_path_parent(path))
	{
		sal_Int32 i = path.lastIndexOf(BACKSLASH);                        
		if (-1 < i)
		{
			path = rtl::OUString(path.getStr(), i);
			return true;                    
		}
	}
	return false;
}
    
//##################################################### 
static void path_travel_to_volume_root(const rtl::OUString& system_path, rtl::OUString& volume_root)
{
	rtl::OUString sys_path(system_path);
        
	while(!is_volume_mount_point(sys_path) && path_get_parent(sys_path))
		/**/;
        
	volume_root = sys_path;
	osl::systemPathEnsureSeparator(volume_root);                            
}

//#############################################
oslFileError SAL_CALL osl_getVolumeInformation( 
    rtl_uString *ustrURL, oslVolumeInfo *pInfo, sal_uInt32 uFieldMask )
{			
	if (!pInfo)
		return osl_File_E_INVAL;

    rtl::OUString system_path;		
	oslFileError error = _osl_getSystemPathFromFileURL(ustrURL, &system_path.pData, sal_False);
    
    if (osl_File_E_None != error)
        return error;
    
    rtl::OUString volume_root;
    path_travel_to_volume_root(system_path, volume_root);        
    			
	pInfo->uValidFields = 0;
    
    if ((error = get_filesystem_attributes(volume_root, uFieldMask, pInfo)) != osl_File_E_None)
        return error;
            	
	if (is_volume_space_info_request(uFieldMask)) 
	    get_volume_space_information(volume_root, pInfo);
	    	    
	if (uFieldMask & osl_VolumeInfo_Mask_DeviceHandle)
	{
		pInfo->uValidFields |= osl_VolumeInfo_Mask_DeviceHandle;
		osl_getFileURLFromSystemPath(volume_root.pData, (rtl_uString**)&pInfo->pDeviceHandle);
	}
    
	return osl_File_E_None;
}

//##################################################### 
static oslFileError SAL_CALL osl_getDriveInfo(
	oslDirectoryItem Item, oslFileStatus *pStatus, sal_uInt32 uFieldMask)
{
	DirectoryItem_Impl	*pItemImpl = (DirectoryItem_Impl *)Item;
	TCHAR				cDrive[3] = TEXT("A:");
	TCHAR				cRoot[4] = TEXT("A:\\");

	if ( !pItemImpl )
		return osl_File_E_INVAL;

	pStatus->uValidFields = 0;

	cDrive[0] = pItemImpl->cDriveString[0];
	cRoot[0] = pItemImpl->cDriveString[0];

	if ( uFieldMask & osl_FileStatus_Mask_FileName )
	{
		if ( pItemImpl->cDriveString[0] == '\\' && pItemImpl->cDriveString[1] == '\\' )
		{
			LPCWSTR	lpFirstBkSlash = wcschr( &pItemImpl->cDriveString[2], '\\' );

			if ( lpFirstBkSlash && lpFirstBkSlash[1] )
			{
				LPCWSTR	lpLastBkSlash = wcschr( &lpFirstBkSlash[1], '\\' );

				if ( lpLastBkSlash )
					rtl_uString_newFromStr_WithLength( &pStatus->ustrFileName, reinterpret_cast<const sal_Unicode*>(&lpFirstBkSlash[1]), lpLastBkSlash - lpFirstBkSlash - 1 );
				else
					rtl_uString_newFromStr( &pStatus->ustrFileName, reinterpret_cast<const sal_Unicode*>(&lpFirstBkSlash[1]) );
				pStatus->uValidFields |= osl_FileStatus_Mask_FileName;
			}
		}
		else switch ( GetDriveType( cRoot ) )
		{
		    case DRIVE_REMOTE:
			{
				TCHAR szBuffer[1024];
				DWORD const dwBufsizeConst = ELEMENTS_OF_ARRAY(szBuffer);
				DWORD dwBufsize = dwBufsizeConst;

				DWORD dwResult = WNetGetConnection( cDrive, szBuffer, &dwBufsize );
				if ( NO_ERROR == dwResult )
				{
					TCHAR szFileName[dwBufsizeConst + 16];

					swprintf( szFileName, L"%s [%s]", cDrive, szBuffer );
					rtl_uString_newFromStr( &pStatus->ustrFileName, reinterpret_cast<const sal_Unicode*>(szFileName) );
				}
				else
					rtl_uString_newFromStr( &pStatus->ustrFileName, reinterpret_cast<const sal_Unicode*>(cDrive) );
			}
			pStatus->uValidFields |= osl_FileStatus_Mask_FileName;
			break;
		    case DRIVE_FIXED:
			{
				TCHAR szVolumeNameBuffer[1024];
				DWORD const dwBufsizeConst = ELEMENTS_OF_ARRAY(szVolumeNameBuffer);

				if ( GetVolumeInformation( cRoot, szVolumeNameBuffer, dwBufsizeConst, NULL, NULL, NULL, NULL, 0 ) )
				{
					TCHAR	szFileName[dwBufsizeConst + 16];

					swprintf( szFileName, L"%s [%s]", cDrive, szVolumeNameBuffer );
					rtl_uString_newFromStr( &pStatus->ustrFileName, reinterpret_cast<const sal_Unicode*>(szFileName) );
				}
				else
					rtl_uString_newFromStr( &pStatus->ustrFileName, reinterpret_cast<const sal_Unicode*>(cDrive) );
			}
			pStatus->uValidFields |= osl_FileStatus_Mask_FileName;
			break;
		    case DRIVE_CDROM:
		    case DRIVE_REMOVABLE:
			    pStatus->uValidFields |= osl_FileStatus_Mask_FileName;
			    rtl_uString_newFromStr( &pStatus->ustrFileName, reinterpret_cast<const sal_Unicode*>(cRoot) );
			    break;
		    case DRIVE_UNKNOWN:
		    default:
			    break;
		}
	}

	pStatus->eType = osl_File_Type_Volume;
	pStatus->uValidFields |= osl_FileStatus_Mask_Type;

	if ( uFieldMask & osl_FileStatus_Mask_FileURL )
	{
		rtl_uString	*ustrSystemPath = NULL;

		rtl_uString_newFromStr( &ustrSystemPath, reinterpret_cast<const sal_Unicode*>(pItemImpl->cDriveString) );
		osl_getFileURLFromSystemPath( ustrSystemPath, &pStatus->ustrFileURL );
		rtl_uString_release( ustrSystemPath );
		pStatus->uValidFields |= osl_FileStatus_Mask_FileURL;
	}
	return osl_File_E_None;
}

//##################################################### 
static oslFileError SAL_CALL osl_getServerInfo( 
	oslDirectoryItem Item, oslFileStatus *pStatus, sal_uInt32 uFieldMask )
{
	DirectoryItem_Impl	*pItemImpl = (DirectoryItem_Impl *)Item;
	if ( !pItemImpl )
		return osl_File_E_INVAL;

	pStatus->uValidFields = 0;

	//	pStatus->uValidFields |= osl_FileStatus_Mask_FileName;

	//	if ( _tcscmp( pItemImpl->FindData.cFileName, TEXT(".") ) == 0 )
	//		rtl_uString_newFromAscii( &pStatus->ustrFileName, "/" );
	//	else
	//		rtl_uString_newFromStr( &pStatus->ustrFileName, pItemImpl->FindData.cFileName );

	pStatus->eType = osl_File_Type_Directory;
	pStatus->uValidFields |= osl_FileStatus_Mask_Type;

	if ( uFieldMask & osl_FileStatus_Mask_FileURL )
	{
		osl_getFileURLFromSystemPath( pItemImpl->m_pFullPath, &pStatus->ustrFileURL );
		pStatus->uValidFields |= osl_FileStatus_Mask_FileURL;
	}
	return osl_File_E_None;
}

//#############################################
oslFileError SAL_CALL osl_getFileStatus( 
    oslDirectoryItem Item, 
    oslFileStatus *pStatus, 
    sal_uInt32 uFieldMask )
{
	DirectoryItem_Impl	*pItemImpl = (DirectoryItem_Impl *)Item;

	if ( !pItemImpl )
		return osl_File_E_INVAL;

	switch ( pItemImpl->uType  )
	{
	case DIRECTORYITEM_DRIVE:
		return osl_getDriveInfo( Item, pStatus, uFieldMask );
	case DIRECTORYITEM_SERVER:
		return osl_getServerInfo( Item, pStatus, uFieldMask );
	default:
		break;
	}

	if ( uFieldMask & osl_FileStatus_Mask_Validate )
	{
		HANDLE	hFind = FindFirstFile( reinterpret_cast<LPCTSTR>( rtl_uString_getStr( pItemImpl->m_pFullPath ) ), &pItemImpl->FindData );

		if ( hFind != INVALID_HANDLE_VALUE )
			FindClose( hFind );
		else
			return oslTranslateFileError( GetLastError() );

		uFieldMask &= ~	osl_FileStatus_Mask_Validate;
	}

	/* If no fields to retrieve left ignore pStatus */
	if ( !uFieldMask )
		return osl_File_E_None;

	/* Otherwise, this must be a valid pointer */
	if ( !pStatus )
		return osl_File_E_INVAL;

	if ( pStatus->uStructSize != sizeof(oslFileStatus) )
		return osl_File_E_INVAL;

	pStatus->uValidFields = 0;

	/* File time stamps */

	if ( (uFieldMask & osl_FileStatus_Mask_ModifyTime) &&
		FileTimeToTimeValue( &pItemImpl->FindData.ftLastWriteTime, &pStatus->aModifyTime ) )
		pStatus->uValidFields |= osl_FileStatus_Mask_ModifyTime;

	if ( (uFieldMask & osl_FileStatus_Mask_AccessTime) &&
		FileTimeToTimeValue( &pItemImpl->FindData.ftLastAccessTime, &pStatus->aAccessTime ) )
		pStatus->uValidFields |= osl_FileStatus_Mask_AccessTime;

	if ( (uFieldMask & osl_FileStatus_Mask_CreationTime) &&
		FileTimeToTimeValue( &pItemImpl->FindData.ftCreationTime, &pStatus->aCreationTime ) )
		pStatus->uValidFields |= osl_FileStatus_Mask_CreationTime;

	/* Most of the fields are already set, regardless of requiered fields */

	rtl_uString_newFromStr( &pStatus->ustrFileName, reinterpret_cast<const sal_Unicode*>(pItemImpl->FindData.cFileName) );
	pStatus->uValidFields |= osl_FileStatus_Mask_FileName;

    if ((FILE_ATTRIBUTE_REPARSE_POINT & pItemImpl->FindData.dwFileAttributes) &&
        (IO_REPARSE_TAG_MOUNT_POINT == pItemImpl->FindData.dwReserved0))
        pStatus->eType = osl_File_Type_Volume;
    else if (pItemImpl->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        pStatus->eType = osl_File_Type_Directory;
    else    
	    pStatus->eType = osl_File_Type_Regular;
	    
	pStatus->uValidFields |= osl_FileStatus_Mask_Type;

	pStatus->uAttributes = pItemImpl->FindData.dwFileAttributes;
	pStatus->uValidFields |= osl_FileStatus_Mask_Attributes;

	pStatus->uFileSize = (sal_uInt64)pItemImpl->FindData.nFileSizeLow + ((sal_uInt64)pItemImpl->FindData.nFileSizeHigh << 32);
	pStatus->uValidFields |= osl_FileStatus_Mask_FileSize;

	if ( uFieldMask & osl_FileStatus_Mask_LinkTargetURL )
	{
		osl_getFileURLFromSystemPath( pItemImpl->m_pFullPath, &pStatus->ustrLinkTargetURL );

		pStatus->uValidFields |= osl_FileStatus_Mask_LinkTargetURL;
	}

	if ( uFieldMask & osl_FileStatus_Mask_FileURL )
	{
		if ( !pItemImpl->bFullPathNormalized )
		{
            sal_uInt32 nLen = rtl_uString_getLength( pItemImpl->m_pFullPath );
            ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
            sal_uInt32 nNewLen = GetCaseCorrectPathName( reinterpret_cast<LPCTSTR>( rtl_uString_getStr( pItemImpl->m_pFullPath ) ),
                                                      ::osl::mingw_reinterpret_cast<LPTSTR>( aBuffer ),
                                                      aBuffer.getBufSizeInSymbols(),
                                                      sal_True );

            if ( nNewLen )
            {
                rtl_uString_newFromStr( &pItemImpl->m_pFullPath, aBuffer );
                pItemImpl->bFullPathNormalized = TRUE;
            }
		}

		osl_getFileURLFromSystemPath( pItemImpl->m_pFullPath, &pStatus->ustrFileURL );
		pStatus->uValidFields |= osl_FileStatus_Mask_FileURL;
	}

	return osl_File_E_None;
}

//#####################################################
// file attributes handling functions
//#####################################################

//#############################################
oslFileError SAL_CALL osl_setFileAttributes( 
    rtl_uString *ustrFileURL, 
    sal_uInt64 uAttributes )
{
	oslFileError	error;
	rtl_uString		*ustrSysPath = NULL;
	DWORD			dwFileAttributes;
	BOOL			fSuccess;

	// Converts the normalized path into a systempath
	error = _osl_getSystemPathFromFileURL( ustrFileURL, &ustrSysPath, sal_False );

	if ( osl_File_E_None != error )
		return error;

	dwFileAttributes = GetFileAttributes( reinterpret_cast<LPCTSTR>(rtl_uString_getStr(ustrSysPath)) );

	if ( (DWORD)-1 != dwFileAttributes )
	{
		dwFileAttributes &= ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN);

		if ( uAttributes & osl_File_Attribute_ReadOnly )
			dwFileAttributes |= FILE_ATTRIBUTE_READONLY;

		if ( uAttributes & osl_File_Attribute_Hidden )
			dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;

		fSuccess = SetFileAttributes( reinterpret_cast<LPCTSTR>(rtl_uString_getStr(ustrSysPath)), dwFileAttributes );
	}
	else
		fSuccess = FALSE;

	if ( !fSuccess )
		error = oslTranslateFileError( GetLastError() );

	rtl_uString_release( ustrSysPath );

	return error;
}

//##################################################### 
oslFileError SAL_CALL osl_setFileTime(
    rtl_uString *filePath, 
    const TimeValue *aCreationTime, 
    const TimeValue *aLastAccessTime, 
    const TimeValue *aLastWriteTime)
{
	oslFileError error;
	rtl_uString *sysPath=NULL;
	FILETIME *lpCreationTime=NULL;
	FILETIME *lpLastAccessTime=NULL;
	FILETIME *lpLastWriteTime=NULL;
	FILETIME ftCreationTime;
	FILETIME ftLastAccessTime;
	FILETIME ftLastWriteTime;
	HANDLE hFile;
	BOOL fSuccess;


	error=_osl_getSystemPathFromFileURL(filePath, &sysPath, sal_False);

	if (error==osl_File_E_INVAL)
		return error;

	hFile=CreateFileW(reinterpret_cast<LPCWSTR>(rtl_uString_getStr(sysPath)), GENERIC_WRITE, 0, NULL , OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	rtl_uString_release(sysPath);

	if (hFile==INVALID_HANDLE_VALUE)
		return osl_File_E_NOENT;

	if (TimeValueToFileTime(aCreationTime, &ftCreationTime))
		lpCreationTime=&ftCreationTime;

	if (TimeValueToFileTime(aLastAccessTime, &ftLastAccessTime))
		lpLastAccessTime=&ftLastAccessTime;

	if (TimeValueToFileTime(aLastWriteTime, &ftLastWriteTime))
		lpLastWriteTime=&ftLastWriteTime;

	fSuccess=SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime);

	CloseHandle(hFile);

	if (!fSuccess)
		return osl_File_E_INVAL;
	else
		return osl_File_E_None;
}
