Avoid potential negative array index access to cached text.
[LibreOffice.git] / sal / osl / unx / file_misc.cxx
blob9b39d5973c832070d1c37e8fc2dd48fc58cf3f1a
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/detail/file.h>
22 #include <osl/diagnose.h>
23 #include <rtl/string.hxx>
24 #include <sal/log.hxx>
26 #include "system.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"
35 #include <errno.h>
36 #include <dirent.h>
37 #include <fcntl.h>
38 #include <limits.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <utime.h>
43 #include <sys/stat.h>
45 #include <algorithm>
46 #include <new>
48 #ifdef ANDROID
49 #include <osl/detail/android-bootstrap.h>
50 #endif
52 /************************************************************************
53 * TODO
55 * - Fix: check for corresponding struct sizes in exported functions
56 * - check size/use of oslDirectory
57 * - check size/use of oslDirectoryItem
58 ***********************************************************************/
60 namespace {
62 struct DirectoryImpl
64 OString strPath; /* holds native directory path */
65 DIR* pDirStruct;
66 #ifdef ANDROID
67 enum Kind
69 KIND_DIRENT = 1,
70 KIND_ASSETS = 2
72 int eKind;
73 lo_apk_dir* pApkDirStruct;
74 #endif
79 DirectoryItem_Impl::DirectoryItem_Impl(
80 OString strFilePath, unsigned char DType)
81 : m_strFilePath (std::move(strFilePath)),
82 m_RefCount (1),
83 m_DType (DType)
86 DirectoryItem_Impl::~DirectoryItem_Impl()
90 void DirectoryItem_Impl::acquire()
92 ++m_RefCount;
94 void DirectoryItem_Impl::release()
96 if (--m_RefCount == 0)
97 delete this;
100 oslFileType DirectoryItem_Impl::getFileType() const
102 switch (m_DType)
104 #ifdef _DIRENT_HAVE_D_TYPE
105 case DT_LNK:
106 return osl_File_Type_Link;
107 case DT_DIR:
108 return osl_File_Type_Directory;
109 case DT_REG:
110 return osl_File_Type_Regular;
111 case DT_FIFO:
112 return osl_File_Type_Fifo;
113 case DT_SOCK:
114 return osl_File_Type_Socket;
115 case DT_CHR:
116 case DT_BLK:
117 return osl_File_Type_Special;
118 #endif /* _DIRENT_HAVE_D_TYPE */
119 default:
120 break;
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)
131 oslFileError eRet;
133 OString path;
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 )
142 return eRet;
144 osl_systemPathRemoveSeparator(path.pData);
146 if (isForbidden(path, osl_File_OpenFlag_Read))
147 return osl_File_E_ACCES;
149 #ifdef MACOSX
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()));
159 #endif /* MACOSX */
161 #ifdef ANDROID
162 if( strncmp( path.getStr(), "/assets/", sizeof( "/assets/" ) - 1) == 0 )
164 lo_apk_dir *pdir = lo_apk_opendir( path.getStr() );
166 if( pdir )
168 DirectoryImpl* pDirImpl = new(std::nothrow) DirectoryImpl;
170 if( pDirImpl )
172 pDirImpl->eKind = DirectoryImpl::KIND_ASSETS;
173 pDirImpl->pApkDirStruct = pdir;
174 pDirImpl->strPath = path;
176 *pDirectory = (oslDirectory) pDirImpl;
177 return osl_File_E_None;
179 else
181 errno = ENOMEM;
182 lo_apk_closedir( pdir );
186 else
187 #endif
189 /* open directory */
190 DIR *pdir = opendir( path.getStr() );
192 if( pdir )
194 SAL_INFO("sal.file", "opendir(" << path << ") => " << pdir);
196 /* create and initialize impl structure */
197 DirectoryImpl* pDirImpl = new(std::nothrow) DirectoryImpl;
199 if( pDirImpl )
201 pDirImpl->pDirStruct = pdir;
202 pDirImpl->strPath = path;
203 #ifdef ANDROID
204 pDirImpl->eKind = DirectoryImpl::KIND_DIRENT;
205 #endif
206 *pDirectory = static_cast<oslDirectory>(pDirImpl);
207 return osl_File_E_None;
209 errno = ENOMEM;
210 closedir( pdir );
212 else
214 int e = errno;
215 SAL_INFO("sal.file", "opendir(" << path << "): " << UnixErrnoString(e));
216 // Restore errno after possible modification by SAL_INFO above
217 errno = e;
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;
230 if (!pDirImpl)
231 return osl_File_E_INVAL;
233 #ifdef ANDROID
234 if (pDirImpl->eKind == DirectoryImpl::KIND_ASSETS)
236 if (lo_apk_closedir(pDirImpl->pApkDirStruct))
237 err = osl_File_E_IO;
239 else
240 #endif
242 if (closedir( pDirImpl->pDirStruct) != 0)
244 int e = errno;
245 SAL_INFO("sal.file", "closedir(" << pDirImpl->pDirStruct << "): " << UnixErrnoString(e));
246 err = oslTranslateFileError(e);
248 else
249 SAL_INFO("sal.file", "closedir(" << pDirImpl->pDirStruct << "): OK");
252 delete pDirImpl;
254 return err;
257 /**********************************************
258 * osl_readdir_impl_
260 * readdir wrapper, filters out "." and ".."
261 * on request
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))
271 continue;
272 break;
275 return pdirent;
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);
285 OString strFileName;
286 struct dirent* pEntry;
288 if ((pDirectory == nullptr) || (pItem == nullptr))
289 return osl_File_E_INVAL;
291 #ifdef ANDROID
292 if(pDirImpl->eKind == DirectoryImpl::KIND_ASSETS)
294 pEntry = lo_apk_readdir(pDirImpl->pApkDirStruct);
296 else
297 #endif
299 pEntry = osl_readdir_impl_(pDirImpl->pDirStruct);
302 if (!pEntry)
303 return osl_File_E_NOENT;
305 char const * filename = pEntry->d_name;
307 #if defined(MACOSX)
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);
314 CFRelease(strRef);
315 filename = composed_name;
316 #endif
318 strFileName = OString(filename, strlen(filename));
320 auto const strFilePath = osl::systemPathMakeAbsolutePath(pDirImpl->strPath, strFileName);
322 DirectoryItem_Impl* pImpl = static_cast< DirectoryItem_Impl* >(*pItem);
323 if (pImpl)
324 pImpl->release();
325 #ifdef _DIRENT_HAVE_D_TYPE
326 pImpl = new DirectoryItem_Impl(strFilePath, pEntry->d_type);
327 #else
328 pImpl = new DirectoryItem_Impl(strFilePath);
329 #endif /* _DIRENT_HAVE_D_TYPE */
330 *pItem = pImpl;
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)
345 return osl_error;
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);
356 else
358 *pItem = new DirectoryItem_Impl(std::move(strSystemPath));
361 return osl_error;
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;
370 pImpl->acquire();
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;
380 pImpl->release();
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)
393 char path[PATH_MAX];
394 oslFileError eRet;
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 )
402 return eRet;
404 #ifdef MACOSX
405 if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
406 return oslTranslateFileError( errno );
407 #endif/* MACOSX */
409 return osl_psz_createDirectory( path, flags );
412 oslFileError SAL_CALL osl_removeDirectory( rtl_uString* ustrDirectoryURL )
414 char path[PATH_MAX];
415 oslFileError eRet;
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 )
423 return eRet;
425 #ifdef MACOSX
426 if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
427 return oslTranslateFileError( errno );
428 #endif/* MACOSX */
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;
438 int nRet=0;
439 int mode
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
449 : S_IWUSR)));
451 nRet = mkdir(pszPath,mode);
453 if ( nRet < 0 )
455 nRet=errno;
456 SAL_INFO("sal.file", "mkdir(" << pszPath << ",0" << std::oct << mode << std::dec << "): " << UnixErrnoString(nRet));
457 return oslTranslateFileError(nRet);
459 else
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);
472 if ( nRet < 0 )
474 nRet=errno;
475 SAL_INFO("sal.file", "rmdir(" << pszPath << "): " << UnixErrnoString(nRet));
476 return oslTranslateFileError(nRet);
478 else
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, '/');
488 if (i > 0)
490 *(path + i) = 0;
491 return i;
493 return 0;
496 static int create_dir_with_callback(
497 char* directory_path,
498 oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
499 void* pData)
501 if (osl::mkdir(directory_path, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
503 if (aDirectoryCreationCallbackFunc)
505 OUString url;
506 osl::detail::convertPathnameToUrl(directory_path, &url);
507 aDirectoryCreationCallbackFunc(pData, url.pData);
509 return 0;
511 return errno;
514 static oslFileError create_dir_recursively_(
515 char* dir_path,
516 oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
517 void* pData)
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);
525 if (native_err == 0)
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
534 // return before
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)
541 return osl_error;
543 dir_path[pos] = '/';
545 return create_dir_recursively_(dir_path, aDirectoryCreationCallbackFunc, pData);
548 oslFileError SAL_CALL osl_createDirectoryPath(
549 rtl_uString* aDirectoryUrl,
550 oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
551 void* pData)
553 if (aDirectoryUrl == nullptr)
554 return osl_File_E_INVAL;
556 OString sys_path;
557 oslFileError osl_error = osl::detail::convertUrlToPathname(
558 OUString::unacquired(&aDirectoryUrl), &sys_path);
560 if (osl_error != osl_File_E_None)
561 return osl_error;
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];
587 oslFileError eRet;
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 )
595 return eRet;
597 /* convert destination url to system path */
598 eRet = FileURLToPath( destPath, PATH_MAX, ustrDestURL );
599 if( eRet != osl_File_E_None )
600 return eRet;
602 if (isForbidden(srcPath, osl_File_OpenFlag_Read) ||
603 isForbidden(destPath, osl_File_OpenFlag_Create))
604 return osl_File_E_ACCES;
606 #ifdef MACOSX
607 if ( macxp_resolveAlias( srcPath, PATH_MAX ) != 0 || macxp_resolveAlias( destPath, PATH_MAX ) != 0 )
608 return oslTranslateFileError( errno );
609 #endif/* MACOSX */
611 return oslDoMoveFile( srcPath, destPath );
614 oslFileError SAL_CALL osl_replaceFile(rtl_uString* ustrFileURL, rtl_uString* ustrDestURL)
616 int nGid = -1;
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);
628 if (nRet == -1)
630 nRet = errno;
631 SAL_INFO("sal.file", "stat(" << destPath << "): " << UnixErrnoString(nRet));
633 else
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);
644 if (nRet == -1)
646 nRet = errno;
647 SAL_INFO("sal.file",
648 "chown(" << destPath << "-1, " << nGid << "): " << UnixErrnoString(nRet));
652 return eRet;
655 oslFileError SAL_CALL osl_copyFile( rtl_uString* ustrFileURL, rtl_uString* ustrDestURL )
657 char srcPath[PATH_MAX];
658 char destPath[PATH_MAX];
659 oslFileError eRet;
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 )
667 return eRet;
669 /* convert destination url to system path */
670 eRet = FileURLToPath( destPath, PATH_MAX, ustrDestURL );
671 if( eRet != osl_File_E_None )
672 return eRet;
674 #ifdef MACOSX
675 if ( macxp_resolveAlias( srcPath, PATH_MAX ) != 0 || macxp_resolveAlias( destPath, PATH_MAX ) != 0 )
676 return oslTranslateFileError( errno );
677 #endif/* MACOSX */
679 return osl_psz_copyFile( srcPath, destPath, false );
682 oslFileError SAL_CALL osl_removeFile(rtl_uString* ustrFileURL)
684 char path[PATH_MAX];
685 oslFileError eRet;
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)
692 return eRet;
694 #ifdef MACOSX
695 if (macxp_resolveAlias(path, PATH_MAX) != 0)
696 return oslTranslateFileError(errno);
697 #endif/* MACOSX */
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)
709 return tErr;
711 if (tErr != osl_File_E_XDEV)
712 return tErr;
714 tErr = osl_psz_copyFile(pszPath,pszDestPath, true);
716 if (tErr != osl_File_E_None)
718 osl_unlinkFile(pszDestPath);
719 return tErr;
722 tErr = osl_unlinkFile(pszPath);
724 return tErr;
727 static oslFileError osl_unlinkFile(const char* pszPath)
729 int nRet=0;
730 struct stat aStat;
732 nRet = lstat_c(pszPath,&aStat);
733 if (nRet < 0)
735 nRet=errno;
736 return oslTranslateFileError(nRet);
739 if (S_ISDIR(aStat.st_mode))
740 return osl_File_E_ISDIR;
742 nRet = unlink(pszPath);
743 if (nRet < 0)
745 nRet=errno;
746 SAL_INFO("sal.file", "unlink(" << pszPath << "): " << UnixErrnoString(nRet));
747 return oslTranslateFileError(nRet);
749 else
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);
763 if (nRet < 0)
765 nRet=errno;
766 SAL_INFO("sal.file", "rename(" << pszPath << "," << pszDestPath << "): " << UnixErrnoString(nRet));
767 return oslTranslateFileError(nRet);
769 else
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 )
777 time_t nAcTime=0;
778 time_t nModTime=0;
779 uid_t nUID=0;
780 gid_t nGID=0;
781 int nRet=0;
782 mode_t nMode=0;
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);
795 if (nRet < 0)
797 nRet=errno;
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);
813 if (nRet < 0)
815 nRet=errno;
817 #ifdef IOS
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.
821 if (nRet == EPERM)
822 DestFileExists=false;
823 #endif
825 if (nRet == ENOENT)
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)
838 return tErr;
840 if (preserveMetadata)
841 attemptChangeMetadata(pszDestPath, nMode, nAcTime, nModTime, nUID, nGID);
843 return tErr;
846 static oslFileError oslDoCopy(const char* pszSourceFileName, const char* pszDestFileName, mode_t nMode, size_t nSourceSize, bool DestFileExists)
848 int nRet=0;
850 OString tmpDestFile;
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)
858 int e = errno;
859 SAL_INFO("sal.file", "rename(" << pszDestFileName << ", " << tmpDestFile
860 << "): " << UnixErrnoString(e));
861 if (e == ENOENT)
863 DestFileExists = false;
865 else
867 return osl_File_E_EXIST; // for want of a better error code
870 else
872 SAL_INFO("sal.file", "rename(" << pszDestFileName << ", " << tmpDestFile
873 << "): OK");
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);
886 else
888 nRet = EINVAL;
891 if ( nRet > 0 && DestFileExists )
893 if (unlink(pszDestFileName) != 0)
895 int e = errno;
896 SAL_INFO("sal.file", "unlink(" << pszDestFileName << "): " << UnixErrnoString(e));
898 else
899 SAL_INFO("sal.file", "unlink(" << pszDestFileName << "): OK");
901 if (rename(tmpDestFile.getStr(), pszDestFileName) != 0)
903 int e = errno;
904 SAL_INFO("sal.file", "rename(" << tmpDestFile << ", " << pszDestFileName
905 << "): " << UnixErrnoString(e));
907 else
908 SAL_INFO("sal.file", "rename(" << tmpDestFile << ", " << pszDestFileName << "): OK");
911 if ( nRet > 0 )
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)
930 #else
931 if ( fchmodat(AT_FDCWD, pszFileName, nMode, AT_SYMLINK_NOFOLLOW) < 0 )
932 #endif
934 int e = errno;
935 SAL_INFO("sal.file", "chmod(" << pszFileName << ",0" << std::oct << nMode << std::dec <<"): " << UnixErrnoString(e));
937 else
938 SAL_INFO("sal.file", "chmod(" << pszFileName << ",0" << std::oct << nMode << std::dec <<"): OK");
940 // No way to change utime of a symlink itself:
941 if (!S_ISLNK(nMode))
943 aTimeBuffer.actime=nAcTime;
944 aTimeBuffer.modtime=nModTime;
945 if ( utime(pszFileName,&aTimeBuffer) < 0 )
947 int e = errno;
948 SAL_INFO("sal.file", "utime(" << pszFileName << "): errno " << e);
952 if ( nUID != getuid() )
954 nUID=getuid();
956 if ( lchown(pszFileName,nUID,nGID) < 0 )
958 int e = errno;
959 SAL_INFO("sal.file", "lchown(" << pszFileName << "): errno " << e);
961 else
962 SAL_INFO("sal.file", "lchown(" << pszFileName << "): OK");
965 static int oslDoCopyLink(const char* pszSourceFileName, const char* pszDestFileName)
967 int nRet=0;
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);
977 if ( nRet < 0 )
979 nRet=errno;
980 return nRet;
983 pszLinkContent[ nRet ] = 0;
985 nRet = symlink(pszLinkContent,pszDestFileName);
987 if ( nRet < 0 )
989 nRet=errno;
990 return nRet;
993 return 0;
996 static int oslDoCopyFile(const char* pszSourceFileName, const char* pszDestFileName, size_t nSourceSize, mode_t mode)
998 oslFileHandle SourceFileFH=nullptr;
999 int DestFileFD=0;
1000 int nRet=0;
1002 if (openFilePath(pszSourceFileName,
1003 &SourceFileFH,
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...
1007 nRet=errno;
1008 return nRet;
1011 DestFileFD=open(pszDestFileName, O_WRONLY | O_CREAT, mode);
1013 if ( DestFileFD < 0 )
1015 nRet=errno;
1016 SAL_INFO("sal.file", "open(" << pszDestFileName << ",O_WRONLY|O_CREAT,0" << std::oct << mode << std::dec << "): " << UnixErrnoString(nRet));
1017 osl_closeFile(SourceFileFH);
1018 return nRet;
1020 else
1021 SAL_INFO("sal.file", "open(" << pszDestFileName << ",O_WRONLY|O_CREAT,0" << std::oct << mode << std::dec << "): OK");
1023 size_t nRemains = nSourceSize;
1025 if ( nRemains )
1027 /* mmap has problems, try the direct streaming */
1028 char pBuffer[0x7FFF];
1032 size_t nToRead = std::min( sizeof(pBuffer), nRemains );
1033 sal_uInt64 nRead;
1034 bool succeeded;
1035 if ( osl_readFile( SourceFileFH, pBuffer, nToRead, &nRead ) != osl_File_E_None || nRead > nToRead || nRead == 0 )
1036 break;
1038 succeeded = safeWrite( DestFileFD, pBuffer, nRead );
1039 if ( !succeeded )
1040 break;
1042 // We know nRead <= nToRead, so it must fit in a size_t
1043 nRemains -= static_cast<size_t>(nRead);
1045 while( nRemains );
1048 if ( nRemains )
1050 if ( errno )
1051 nRet = errno;
1052 else
1053 nRet = ENOSPC;
1056 osl_closeFile( SourceFileFH );
1057 if ( close( DestFileFD ) == -1 )
1059 int e = errno;
1060 SAL_INFO("sal.file", "close(" << DestFileFD << "): " << UnixErrnoString(e));
1061 if ( nRet == 0 )
1062 nRet = e;
1064 else
1065 SAL_INFO("sal.file", "close(" << DestFileFD << "): OK");
1067 return nRet;
1070 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */