1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 <osl/detail/file.h>
22 #include <osl/diagnose.h>
23 #include <rtl/string.hxx>
24 #include <sal/log.hxx>
27 #include "file_impl.hxx"
28 #include "file_error_transl.hxx"
29 #include "file_path_helper.hxx"
30 #include "file_url.hxx"
31 #include "uunxapi.hxx"
32 #include "readwrite_helper.hxx"
33 #include "unixerrnostring.hxx"
49 #include <osl/detail/android-bootstrap.h>
52 /************************************************************************
55 * - Fix: check for corresponding struct sizes in exported functions
56 * - check size/use of oslDirectory
57 * - check size/use of oslDirectoryItem
58 ***********************************************************************/
64 OString strPath
; /* holds native directory path */
73 lo_apk_dir
* pApkDirStruct
;
79 DirectoryItem_Impl::DirectoryItem_Impl(
80 OString strFilePath
, unsigned char DType
)
81 : m_strFilePath (std::move(strFilePath
)),
86 DirectoryItem_Impl::~DirectoryItem_Impl()
90 void DirectoryItem_Impl::acquire()
94 void DirectoryItem_Impl::release()
96 if (--m_RefCount
== 0)
100 oslFileType
DirectoryItem_Impl::getFileType() const
104 #ifdef _DIRENT_HAVE_D_TYPE
106 return osl_File_Type_Link
;
108 return osl_File_Type_Directory
;
110 return osl_File_Type_Regular
;
112 return osl_File_Type_Fifo
;
114 return osl_File_Type_Socket
;
117 return osl_File_Type_Special
;
118 #endif /* _DIRENT_HAVE_D_TYPE */
122 return osl_File_Type_Unknown
;
125 static oslFileError
osl_psz_createDirectory(
126 char const * pszPath
, sal_uInt32 flags
);
127 static oslFileError
osl_psz_removeDirectory(const char* pszPath
);
129 oslFileError SAL_CALL
osl_openDirectory(rtl_uString
* ustrDirectoryURL
, oslDirectory
* pDirectory
)
135 if ((ustrDirectoryURL
== nullptr) || (ustrDirectoryURL
->length
== 0) || (pDirectory
== nullptr))
136 return osl_File_E_INVAL
;
138 /* convert file URL to system path */
139 eRet
= osl::detail::convertUrlToPathname(OUString::unacquired(&ustrDirectoryURL
), &path
);
141 if( eRet
!= osl_File_E_None
)
144 osl_systemPathRemoveSeparator(path
.pData
);
146 if (isForbidden(path
, osl_File_OpenFlag_Read
))
147 return osl_File_E_ACCES
;
151 auto const n
= std::max(int(path
.getLength() + 1), int(PATH_MAX
));
152 auto const tmp
= std::make_unique
<char[]>(n
);
153 std::strcpy(tmp
.get(), path
.getStr());
154 if (macxp_resolveAlias(tmp
.get(), n
) != 0) {
155 return oslTranslateFileError(errno
);
157 path
= OString(tmp
.get(), std::strlen(tmp
.get()));
162 if( strncmp( path
.getStr(), "/assets/", sizeof( "/assets/" ) - 1) == 0 )
164 lo_apk_dir
*pdir
= lo_apk_opendir( path
.getStr() );
168 DirectoryImpl
* pDirImpl
= new(std::nothrow
) DirectoryImpl
;
172 pDirImpl
->eKind
= DirectoryImpl::KIND_ASSETS
;
173 pDirImpl
->pApkDirStruct
= pdir
;
174 pDirImpl
->strPath
= path
;
176 *pDirectory
= (oslDirectory
) pDirImpl
;
177 return osl_File_E_None
;
182 lo_apk_closedir( pdir
);
190 DIR *pdir
= opendir( path
.getStr() );
194 SAL_INFO("sal.file", "opendir(" << path
<< ") => " << pdir
);
196 /* create and initialize impl structure */
197 DirectoryImpl
* pDirImpl
= new(std::nothrow
) DirectoryImpl
;
201 pDirImpl
->pDirStruct
= pdir
;
202 pDirImpl
->strPath
= path
;
204 pDirImpl
->eKind
= DirectoryImpl::KIND_DIRENT
;
206 *pDirectory
= static_cast<oslDirectory
>(pDirImpl
);
207 return osl_File_E_None
;
215 SAL_INFO("sal.file", "opendir(" << path
<< "): " << UnixErrnoString(e
));
216 // Restore errno after possible modification by SAL_INFO above
221 return oslTranslateFileError(errno
);
224 oslFileError SAL_CALL
osl_closeDirectory(oslDirectory pDirectory
)
226 SAL_WARN_IF(!pDirectory
, "sal.file", "pDirectory is nullptr");
227 DirectoryImpl
* pDirImpl
= static_cast<DirectoryImpl
*>(pDirectory
);
228 oslFileError err
= osl_File_E_None
;
231 return osl_File_E_INVAL
;
234 if (pDirImpl
->eKind
== DirectoryImpl::KIND_ASSETS
)
236 if (lo_apk_closedir(pDirImpl
->pApkDirStruct
))
242 if (closedir( pDirImpl
->pDirStruct
) != 0)
245 SAL_INFO("sal.file", "closedir(" << pDirImpl
->pDirStruct
<< "): " << UnixErrnoString(e
));
246 err
= oslTranslateFileError(e
);
249 SAL_INFO("sal.file", "closedir(" << pDirImpl
->pDirStruct
<< "): OK");
257 /**********************************************
260 * readdir wrapper, filters out "." and ".."
262 *********************************************/
264 static struct dirent
* osl_readdir_impl_(DIR* pdir
)
266 struct dirent
* pdirent
;
268 while ((pdirent
= readdir(pdir
)) != nullptr)
270 if ((strcmp(pdirent
->d_name
, ".") == 0) || (strcmp(pdirent
->d_name
, "..") == 0))
278 oslFileError SAL_CALL
osl_getNextDirectoryItem(oslDirectory pDirectory
,
279 oslDirectoryItem
* pItem
, SAL_UNUSED_PARAMETER sal_uInt32
/*uHint*/)
281 SAL_WARN_IF(!pDirectory
, "sal.file", "pDirectory is nullptr");
282 SAL_WARN_IF(!pItem
, "sal.file", "pItem is nullptr");
284 DirectoryImpl
* pDirImpl
= static_cast<DirectoryImpl
*>(pDirectory
);
286 struct dirent
* pEntry
;
288 if ((pDirectory
== nullptr) || (pItem
== nullptr))
289 return osl_File_E_INVAL
;
292 if(pDirImpl
->eKind
== DirectoryImpl::KIND_ASSETS
)
294 pEntry
= lo_apk_readdir(pDirImpl
->pApkDirStruct
);
299 pEntry
= osl_readdir_impl_(pDirImpl
->pDirStruct
);
303 return osl_File_E_NOENT
;
305 char const * filename
= pEntry
->d_name
;
308 // convert decomposed filename to precomposed UTF-8
309 char composed_name
[BUFSIZ
];
310 CFMutableStringRef strRef
= CFStringCreateMutable(nullptr, 0 );
311 CFStringAppendCString(strRef
, filename
, kCFStringEncodingUTF8
); // UTF8 is default on Mac OSX
312 CFStringNormalize(strRef
, kCFStringNormalizationFormC
);
313 CFStringGetCString(strRef
, composed_name
, BUFSIZ
, kCFStringEncodingUTF8
);
315 filename
= composed_name
;
318 strFileName
= OString(filename
, strlen(filename
));
320 auto const strFilePath
= osl::systemPathMakeAbsolutePath(pDirImpl
->strPath
, strFileName
);
322 DirectoryItem_Impl
* pImpl
= static_cast< DirectoryItem_Impl
* >(*pItem
);
325 #ifdef _DIRENT_HAVE_D_TYPE
326 pImpl
= new DirectoryItem_Impl(strFilePath
, pEntry
->d_type
);
328 pImpl
= new DirectoryItem_Impl(strFilePath
);
329 #endif /* _DIRENT_HAVE_D_TYPE */
332 return osl_File_E_None
;
335 oslFileError SAL_CALL
osl_getDirectoryItem(rtl_uString
* ustrFileURL
, oslDirectoryItem
* pItem
)
337 OString strSystemPath
;
338 oslFileError osl_error
= osl_File_E_INVAL
;
340 if ((!ustrFileURL
) || (ustrFileURL
->length
== 0) || (!pItem
))
341 return osl_File_E_INVAL
;
343 osl_error
= osl::detail::convertUrlToPathname(OUString::unacquired(&ustrFileURL
), &strSystemPath
);
344 if (osl_error
!= osl_File_E_None
)
347 osl_systemPathRemoveSeparator(strSystemPath
.pData
);
349 if (isForbidden(strSystemPath
, osl_File_OpenFlag_Read
))
350 return osl_File_E_ACCES
;
352 if (osl::access(strSystemPath
, F_OK
) == -1)
354 osl_error
= oslTranslateFileError(errno
);
358 *pItem
= new DirectoryItem_Impl(std::move(strSystemPath
));
364 oslFileError SAL_CALL
osl_acquireDirectoryItem( oslDirectoryItem Item
)
366 DirectoryItem_Impl
* pImpl
= static_cast< DirectoryItem_Impl
* >(Item
);
367 if (pImpl
== nullptr)
368 return osl_File_E_INVAL
;
371 return osl_File_E_None
;
374 oslFileError SAL_CALL
osl_releaseDirectoryItem( oslDirectoryItem Item
)
376 DirectoryItem_Impl
* pImpl
= static_cast< DirectoryItem_Impl
* >(Item
);
377 if (pImpl
== nullptr)
378 return osl_File_E_INVAL
;
381 return osl_File_E_None
;
384 oslFileError SAL_CALL
osl_createDirectory( rtl_uString
* ustrDirectoryURL
)
386 return osl_createDirectoryWithFlags(
387 ustrDirectoryURL
, osl_File_OpenFlag_Read
| osl_File_OpenFlag_Write
);
390 oslFileError
osl_createDirectoryWithFlags(
391 rtl_uString
* ustrDirectoryURL
, sal_uInt32 flags
)
396 SAL_WARN_IF((!ustrDirectoryURL
) || (ustrDirectoryURL
->length
== 0),
397 "sal.file", "Invalid directory URL");
399 /* convert directory url to system path */
400 eRet
= FileURLToPath( path
, PATH_MAX
, ustrDirectoryURL
);
401 if( eRet
!= osl_File_E_None
)
405 if ( macxp_resolveAlias( path
, PATH_MAX
) != 0 )
406 return oslTranslateFileError( errno
);
409 return osl_psz_createDirectory( path
, flags
);
412 oslFileError SAL_CALL
osl_removeDirectory( rtl_uString
* ustrDirectoryURL
)
417 SAL_WARN_IF((!ustrDirectoryURL
) || (ustrDirectoryURL
->length
== 0),
418 "sal.file", "Invalid directory URL");
420 /* convert directory url to system path */
421 eRet
= FileURLToPath( path
, PATH_MAX
, ustrDirectoryURL
);
422 if( eRet
!= osl_File_E_None
)
426 if ( macxp_resolveAlias( path
, PATH_MAX
) != 0 )
427 return oslTranslateFileError( errno
);
430 return osl_psz_removeDirectory( path
);
433 oslFileError
osl_psz_createDirectory(char const * pszPath
, sal_uInt32 flags
)
435 if (isForbidden(pszPath
, osl_File_OpenFlag_Create
))
436 return osl_File_E_ACCES
;
440 = (((flags
& osl_File_OpenFlag_Read
) == 0
442 : ((flags
& osl_File_OpenFlag_Private
) == 0
443 ? S_IRUSR
| S_IXUSR
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
444 : S_IRUSR
| S_IXUSR
))
445 | ((flags
& osl_File_OpenFlag_Write
) == 0
447 : ((flags
& osl_File_OpenFlag_Private
) == 0
448 ? S_IWUSR
| S_IWGRP
| S_IWOTH
451 nRet
= mkdir(pszPath
,mode
);
456 SAL_INFO("sal.file", "mkdir(" << pszPath
<< ",0" << std::oct
<< mode
<< std::dec
<< "): " << UnixErrnoString(nRet
));
457 return oslTranslateFileError(nRet
);
460 SAL_INFO("sal.file", "mkdir(" << pszPath
<< ",0" << std::oct
<< mode
<< std::dec
<< "): OK");
462 return osl_File_E_None
;
465 static oslFileError
osl_psz_removeDirectory( const char* pszPath
)
467 if (isForbidden(pszPath
, osl_File_OpenFlag_Write
))
468 return osl_File_E_ACCES
;
470 int nRet
= rmdir(pszPath
);
475 SAL_INFO("sal.file", "rmdir(" << pszPath
<< "): " << UnixErrnoString(nRet
));
476 return oslTranslateFileError(nRet
);
479 SAL_INFO("sal.file", "rmdir(" << pszPath
<< "): OK");
481 return osl_File_E_None
;
484 static int path_make_parent(char* path
)
486 int i
= rtl_str_lastIndexOfChar(path
, '/');
496 static int create_dir_with_callback(
497 char* directory_path
,
498 oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc
,
501 if (osl::mkdir(directory_path
, S_IRWXU
| S_IRWXG
| S_IRWXO
) == 0)
503 if (aDirectoryCreationCallbackFunc
)
506 osl::detail::convertPathnameToUrl(directory_path
, &url
);
507 aDirectoryCreationCallbackFunc(pData
, url
.pData
);
514 static oslFileError
create_dir_recursively_(
516 oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc
,
519 OSL_PRECOND((rtl_str_getLength(dir_path
) > 0) && ((dir_path
+ (rtl_str_getLength(dir_path
) - 1)) != (dir_path
+ rtl_str_lastIndexOfChar(dir_path
, '/'))),
520 "Path must not end with a slash");
522 int native_err
= create_dir_with_callback(
523 dir_path
, aDirectoryCreationCallbackFunc
, pData
);
526 return osl_File_E_None
;
528 if (native_err
!= ENOENT
)
529 return oslTranslateFileError(native_err
);
531 // we step back until '/a_dir' at maximum because
532 // we should get an error unequal ENOENT when
533 // we try to create 'a_dir' at '/' and would so
535 int pos
= path_make_parent(dir_path
);
537 oslFileError osl_error
= create_dir_recursively_(
538 dir_path
, aDirectoryCreationCallbackFunc
, pData
);
540 if (osl_error
!= osl_File_E_None
&& osl_error
!= osl_File_E_EXIST
)
545 return create_dir_recursively_(dir_path
, aDirectoryCreationCallbackFunc
, pData
);
548 oslFileError SAL_CALL
osl_createDirectoryPath(
549 rtl_uString
* aDirectoryUrl
,
550 oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc
,
553 if (aDirectoryUrl
== nullptr)
554 return osl_File_E_INVAL
;
557 oslFileError osl_error
= osl::detail::convertUrlToPathname(
558 OUString::unacquired(&aDirectoryUrl
), &sys_path
);
560 if (osl_error
!= osl_File_E_None
)
563 osl::systemPathRemoveSeparator(sys_path
);
565 if (isForbidden(sys_path
, osl_File_OpenFlag_Create
))
566 return osl_File_E_ACCES
;
568 // const_cast because sys_path is a local copy which we want to modify inplace instead of
569 // copy it into another buffer on the heap again
570 return create_dir_recursively_(sys_path
.pData
->buffer
, aDirectoryCreationCallbackFunc
, pData
);
573 static oslFileError
osl_unlinkFile(const char* pszPath
);
574 static oslFileError
osl_psz_copyFile(const char* pszPath
, const char* pszDestPath
, bool preserveMetadata
);
575 static oslFileError
osl_psz_moveFile(const char* pszPath
, const char* pszDestPath
);
577 static oslFileError
oslDoCopy(const char* pszSourceFileName
, const char* pszDestFileName
, mode_t nMode
, size_t nSourceSize
, bool DestFileExists
);
578 static void attemptChangeMetadata(const char* pszFileName
, mode_t nMode
, time_t nAcTime
, time_t nModTime
, uid_t nUID
, gid_t nGID
);
579 static int oslDoCopyLink(const char* pszSourceFileName
, const char* pszDestFileName
);
580 static int oslDoCopyFile(const char* pszSourceFileName
, const char* pszDestFileName
, size_t nSourceSize
, mode_t mode
);
581 static oslFileError
oslDoMoveFile(const char* pszPath
, const char* pszDestPath
);
583 oslFileError SAL_CALL
osl_moveFile( rtl_uString
* ustrFileURL
, rtl_uString
* ustrDestURL
)
585 char srcPath
[PATH_MAX
];
586 char destPath
[PATH_MAX
];
589 SAL_WARN_IF((!ustrFileURL
) || (ustrFileURL
->length
== 0), "sal.file", "Invalid source file URL");
590 SAL_WARN_IF((!ustrDestURL
) || (ustrDestURL
->length
== 0), "sal.file", "Invalid destination file URL");
592 /* convert source url to system path */
593 eRet
= FileURLToPath( srcPath
, PATH_MAX
, ustrFileURL
);
594 if( eRet
!= osl_File_E_None
)
597 /* convert destination url to system path */
598 eRet
= FileURLToPath( destPath
, PATH_MAX
, ustrDestURL
);
599 if( eRet
!= osl_File_E_None
)
602 if (isForbidden(srcPath
, osl_File_OpenFlag_Read
) ||
603 isForbidden(destPath
, osl_File_OpenFlag_Create
))
604 return osl_File_E_ACCES
;
607 if ( macxp_resolveAlias( srcPath
, PATH_MAX
) != 0 || macxp_resolveAlias( destPath
, PATH_MAX
) != 0 )
608 return oslTranslateFileError( errno
);
611 return oslDoMoveFile( srcPath
, destPath
);
614 oslFileError SAL_CALL
osl_replaceFile(rtl_uString
* ustrFileURL
, rtl_uString
* ustrDestURL
)
617 char destPath
[PATH_MAX
];
618 oslFileError eRet
= FileURLToPath(destPath
, PATH_MAX
, ustrDestURL
);
620 if (isForbidden(destPath
, osl_File_OpenFlag_Create
))
621 return osl_File_E_ACCES
;
623 if (eRet
== osl_File_E_None
)
625 struct stat aFileStat
;
626 // coverity[fs_check_call] - unavoidable TOCTOU
627 int nRet
= stat(destPath
, &aFileStat
);
631 SAL_INFO("sal.file", "stat(" << destPath
<< "): " << UnixErrnoString(nRet
));
635 nGid
= aFileStat
.st_gid
;
639 eRet
= osl_moveFile(ustrFileURL
, ustrDestURL
);
641 if (eRet
== osl_File_E_None
&& nGid
!= -1)
643 int nRet
= chown(destPath
, -1, nGid
);
648 "chown(" << destPath
<< "-1, " << nGid
<< "): " << UnixErrnoString(nRet
));
655 oslFileError SAL_CALL
osl_copyFile( rtl_uString
* ustrFileURL
, rtl_uString
* ustrDestURL
)
657 char srcPath
[PATH_MAX
];
658 char destPath
[PATH_MAX
];
661 SAL_WARN_IF((!ustrFileURL
) || (ustrFileURL
->length
== 0), "sal.file", "Invalid source file URL");
662 SAL_WARN_IF((!ustrDestURL
) || (ustrDestURL
->length
== 0), "sal.file", "Invalid destination file URL");
664 /* convert source url to system path */
665 eRet
= FileURLToPath( srcPath
, PATH_MAX
, ustrFileURL
);
666 if( eRet
!= osl_File_E_None
)
669 /* convert destination url to system path */
670 eRet
= FileURLToPath( destPath
, PATH_MAX
, ustrDestURL
);
671 if( eRet
!= osl_File_E_None
)
675 if ( macxp_resolveAlias( srcPath
, PATH_MAX
) != 0 || macxp_resolveAlias( destPath
, PATH_MAX
) != 0 )
676 return oslTranslateFileError( errno
);
679 return osl_psz_copyFile( srcPath
, destPath
, false );
682 oslFileError SAL_CALL
osl_removeFile(rtl_uString
* ustrFileURL
)
687 SAL_WARN_IF(!ustrFileURL
|| ustrFileURL
->length
== 0, "sal.file", "Invalid file URL");
689 /* convert file url to system path */
690 eRet
= FileURLToPath(path
, PATH_MAX
, ustrFileURL
);
691 if (eRet
!= osl_File_E_None
)
695 if (macxp_resolveAlias(path
, PATH_MAX
) != 0)
696 return oslTranslateFileError(errno
);
699 if (isForbidden(path
, osl_File_OpenFlag_Write
))
700 return osl_File_E_ACCES
;
702 return osl_unlinkFile(path
);
705 static oslFileError
oslDoMoveFile(const char* pszPath
, const char* pszDestPath
)
707 oslFileError tErr
= osl_psz_moveFile(pszPath
,pszDestPath
);
708 if (tErr
== osl_File_E_None
)
711 if (tErr
!= osl_File_E_XDEV
)
714 tErr
= osl_psz_copyFile(pszPath
,pszDestPath
, true);
716 if (tErr
!= osl_File_E_None
)
718 osl_unlinkFile(pszDestPath
);
722 tErr
= osl_unlinkFile(pszPath
);
727 static oslFileError
osl_unlinkFile(const char* pszPath
)
732 nRet
= lstat_c(pszPath
,&aStat
);
736 return oslTranslateFileError(nRet
);
739 if (S_ISDIR(aStat
.st_mode
))
740 return osl_File_E_ISDIR
;
742 nRet
= unlink(pszPath
);
746 SAL_INFO("sal.file", "unlink(" << pszPath
<< "): " << UnixErrnoString(nRet
));
747 return oslTranslateFileError(nRet
);
750 SAL_INFO("sal.file", "unlink(" << pszPath
<< "): OK");
752 return osl_File_E_None
;
755 static oslFileError
osl_psz_moveFile(const char* pszPath
, const char* pszDestPath
)
757 if (isForbidden(pszPath
, osl_File_OpenFlag_Read
) ||
758 isForbidden(pszDestPath
, osl_File_OpenFlag_Create
))
759 return osl_File_E_ACCES
;
761 int nRet
= rename(pszPath
,pszDestPath
);
766 SAL_INFO("sal.file", "rename(" << pszPath
<< "," << pszDestPath
<< "): " << UnixErrnoString(nRet
));
767 return oslTranslateFileError(nRet
);
770 SAL_INFO("sal.file", "rename(" << pszPath
<< "," << pszDestPath
<< "): OK");
772 return osl_File_E_None
;
775 static oslFileError
osl_psz_copyFile( const char* pszPath
, const char* pszDestPath
, bool preserveMetadata
)
783 struct stat aFileStat
;
784 oslFileError tErr
=osl_File_E_invalidError
;
785 size_t nSourceSize
=0;
786 bool DestFileExists
=true;
788 if (isForbidden(pszPath
, osl_File_OpenFlag_Read
) ||
789 isForbidden(pszDestPath
, osl_File_OpenFlag_Create
))
790 return osl_File_E_ACCES
;
792 /* mfe: does the source file really exists? */
793 nRet
= lstat_c(pszPath
,&aFileStat
);
798 return oslTranslateFileError(nRet
);
801 /* we do only copy files here */
802 if (S_ISDIR(aFileStat
.st_mode
))
803 return osl_File_E_ISDIR
;
805 nSourceSize
= static_cast< size_t >(aFileStat
.st_size
);
806 nMode
= aFileStat
.st_mode
;
807 nAcTime
= aFileStat
.st_atime
;
808 nModTime
= aFileStat
.st_mtime
;
809 nUID
= aFileStat
.st_uid
;
810 nGID
= aFileStat
.st_gid
;
812 nRet
= stat_c(pszDestPath
,&aFileStat
);
818 // Checking for nonexistent files at least in the iCloud cache directory (like
819 // "/private/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/helloodt0.odt" fails
820 // with EPERM, not ENOENT.
822 DestFileExists
=false;
826 DestFileExists
=false;
829 /* mfe: the destination file must not be a directory! */
830 if (nRet
== 0 && S_ISDIR(aFileStat
.st_mode
))
831 return osl_File_E_ISDIR
;
833 /* mfe: file does not exists or is no dir */
835 tErr
= oslDoCopy(pszPath
, pszDestPath
, nMode
, nSourceSize
, DestFileExists
);
837 if (tErr
!= osl_File_E_None
)
840 if (preserveMetadata
)
841 attemptChangeMetadata(pszDestPath
, nMode
, nAcTime
, nModTime
, nUID
, nGID
);
846 static oslFileError
oslDoCopy(const char* pszSourceFileName
, const char* pszDestFileName
, mode_t nMode
, size_t nSourceSize
, bool DestFileExists
)
851 if ( DestFileExists
)
853 //TODO: better pick a temp file name instead of adding .osl-tmp:
854 // use the destination file to avoid EXDEV /* Cross-device link */
855 tmpDestFile
= pszDestFileName
+ OString::Concat(".osl-tmp");
856 if (rename(pszDestFileName
, tmpDestFile
.getStr()) != 0)
859 SAL_INFO("sal.file", "rename(" << pszDestFileName
<< ", " << tmpDestFile
860 << "): " << UnixErrnoString(e
));
863 DestFileExists
= false;
867 return osl_File_E_EXIST
; // for want of a better error code
872 SAL_INFO("sal.file", "rename(" << pszDestFileName
<< ", " << tmpDestFile
877 if ( S_ISREG(nMode
) )
879 /* copy SourceFile to DestFile */
880 nRet
= oslDoCopyFile(pszSourceFileName
,pszDestFileName
,nSourceSize
, nMode
);
882 else if ( S_ISLNK(nMode
) )
884 nRet
= oslDoCopyLink(pszSourceFileName
,pszDestFileName
);
891 if ( nRet
> 0 && DestFileExists
)
893 if (unlink(pszDestFileName
) != 0)
896 SAL_INFO("sal.file", "unlink(" << pszDestFileName
<< "): " << UnixErrnoString(e
));
899 SAL_INFO("sal.file", "unlink(" << pszDestFileName
<< "): OK");
901 if (rename(tmpDestFile
.getStr(), pszDestFileName
) != 0)
904 SAL_INFO("sal.file", "rename(" << tmpDestFile
<< ", " << pszDestFileName
905 << "): " << UnixErrnoString(e
));
908 SAL_INFO("sal.file", "rename(" << tmpDestFile
<< ", " << pszDestFileName
<< "): OK");
913 return oslTranslateFileError(nRet
);
916 if ( DestFileExists
)
918 unlink(tmpDestFile
.getStr());
921 return osl_File_E_None
;
924 void attemptChangeMetadata( const char* pszFileName
, mode_t nMode
, time_t nAcTime
, time_t nModTime
, uid_t nUID
, gid_t nGID
)
926 struct utimbuf aTimeBuffer
;
928 #if !defined AT_FDCWD
929 if (!S_ISLNK(nMode
) && chmod(pszFileName
, nMode
) < 0)
931 if ( fchmodat(AT_FDCWD
, pszFileName
, nMode
, AT_SYMLINK_NOFOLLOW
) < 0 )
935 SAL_INFO("sal.file", "chmod(" << pszFileName
<< ",0" << std::oct
<< nMode
<< std::dec
<<"): " << UnixErrnoString(e
));
938 SAL_INFO("sal.file", "chmod(" << pszFileName
<< ",0" << std::oct
<< nMode
<< std::dec
<<"): OK");
940 // No way to change utime of a symlink itself:
943 aTimeBuffer
.actime
=nAcTime
;
944 aTimeBuffer
.modtime
=nModTime
;
945 if ( utime(pszFileName
,&aTimeBuffer
) < 0 )
948 SAL_INFO("sal.file", "utime(" << pszFileName
<< "): errno " << e
);
952 if ( nUID
!= getuid() )
956 if ( lchown(pszFileName
,nUID
,nGID
) < 0 )
959 SAL_INFO("sal.file", "lchown(" << pszFileName
<< "): errno " << e
);
962 SAL_INFO("sal.file", "lchown(" << pszFileName
<< "): OK");
965 static int oslDoCopyLink(const char* pszSourceFileName
, const char* pszDestFileName
)
969 /* mfe: if dest file is symbolic link remove the link and place the file instead (hro says so) */
970 /* mfe: if source is a link copy the link and not the file it points to (hro says so) */
971 char pszLinkContent
[PATH_MAX
+1];
973 pszLinkContent
[0] = '\0';
975 nRet
= readlink(pszSourceFileName
,pszLinkContent
,PATH_MAX
);
983 pszLinkContent
[ nRet
] = 0;
985 nRet
= symlink(pszLinkContent
,pszDestFileName
);
996 static int oslDoCopyFile(const char* pszSourceFileName
, const char* pszDestFileName
, size_t nSourceSize
, mode_t mode
)
998 oslFileHandle SourceFileFH
=nullptr;
1002 if (openFilePath(pszSourceFileName
,
1004 osl_File_OpenFlag_Read
|osl_File_OpenFlag_NoLock
|osl_File_OpenFlag_NoExcl
, mode_t(-1)) != osl_File_E_None
)
1006 // Let's hope errno is still set relevantly after openFilePath...
1011 DestFileFD
=open(pszDestFileName
, O_WRONLY
| O_CREAT
, mode
);
1013 if ( DestFileFD
< 0 )
1016 SAL_INFO("sal.file", "open(" << pszDestFileName
<< ",O_WRONLY|O_CREAT,0" << std::oct
<< mode
<< std::dec
<< "): " << UnixErrnoString(nRet
));
1017 osl_closeFile(SourceFileFH
);
1021 SAL_INFO("sal.file", "open(" << pszDestFileName
<< ",O_WRONLY|O_CREAT,0" << std::oct
<< mode
<< std::dec
<< "): OK");
1023 size_t nRemains
= nSourceSize
;
1027 /* mmap has problems, try the direct streaming */
1028 char pBuffer
[0x7FFF];
1032 size_t nToRead
= std::min( sizeof(pBuffer
), nRemains
);
1035 if ( osl_readFile( SourceFileFH
, pBuffer
, nToRead
, &nRead
) != osl_File_E_None
|| nRead
> nToRead
|| nRead
== 0 )
1038 succeeded
= safeWrite( DestFileFD
, pBuffer
, nRead
);
1042 // We know nRead <= nToRead, so it must fit in a size_t
1043 nRemains
-= static_cast<size_t>(nRead
);
1056 osl_closeFile( SourceFileFH
);
1057 if ( close( DestFileFD
) == -1 )
1060 SAL_INFO("sal.file", "close(" << DestFileFD
<< "): " << UnixErrnoString(e
));
1065 SAL_INFO("sal.file", "close(" << DestFileFD
<< "): OK");
1070 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */