/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <osl/detail/file.h>
#include <osl/diagnose.h>
#include <rtl/string.hxx>
#include <sal/log.hxx>
#include "system.hxx"
#include "file_impl.hxx"
#include "file_error_transl.hxx"
#include "file_path_helper.hxx"
#include "file_url.hxx"
#include "uunxapi.hxx"
#include "readwrite_helper.hxx"
#include "unixerrnostring.hxx"
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <utime.h>
#include <sys/stat.h>
#include <algorithm>
#include <
new >
#ifdef ANDROID
#include <osl/detail/android-bootstrap.h>
#endif
/************************************************************************
* TODO
*
* - Fix: check for corresponding struct sizes in exported functions
* - check size/use of oslDirectory
* - check size/use of oslDirectoryItem
***********************************************************************/
namespace {
struct DirectoryImpl
{
OString strPath;
/* holds native directory path */
DIR* pDirStruct;
#ifdef ANDROID
enum Kind
{
KIND_DIRENT = 1,
KIND_ASSETS = 2
};
int eKind;
lo_apk_dir* pApkDirStruct;
#endif
};
}
DirectoryItem_Impl::DirectoryItem_Impl(
OString strFilePath,
unsigned char DType)
: m_strFilePath (std::move(strFilePath)),
m_RefCount (1),
m_DType (DType)
{
}
DirectoryItem_Impl::~DirectoryItem_Impl()
{
}
void DirectoryItem_Impl::acquire()
{
++m_RefCount;
}
void DirectoryItem_Impl::release()
{
if (--m_RefCount == 0)
delete this ;
}
oslFileType DirectoryItem_Impl::getFileType()
const
{
switch (m_DType)
{
#ifdef _DIRENT_HAVE_D_TYPE
case DT_LNK:
return osl_File_Type_Link;
case DT_DIR:
return osl_File_Type_Directory;
case DT_REG:
return osl_File_Type_Regular;
case DT_FIFO:
return osl_File_Type_Fifo;
case DT_SOCK:
return osl_File_Type_Socket;
case DT_CHR:
case DT_BLK:
return osl_File_Type_Special;
#endif /* _DIRENT_HAVE_D_TYPE */
default :
break ;
}
return osl_File_Type_Unknown;
}
static oslFileError osl_psz_createDirectory(
char const * pszPath, sal_uInt32 flags);
static oslFileError osl_psz_removeDirectory(
const char * pszPath);
oslFileError SAL_CALL osl_openDirectory(rtl_uString* ustrDirectoryURL, oslDirectory*
pDirectory)
{
oslFileError eRet;
OString path;
if ((ustrDirectoryURL == nullptr) || (ustrDirectoryURL->length == 0) || (pDirectory == nullptr))
return osl_File_E_INVAL;
/* convert file URL to system path */
eRet = osl::detail::convertUrlToPathname(OUString::unacquired(&ustrDirectoryURL), &path);
if ( eRet != osl_File_E_None )
return eRet;
osl_systemPathRemoveSeparator(path.pData);
#ifdef MACOSX
{
auto const n = std::max(int (path.getLength() + 1), int (PATH_MAX));
auto const tmp = std::make_unique<char []>(n);
std::strcpy(tmp.get(), path.getStr());
if (macxp_resolveAlias(tmp.get(), n) != 0) {
return oslTranslateFileError(errno);
}
path = OString(tmp.get(), std::strlen(tmp.get()));
}
#endif /* MACOSX */
#ifdef ANDROID
if ( strncmp( path.getStr(), "/assets/" , sizeof ( "/assets/" ) - 1) == 0 )
{
lo_apk_dir *pdir = lo_apk_opendir( path.getStr() );
if ( pdir )
{
DirectoryImpl* pDirImpl = new (std::nothrow) DirectoryImpl;
if ( pDirImpl )
{
pDirImpl->eKind = DirectoryImpl::KIND_ASSETS;
pDirImpl->pApkDirStruct = pdir;
pDirImpl->strPath = path;
*pDirectory = (oslDirectory) pDirImpl;
return osl_File_E_None;
}
else
{
errno = ENOMEM;
lo_apk_closedir( pdir );
}
}
}
else
#endif
{
/* open directory */
DIR *pdir = opendir( path.getStr() );
if ( pdir )
{
SAL_INFO("sal.file" , "opendir(" << path << ") => " << pdir);
/* create and initialize impl structure */
DirectoryImpl* pDirImpl = new (std::nothrow) DirectoryImpl;
if ( pDirImpl )
{
pDirImpl->pDirStruct = pdir;
pDirImpl->strPath = path;
#ifdef ANDROID
pDirImpl->eKind = DirectoryImpl::KIND_DIRENT;
#endif
*pDirectory = static_cast <oslDirectory>(pDirImpl);
return osl_File_E_None;
}
errno = ENOMEM;
closedir( pdir );
}
else
{
int e = errno;
SAL_INFO("sal.file" , "opendir(" << path << "): " << UnixErrnoString(e));
// Restore errno after possible modification by SAL_INFO above
errno = e;
}
}
return oslTranslateFileError(errno);
}
oslFileError SAL_CALL osl_closeDirectory(oslDirectory pDirectory)
{
SAL_WARN_IF(!pDirectory, "sal.file" , "pDirectory is nullptr" );
DirectoryImpl* pDirImpl = static_cast <DirectoryImpl*>(pDirectory);
oslFileError err = osl_File_E_None;
if (!pDirImpl)
return osl_File_E_INVAL;
#ifdef ANDROID
if (pDirImpl->eKind == DirectoryImpl::KIND_ASSETS)
{
if (lo_apk_closedir(pDirImpl->pApkDirStruct))
err = osl_File_E_IO;
}
else
#endif
{
if (closedir( pDirImpl->pDirStruct) != 0)
{
int e = errno;
SAL_INFO("sal.file" , "closedir(" << pDirImpl->pDirStruct << "): " << UnixErrnoString(e));
err = oslTranslateFileError(e);
}
else
SAL_INFO("sal.file" , "closedir(" << pDirImpl->pDirStruct << "): OK" );
}
delete pDirImpl;
return err;
}
/**********************************************
* osl_readdir_impl_
*
* readdir wrapper, filters out "." and ".."
* on request
*********************************************/
static struct dirent* osl_readdir_impl_(DIR* pdir)
{
struct dirent* pdirent;
while ((pdirent = readdir(pdir)) != nullptr)
{
if ((strcmp(pdirent->d_name, "." ) == 0) || (strcmp(pdirent->d_name, ".." ) == 0))
continue ;
break ;
}
return pdirent;
}
oslFileError SAL_CALL osl_getNextDirectoryItem(oslDirectory pDirectory,
oslDirectoryItem* pItem, SAL_UNUSED_PARAMETER sal_uInt32 /*uHint*/)
{
SAL_WARN_IF(!pDirectory, "sal.file" , "pDirectory is nullptr" );
SAL_WARN_IF(!pItem, "sal.file" , "pItem is nullptr" );
DirectoryImpl* pDirImpl = static_cast <DirectoryImpl*>(pDirectory);
OString strFileName;
struct dirent* pEntry;
if ((pDirectory == nullptr) || (pItem == nullptr))
return osl_File_E_INVAL;
#ifdef ANDROID
if (pDirImpl->eKind == DirectoryImpl::KIND_ASSETS)
{
pEntry = lo_apk_readdir(pDirImpl->pApkDirStruct);
}
else
#endif
{
pEntry = osl_readdir_impl_(pDirImpl->pDirStruct);
}
if (!pEntry)
return osl_File_E_NOENT;
char const * filename = pEntry->d_name;
#if defined (MACOSX)
// convert decomposed filename to precomposed UTF-8
char composed_name[BUFSIZ];
CFMutableStringRef strRef = CFStringCreateMutable(nullptr, 0 );
CFStringAppendCString(strRef, filename, kCFStringEncodingUTF8); // UTF8 is default on Mac OSX
CFStringNormalize(strRef, kCFStringNormalizationFormC);
CFStringGetCString(strRef, composed_name, BUFSIZ, kCFStringEncodingUTF8);
CFRelease(strRef);
filename = composed_name;
#endif
strFileName = OString(filename, strlen(filename));
auto const strFilePath = osl::systemPathMakeAbsolutePath(pDirImpl->strPath, strFileName);
DirectoryItem_Impl* pImpl = static_cast < DirectoryItem_Impl* >(*pItem);
if (pImpl)
pImpl->release();
#ifdef _DIRENT_HAVE_D_TYPE
pImpl = new DirectoryItem_Impl(strFilePath, pEntry->d_type);
#else
pImpl = new DirectoryItem_Impl(strFilePath);
#endif /* _DIRENT_HAVE_D_TYPE */
*pItem = pImpl;
return osl_File_E_None;
}
oslFileError SAL_CALL osl_getDirectoryItem(rtl_uString* ustrFileURL, oslDirectoryItem* pItem)
{
OString strSystemPath;
oslFileError osl_error = osl_File_E_INVAL;
if ((!ustrFileURL) || (ustrFileURL->length == 0) || (!pItem))
return osl_File_E_INVAL;
osl_error = osl::detail::convertUrlToPathname(OUString::unacquired(&ustrFileURL), &strSystemPath);
if (osl_error != osl_File_E_None)
return osl_error;
osl_systemPathRemoveSeparator(strSystemPath.pData);
if (osl::access(strSystemPath, F_OK) == -1)
{
osl_error = oslTranslateFileError(errno);
}
else
{
*pItem = new DirectoryItem_Impl(std::move(strSystemPath));
}
return osl_error;
}
oslFileError SAL_CALL osl_acquireDirectoryItem( oslDirectoryItem Item )
{
DirectoryItem_Impl * pImpl = static_cast < DirectoryItem_Impl* >(Item);
if (pImpl == nullptr)
return osl_File_E_INVAL;
pImpl->acquire();
return osl_File_E_None;
}
oslFileError SAL_CALL osl_releaseDirectoryItem( oslDirectoryItem Item )
{
DirectoryItem_Impl * pImpl = static_cast < DirectoryItem_Impl* >(Item);
if (pImpl == nullptr)
return osl_File_E_INVAL;
pImpl->release();
return osl_File_E_None;
}
oslFileError SAL_CALL osl_createDirectory( rtl_uString* ustrDirectoryURL )
{
return osl_createDirectoryWithFlags(
ustrDirectoryURL, osl_File_OpenFlag_Read | osl_File_OpenFlag_Write);
}
oslFileError osl_createDirectoryWithFlags(
rtl_uString * ustrDirectoryURL, sal_uInt32 flags)
{
char path[PATH_MAX];
oslFileError eRet;
SAL_WARN_IF((!ustrDirectoryURL) || (ustrDirectoryURL->length == 0),
"sal.file" , "Invalid directory URL" );
/* convert directory url to system path */
eRet = FileURLToPath( path, PATH_MAX, ustrDirectoryURL );
if ( eRet != osl_File_E_None )
return eRet;
#ifdef MACOSX
if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
return oslTranslateFileError( errno );
#endif /* MACOSX */
return osl_psz_createDirectory( path, flags );
}
oslFileError SAL_CALL osl_removeDirectory( rtl_uString* ustrDirectoryURL )
{
char path[PATH_MAX];
oslFileError eRet;
SAL_WARN_IF((!ustrDirectoryURL) || (ustrDirectoryURL->length == 0),
"sal.file" , "Invalid directory URL" );
/* convert directory url to system path */
eRet = FileURLToPath( path, PATH_MAX, ustrDirectoryURL );
if ( eRet != osl_File_E_None )
return eRet;
#ifdef MACOSX
if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
return oslTranslateFileError( errno );
#endif /* MACOSX */
return osl_psz_removeDirectory( path );
}
oslFileError osl_psz_createDirectory(char const * pszPath, sal_uInt32 flags)
{
int nRet=0;
int mode
= (((flags & osl_File_OpenFlag_Read) == 0
? 0
: ((flags & osl_File_OpenFlag_Private) == 0
? S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
: S_IRUSR | S_IXUSR))
| ((flags & osl_File_OpenFlag_Write) == 0
? 0
: ((flags & osl_File_OpenFlag_Private) == 0
? S_IWUSR | S_IWGRP | S_IWOTH
: S_IWUSR)));
nRet = mkdir(pszPath,mode);
if ( nRet < 0 )
{
nRet=errno;
SAL_INFO("sal.file" , "mkdir(" << pszPath << ",0" << std::oct << mode << std::dec << "): " << UnixErrnoString(nRet));
return oslTranslateFileError(nRet);
}
else
SAL_INFO("sal.file" , "mkdir(" << pszPath << ",0" << std::oct << mode << std::dec << "): OK" );
return osl_File_E_None;
}
static oslFileError osl_psz_removeDirectory( const char * pszPath )
{
int nRet = rmdir(pszPath);
if ( nRet < 0 )
{
nRet=errno;
SAL_INFO("sal.file" , "rmdir(" << pszPath << "): " << UnixErrnoString(nRet));
return oslTranslateFileError(nRet);
}
else
SAL_INFO("sal.file" , "rmdir(" << pszPath << "): OK" );
return osl_File_E_None;
}
static int path_make_parent(char * path)
{
int i = rtl_str_lastIndexOfChar(path, '/' );
if (i > 0)
{
*(path + i) = 0;
return i;
}
return 0;
}
static oslFileError create_dir_with_callback(
char * directory_path,
oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
void * pData)
{
if (osl::mkdir(directory_path, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
{
if (aDirectoryCreationCallbackFunc)
{
OUString url;
osl::detail::convertPathnameToUrl(directory_path, &url);
aDirectoryCreationCallbackFunc(pData, url.pData);
}
return osl_File_E_None;
}
return oslTranslateFileError(errno);
}
static oslFileError create_dir_recursively_(
char * dir_path,
oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
void * pData)
{
OSL_PRECOND((rtl_str_getLength(dir_path) > 0) && ((dir_path + (rtl_str_getLength(dir_path) - 1)) != (dir_path + rtl_str_lastIndexOfChar(dir_path, '/' ))),
"Path must not end with a slash" );
oslFileError osl_error = create_dir_with_callback(
dir_path, aDirectoryCreationCallbackFunc, pData);
if (osl_error != osl_File_E_NOENT)
return osl_error;
// we step back until '/a_dir' at maximum because
// we should get an error unequal ENOENT when
// we try to create 'a_dir' at '/' and would so
// return before
int pos = path_make_parent(dir_path);
osl_error = create_dir_recursively_(
dir_path, aDirectoryCreationCallbackFunc, pData);
if (osl_error != osl_File_E_None && osl_error != osl_File_E_EXIST)
return osl_error;
dir_path[pos] = '/' ;
return create_dir_with_callback(dir_path, aDirectoryCreationCallbackFunc, pData);
}
oslFileError SAL_CALL osl_createDirectoryPath(
rtl_uString* aDirectoryUrl,
oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
void * pData)
{
if (aDirectoryUrl == nullptr)
return osl_File_E_INVAL;
OString sys_path;
oslFileError osl_error = osl::detail::convertUrlToPathname(
OUString::unacquired(&aDirectoryUrl), &sys_path);
if (osl_error != osl_File_E_None)
return osl_error;
osl::systemPathRemoveSeparator(sys_path);
return create_dir_recursively_(sys_path.pData->buffer, aDirectoryCreationCallbackFunc, pData);
}
static oslFileError osl_unlinkFile(const char * pszPath);
static oslFileError osl_psz_copyFile(const char * pszPath, const char * pszDestPath, bool preserveMetadata);
static oslFileError osl_psz_moveFile(const char * pszPath, const char * pszDestPath);
static oslFileError oslDoCopy(const char * pszSourceFileName, const char * pszDestFileName, mode_t nMode, size_t nSourceSize, bool DestFileExists);
static void attemptChangeMetadata(const char * pszFileName, mode_t nMode, time_t nAcTime, time_t nModTime, uid_t nUID, gid_t nGID);
static int oslDoCopyLink(const char * pszSourceFileName, const char * pszDestFileName);
static int oslDoCopyFile(const char * pszSourceFileName, const char * pszDestFileName, size_t nSourceSize, mode_t mode);
static oslFileError oslDoMoveFile(const char * pszPath, const char * pszDestPath);
oslFileError SAL_CALL osl_moveFile( rtl_uString* ustrFileURL, rtl_uString* ustrDestURL )
{
char srcPath[PATH_MAX];
char destPath[PATH_MAX];
oslFileError eRet;
SAL_WARN_IF((!ustrFileURL) || (ustrFileURL->length == 0), "sal.file" , "Invalid source file URL" );
SAL_WARN_IF((!ustrDestURL) || (ustrDestURL->length == 0), "sal.file" , "Invalid destination file URL" );
/* convert source url to system path */
eRet = FileURLToPath( srcPath, PATH_MAX, ustrFileURL );
if ( eRet != osl_File_E_None )
return eRet;
/* convert destination url to system path */
eRet = FileURLToPath( destPath, PATH_MAX, ustrDestURL );
if ( eRet != osl_File_E_None )
return eRet;
#ifdef MACOSX
if ( macxp_resolveAlias( srcPath, PATH_MAX ) != 0 || macxp_resolveAlias( destPath, PATH_MAX ) != 0 )
return oslTranslateFileError( errno );
#endif /* MACOSX */
return oslDoMoveFile( srcPath, destPath );
}
oslFileError SAL_CALL osl_replaceFile(rtl_uString* ustrFileURL, rtl_uString* ustrDestURL)
{
int nGid = -1;
char destPath[PATH_MAX];
oslFileError eRet = FileURLToPath(destPath, PATH_MAX, ustrDestURL);
if (eRet == osl_File_E_None)
{
struct stat aFileStat;
// coverity[fs_check_call] - unavoidable TOCTOU
int nRet = stat(destPath, &aFileStat);
if (nRet == -1)
{
nRet = errno;
SAL_INFO("sal.file" , "stat(" << destPath << "): " << UnixErrnoString(nRet));
}
else
{
nGid = aFileStat.st_gid;
}
}
eRet = osl_moveFile(ustrFileURL, ustrDestURL);
if (eRet == osl_File_E_None && nGid != -1)
{
int nRet = chown(destPath, -1, nGid);
if (nRet == -1)
{
nRet = errno;
SAL_INFO("sal.file" ,
"chown(" << destPath << "-1, " << nGid << "): " << UnixErrnoString(nRet));
}
}
return eRet;
}
oslFileError SAL_CALL osl_copyFile( rtl_uString* ustrFileURL, rtl_uString* ustrDestURL )
{
char srcPath[PATH_MAX];
char destPath[PATH_MAX];
oslFileError eRet;
SAL_WARN_IF((!ustrFileURL) || (ustrFileURL->length == 0), "sal.file" , "Invalid source file URL" );
SAL_WARN_IF((!ustrDestURL) || (ustrDestURL->length == 0), "sal.file" , "Invalid destination file URL" );
/* convert source url to system path */
eRet = FileURLToPath( srcPath, PATH_MAX, ustrFileURL );
if ( eRet != osl_File_E_None )
return eRet;
/* convert destination url to system path */
eRet = FileURLToPath( destPath, PATH_MAX, ustrDestURL );
if ( eRet != osl_File_E_None )
return eRet;
#ifdef MACOSX
if ( macxp_resolveAlias( srcPath, PATH_MAX ) != 0 || macxp_resolveAlias( destPath, PATH_MAX ) != 0 )
return oslTranslateFileError( errno );
#endif /* MACOSX */
return osl_psz_copyFile( srcPath, destPath, false );
}
oslFileError SAL_CALL osl_removeFile(rtl_uString* ustrFileURL)
{
char path[PATH_MAX];
oslFileError eRet;
SAL_WARN_IF(!ustrFileURL || ustrFileURL->length == 0, "sal.file" , "Invalid file URL" );
/* convert file url to system path */
eRet = FileURLToPath(path, PATH_MAX, ustrFileURL);
if (eRet != osl_File_E_None)
return eRet;
#ifdef MACOSX
if (macxp_resolveAlias(path, PATH_MAX) != 0)
return oslTranslateFileError(errno);
#endif /* MACOSX */
return osl_unlinkFile(path);
}
static oslFileError oslDoMoveFile(const char * pszPath, const char * pszDestPath)
{
oslFileError tErr = osl_psz_moveFile(pszPath,pszDestPath);
if (tErr == osl_File_E_None)
return tErr;
if (tErr != osl_File_E_XDEV)
return tErr;
tErr = osl_psz_copyFile(pszPath,pszDestPath, true );
if (tErr != osl_File_E_None)
{
osl_unlinkFile(pszDestPath);
return tErr;
}
tErr = osl_unlinkFile(pszPath);
return tErr;
}
static oslFileError osl_unlinkFile(const char * pszPath)
{
int nRet=0;
struct stat aStat;
nRet = lstat_c(pszPath,&aStat);
if (nRet < 0)
{
nRet=errno;
return oslTranslateFileError(nRet);
}
if (S_ISDIR(aStat.st_mode))
return osl_File_E_ISDIR;
nRet = unlink(pszPath);
if (nRet < 0)
{
nRet=errno;
SAL_INFO("sal.file" , "unlink(" << pszPath << "): " << UnixErrnoString(nRet));
return oslTranslateFileError(nRet);
}
else
SAL_INFO("sal.file" , "unlink(" << pszPath << "): OK" );
return osl_File_E_None;
}
static oslFileError osl_psz_moveFile(const char * pszPath, const char * pszDestPath)
{
int nRet = rename(pszPath,pszDestPath);
if (nRet < 0)
{
nRet=errno;
SAL_INFO("sal.file" , "rename(" << pszPath << "," << pszDestPath << "): " << UnixErrnoString(nRet));
return oslTranslateFileError(nRet);
}
else
SAL_INFO("sal.file" , "rename(" << pszPath << "," << pszDestPath << "): OK" );
return osl_File_E_None;
}
static oslFileError osl_psz_copyFile( const char * pszPath, const char * pszDestPath, bool preserveMetadata )
{
time_t nAcTime=0;
time_t nModTime=0;
uid_t nUID=0;
gid_t nGID=0;
int nRet=0;
mode_t nMode=0;
struct stat aFileStat;
oslFileError tErr=osl_File_E_invalidError;
size_t nSourceSize=0;
bool DestFileExists=true ;
/* mfe: does the source file really exists? */
nRet = lstat_c(pszPath,&aFileStat);
if (nRet < 0)
{
nRet=errno;
return oslTranslateFileError(nRet);
}
/* we do only copy files here */
if (S_ISDIR(aFileStat.st_mode))
return osl_File_E_ISDIR;
nSourceSize = static_cast < size_t >(aFileStat.st_size);
nMode = aFileStat.st_mode;
nAcTime = aFileStat.st_atime;
nModTime = aFileStat.st_mtime;
nUID = aFileStat.st_uid;
nGID = aFileStat.st_gid;
nRet = stat_c(pszDestPath,&aFileStat);
if (nRet < 0)
{
nRet=errno;
#ifdef IOS
// Checking for nonexistent files at least in the iCloud cache directory (like
// "/private/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/helloodt0.odt" fails
// with EPERM, not ENOENT.
if (nRet == EPERM)
DestFileExists=false ;
#endif
if (nRet == ENOENT)
DestFileExists=false ;
}
/* mfe: the destination file must not be a directory! */
if (nRet == 0 && S_ISDIR(aFileStat.st_mode))
return osl_File_E_ISDIR;
/* mfe: file does not exists or is no dir */
tErr = oslDoCopy(pszPath, pszDestPath, nMode, nSourceSize, DestFileExists);
if (tErr != osl_File_E_None)
return tErr;
if (preserveMetadata)
attemptChangeMetadata(pszDestPath, nMode, nAcTime, nModTime, nUID, nGID);
return tErr;
}
static oslFileError oslDoCopy(const char * pszSourceFileName, const char * pszDestFileName, mode_t nMode, size_t nSourceSize, bool DestFileExists)
{
int nRet=0;
OString tmpDestFile;
if ( DestFileExists )
{
//TODO: better pick a temp file name instead of adding .osl-tmp:
// use the destination file to avoid EXDEV /* Cross-device link */
tmpDestFile = pszDestFileName + OString::Concat(".osl-tmp" );
if (rename(pszDestFileName, tmpDestFile.getStr()) != 0)
{
int e = errno;
SAL_INFO("sal.file" , "rename(" << pszDestFileName << ", " << tmpDestFile
<< "): " << UnixErrnoString(e));
if (e == ENOENT)
{
DestFileExists = false ;
}
else
{
return osl_File_E_EXIST; // for want of a better error code
}
}
else
{
SAL_INFO("sal.file" , "rename(" << pszDestFileName << ", " << tmpDestFile
<< "): OK" );
}
}
if ( S_ISREG(nMode) )
{
/* copy SourceFile to DestFile */
nRet = oslDoCopyFile(pszSourceFileName,pszDestFileName,nSourceSize, nMode);
}
else if ( S_ISLNK(nMode) )
{
nRet = oslDoCopyLink(pszSourceFileName,pszDestFileName);
}
else
{
nRet = EINVAL;
}
if ( nRet > 0 && DestFileExists )
{
if (unlink(pszDestFileName) != 0)
{
int e = errno;
SAL_INFO("sal.file" , "unlink(" << pszDestFileName << "): " << UnixErrnoString(e));
}
else
SAL_INFO("sal.file" , "unlink(" << pszDestFileName << "): OK" );
if (rename(tmpDestFile.getStr(), pszDestFileName) != 0)
{
int e = errno;
SAL_INFO("sal.file" , "rename(" << tmpDestFile << ", " << pszDestFileName
<< "): " << UnixErrnoString(e));
}
else
SAL_INFO("sal.file" , "rename(" << tmpDestFile << ", " << pszDestFileName << "): OK" );
}
if ( nRet > 0 )
{
return oslTranslateFileError(nRet);
}
if ( DestFileExists )
{
unlink(tmpDestFile.getStr());
}
return osl_File_E_None;
}
void attemptChangeMetadata( const char * pszFileName, mode_t nMode, time_t nAcTime, time_t nModTime, uid_t nUID, gid_t nGID)
{
struct utimbuf aTimeBuffer;
#if !defined AT_FDCWD
if (!S_ISLNK(nMode) && chmod(pszFileName, nMode) < 0)
#else
if ( fchmodat(AT_FDCWD, pszFileName, nMode, AT_SYMLINK_NOFOLLOW) < 0 )
#endif
{
int e = errno;
SAL_INFO("sal.file" , "chmod(" << pszFileName << ",0" << std::oct << nMode << std::dec <<"): " << UnixErrnoString(e));
}
else
SAL_INFO("sal.file" , "chmod(" << pszFileName << ",0" << std::oct << nMode << std::dec <<"): OK" );
// No way to change utime of a symlink itself:
if (!S_ISLNK(nMode))
{
aTimeBuffer.actime=nAcTime;
aTimeBuffer.modtime=nModTime;
if ( utime(pszFileName,&aTimeBuffer) < 0 )
{
int e = errno;
SAL_INFO("sal.file" , "utime(" << pszFileName << "): errno " << e);
}
}
if ( nUID != getuid() )
{
nUID=getuid();
}
if ( lchown(pszFileName,nUID,nGID) < 0 )
{
int e = errno;
SAL_INFO("sal.file" , "lchown(" << pszFileName << "): errno " << e);
}
else
SAL_INFO("sal.file" , "lchown(" << pszFileName << "): OK" );
}
static int oslDoCopyLink(const char * pszSourceFileName, const char * pszDestFileName)
{
int nRet=0;
/* mfe: if dest file is symbolic link remove the link and place the file instead (hro says so) */
/* mfe: if source is a link copy the link and not the file it points to (hro says so) */
char pszLinkContent[PATH_MAX+1];
pszLinkContent[0] = '\0' ;
nRet = readlink(pszSourceFileName,pszLinkContent,PATH_MAX);
if ( nRet < 0 )
{
nRet=errno;
return nRet;
}
pszLinkContent[ nRet ] = 0;
nRet = symlink(pszLinkContent,pszDestFileName);
if ( nRet < 0 )
{
nRet=errno;
return nRet;
}
return 0;
}
static int oslDoCopyFile(const char * pszSourceFileName, const char * pszDestFileName, size_t nSourceSize, mode_t mode)
{
oslFileHandle SourceFileFH=nullptr;
int DestFileFD=0;
int nRet=0;
if (openFilePath(pszSourceFileName,
&SourceFileFH,
osl_File_OpenFlag_Read|osl_File_OpenFlag_NoLock|osl_File_OpenFlag_NoExcl, mode_t(-1)) != osl_File_E_None)
{
// Let's hope errno is still set relevantly after openFilePath...
nRet=errno;
return nRet;
}
DestFileFD=open(pszDestFileName, O_WRONLY | O_CREAT, mode);
if ( DestFileFD < 0 )
{
nRet=errno;
SAL_INFO("sal.file" , "open(" << pszDestFileName << ",O_WRONLY|O_CREAT,0" << std::oct << mode << std::dec << "): " << UnixErrnoString(nRet));
osl_closeFile(SourceFileFH);
return nRet;
}
else
SAL_INFO("sal.file" , "open(" << pszDestFileName << ",O_WRONLY|O_CREAT,0" << std::oct << mode << std::dec << "): OK" );
size_t nRemains = nSourceSize;
if ( nRemains )
{
/* mmap has problems, try the direct streaming */
char pBuffer[0x7FFF];
do
{
size_t nToRead = std::min( sizeof (pBuffer), nRemains );
sal_uInt64 nRead;
bool succeeded;
if ( osl_readFile( SourceFileFH, pBuffer, nToRead, &nRead ) != osl_File_E_None || nRead > nToRead || nRead == 0 )
break ;
succeeded = safeWrite( DestFileFD, pBuffer, nRead );
if ( !succeeded )
break ;
// We know nRead <= nToRead, so it must fit in a size_t
nRemains -= static_cast <size_t>(nRead);
}
while ( nRemains );
}
if ( nRemains )
{
if ( errno )
nRet = errno;
else
nRet = ENOSPC;
}
osl_closeFile( SourceFileFH );
if ( close( DestFileFD ) == -1 )
{
int e = errno;
SAL_INFO("sal.file" , "close(" << DestFileFD << "): " << UnixErrnoString(e));
if ( nRet == 0 )
nRet = e;
}
else
SAL_INFO("sal.file" , "close(" << DestFileFD << "): OK" );
return nRet;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 C=96 H=99 G=97
¤ Dauer der Verarbeitung: 0.6 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland