Get the style color and number just once
[LibreOffice.git] / sal / osl / w32 / file_dirvol.cxx
blob7599be112a8dc9c760b2c7c1cb6a701365cf8f01
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <systools/win32/uwinapi.h>
22 #include "file_url.hxx"
23 #include "filetime.hxx"
24 #include "file_error.hxx"
26 #include "path_helper.hxx"
28 #include <rtl/alloc.h>
29 #include <rtl/ustring.hxx>
30 #include <rtl/character.hxx>
31 #include <sal/log.hxx>
32 #include <o3tl/char16_t2wchar_t.hxx>
34 #include <memory>
36 BOOL TimeValueToFileTime(const TimeValue *cpTimeVal, FILETIME *pFTime)
38 SYSTEMTIME BaseSysTime;
39 FILETIME BaseFileTime;
40 FILETIME FTime;
41 bool fSuccess = false;
43 BaseSysTime.wYear = 1970;
44 BaseSysTime.wMonth = 1;
45 BaseSysTime.wDayOfWeek = 0;
46 BaseSysTime.wDay = 1;
47 BaseSysTime.wHour = 0;
48 BaseSysTime.wMinute = 0;
49 BaseSysTime.wSecond = 0;
50 BaseSysTime.wMilliseconds = 0;
52 if (cpTimeVal==nullptr)
53 return fSuccess;
55 if ( SystemTimeToFileTime(&BaseSysTime, &BaseFileTime) )
57 __int64 timeValue;
59 __int64 localTime = cpTimeVal->Seconds*__int64(10000000)+cpTimeVal->Nanosec/100;
60 osl::detail::setFiletime(FTime, localTime);
61 fSuccess = 0 <= (timeValue= osl::detail::getFiletime(BaseFileTime) + osl::detail::getFiletime(FTime));
62 if (fSuccess)
63 osl::detail::setFiletime(*pFTime, timeValue);
65 return fSuccess;
68 BOOL FileTimeToTimeValue(const FILETIME *cpFTime, TimeValue *pTimeVal)
70 SYSTEMTIME BaseSysTime;
71 FILETIME BaseFileTime;
72 bool fSuccess = false; /* Assume failure */
74 BaseSysTime.wYear = 1970;
75 BaseSysTime.wMonth = 1;
76 BaseSysTime.wDayOfWeek = 0;
77 BaseSysTime.wDay = 1;
78 BaseSysTime.wHour = 0;
79 BaseSysTime.wMinute = 0;
80 BaseSysTime.wSecond = 0;
81 BaseSysTime.wMilliseconds = 0;
83 if ( SystemTimeToFileTime(&BaseSysTime, &BaseFileTime) )
85 __int64 Value;
87 fSuccess = 0 <= (Value = osl::detail::getFiletime(*cpFTime) - osl::detail::getFiletime(BaseFileTime));
89 if ( fSuccess )
91 pTimeVal->Seconds = static_cast<unsigned long>(Value / 10000000L);
92 pTimeVal->Nanosec = static_cast<unsigned long>((Value % 10000000L) * 100);
95 return fSuccess;
98 namespace
100 // Returns whether a given path is only a logical drive pattern or not.
101 // A logical drive pattern is something like "a:\", "c:\".
102 // No logical drive pattern is something like "c:\test"
103 bool systemPathIsLogicalDrivePattern(std::u16string_view path)
105 // is [A-Za-z]:[/|\]\0
106 if (path.length() < 2 || !rtl::isAsciiAlpha(path[0]) || path[1] != ':')
107 return false;
108 auto rest = path.substr(2);
109 return rest.empty() // "c:"
110 || rest == u"\\" // "c:\"
111 || rest == u"/" // "c:/"
112 || rest == u".\\"; // "c:.\"
113 // degenerated case returned by the Windows FileOpen dialog
114 // when someone enters for instance "x:filename", the Win32
115 // API accepts this case
118 // Adds a trailing path separator to the given system path if not
119 // already there and if the path is not the root path or a logical
120 // drive alone
121 void systemPathEnsureSeparator(/*inout*/ OUString& path)
123 if (!path.endsWith(u"\\") && !path.endsWith(u"/"))
124 path += "\\";
126 SAL_WARN_IF(!path.endsWith(u"\\"), "sal.osl",
127 "systemPathEnsureSeparator: Post condition failed");
130 // Removes the last separator from the given system path if any and
131 // if the path is not the root path '\'
132 void systemPathRemoveSeparator(/*inout*/ OUString& path)
134 if (!systemPathIsLogicalDrivePattern(path) && (path.endsWith(u"\\") || path.endsWith(u"/")))
135 path = path.copy(0, path.getLength() - 1);
138 struct Component
140 bool isPresent() const { return begin_ < end_; }
142 const sal_Unicode* begin_ = nullptr;
143 const sal_Unicode* end_ = nullptr;
146 struct UNCComponents
148 Component server_;
149 Component share_;
150 Component resource_;
153 bool is_UNC_path(std::u16string_view path) { return path.starts_with(u"\\\\"); }
155 UNCComponents parse_UNC_path(std::u16string_view path)
157 OSL_PRECOND(is_UNC_path(path), "Precondition violated: No UNC path");
158 OSL_PRECOND(path.find('/') == std::u16string_view::npos, "Path must not contain slashes");
160 const sal_Unicode* pend = path.data() + path.length();
161 const sal_Unicode* ppos = path.data() + 2;
162 UNCComponents uncc;
164 uncc.server_.begin_ = ppos;
165 while ((ppos < pend) && (*ppos != '\\'))
166 ppos++;
168 uncc.server_.end_ = ppos;
170 if (ppos < pend)
172 uncc.share_.begin_ = ++ppos;
173 while ((ppos < pend) && (*ppos != '\\'))
174 ppos++;
176 uncc.share_.end_ = ppos;
178 if (ppos < pend)
180 uncc.resource_.begin_ = ppos + 1;
181 uncc.resource_.end_ = pend;
185 SAL_WARN_IF(!uncc.server_.isPresent() || !uncc.share_.isPresent(),
186 "sal.osl",
187 "Postcondition violated: Invalid UNC path detected");
188 return uncc;
191 bool has_path_parent(std::u16string_view path)
193 // Has the given path a parent or are we already there,
194 // e.g. 'c:\' or '\\server\share\'?
196 bool has_parent = false;
197 if (is_UNC_path(path))
199 UNCComponents unc_comp = parse_UNC_path(path);
200 has_parent = unc_comp.resource_.isPresent();
202 else
204 has_parent = !systemPathIsLogicalDrivePattern(path);
206 return has_parent;
210 oslFileError SAL_CALL osl_acquireVolumeDeviceHandle( oslVolumeDeviceHandle Handle )
212 if ( Handle )
214 rtl_uString_acquire( static_cast<rtl_uString *>(Handle) );
215 return osl_File_E_None;
217 else
218 return osl_File_E_INVAL;
221 oslFileError SAL_CALL osl_releaseVolumeDeviceHandle( oslVolumeDeviceHandle Handle )
223 if ( Handle )
225 rtl_uString_release( static_cast<rtl_uString *>(Handle) );
226 return osl_File_E_None;
228 else
229 return osl_File_E_INVAL;
232 oslFileError SAL_CALL osl_getVolumeDeviceMountPath( oslVolumeDeviceHandle Handle, rtl_uString **pstrPath )
234 if ( Handle && pstrPath )
236 rtl_uString_assign( pstrPath, static_cast<rtl_uString *>(Handle) );
237 return osl_File_E_None;
239 else
240 return osl_File_E_INVAL;
243 #define DIRECTORYITEM_DRIVE 0
244 #define DIRECTORYITEM_FILE 1
245 #define DIRECTORYITEM_SERVER 2
247 namespace {
249 struct DirectoryItem_Impl
251 UINT uType = 0;
252 union {
253 WIN32_FIND_DATAW FindData;
254 WCHAR cDriveString[MAX_PATH];
256 OUString m_sFullPath;
257 bool bFullPathNormalized = false;
258 int nRefCount = 0;
263 #define DIRECTORYTYPE_LOCALROOT 0
264 #define DIRECTORYTYPE_NETROOT 1
265 #define DIRECTORYTYPE_FILESYSTEM 3
267 namespace {
269 struct Directory_Impl
271 UINT uType = 0;
272 union {
273 HANDLE hDirectory = nullptr;
274 HANDLE hEnumDrives;
276 OUString m_sDirectoryPath;
279 typedef struct tagDRIVEENUM
281 LPCWSTR lpIdent;
282 WCHAR cBuffer[/*('Z' - 'A' + 1) * sizeof("A:\\") + 1*/256];
283 LPCWSTR lpCurrent;
284 } DRIVEENUM, *PDRIVEENUM, *LPDRIVEENUM;
288 static HANDLE OpenLogicalDrivesEnum()
290 auto xEnum = std::make_unique<DRIVEENUM>();
291 DWORD dwNumCopied = GetLogicalDriveStringsW( SAL_N_ELEMENTS(xEnum->cBuffer) - 1, xEnum->cBuffer );
293 if ( dwNumCopied && dwNumCopied < SAL_N_ELEMENTS(xEnum->cBuffer) )
295 xEnum->lpCurrent = xEnum->cBuffer;
296 xEnum->lpIdent = L"tagDRIVEENUM";
298 else
300 xEnum.reset();
302 return xEnum ? static_cast<HANDLE>(xEnum.release()) : INVALID_HANDLE_VALUE;
305 static bool EnumLogicalDrives(HANDLE hEnum, LPWSTR lpBuffer)
307 LPDRIVEENUM pEnum = static_cast<LPDRIVEENUM>(hEnum);
308 if ( !pEnum )
310 SetLastError( ERROR_INVALID_HANDLE );
311 return false;
314 int nLen = wcslen( pEnum->lpCurrent );
315 if ( !nLen )
317 SetLastError( ERROR_NO_MORE_FILES );
318 return false;
321 CopyMemory( lpBuffer, pEnum->lpCurrent, (nLen + 1) * sizeof(WCHAR) );
322 pEnum->lpCurrent += nLen + 1;
323 return true;
326 static bool CloseLogicalDrivesEnum(HANDLE hEnum)
328 bool fSuccess = false;
329 LPDRIVEENUM pEnum = static_cast<LPDRIVEENUM>(hEnum);
331 if ( pEnum )
333 delete pEnum;
334 fSuccess = true;
336 else
337 SetLastError( ERROR_INVALID_HANDLE );
339 return fSuccess;
342 namespace {
344 typedef struct tagDIRECTORY
346 HANDLE hFind;
347 WIN32_FIND_DATAW aFirstData;
348 } DIRECTORY, *PDIRECTORY, *LPDIRECTORY;
352 static HANDLE OpenDirectory(const OUString& path)
354 if (path.isEmpty())
355 return nullptr;
357 std::u16string_view suffix;
358 if (!path.endsWith(u"\\"))
359 suffix = u"*.*";
360 else
361 suffix = u"\\*.*";
363 std::unique_ptr<WCHAR[]> szFileMask(new (std::nothrow) WCHAR[path.getLength() + suffix.length() + 1]);
364 assert(szFileMask); // Don't handle OOM conditions
365 WCHAR* pos = std::copy_n(path.getStr(), path.getLength(), szFileMask.get());
366 pos = std::copy_n(suffix.data(), suffix.length(), pos);
367 *pos = 0;
369 auto xDirectory = std::make_unique<DIRECTORY>();
370 xDirectory->hFind = FindFirstFileW(szFileMask.get(), &xDirectory->aFirstData);
372 if (!IsValidHandle(xDirectory->hFind))
374 if ( GetLastError() != ERROR_NO_MORE_FILES )
376 xDirectory.reset();
380 return static_cast<HANDLE>(xDirectory.release());
383 static bool EnumDirectory(HANDLE hDirectory, LPWIN32_FIND_DATAW pFindData)
385 LPDIRECTORY pDirectory = static_cast<LPDIRECTORY>(hDirectory);
386 if ( !pDirectory )
388 SetLastError( ERROR_INVALID_HANDLE );
389 return false;
392 bool fSuccess = false;
393 bool fValid;
396 if ( pDirectory->aFirstData.cFileName[0] )
398 *pFindData = pDirectory->aFirstData;
399 fSuccess = true;
400 pDirectory->aFirstData.cFileName[0] = 0;
402 else if ( IsValidHandle( pDirectory->hFind ) )
403 fSuccess = FindNextFileW( pDirectory->hFind, pFindData );
404 else
406 fSuccess = false;
407 SetLastError( ERROR_NO_MORE_FILES );
410 fValid = fSuccess && wcscmp( L".", pFindData->cFileName ) != 0 && wcscmp( L"..", pFindData->cFileName ) != 0;
412 } while( fSuccess && !fValid );
414 return fSuccess;
417 static bool CloseDirectory(HANDLE hDirectory)
419 bool fSuccess = false;
420 LPDIRECTORY pDirectory = static_cast<LPDIRECTORY>(hDirectory);
422 if (pDirectory)
424 if (IsValidHandle(pDirectory->hFind))
425 fSuccess = FindClose(pDirectory->hFind);
427 delete pDirectory;
429 else
430 SetLastError(ERROR_INVALID_HANDLE);
432 return fSuccess;
435 static oslFileError osl_openLocalRoot(
436 rtl_uString *strDirectoryPath, oslDirectory *pDirectory)
438 if ( !pDirectory )
439 return osl_File_E_INVAL;
441 *pDirectory = nullptr;
443 OUString strSysPath;
444 oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strDirectoryPath), &strSysPath.pData, false);
445 if ( osl_File_E_None != error )
446 return error;
448 std::unique_ptr<Directory_Impl> pDirImpl(new (std::nothrow) Directory_Impl);
449 assert(pDirImpl); // Don't handle OOM conditions
450 pDirImpl->m_sDirectoryPath = strSysPath;
452 /* Append backslash if necessary */
454 /* @@@ToDo
455 use function ensure backslash
457 sal_uInt32 nLen = pDirImpl->m_sDirectoryPath.getLength();
458 if ( nLen && pDirImpl->m_sDirectoryPath[nLen - 1] != L'\\' )
460 pDirImpl->m_sDirectoryPath += "\\";
463 pDirImpl->uType = DIRECTORYTYPE_LOCALROOT;
464 pDirImpl->hEnumDrives = OpenLogicalDrivesEnum();
466 /* @@@ToDo
467 Use IsValidHandle(...)
469 if (pDirImpl->hEnumDrives == INVALID_HANDLE_VALUE)
470 return oslTranslateFileError(GetLastError());
472 *pDirectory = pDirImpl.release();
473 return osl_File_E_None;
476 static oslFileError osl_openFileDirectory(
477 rtl_uString *strDirectoryPath, oslDirectory *pDirectory)
479 if ( !pDirectory )
480 return osl_File_E_INVAL;
481 *pDirectory = nullptr;
483 std::unique_ptr<Directory_Impl> pDirImpl(new (std::nothrow) Directory_Impl);
484 assert(pDirImpl); // Don't handle OOM conditions
485 pDirImpl->m_sDirectoryPath = strDirectoryPath;
487 /* Append backslash if necessary */
489 /* @@@ToDo
490 use function ensure backslash
492 sal_uInt32 nLen = pDirImpl->m_sDirectoryPath.getLength();
493 if ( nLen && pDirImpl->m_sDirectoryPath[nLen - 1] != '\\' )
494 pDirImpl->m_sDirectoryPath += "\\";
496 pDirImpl->uType = DIRECTORYTYPE_FILESYSTEM;
497 pDirImpl->hDirectory = OpenDirectory(pDirImpl->m_sDirectoryPath);
499 if ( !pDirImpl->hDirectory )
500 return oslTranslateFileError(GetLastError());
502 *pDirectory = pDirImpl.release();
503 return osl_File_E_None;
506 static oslFileError osl_openNetworkServer(
507 rtl_uString *strSysDirPath, oslDirectory *pDirectory)
509 NETRESOURCEW aNetResource;
510 HANDLE hEnum;
511 DWORD dwError;
513 ZeroMemory( &aNetResource, sizeof(aNetResource) );
515 aNetResource.lpRemoteName = o3tl::toW(strSysDirPath->buffer);
517 dwError = WNetOpenEnumW(
518 RESOURCE_GLOBALNET,
519 RESOURCETYPE_DISK,
520 RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER,
521 &aNetResource,
522 &hEnum );
524 if ( ERROR_SUCCESS == dwError )
526 Directory_Impl *pDirImpl = new (std::nothrow) Directory_Impl;
527 assert(pDirImpl); // Don't handle OOM conditions
528 pDirImpl->uType = DIRECTORYTYPE_NETROOT;
529 pDirImpl->hDirectory = hEnum;
530 *pDirectory = static_cast<oslDirectory>(pDirImpl);
532 return oslTranslateFileError( dwError );
535 static DWORD create_dir_with_callback(
536 rtl_uString * dir_path,
537 oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
538 void* pData)
540 // Create the specified directory and call the
541 // user specified callback function. On success
542 // the function returns ERROR_SUCCESS else a Win32 error code.
544 bool bCreated = CreateDirectoryW( o3tl::toW(rtl_uString_getStr( dir_path )), nullptr );
546 if ( bCreated )
548 if (aDirectoryCreationCallbackFunc)
550 OUString url;
551 osl_getFileURLFromSystemPath(dir_path, &(url.pData));
552 aDirectoryCreationCallbackFunc(pData, url.pData);
554 return ERROR_SUCCESS;
556 return GetLastError();
559 static sal_Int32 path_make_parent(rtl_uString* path)
561 /* Cut off the last part of the given path to
562 get the parent only, e.g. 'c:\dir\subdir' ->
563 'c:\dir' or '\\share\sub\dir' -> '\\share\sub'
564 @return The position where the path has been cut
565 off (this is the position of the last backslash).
566 If there are no more parents 0 will be returned,
567 e.g. 'c:\' or '\\Share' have no more parents */
569 OSL_PRECOND(OUString::unacquired(&path).indexOf('/') == -1, "Path must not contain slashes");
570 OSL_PRECOND(has_path_parent(OUString::unacquired(&path)), "Path must have a parent");
572 sal_Int32 pos = OUString::unacquired(&path).lastIndexOf('\\');
573 assert(pos >= 0);
574 *(path->buffer + pos) = 0;
575 return pos;
578 static DWORD create_dir_recursively_(
579 rtl_uString * dir_path,
580 oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
581 void* pData)
583 OSL_PRECOND(!OUString::unacquired(&dir_path).endsWith(u"\\"),
584 "Path must not end with a backslash");
586 DWORD w32_error = create_dir_with_callback(
587 dir_path, aDirectoryCreationCallbackFunc, pData);
588 if ((w32_error != ERROR_PATH_NOT_FOUND) || !has_path_parent(OUString::unacquired(&dir_path)))
589 return w32_error;
591 const sal_Int32 oldLen = dir_path->length;
592 dir_path->length = path_make_parent(dir_path); // dir_path->buffer[pos] = 0, restore below
594 w32_error = create_dir_recursively_(
595 dir_path, aDirectoryCreationCallbackFunc, pData);
597 dir_path->buffer[dir_path->length] = '\\'; // restore
598 dir_path->length = oldLen;
600 if (ERROR_SUCCESS != w32_error && ERROR_ALREADY_EXISTS != w32_error)
601 return w32_error;
603 return create_dir_with_callback(dir_path, aDirectoryCreationCallbackFunc, pData);
606 oslFileError SAL_CALL osl_createDirectoryPath(
607 rtl_uString* aDirectoryUrl,
608 oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
609 void* pData)
611 if (aDirectoryUrl == nullptr)
612 return osl_File_E_INVAL;
614 OUString sys_path;
615 oslFileError osl_error =
616 osl_getSystemPathFromFileURL_(OUString::unacquired(&aDirectoryUrl), &sys_path.pData, false);
618 if (osl_error != osl_File_E_None)
619 return osl_error;
621 systemPathRemoveSeparator(sys_path);
623 return oslTranslateFileError(create_dir_recursively_(
624 sys_path.pData, aDirectoryCreationCallbackFunc, pData));
627 oslFileError SAL_CALL osl_createDirectory(rtl_uString* strPath)
629 return osl_createDirectoryWithFlags(
630 strPath, osl_File_OpenFlag_Read | osl_File_OpenFlag_Write);
633 oslFileError osl_createDirectoryWithFlags(rtl_uString * strPath, sal_uInt32)
635 OUString strSysPath;
636 oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false);
638 if ( osl_File_E_None != error )
639 return error;
641 bool bCreated = CreateDirectoryW(o3tl::toW(strSysPath.getStr()), nullptr);
642 if ( !bCreated )
644 /*@@@ToDo
645 The following case is a hack because the ucb or the webtop had some
646 problems with the error code that CreateDirectory returns in
647 case the path is only a logical drive, should be removed!
650 if ((strSysPath.getLength() == 2 || (strSysPath.getLength() == 3 && strSysPath[2] == '\\'))
651 && rtl::isAsciiAlpha(strSysPath[0]) && strSysPath[1] == ':')
652 SetLastError( ERROR_ALREADY_EXISTS );
654 error = oslTranslateFileError( GetLastError() );
657 return error;
660 oslFileError SAL_CALL osl_removeDirectory(rtl_uString* strPath)
662 OUString strSysPath;
663 oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false);
665 if ( osl_File_E_None == error )
667 if (RemoveDirectoryW(o3tl::toW(strSysPath.getStr())))
668 error = osl_File_E_None;
669 else
670 error = oslTranslateFileError( GetLastError() );
672 return error;
675 oslFileError SAL_CALL osl_openDirectory(rtl_uString *strDirectoryPath, oslDirectory *pDirectory)
677 oslFileError error;
679 if ( 0 == rtl_ustr_ascii_compareIgnoreAsciiCase( strDirectoryPath->buffer, "file:///" ) )
680 error = osl_openLocalRoot( strDirectoryPath, pDirectory );
681 else
683 OUString strSysDirectoryPath;
684 DWORD dwPathType;
686 error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strDirectoryPath), &strSysDirectoryPath.pData, false);
688 if ( osl_File_E_None != error )
689 return error;
691 dwPathType = IsValidFilePath(strSysDirectoryPath, VALIDATEPATH_NORMAL, nullptr);
693 if ( dwPathType & PATHTYPE_IS_SERVER )
694 error = osl_openNetworkServer(strSysDirectoryPath.pData, pDirectory);
695 else
696 error = osl_openFileDirectory(strSysDirectoryPath.pData, pDirectory);
698 return error;
701 static oslFileError osl_getNextNetResource(
702 oslDirectory Directory, oslDirectoryItem *pItem, sal_uInt32 /*uHint*/ )
704 Directory_Impl *pDirImpl = static_cast<Directory_Impl *>(Directory);
705 DirectoryItem_Impl *pItemImpl = nullptr;
706 BYTE buffer[16384];
707 LPNETRESOURCEW lpNetResource = reinterpret_cast<LPNETRESOURCEW>(buffer);
708 DWORD dwError, dwCount, dwBufSize;
710 if ( !pItem )
711 return osl_File_E_INVAL;
712 *pItem = nullptr;
714 if ( !pDirImpl )
715 return osl_File_E_INVAL;
717 dwCount = 1;
718 dwBufSize = sizeof(buffer);
719 dwError = WNetEnumResourceW( pDirImpl->hDirectory, &dwCount, lpNetResource, &dwBufSize );
721 switch ( dwError )
723 case NO_ERROR:
724 case ERROR_MORE_DATA:
726 pItemImpl = new (std::nothrow) DirectoryItem_Impl;
727 if ( !pItemImpl )
728 return osl_File_E_NOMEM;
730 pItemImpl->uType = DIRECTORYITEM_DRIVE;
731 osl_acquireDirectoryItem( static_cast<oslDirectoryItem>(pItemImpl) );
733 wcscpy( pItemImpl->cDriveString, lpNetResource->lpRemoteName );
735 *pItem = pItemImpl;
737 return osl_File_E_None;
738 case ERROR_NO_MORE_ITEMS:
739 return osl_File_E_NOENT;
740 default:
741 return oslTranslateFileError( dwError );
745 static oslFileError osl_getNextDrive(
746 oslDirectory Directory, oslDirectoryItem *pItem, sal_uInt32 /*uHint*/ )
748 Directory_Impl *pDirImpl = static_cast<Directory_Impl *>(Directory);
750 if ( !pItem )
751 return osl_File_E_INVAL;
752 *pItem = nullptr;
754 if ( !pDirImpl )
755 return osl_File_E_INVAL;
757 std::unique_ptr<DirectoryItem_Impl> pItemImpl(new (std::nothrow) DirectoryItem_Impl);
758 if ( !pItemImpl )
759 return osl_File_E_NOMEM;
761 pItemImpl->uType = DIRECTORYITEM_DRIVE;
762 osl_acquireDirectoryItem(pItemImpl.get());
763 if (!EnumLogicalDrives(pDirImpl->hEnumDrives, pItemImpl->cDriveString))
764 return oslTranslateFileError(GetLastError());
766 *pItem = pItemImpl.release();
767 return osl_File_E_None;
770 static oslFileError osl_getNextFileItem(
771 oslDirectory Directory, oslDirectoryItem *pItem, sal_uInt32 /*uHint*/)
773 Directory_Impl *pDirImpl = static_cast<Directory_Impl *>(Directory);
775 if ( !pItem )
776 return osl_File_E_INVAL;
777 *pItem = nullptr;
779 if ( !pDirImpl )
780 return osl_File_E_INVAL;
782 std::unique_ptr<DirectoryItem_Impl> pItemImpl(new (std::nothrow) DirectoryItem_Impl);
783 if ( !pItemImpl )
784 return osl_File_E_NOMEM;
786 if (!EnumDirectory(pDirImpl->hDirectory, &pItemImpl->FindData))
787 return oslTranslateFileError( GetLastError() );
789 pItemImpl->uType = DIRECTORYITEM_FILE;
790 pItemImpl->nRefCount = 1;
792 pItemImpl->m_sFullPath = pDirImpl->m_sDirectoryPath + o3tl::toU(pItemImpl->FindData.cFileName);
794 pItemImpl->bFullPathNormalized = true;
795 *pItem = pItemImpl.release();
796 return osl_File_E_None;
799 oslFileError SAL_CALL osl_getNextDirectoryItem(
800 oslDirectory Directory, oslDirectoryItem *pItem, sal_uInt32 uHint)
802 Directory_Impl *pDirImpl = static_cast<Directory_Impl *>(Directory);
804 /* Assume failure */
806 if ( !pItem )
807 return osl_File_E_INVAL;
808 *pItem = nullptr;
810 if ( !pDirImpl )
811 return osl_File_E_INVAL;
813 switch ( pDirImpl->uType )
815 case DIRECTORYTYPE_LOCALROOT:
816 return osl_getNextDrive( Directory, pItem, uHint );
817 case DIRECTORYTYPE_NETROOT:
818 return osl_getNextNetResource( Directory, pItem, uHint );
819 case DIRECTORYTYPE_FILESYSTEM:
820 return osl_getNextFileItem( Directory, pItem, uHint );
821 default:
822 return osl_File_E_INVAL;
826 oslFileError SAL_CALL osl_closeDirectory(oslDirectory Directory)
828 Directory_Impl *pDirImpl = static_cast<Directory_Impl *>(Directory);
829 oslFileError eError = osl_File_E_INVAL;
831 if ( pDirImpl )
833 switch ( pDirImpl->uType )
835 case DIRECTORYTYPE_FILESYSTEM:
836 eError = CloseDirectory( pDirImpl->hDirectory ) ? osl_File_E_None : oslTranslateFileError( GetLastError() );
837 break;
838 case DIRECTORYTYPE_LOCALROOT:
839 eError = CloseLogicalDrivesEnum( pDirImpl->hEnumDrives ) ? osl_File_E_None : oslTranslateFileError( GetLastError() );
840 break;
841 case DIRECTORYTYPE_NETROOT:
843 DWORD err = WNetCloseEnum(pDirImpl->hDirectory);
844 eError = (err == NO_ERROR) ? osl_File_E_None : oslTranslateFileError(err);
846 break;
847 default:
848 OSL_FAIL( "Invalid directory type" );
849 break;
852 delete pDirImpl;
854 return eError;
857 namespace {
859 /* Different types of paths */
860 enum PATHTYPE
862 PATHTYPE_SYNTAXERROR = 0,
863 PATHTYPE_NETROOT,
864 PATHTYPE_NETSERVER,
865 PATHTYPE_VOLUME,
866 PATHTYPE_FILE
871 oslFileError SAL_CALL osl_getDirectoryItem(rtl_uString *strFilePath, oslDirectoryItem *pItem)
873 oslFileError error = osl_File_E_None;
874 OUString strSysFilePath;
875 PATHTYPE type = PATHTYPE_FILE;
876 DWORD dwPathType;
878 /* Assume failure */
880 if ( !pItem )
881 return osl_File_E_INVAL;
883 *pItem = nullptr;
885 error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strFilePath), &strSysFilePath.pData, false);
887 if ( osl_File_E_None != error )
888 return error;
890 dwPathType = IsValidFilePath( strSysFilePath, VALIDATEPATH_NORMAL, nullptr );
892 if ( dwPathType & PATHTYPE_IS_VOLUME )
893 type = PATHTYPE_VOLUME;
894 else if ( dwPathType & PATHTYPE_IS_SERVER )
895 type = PATHTYPE_NETSERVER;
896 else
897 type = PATHTYPE_FILE;
899 switch ( type )
901 case PATHTYPE_NETSERVER:
903 DirectoryItem_Impl* pItemImpl = new (std::nothrow) DirectoryItem_Impl;
905 if ( !pItemImpl )
906 return osl_File_E_NOMEM;
908 pItemImpl->uType = DIRECTORYITEM_SERVER;
910 osl_acquireDirectoryItem(pItemImpl);
911 pItemImpl->m_sFullPath = strSysFilePath;
913 // Assign a title anyway
915 int iSrc = 2;
916 int iDst = 0;
918 while( iSrc < strSysFilePath.getLength() && strSysFilePath[iSrc] && strSysFilePath[iSrc] != '\\')
920 pItemImpl->FindData.cFileName[iDst++] = strSysFilePath[iSrc++];
924 *pItem = pItemImpl;
926 break;
927 case PATHTYPE_VOLUME:
929 DirectoryItem_Impl* pItemImpl = new (std::nothrow) DirectoryItem_Impl;
931 if ( !pItemImpl )
932 return osl_File_E_NOMEM;
934 pItemImpl->uType = DIRECTORYITEM_DRIVE;
936 osl_acquireDirectoryItem(pItemImpl);
938 auto pos = std::copy_n(strSysFilePath.getStr(), strSysFilePath.getLength(), pItemImpl->cDriveString);
939 pItemImpl->cDriveString[0] = rtl::toAsciiUpperCase( pItemImpl->cDriveString[0] );
941 if (!strSysFilePath.endsWith(u"\\"))
942 *pos++ = '\\';
943 *pos = 0;
945 *pItem = pItemImpl;
947 break;
948 case PATHTYPE_SYNTAXERROR:
949 case PATHTYPE_NETROOT:
950 case PATHTYPE_FILE:
952 HANDLE hFind;
953 WIN32_FIND_DATAW aFindData;
955 if (!strSysFilePath.isEmpty() && strSysFilePath[strSysFilePath.getLength() - 1] == '\\')
956 strSysFilePath = strSysFilePath.copy(0, strSysFilePath.getLength() - 1);
958 hFind = FindFirstFileW( o3tl::toW(strSysFilePath.getStr()), &aFindData );
960 if ( hFind != INVALID_HANDLE_VALUE )
962 DirectoryItem_Impl *pItemImpl = new (std::nothrow) DirectoryItem_Impl;
963 if (!pItemImpl)
964 error = osl_File_E_NOMEM;
966 if (osl_File_E_None == error)
968 osl_acquireDirectoryItem(static_cast<oslDirectoryItem>(pItemImpl));
970 CopyMemory(&pItemImpl->FindData, &aFindData, sizeof(WIN32_FIND_DATAW));
971 pItemImpl->m_sFullPath = strSysFilePath;
973 pItemImpl->uType = DIRECTORYITEM_FILE;
974 *pItem = pItemImpl;
977 FindClose( hFind );
979 else
980 error = oslTranslateFileError( GetLastError() );
982 break;
985 return error;
988 oslFileError SAL_CALL osl_acquireDirectoryItem( oslDirectoryItem Item )
990 DirectoryItem_Impl *pItemImpl = static_cast<DirectoryItem_Impl *>(Item);
992 if ( !pItemImpl )
993 return osl_File_E_INVAL;
995 pItemImpl->nRefCount++;
996 return osl_File_E_None;
999 oslFileError SAL_CALL osl_releaseDirectoryItem( oslDirectoryItem Item )
1001 DirectoryItem_Impl *pItemImpl = static_cast<DirectoryItem_Impl *>(Item);
1003 if ( !pItemImpl )
1004 return osl_File_E_INVAL;
1006 if ( ! --pItemImpl->nRefCount )
1007 delete pItemImpl;
1009 return osl_File_E_None;
1012 sal_Bool
1013 SAL_CALL osl_identicalDirectoryItem( oslDirectoryItem a, oslDirectoryItem b)
1015 DirectoryItem_Impl *pA = static_cast<DirectoryItem_Impl *>(a);
1016 DirectoryItem_Impl *pB = static_cast<DirectoryItem_Impl *>(b);
1017 if (a == b)
1018 return true;
1019 /* same name => same item, unless renaming / moving madness has occurred */
1020 if (pA->m_sFullPath == pB->m_sFullPath)
1021 return true;
1023 // FIXME: as/when/if this is used in anger on Windows we could
1024 // do better here.
1026 return false;
1029 static bool is_floppy_A_present()
1030 { return (GetLogicalDrives() & 1); }
1032 static bool is_floppy_B_present()
1033 { return (GetLogicalDrives() & 2); }
1035 static bool is_floppy_volume_mount_point(const OUString& path)
1037 // determines if a volume mount point shows to a floppy
1038 // disk by comparing the unique volume names
1039 static const LPCWSTR FLOPPY_A = L"A:\\";
1040 static const LPCWSTR FLOPPY_B = L"B:\\";
1042 OUString p(path);
1043 systemPathEnsureSeparator(p);
1045 WCHAR vn[51];
1046 if (GetVolumeNameForVolumeMountPointW(o3tl::toW(p.getStr()), vn, SAL_N_ELEMENTS(vn)))
1048 WCHAR vnfloppy[51];
1049 if (is_floppy_A_present() &&
1050 GetVolumeNameForVolumeMountPointW(FLOPPY_A, vnfloppy, SAL_N_ELEMENTS(vnfloppy)) &&
1051 (0 == wcscmp(vn, vnfloppy)))
1052 return true;
1054 if (is_floppy_B_present() &&
1055 GetVolumeNameForVolumeMountPointW(FLOPPY_B, vnfloppy, SAL_N_ELEMENTS(vnfloppy)) &&
1056 (0 == wcscmp(vn, vnfloppy)))
1057 return true;
1059 return false;
1062 static bool is_floppy_drive(const OUString& path)
1064 static const LPCWSTR FLOPPY_DRV_LETTERS = L"AaBb";
1066 // we must take into account that even a floppy
1067 // drive may be mounted to a directory so checking
1068 // for the drive letter alone is not sufficient
1069 // we must compare the unique volume name with
1070 // that of the available floppy disks
1072 const sal_Unicode* pszPath = path.getStr();
1073 return ((wcschr(FLOPPY_DRV_LETTERS, pszPath[0]) && (L':' == pszPath[1])) || is_floppy_volume_mount_point(path));
1076 static bool is_volume_mount_point(const OUString& path)
1078 OUString p(path);
1079 systemPathRemoveSeparator(p);
1081 if (is_floppy_drive(p))
1082 return false;
1084 DWORD fattr = GetFileAttributesW(o3tl::toW(p.getStr()));
1085 if ((INVALID_FILE_ATTRIBUTES == fattr) ||
1086 !(FILE_ATTRIBUTE_REPARSE_POINT & fattr))
1087 return false;
1089 bool is_volume_root = false;
1090 WIN32_FIND_DATAW find_data;
1091 HANDLE h_find = FindFirstFileW(o3tl::toW(p.getStr()), &find_data);
1093 if (IsValidHandle(h_find) &&
1094 (FILE_ATTRIBUTE_REPARSE_POINT & find_data.dwFileAttributes) &&
1095 (IO_REPARSE_TAG_MOUNT_POINT == find_data.dwReserved0))
1097 is_volume_root = true;
1099 if (IsValidHandle(h_find))
1100 FindClose(h_find);
1101 return is_volume_root;
1104 static UINT get_volume_mount_point_drive_type(const OUString& path)
1106 if (0 == path.getLength())
1107 return GetDriveTypeW(nullptr);
1109 OUString p(path);
1110 systemPathEnsureSeparator(p);
1112 WCHAR vn[51];
1113 if (GetVolumeNameForVolumeMountPointW(o3tl::toW(p.getStr()), vn, SAL_N_ELEMENTS(vn)))
1114 return GetDriveTypeW(vn);
1116 return DRIVE_NO_ROOT_DIR;
1119 static bool is_drivetype_request(sal_uInt32 field_mask)
1121 return (field_mask & osl_VolumeInfo_Mask_Attributes);
1124 static oslFileError osl_get_drive_type(
1125 const OUString& path, oslVolumeInfo* pInfo)
1127 // GetDriveType fails on empty volume mount points
1128 // see Knowledge Base Q244089
1129 UINT drive_type;
1130 if (is_volume_mount_point(path))
1131 drive_type = get_volume_mount_point_drive_type(path);
1132 else
1133 drive_type = GetDriveTypeW(o3tl::toW(path.getStr()));
1135 if (DRIVE_NO_ROOT_DIR == drive_type)
1136 return oslTranslateFileError(ERROR_INVALID_DRIVE);
1138 pInfo->uValidFields |= osl_VolumeInfo_Mask_Attributes;
1140 switch (drive_type)
1142 case DRIVE_CDROM:
1143 pInfo->uAttributes |= osl_Volume_Attribute_CompactDisc | osl_Volume_Attribute_Removeable;
1144 break;
1145 case DRIVE_REMOVABLE:
1146 pInfo->uAttributes |= osl_Volume_Attribute_Removeable;
1147 if (is_floppy_drive(path))
1148 pInfo->uAttributes |= osl_Volume_Attribute_FloppyDisk;
1149 break;
1150 case DRIVE_FIXED:
1151 pInfo->uAttributes |= osl_Volume_Attribute_FixedDisk;
1152 break;
1153 case DRIVE_RAMDISK:
1154 pInfo->uAttributes |= osl_Volume_Attribute_RAMDisk;
1155 break;
1156 case DRIVE_REMOTE:
1157 pInfo->uAttributes |= osl_Volume_Attribute_Remote;
1158 break;
1159 case DRIVE_UNKNOWN:
1160 pInfo->uAttributes = 0;
1161 break;
1162 default:
1163 pInfo->uValidFields &= ~osl_VolumeInfo_Mask_Attributes;
1164 pInfo->uAttributes = 0;
1165 break;
1167 return osl_File_E_None;
1170 static bool is_volume_space_info_request(sal_uInt32 field_mask)
1172 return (field_mask &
1173 (osl_VolumeInfo_Mask_TotalSpace |
1174 osl_VolumeInfo_Mask_UsedSpace |
1175 osl_VolumeInfo_Mask_FreeSpace));
1178 static void get_volume_space_information(
1179 const OUString& path, oslVolumeInfo *pInfo)
1181 bool ret = GetDiskFreeSpaceExW(
1182 o3tl::toW(path.getStr()),
1183 reinterpret_cast<PULARGE_INTEGER>(&pInfo->uFreeSpace),
1184 reinterpret_cast<PULARGE_INTEGER>(&pInfo->uTotalSpace),
1185 nullptr);
1187 if (ret)
1189 pInfo->uUsedSpace = pInfo->uTotalSpace - pInfo->uFreeSpace;
1190 pInfo->uValidFields |= osl_VolumeInfo_Mask_TotalSpace |
1191 osl_VolumeInfo_Mask_UsedSpace |
1192 osl_VolumeInfo_Mask_FreeSpace;
1196 static bool is_filesystem_attributes_request(sal_uInt32 field_mask)
1198 return (field_mask &
1199 (osl_VolumeInfo_Mask_MaxNameLength |
1200 osl_VolumeInfo_Mask_MaxPathLength |
1201 osl_VolumeInfo_Mask_FileSystemName |
1202 osl_VolumeInfo_Mask_FileSystemCaseHandling));
1205 static oslFileError get_filesystem_attributes(
1206 const OUString& path, sal_uInt32 field_mask, oslVolumeInfo* pInfo)
1208 pInfo->uAttributes = 0;
1210 // osl_get_drive_type must be called first because
1211 // this function resets osl_VolumeInfo_Mask_Attributes
1212 // on failure
1213 if (is_drivetype_request(field_mask))
1215 oslFileError osl_error = osl_get_drive_type(path, pInfo);
1216 if (osl_File_E_None != osl_error)
1217 return osl_error;
1219 if (is_filesystem_attributes_request(field_mask))
1221 /* the following two parameters can not be longer than MAX_PATH+1 */
1222 WCHAR vn[MAX_PATH+1];
1223 WCHAR fsn[MAX_PATH+1];
1225 DWORD serial;
1226 DWORD mcl;
1227 DWORD flags;
1229 LPCWSTR pszPath = o3tl::toW(path.getStr());
1230 if (GetVolumeInformationW(pszPath, vn, MAX_PATH+1, &serial, &mcl, &flags, fsn, MAX_PATH+1))
1232 // Currently sal does not use this value, instead MAX_PATH is used
1233 pInfo->uValidFields |= osl_VolumeInfo_Mask_MaxNameLength;
1234 pInfo->uMaxNameLength = mcl;
1236 // Should the uMaxPathLength be set to 32767, "\\?\" prefix allows it
1237 pInfo->uValidFields |= osl_VolumeInfo_Mask_MaxPathLength;
1238 pInfo->uMaxPathLength = MAX_PATH;
1240 pInfo->uValidFields |= osl_VolumeInfo_Mask_FileSystemName;
1241 rtl_uString_newFromStr(&pInfo->ustrFileSystemName, o3tl::toU(fsn));
1243 // volumes (even NTFS) will always be considered case
1244 // insensitive because the Win32 API is not able to
1245 // deal with case sensitive volumes see M$ Knowledge Base
1246 // article 100625 that's why we never set the attribute
1247 // osl_Volume_Attribute_Case_Sensitive
1249 if (flags & FS_CASE_IS_PRESERVED)
1250 pInfo->uAttributes |= osl_Volume_Attribute_Case_Is_Preserved;
1252 pInfo->uValidFields |= osl_VolumeInfo_Mask_Attributes;
1255 return osl_File_E_None;
1258 static bool path_get_parent(OUString& path)
1260 OSL_PRECOND(path.lastIndexOf('/') == -1, "Path must not have slashes");
1262 if (!has_path_parent(path))
1264 sal_Int32 i = path.lastIndexOf('\\');
1265 if (-1 < i)
1267 path = path.copy(0, i);
1268 return true;
1271 return false;
1274 static void path_travel_to_volume_root(const OUString& system_path, OUString& volume_root)
1276 OUString sys_path(system_path);
1278 while(!is_volume_mount_point(sys_path) && path_get_parent(sys_path))
1279 /**/;
1281 volume_root = sys_path;
1282 systemPathEnsureSeparator(volume_root);
1285 oslFileError SAL_CALL osl_getVolumeInformation(
1286 rtl_uString *ustrURL, oslVolumeInfo *pInfo, sal_uInt32 uFieldMask )
1288 if (!pInfo)
1289 return osl_File_E_INVAL;
1291 OUString system_path;
1292 oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrURL), &system_path.pData, false);
1294 if (osl_File_E_None != error)
1295 return error;
1297 OUString volume_root;
1298 path_travel_to_volume_root(system_path, volume_root);
1300 pInfo->uValidFields = 0;
1302 if ((error = get_filesystem_attributes(volume_root, uFieldMask, pInfo)) != osl_File_E_None)
1303 return error;
1305 if (is_volume_space_info_request(uFieldMask))
1306 get_volume_space_information(volume_root, pInfo);
1308 if (uFieldMask & osl_VolumeInfo_Mask_DeviceHandle)
1310 error = osl_getFileURLFromSystemPath(volume_root.pData, reinterpret_cast<rtl_uString**>(&pInfo->pDeviceHandle));
1311 if (error != osl_File_E_None)
1312 return error;
1313 pInfo->uValidFields |= osl_VolumeInfo_Mask_DeviceHandle;
1316 return osl_File_E_None;
1319 static oslFileError osl_getDriveInfo(
1320 oslDirectoryItem Item, oslFileStatus *pStatus, sal_uInt32 uFieldMask)
1322 DirectoryItem_Impl *pItemImpl = static_cast<DirectoryItem_Impl *>(Item);
1323 WCHAR cDrive[3] = L"A:";
1324 WCHAR cRoot[4] = L"A:\\";
1326 if ( !pItemImpl )
1327 return osl_File_E_INVAL;
1329 pStatus->uValidFields = 0;
1331 cDrive[0] = pItemImpl->cDriveString[0];
1332 cRoot[0] = pItemImpl->cDriveString[0];
1334 if ( uFieldMask & osl_FileStatus_Mask_FileName )
1336 if ( pItemImpl->cDriveString[0] == '\\' && pItemImpl->cDriveString[1] == '\\' )
1338 LPCWSTR lpFirstBkSlash = wcschr( &pItemImpl->cDriveString[2], '\\' );
1340 if ( lpFirstBkSlash && lpFirstBkSlash[1] )
1342 LPCWSTR lpLastBkSlash = wcschr( &lpFirstBkSlash[1], '\\' );
1344 if ( lpLastBkSlash )
1345 rtl_uString_newFromStr_WithLength( &pStatus->ustrFileName, o3tl::toU(&lpFirstBkSlash[1]), lpLastBkSlash - lpFirstBkSlash - 1 );
1346 else
1347 rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(&lpFirstBkSlash[1]) );
1348 pStatus->uValidFields |= osl_FileStatus_Mask_FileName;
1351 else switch ( GetDriveTypeW( cRoot ) )
1353 case DRIVE_REMOTE:
1355 WCHAR szBuffer[1024];
1356 DWORD const dwBufsizeConst = SAL_N_ELEMENTS(szBuffer);
1357 DWORD dwBufsize = dwBufsizeConst;
1359 DWORD dwResult = WNetGetConnectionW( cDrive, szBuffer, &dwBufsize );
1360 if ( NO_ERROR == dwResult )
1362 WCHAR szFileName[dwBufsizeConst + 16];
1364 swprintf( szFileName, L"%s [%s]", cDrive, szBuffer );
1365 rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(szFileName) );
1367 else
1368 rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(cDrive) );
1370 pStatus->uValidFields |= osl_FileStatus_Mask_FileName;
1371 break;
1372 case DRIVE_FIXED:
1374 WCHAR szVolumeNameBuffer[1024];
1375 DWORD const dwBufsizeConst = SAL_N_ELEMENTS(szVolumeNameBuffer);
1377 if ( GetVolumeInformationW( cRoot, szVolumeNameBuffer, dwBufsizeConst, nullptr, nullptr, nullptr, nullptr, 0 ) )
1379 WCHAR szFileName[dwBufsizeConst + 16];
1381 swprintf( szFileName, L"%s [%s]", cDrive, szVolumeNameBuffer );
1382 rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(szFileName) );
1384 else
1385 rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(cDrive) );
1387 pStatus->uValidFields |= osl_FileStatus_Mask_FileName;
1388 break;
1389 case DRIVE_CDROM:
1390 case DRIVE_REMOVABLE:
1391 pStatus->uValidFields |= osl_FileStatus_Mask_FileName;
1392 rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(cRoot) );
1393 break;
1394 case DRIVE_UNKNOWN:
1395 default:
1396 break;
1400 pStatus->eType = osl_File_Type_Volume;
1401 pStatus->uValidFields |= osl_FileStatus_Mask_Type;
1403 if ( uFieldMask & osl_FileStatus_Mask_FileURL )
1405 rtl_uString *ustrSystemPath = nullptr;
1407 rtl_uString_newFromStr( &ustrSystemPath, o3tl::toU(pItemImpl->cDriveString) );
1408 oslFileError error = osl_getFileURLFromSystemPath( ustrSystemPath, &pStatus->ustrFileURL );
1409 rtl_uString_release( ustrSystemPath );
1410 if (error != osl_File_E_None)
1411 return error;
1412 pStatus->uValidFields |= osl_FileStatus_Mask_FileURL;
1414 return osl_File_E_None;
1417 static oslFileError osl_getServerInfo(
1418 oslDirectoryItem Item, oslFileStatus *pStatus, sal_uInt32 uFieldMask )
1420 DirectoryItem_Impl *pItemImpl = static_cast<DirectoryItem_Impl *>(Item);
1421 if ( !pItemImpl )
1422 return osl_File_E_INVAL;
1424 pStatus->uValidFields = 0;
1425 pStatus->eType = osl_File_Type_Directory;
1426 pStatus->uValidFields |= osl_FileStatus_Mask_Type;
1428 if ( uFieldMask & osl_FileStatus_Mask_FileURL )
1430 oslFileError error = osl_getFileURLFromSystemPath( pItemImpl->m_sFullPath.pData, &pStatus->ustrFileURL );
1431 if (error != osl_File_E_None)
1432 return error;
1433 pStatus->uValidFields |= osl_FileStatus_Mask_FileURL;
1435 return osl_File_E_None;
1438 oslFileError SAL_CALL osl_getFileStatus(
1439 oslDirectoryItem Item,
1440 oslFileStatus *pStatus,
1441 sal_uInt32 uFieldMask )
1443 DirectoryItem_Impl *pItemImpl = static_cast<DirectoryItem_Impl *>(Item);
1445 if ( !pItemImpl )
1446 return osl_File_E_INVAL;
1448 switch ( pItemImpl->uType )
1450 case DIRECTORYITEM_DRIVE:
1451 return osl_getDriveInfo( Item, pStatus, uFieldMask );
1452 case DIRECTORYITEM_SERVER:
1453 return osl_getServerInfo( Item, pStatus, uFieldMask );
1454 default:
1455 break;
1458 OUString sFullPath(pItemImpl->m_sFullPath);
1460 // Prefix long paths, windows API calls expect this prefix
1461 // (only local paths starting with something like C: or D:)
1462 if (sFullPath.getLength() >= MAX_PATH && isalpha(sFullPath[0]) && sFullPath[1] == ':')
1463 sFullPath = "\\\\?\\" + sFullPath;
1465 if ( uFieldMask & osl_FileStatus_Mask_Validate )
1467 HANDLE hFind = FindFirstFileW( o3tl::toW(sFullPath.getStr() ), &pItemImpl->FindData );
1469 if ( hFind != INVALID_HANDLE_VALUE )
1470 FindClose( hFind );
1471 else
1472 return oslTranslateFileError( GetLastError() );
1474 uFieldMask &= ~ osl_FileStatus_Mask_Validate;
1477 /* If no fields to retrieve left ignore pStatus */
1478 if ( !uFieldMask )
1479 return osl_File_E_None;
1481 /* Otherwise, this must be a valid pointer */
1482 if ( !pStatus )
1483 return osl_File_E_INVAL;
1485 if ( pStatus->uStructSize != sizeof(oslFileStatus) )
1486 return osl_File_E_INVAL;
1488 pStatus->uValidFields = 0;
1490 /* File time stamps */
1492 if ( (uFieldMask & osl_FileStatus_Mask_ModifyTime) &&
1493 FileTimeToTimeValue( &pItemImpl->FindData.ftLastWriteTime, &pStatus->aModifyTime ) )
1494 pStatus->uValidFields |= osl_FileStatus_Mask_ModifyTime;
1496 if ( (uFieldMask & osl_FileStatus_Mask_AccessTime) &&
1497 FileTimeToTimeValue( &pItemImpl->FindData.ftLastAccessTime, &pStatus->aAccessTime ) )
1498 pStatus->uValidFields |= osl_FileStatus_Mask_AccessTime;
1500 if ( (uFieldMask & osl_FileStatus_Mask_CreationTime) &&
1501 FileTimeToTimeValue( &pItemImpl->FindData.ftCreationTime, &pStatus->aCreationTime ) )
1502 pStatus->uValidFields |= osl_FileStatus_Mask_CreationTime;
1504 /* Most of the fields are already set, regardless of required fields */
1506 rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(pItemImpl->FindData.cFileName) );
1507 pStatus->uValidFields |= osl_FileStatus_Mask_FileName;
1509 if ((FILE_ATTRIBUTE_REPARSE_POINT & pItemImpl->FindData.dwFileAttributes) &&
1510 (IO_REPARSE_TAG_MOUNT_POINT == pItemImpl->FindData.dwReserved0))
1511 pStatus->eType = osl_File_Type_Volume;
1512 else if (pItemImpl->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1513 pStatus->eType = osl_File_Type_Directory;
1514 else
1515 pStatus->eType = osl_File_Type_Regular;
1517 pStatus->uValidFields |= osl_FileStatus_Mask_Type;
1519 pStatus->uAttributes = pItemImpl->FindData.dwFileAttributes;
1520 // tdf#157448: RO attribute is ignored for directories on Windows:
1521 // https://learn.microsoft.com/en-us/windows/desktop/FileIO/file-attribute-constants
1522 if (pStatus->uAttributes & FILE_ATTRIBUTE_DIRECTORY)
1523 pStatus->uAttributes &= ~sal_uInt64(FILE_ATTRIBUTE_READONLY);
1524 pStatus->uValidFields |= osl_FileStatus_Mask_Attributes;
1526 pStatus->uFileSize = static_cast<sal_uInt64>(pItemImpl->FindData.nFileSizeLow) + (static_cast<sal_uInt64>(pItemImpl->FindData.nFileSizeHigh) << 32);
1527 pStatus->uValidFields |= osl_FileStatus_Mask_FileSize;
1529 if ( uFieldMask & osl_FileStatus_Mask_LinkTargetURL )
1531 oslFileError error = osl_getFileURLFromSystemPath( sFullPath.pData, &pStatus->ustrLinkTargetURL );
1532 if (error != osl_File_E_None)
1533 return error;
1535 pStatus->uValidFields |= osl_FileStatus_Mask_LinkTargetURL;
1538 if ( uFieldMask & osl_FileStatus_Mask_FileURL )
1540 if ( !pItemImpl->bFullPathNormalized )
1542 ::osl::LongPathBuffer<sal_Unicode> aBuffer(MAX_LONG_PATH);
1543 sal_uInt32 nNewLen = GetLongPathNameW(o3tl::toW(sFullPath.getStr()), o3tl::toW(aBuffer),
1544 aBuffer.getBufSizeInSymbols());
1546 if ( nNewLen )
1548 /* Capitalizes drive name (single letter). Windows file paths are processed
1549 case-sensitively. While parsing a path, function osl_DirectoryItem has case
1550 PATHTYPE_VOLUME for drives, and capitalizes them. That can be overwritten by
1551 function osl_getFileStatus, in it win32 api GetLongPathNameW does no
1552 capitalization. Thus it needs to be postprocessed.*/
1553 sal_Int32 nIndex = rtl_ustr_indexOfChar(aBuffer, ':');
1554 if (nIndex > 0) {
1555 aBuffer[nIndex - 1] = rtl::toAsciiUpperCase(aBuffer[nIndex - 1]);
1558 pItemImpl->m_sFullPath = OUString(&*aBuffer, nNewLen);
1559 sFullPath = pItemImpl->m_sFullPath;
1560 pItemImpl->bFullPathNormalized = true;
1564 oslFileError error = osl_getFileURLFromSystemPath( sFullPath.pData, &pStatus->ustrFileURL );
1565 if (error != osl_File_E_None)
1566 return error;
1567 pStatus->uValidFields |= osl_FileStatus_Mask_FileURL;
1570 return osl_File_E_None;
1573 oslFileError SAL_CALL osl_setFileAttributes(
1574 rtl_uString *ustrFileURL,
1575 sal_uInt64 uAttributes )
1577 oslFileError error;
1578 OUString ustrSysPath;
1579 DWORD dwFileAttributes;
1580 bool fSuccess;
1582 // Converts the normalized path into a systempath
1583 error = osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrFileURL), &ustrSysPath.pData, false);
1585 if ( osl_File_E_None != error )
1586 return error;
1588 dwFileAttributes = GetFileAttributesW(o3tl::toW(ustrSysPath.getStr()));
1590 if ( DWORD(-1) != dwFileAttributes )
1592 dwFileAttributes &= ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN);
1594 if ( uAttributes & osl_File_Attribute_ReadOnly )
1595 dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
1597 if ( uAttributes & osl_File_Attribute_Hidden )
1598 dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
1600 fSuccess = SetFileAttributesW(o3tl::toW(ustrSysPath.getStr()), dwFileAttributes);
1602 else
1604 fSuccess = false;
1607 if ( !fSuccess )
1608 error = oslTranslateFileError( GetLastError() );
1610 return error;
1613 oslFileError SAL_CALL osl_setFileTime(
1614 rtl_uString *filePath,
1615 const TimeValue *aCreationTime,
1616 const TimeValue *aLastAccessTime,
1617 const TimeValue *aLastWriteTime)
1619 oslFileError error;
1620 OUString sysPath;
1621 FILETIME *lpCreationTime=nullptr;
1622 FILETIME *lpLastAccessTime=nullptr;
1623 FILETIME *lpLastWriteTime=nullptr;
1624 FILETIME ftCreationTime;
1625 FILETIME ftLastAccessTime;
1626 FILETIME ftLastWriteTime;
1627 HANDLE hFile;
1628 bool fSuccess;
1630 error=osl_getSystemPathFromFileURL_(OUString::unacquired(&filePath), &sysPath.pData, false);
1632 if (error==osl_File_E_INVAL)
1633 return error;
1635 hFile=CreateFileW(o3tl::toW(sysPath.getStr()), GENERIC_WRITE, 0, nullptr , OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
1637 if (hFile==INVALID_HANDLE_VALUE)
1638 return osl_File_E_NOENT;
1640 if (TimeValueToFileTime(aCreationTime, &ftCreationTime))
1641 lpCreationTime=&ftCreationTime;
1643 if (TimeValueToFileTime(aLastAccessTime, &ftLastAccessTime))
1644 lpLastAccessTime=&ftLastAccessTime;
1646 if (TimeValueToFileTime(aLastWriteTime, &ftLastWriteTime))
1647 lpLastWriteTime=&ftLastWriteTime;
1649 fSuccess=SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
1651 CloseHandle(hFile);
1653 if (!fSuccess)
1654 return osl_File_E_INVAL;
1655 else
1656 return osl_File_E_None;
1659 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */