tdf#154546 skip dispatch when presenter controller is not set
[LibreOffice.git] / sal / osl / unx / file_misc.cxx
blob01842dc73154e61f1bee52e71f051ec1bb989bac
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 <osl/file.hxx>
21 #include <osl/detail/file.h>
23 #include <osl/diagnose.h>
24 #include <osl/thread.h>
25 #include <osl/signal.h>
26 #include <rtl/alloc.h>
27 #include <rtl/string.hxx>
28 #include <sal/log.hxx>
30 #include "system.hxx"
31 #include "file_impl.hxx"
32 #include "file_error_transl.hxx"
33 #include "file_path_helper.hxx"
34 #include "file_url.hxx"
35 #include "uunxapi.hxx"
36 #include "readwrite_helper.hxx"
37 #include "unixerrnostring.hxx"
39 #include <sys/types.h>
40 #include <errno.h>
41 #include <dirent.h>
42 #include <limits.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <sys/stat.h>
47 #include <sys/mman.h>
49 #include <algorithm>
50 #include <cassert>
51 #include <cstring>
52 #include <memory>
53 #include <new>
55 #ifdef ANDROID
56 #include <osl/detail/android-bootstrap.h>
57 #endif
59 /************************************************************************
60 * TODO
62 * - Fix: check for corresponding struct sizes in exported functions
63 * - check size/use of oslDirectory
64 * - check size/use of oslDirectoryItem
65 ***********************************************************************/
67 namespace {
69 struct DirectoryImpl
71 OString strPath; /* holds native directory path */
72 DIR* pDirStruct;
73 #ifdef ANDROID
74 enum Kind
76 KIND_DIRENT = 1,
77 KIND_ASSETS = 2
79 int eKind;
80 lo_apk_dir* pApkDirStruct;
81 #endif
86 DirectoryItem_Impl::DirectoryItem_Impl(
87 OString strFilePath, unsigned char DType)
88 : m_strFilePath (std::move(strFilePath)),
89 m_RefCount (1),
90 m_DType (DType)
93 DirectoryItem_Impl::~DirectoryItem_Impl()
97 void DirectoryItem_Impl::acquire()
99 ++m_RefCount;
101 void DirectoryItem_Impl::release()
103 if (--m_RefCount == 0)
104 delete this;
107 oslFileType DirectoryItem_Impl::getFileType() const
109 switch (m_DType)
111 #ifdef _DIRENT_HAVE_D_TYPE
112 case DT_LNK:
113 return osl_File_Type_Link;
114 case DT_DIR:
115 return osl_File_Type_Directory;
116 case DT_REG:
117 return osl_File_Type_Regular;
118 case DT_FIFO:
119 return osl_File_Type_Fifo;
120 case DT_SOCK:
121 return osl_File_Type_Socket;
122 case DT_CHR:
123 case DT_BLK:
124 return osl_File_Type_Special;
125 #endif /* _DIRENT_HAVE_D_TYPE */
126 default:
127 break;
129 return osl_File_Type_Unknown;
132 static oslFileError osl_psz_createDirectory(
133 char const * pszPath, sal_uInt32 flags);
134 static oslFileError osl_psz_removeDirectory(const char* pszPath);
136 oslFileError SAL_CALL osl_openDirectory(rtl_uString* ustrDirectoryURL, oslDirectory* pDirectory)
138 oslFileError eRet;
140 OString path;
142 if ((ustrDirectoryURL == nullptr) || (ustrDirectoryURL->length == 0) || (pDirectory == nullptr))
143 return osl_File_E_INVAL;
145 /* convert file URL to system path */
146 eRet = osl::detail::convertUrlToPathname(OUString::unacquired(&ustrDirectoryURL), &path);
148 if( eRet != osl_File_E_None )
149 return eRet;
151 osl_systemPathRemoveSeparator(path.pData);
153 #ifdef MACOSX
155 auto const n = std::max(int(path.getLength() + 1), int(PATH_MAX));
156 auto const tmp = std::make_unique<char[]>(n);
157 std::strcpy(tmp.get(), path.getStr());
158 if (macxp_resolveAlias(tmp.get(), n) != 0) {
159 return oslTranslateFileError(errno);
161 path = OString(tmp.get(), std::strlen(tmp.get()));
163 #endif /* MACOSX */
165 #ifdef ANDROID
166 if( strncmp( path.getStr(), "/assets/", sizeof( "/assets/" ) - 1) == 0 )
168 lo_apk_dir *pdir = lo_apk_opendir( path.getStr() );
170 if( pdir )
172 DirectoryImpl* pDirImpl = new(std::nothrow) DirectoryImpl;
174 if( pDirImpl )
176 pDirImpl->eKind = DirectoryImpl::KIND_ASSETS;
177 pDirImpl->pApkDirStruct = pdir;
178 pDirImpl->strPath = path;
180 *pDirectory = (oslDirectory) pDirImpl;
181 return osl_File_E_None;
183 else
185 errno = ENOMEM;
186 lo_apk_closedir( pdir );
190 else
191 #endif
193 /* open directory */
194 DIR *pdir = opendir( path.getStr() );
196 if( pdir )
198 SAL_INFO("sal.file", "opendir(" << path << ") => " << pdir);
200 /* create and initialize impl structure */
201 DirectoryImpl* pDirImpl = new(std::nothrow) DirectoryImpl;
203 if( pDirImpl )
205 pDirImpl->pDirStruct = pdir;
206 pDirImpl->strPath = path;
207 #ifdef ANDROID
208 pDirImpl->eKind = DirectoryImpl::KIND_DIRENT;
209 #endif
210 *pDirectory = static_cast<oslDirectory>(pDirImpl);
211 return osl_File_E_None;
213 errno = ENOMEM;
214 closedir( pdir );
216 else
218 int e = errno;
219 SAL_INFO("sal.file", "opendir(" << path << "): " << UnixErrnoString(e));
220 // Restore errno after possible modification by SAL_INFO above
221 errno = e;
225 return oslTranslateFileError(errno);
228 oslFileError SAL_CALL osl_closeDirectory(oslDirectory pDirectory)
230 SAL_WARN_IF(!pDirectory, "sal.file", "pDirectory is nullptr");
231 DirectoryImpl* pDirImpl = static_cast<DirectoryImpl*>(pDirectory);
232 oslFileError err = osl_File_E_None;
234 if (!pDirImpl)
235 return osl_File_E_INVAL;
237 #ifdef ANDROID
238 if (pDirImpl->eKind == DirectoryImpl::KIND_ASSETS)
240 if (lo_apk_closedir(pDirImpl->pApkDirStruct))
241 err = osl_File_E_IO;
243 else
244 #endif
246 if (closedir( pDirImpl->pDirStruct) != 0)
248 int e = errno;
249 SAL_INFO("sal.file", "closedir(" << pDirImpl->pDirStruct << "): " << UnixErrnoString(e));
250 err = oslTranslateFileError(e);
252 else
253 SAL_INFO("sal.file", "closedir(" << pDirImpl->pDirStruct << "): OK");
256 delete pDirImpl;
258 return err;
261 /**********************************************
262 * osl_readdir_impl_
264 * readdir wrapper, filters out "." and ".."
265 * on request
266 *********************************************/
268 static struct dirent* osl_readdir_impl_(DIR* pdir)
270 struct dirent* pdirent;
272 while ((pdirent = readdir(pdir)) != nullptr)
274 if ((strcmp(pdirent->d_name, ".") == 0) || (strcmp(pdirent->d_name, "..") == 0))
275 continue;
276 break;
279 return pdirent;
282 oslFileError SAL_CALL osl_getNextDirectoryItem(oslDirectory pDirectory,
283 oslDirectoryItem* pItem, SAL_UNUSED_PARAMETER sal_uInt32 /*uHint*/)
285 SAL_WARN_IF(!pDirectory, "sal.file", "pDirectory is nullptr");
286 SAL_WARN_IF(!pItem, "sal.file", "pItem is nullptr");
288 DirectoryImpl* pDirImpl = static_cast<DirectoryImpl*>(pDirectory);
289 OString strFileName;
290 struct dirent* pEntry;
292 if ((pDirectory == nullptr) || (pItem == nullptr))
293 return osl_File_E_INVAL;
295 #ifdef ANDROID
296 if(pDirImpl->eKind == DirectoryImpl::KIND_ASSETS)
298 pEntry = lo_apk_readdir(pDirImpl->pApkDirStruct);
300 else
301 #endif
303 pEntry = osl_readdir_impl_(pDirImpl->pDirStruct);
306 if (!pEntry)
307 return osl_File_E_NOENT;
309 char const * filename = pEntry->d_name;
311 #if defined(MACOSX)
312 // convert decomposed filename to precomposed UTF-8
313 char composed_name[BUFSIZ];
314 CFMutableStringRef strRef = CFStringCreateMutable(nullptr, 0 );
315 CFStringAppendCString(strRef, filename, kCFStringEncodingUTF8); // UTF8 is default on Mac OSX
316 CFStringNormalize(strRef, kCFStringNormalizationFormC);
317 CFStringGetCString(strRef, composed_name, BUFSIZ, kCFStringEncodingUTF8);
318 CFRelease(strRef);
319 filename = composed_name;
320 #endif
322 strFileName = OString(filename, strlen(filename));
324 auto const strFilePath = osl::systemPathMakeAbsolutePath(pDirImpl->strPath, strFileName);
326 DirectoryItem_Impl* pImpl = static_cast< DirectoryItem_Impl* >(*pItem);
327 if (pImpl)
329 pImpl->release();
330 pImpl = nullptr;
332 #ifdef _DIRENT_HAVE_D_TYPE
333 pImpl = new DirectoryItem_Impl(strFilePath, pEntry->d_type);
334 #else
335 pImpl = new DirectoryItem_Impl(strFilePath);
336 #endif /* _DIRENT_HAVE_D_TYPE */
337 *pItem = pImpl;
339 return osl_File_E_None;
342 oslFileError SAL_CALL osl_getDirectoryItem(rtl_uString* ustrFileURL, oslDirectoryItem* pItem)
344 OString strSystemPath;
345 oslFileError osl_error = osl_File_E_INVAL;
347 if ((!ustrFileURL) || (ustrFileURL->length == 0) || (!pItem))
348 return osl_File_E_INVAL;
350 osl_error = osl::detail::convertUrlToPathname(OUString::unacquired(&ustrFileURL), &strSystemPath);
351 if (osl_error != osl_File_E_None)
352 return osl_error;
354 osl_systemPathRemoveSeparator(strSystemPath.pData);
356 if (osl::access(strSystemPath, F_OK) == -1)
358 osl_error = oslTranslateFileError(errno);
360 else
362 *pItem = new DirectoryItem_Impl(std::move(strSystemPath));
365 return osl_error;
368 oslFileError SAL_CALL osl_acquireDirectoryItem( oslDirectoryItem Item )
370 DirectoryItem_Impl * pImpl = static_cast< DirectoryItem_Impl* >(Item);
371 if (pImpl == nullptr)
372 return osl_File_E_INVAL;
374 pImpl->acquire();
375 return osl_File_E_None;
378 oslFileError SAL_CALL osl_releaseDirectoryItem( oslDirectoryItem Item )
380 DirectoryItem_Impl * pImpl = static_cast< DirectoryItem_Impl* >(Item);
381 if (pImpl == nullptr)
382 return osl_File_E_INVAL;
384 pImpl->release();
385 return osl_File_E_None;
388 oslFileError SAL_CALL osl_createDirectory( rtl_uString* ustrDirectoryURL )
390 return osl_createDirectoryWithFlags(
391 ustrDirectoryURL, osl_File_OpenFlag_Read | osl_File_OpenFlag_Write);
394 oslFileError osl_createDirectoryWithFlags(
395 rtl_uString * ustrDirectoryURL, sal_uInt32 flags)
397 char path[PATH_MAX];
398 oslFileError eRet;
400 SAL_WARN_IF((!ustrDirectoryURL) || (ustrDirectoryURL->length == 0),
401 "sal.file", "Invalid directory URL");
403 /* convert directory url to system path */
404 eRet = FileURLToPath( path, PATH_MAX, ustrDirectoryURL );
405 if( eRet != osl_File_E_None )
406 return eRet;
408 #ifdef MACOSX
409 if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
410 return oslTranslateFileError( errno );
411 #endif/* MACOSX */
413 return osl_psz_createDirectory( path, flags );
416 oslFileError SAL_CALL osl_removeDirectory( rtl_uString* ustrDirectoryURL )
418 char path[PATH_MAX];
419 oslFileError eRet;
421 SAL_WARN_IF((!ustrDirectoryURL) || (ustrDirectoryURL->length == 0),
422 "sal.file", "Invalid directory URL");
424 /* convert directory url to system path */
425 eRet = FileURLToPath( path, PATH_MAX, ustrDirectoryURL );
426 if( eRet != osl_File_E_None )
427 return eRet;
429 #ifdef MACOSX
430 if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
431 return oslTranslateFileError( errno );
432 #endif/* MACOSX */
434 return osl_psz_removeDirectory( path );
437 oslFileError osl_psz_createDirectory(char const * pszPath, sal_uInt32 flags)
439 int nRet=0;
440 int mode
441 = (((flags & osl_File_OpenFlag_Read) == 0
443 : ((flags & osl_File_OpenFlag_Private) == 0
444 ? S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
445 : S_IRUSR | S_IXUSR))
446 | ((flags & osl_File_OpenFlag_Write) == 0
448 : ((flags & osl_File_OpenFlag_Private) == 0
449 ? S_IWUSR | S_IWGRP | S_IWOTH
450 : S_IWUSR)));
452 nRet = mkdir(pszPath,mode);
454 if ( nRet < 0 )
456 nRet=errno;
457 SAL_INFO("sal.file", "mkdir(" << pszPath << ",0" << std::oct << mode << std::dec << "): " << UnixErrnoString(nRet));
458 return oslTranslateFileError(nRet);
460 else
461 SAL_INFO("sal.file", "mkdir(" << pszPath << ",0" << std::oct << mode << std::dec << "): OK");
463 return osl_File_E_None;
466 static oslFileError osl_psz_removeDirectory( const char* pszPath )
468 int nRet = rmdir(pszPath);
470 if ( nRet < 0 )
472 nRet=errno;
473 SAL_INFO("sal.file", "rmdir(" << pszPath << "): " << UnixErrnoString(nRet));
474 return oslTranslateFileError(nRet);
476 else
477 SAL_INFO("sal.file", "rmdir(" << pszPath << "): OK");
479 return osl_File_E_None;
482 static int path_make_parent(char* path)
484 int i = rtl_str_lastIndexOfChar(path, '/');
486 if (i > 0)
488 *(path + i) = 0;
489 return i;
491 return 0;
494 static int create_dir_with_callback(
495 char* directory_path,
496 oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
497 void* pData)
499 if (osl::mkdir(directory_path, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
501 if (aDirectoryCreationCallbackFunc)
503 OUString url;
504 osl::detail::convertPathnameToUrl(directory_path, &url);
505 aDirectoryCreationCallbackFunc(pData, url.pData);
507 return 0;
509 return errno;
512 static oslFileError create_dir_recursively_(
513 char* dir_path,
514 oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
515 void* pData)
517 OSL_PRECOND((rtl_str_getLength(dir_path) > 0) && ((dir_path + (rtl_str_getLength(dir_path) - 1)) != (dir_path + rtl_str_lastIndexOfChar(dir_path, '/'))),
518 "Path must not end with a slash");
520 int native_err = create_dir_with_callback(
521 dir_path, aDirectoryCreationCallbackFunc, pData);
523 if (native_err == 0)
524 return osl_File_E_None;
526 if (native_err != ENOENT)
527 return oslTranslateFileError(native_err);
529 // we step back until '/a_dir' at maximum because
530 // we should get an error unequal ENOENT when
531 // we try to create 'a_dir' at '/' and would so
532 // return before
533 int pos = path_make_parent(dir_path);
535 oslFileError osl_error = create_dir_recursively_(
536 dir_path, aDirectoryCreationCallbackFunc, pData);
538 if (osl_error != osl_File_E_None && osl_error != osl_File_E_EXIST)
539 return osl_error;
541 dir_path[pos] = '/';
543 return create_dir_recursively_(dir_path, aDirectoryCreationCallbackFunc, pData);
546 oslFileError SAL_CALL osl_createDirectoryPath(
547 rtl_uString* aDirectoryUrl,
548 oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
549 void* pData)
551 if (aDirectoryUrl == nullptr)
552 return osl_File_E_INVAL;
554 OString sys_path;
555 oslFileError osl_error = osl::detail::convertUrlToPathname(
556 OUString::unacquired(&aDirectoryUrl), &sys_path);
558 if (osl_error != osl_File_E_None)
559 return osl_error;
561 osl::systemPathRemoveSeparator(sys_path);
563 // const_cast because sys_path is a local copy which we want to modify inplace instead of
564 // copy it into another buffer on the heap again
565 return create_dir_recursively_(sys_path.pData->buffer, aDirectoryCreationCallbackFunc, pData);
568 static oslFileError osl_unlinkFile(const char* pszPath);
569 static oslFileError osl_psz_copyFile(const char* pszPath, const char* pszDestPath, bool preserveMetadata);
570 static oslFileError osl_psz_moveFile(const char* pszPath, const char* pszDestPath);
572 static oslFileError oslDoCopy(const char* pszSourceFileName, const char* pszDestFileName, mode_t nMode, size_t nSourceSize, bool DestFileExists);
573 static void attemptChangeMetadata(const char* pszFileName, mode_t nMode, time_t nAcTime, time_t nModTime, uid_t nUID, gid_t nGID);
574 static int oslDoCopyLink(const char* pszSourceFileName, const char* pszDestFileName);
575 static int oslDoCopyFile(const char* pszSourceFileName, const char* pszDestFileName, size_t nSourceSize, mode_t mode);
576 static oslFileError oslDoMoveFile(const char* pszPath, const char* pszDestPath);
578 oslFileError SAL_CALL osl_moveFile( rtl_uString* ustrFileURL, rtl_uString* ustrDestURL )
580 char srcPath[PATH_MAX];
581 char destPath[PATH_MAX];
582 oslFileError eRet;
584 SAL_WARN_IF((!ustrFileURL) || (ustrFileURL->length == 0), "sal.file", "Invalid source file URL");
585 SAL_WARN_IF((!ustrDestURL) || (ustrDestURL->length == 0), "sal.file", "Invalid destination file URL");
587 /* convert source url to system path */
588 eRet = FileURLToPath( srcPath, PATH_MAX, ustrFileURL );
589 if( eRet != osl_File_E_None )
590 return eRet;
592 /* convert destination url to system path */
593 eRet = FileURLToPath( destPath, PATH_MAX, ustrDestURL );
594 if( eRet != osl_File_E_None )
595 return eRet;
597 #ifdef MACOSX
598 if ( macxp_resolveAlias( srcPath, PATH_MAX ) != 0 || macxp_resolveAlias( destPath, PATH_MAX ) != 0 )
599 return oslTranslateFileError( errno );
600 #endif/* MACOSX */
602 return oslDoMoveFile( srcPath, destPath );
605 oslFileError SAL_CALL osl_replaceFile(rtl_uString* ustrFileURL, rtl_uString* ustrDestURL)
607 int nGid = -1;
608 char destPath[PATH_MAX];
609 oslFileError eRet = FileURLToPath(destPath, PATH_MAX, ustrDestURL);
610 if (eRet == osl_File_E_None)
612 struct stat aFileStat;
613 // coverity[fs_check_call] - unavoidable TOCTOU
614 int nRet = stat(destPath, &aFileStat);
615 if (nRet == -1)
617 nRet = errno;
618 SAL_INFO("sal.file", "stat(" << destPath << "): " << UnixErrnoString(nRet));
620 else
622 nGid = aFileStat.st_gid;
626 eRet = osl_moveFile(ustrFileURL, ustrDestURL);
628 if (eRet == osl_File_E_None && nGid != -1)
630 int nRet = chown(destPath, -1, nGid);
631 if (nRet == -1)
633 nRet = errno;
634 SAL_INFO("sal.file",
635 "chown(" << destPath << "-1, " << nGid << "): " << UnixErrnoString(nRet));
639 return eRet;
642 oslFileError SAL_CALL osl_copyFile( rtl_uString* ustrFileURL, rtl_uString* ustrDestURL )
644 char srcPath[PATH_MAX];
645 char destPath[PATH_MAX];
646 oslFileError eRet;
648 SAL_WARN_IF((!ustrFileURL) || (ustrFileURL->length == 0), "sal.file", "Invalid source file URL");
649 SAL_WARN_IF((!ustrDestURL) || (ustrDestURL->length == 0), "sal.file", "Invalid destination file URL");
651 /* convert source url to system path */
652 eRet = FileURLToPath( srcPath, PATH_MAX, ustrFileURL );
653 if( eRet != osl_File_E_None )
654 return eRet;
656 /* convert destination url to system path */
657 eRet = FileURLToPath( destPath, PATH_MAX, ustrDestURL );
658 if( eRet != osl_File_E_None )
659 return eRet;
661 #ifdef MACOSX
662 if ( macxp_resolveAlias( srcPath, PATH_MAX ) != 0 || macxp_resolveAlias( destPath, PATH_MAX ) != 0 )
663 return oslTranslateFileError( errno );
664 #endif/* MACOSX */
666 return osl_psz_copyFile( srcPath, destPath, false );
669 oslFileError SAL_CALL osl_removeFile(rtl_uString* ustrFileURL)
671 char path[PATH_MAX];
672 oslFileError eRet;
674 SAL_WARN_IF(!ustrFileURL || ustrFileURL->length == 0, "sal.file", "Invalid file URL");
676 /* convert file url to system path */
677 eRet = FileURLToPath(path, PATH_MAX, ustrFileURL);
678 if (eRet != osl_File_E_None)
679 return eRet;
681 #ifdef MACOSX
682 if (macxp_resolveAlias(path, PATH_MAX) != 0)
683 return oslTranslateFileError(errno);
684 #endif/* MACOSX */
686 return osl_unlinkFile(path);
689 static oslFileError oslDoMoveFile(const char* pszPath, const char* pszDestPath)
691 oslFileError tErr = osl_psz_moveFile(pszPath,pszDestPath);
692 if (tErr == osl_File_E_None)
693 return tErr;
695 if (tErr != osl_File_E_XDEV)
696 return tErr;
698 tErr = osl_psz_copyFile(pszPath,pszDestPath, true);
700 if (tErr != osl_File_E_None)
702 osl_unlinkFile(pszDestPath);
703 return tErr;
706 tErr = osl_unlinkFile(pszPath);
708 return tErr;
711 static oslFileError osl_unlinkFile(const char* pszPath)
713 int nRet=0;
714 struct stat aStat;
716 nRet = lstat_c(pszPath,&aStat);
717 if (nRet < 0)
719 nRet=errno;
720 return oslTranslateFileError(nRet);
723 if (S_ISDIR(aStat.st_mode))
724 return osl_File_E_ISDIR;
726 nRet = unlink(pszPath);
727 if (nRet < 0)
729 nRet=errno;
730 SAL_INFO("sal.file", "unlink(" << pszPath << "): " << UnixErrnoString(nRet));
731 return oslTranslateFileError(nRet);
733 else
734 SAL_INFO("sal.file", "unlink(" << pszPath << "): OK");
736 return osl_File_E_None;
739 static oslFileError osl_psz_moveFile(const char* pszPath, const char* pszDestPath)
741 int nRet = rename(pszPath,pszDestPath);
743 if (nRet < 0)
745 nRet=errno;
746 SAL_INFO("sal.file", "rename(" << pszPath << "," << pszDestPath << "): " << UnixErrnoString(nRet));
747 return oslTranslateFileError(nRet);
749 else
750 SAL_INFO("sal.file", "rename(" << pszPath << "," << pszDestPath << "): OK");
752 return osl_File_E_None;
755 static oslFileError osl_psz_copyFile( const char* pszPath, const char* pszDestPath, bool preserveMetadata )
757 time_t nAcTime=0;
758 time_t nModTime=0;
759 uid_t nUID=0;
760 gid_t nGID=0;
761 int nRet=0;
762 mode_t nMode=0;
763 struct stat aFileStat;
764 oslFileError tErr=osl_File_E_invalidError;
765 size_t nSourceSize=0;
766 bool DestFileExists=true;
768 /* mfe: does the source file really exists? */
769 nRet = lstat_c(pszPath,&aFileStat);
771 if (nRet < 0)
773 nRet=errno;
774 return oslTranslateFileError(nRet);
777 /* we do only copy files here */
778 if (S_ISDIR(aFileStat.st_mode))
779 return osl_File_E_ISDIR;
781 nSourceSize = static_cast< size_t >(aFileStat.st_size);
782 nMode = aFileStat.st_mode;
783 nAcTime = aFileStat.st_atime;
784 nModTime = aFileStat.st_mtime;
785 nUID = aFileStat.st_uid;
786 nGID = aFileStat.st_gid;
788 nRet = stat_c(pszDestPath,&aFileStat);
789 if (nRet < 0)
791 nRet=errno;
793 #ifdef IOS
794 // Checking for nonexistent files at least in the iCloud cache directory (like
795 // "/private/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/helloodt0.odt" fails
796 // with EPERM, not ENOENT.
797 if (nRet == EPERM)
798 DestFileExists=false;
799 #endif
801 if (nRet == ENOENT)
802 DestFileExists=false;
805 /* mfe: the destination file must not be a directory! */
806 if (nRet == 0 && S_ISDIR(aFileStat.st_mode))
807 return osl_File_E_ISDIR;
809 /* mfe: file does not exists or is no dir */
811 tErr = oslDoCopy(pszPath, pszDestPath, nMode, nSourceSize, DestFileExists);
813 if (tErr != osl_File_E_None)
814 return tErr;
816 if (preserveMetadata)
817 attemptChangeMetadata(pszDestPath, nMode, nAcTime, nModTime, nUID, nGID);
819 return tErr;
822 static oslFileError oslDoCopy(const char* pszSourceFileName, const char* pszDestFileName, mode_t nMode, size_t nSourceSize, bool DestFileExists)
824 int nRet=0;
826 OString tmpDestFile;
827 if ( DestFileExists )
829 //TODO: better pick a temp file name instead of adding .osl-tmp:
830 // use the destination file to avoid EXDEV /* Cross-device link */
831 tmpDestFile = pszDestFileName + OString::Concat(".osl-tmp");
832 if (rename(pszDestFileName, tmpDestFile.getStr()) != 0)
834 int e = errno;
835 SAL_INFO("sal.file", "rename(" << pszDestFileName << ", " << tmpDestFile
836 << "): " << UnixErrnoString(e));
837 if (e == ENOENT)
839 DestFileExists = false;
841 else
843 return osl_File_E_EXIST; // for want of a better error code
846 else
848 SAL_INFO("sal.file", "rename(" << pszDestFileName << ", " << tmpDestFile
849 << "): OK");
853 if ( S_ISREG(nMode) )
855 /* copy SourceFile to DestFile */
856 nRet = oslDoCopyFile(pszSourceFileName,pszDestFileName,nSourceSize, nMode);
858 else if ( S_ISLNK(nMode) )
860 nRet = oslDoCopyLink(pszSourceFileName,pszDestFileName);
862 else
864 nRet = EINVAL;
867 if ( nRet > 0 && DestFileExists )
869 if (unlink(pszDestFileName) != 0)
871 int e = errno;
872 SAL_INFO("sal.file", "unlink(" << pszDestFileName << "): " << UnixErrnoString(e));
874 else
875 SAL_INFO("sal.file", "unlink(" << pszDestFileName << "): OK");
877 if (rename(tmpDestFile.getStr(), pszDestFileName) != 0)
879 int e = errno;
880 SAL_INFO("sal.file", "rename(" << tmpDestFile << ", " << pszDestFileName
881 << "): " << UnixErrnoString(e));
883 else
884 SAL_INFO("sal.file", "rename(" << tmpDestFile << ", " << pszDestFileName << "): OK");
887 if ( nRet > 0 )
889 return oslTranslateFileError(nRet);
892 if ( DestFileExists )
894 unlink(tmpDestFile.getStr());
897 return osl_File_E_None;
900 void attemptChangeMetadata( const char* pszFileName, mode_t nMode, time_t nAcTime, time_t nModTime, uid_t nUID, gid_t nGID)
902 struct utimbuf aTimeBuffer;
904 #if !defined AT_FDCWD
905 if (!S_ISLNK(nMode) && chmod(pszFileName, nMode) < 0)
906 #else
907 if ( fchmodat(AT_FDCWD, pszFileName, nMode, AT_SYMLINK_NOFOLLOW) < 0 )
908 #endif
910 int e = errno;
911 SAL_INFO("sal.file", "chmod(" << pszFileName << ",0" << std::oct << nMode << std::dec <<"): " << UnixErrnoString(e));
913 else
914 SAL_INFO("sal.file", "chmod(" << pszFileName << ",0" << std::oct << nMode << std::dec <<"): OK");
916 // No way to change utime of a symlink itself:
917 if (!S_ISLNK(nMode))
919 aTimeBuffer.actime=nAcTime;
920 aTimeBuffer.modtime=nModTime;
921 if ( utime(pszFileName,&aTimeBuffer) < 0 )
923 int e = errno;
924 SAL_INFO("sal.file", "utime(" << pszFileName << "): errno " << e);
928 if ( nUID != getuid() )
930 nUID=getuid();
932 if ( lchown(pszFileName,nUID,nGID) < 0 )
934 int e = errno;
935 SAL_INFO("sal.file", "lchown(" << pszFileName << "): errno " << e);
937 else
938 SAL_INFO("sal.file", "lchown(" << pszFileName << "): OK");
941 static int oslDoCopyLink(const char* pszSourceFileName, const char* pszDestFileName)
943 int nRet=0;
945 /* mfe: if dest file is symbolic link remove the link and place the file instead (hro says so) */
946 /* mfe: if source is a link copy the link and not the file it points to (hro says so) */
947 char pszLinkContent[PATH_MAX+1];
949 pszLinkContent[0] = '\0';
951 nRet = readlink(pszSourceFileName,pszLinkContent,PATH_MAX);
953 if ( nRet < 0 )
955 nRet=errno;
956 return nRet;
959 pszLinkContent[ nRet ] = 0;
961 nRet = symlink(pszLinkContent,pszDestFileName);
963 if ( nRet < 0 )
965 nRet=errno;
966 return nRet;
969 return 0;
972 static int oslDoCopyFile(const char* pszSourceFileName, const char* pszDestFileName, size_t nSourceSize, mode_t mode)
974 oslFileHandle SourceFileFH=nullptr;
975 int DestFileFD=0;
976 int nRet=0;
978 if (openFilePath(pszSourceFileName,
979 &SourceFileFH,
980 osl_File_OpenFlag_Read|osl_File_OpenFlag_NoLock|osl_File_OpenFlag_NoExcl, mode_t(-1)) != osl_File_E_None)
982 // Let's hope errno is still set relevantly after openFilePath...
983 nRet=errno;
984 return nRet;
987 DestFileFD=open(pszDestFileName, O_WRONLY | O_CREAT, mode);
989 if ( DestFileFD < 0 )
991 nRet=errno;
992 SAL_INFO("sal.file", "open(" << pszDestFileName << ",O_WRONLY|O_CREAT,0" << std::oct << mode << std::dec << "): " << UnixErrnoString(nRet));
993 osl_closeFile(SourceFileFH);
994 return nRet;
996 else
997 SAL_INFO("sal.file", "open(" << pszDestFileName << ",O_WRONLY|O_CREAT,0" << std::oct << mode << std::dec << "): OK");
999 size_t nRemains = nSourceSize;
1001 if ( nRemains )
1003 /* mmap has problems, try the direct streaming */
1004 char pBuffer[0x7FFF];
1008 size_t nToRead = std::min( sizeof(pBuffer), nRemains );
1009 sal_uInt64 nRead;
1010 bool succeeded;
1011 if ( osl_readFile( SourceFileFH, pBuffer, nToRead, &nRead ) != osl_File_E_None || nRead > nToRead || nRead == 0 )
1012 break;
1014 succeeded = safeWrite( DestFileFD, pBuffer, nRead );
1015 if ( !succeeded )
1016 break;
1018 // We know nRead <= nToRead, so it must fit in a size_t
1019 nRemains -= static_cast<size_t>(nRead);
1021 while( nRemains );
1024 if ( nRemains )
1026 if ( errno )
1027 nRet = errno;
1028 else
1029 nRet = ENOSPC;
1032 osl_closeFile( SourceFileFH );
1033 if ( close( DestFileFD ) == -1 )
1035 int e = errno;
1036 SAL_INFO("sal.file", "close(" << DestFileFD << "): " << UnixErrnoString(e));
1037 if ( nRet == 0 )
1038 nRet = e;
1040 else
1041 SAL_INFO("sal.file", "close(" << DestFileFD << "): OK");
1043 return nRet;
1046 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */