As such the code handles it perfectly fine, and returns an error from the function, but. Change-Id: I356b8140381d3ccd21ff0a7f5c666552571b12f4
1003 lines
28 KiB
C++
1003 lines
28 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* 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/file.hxx"
|
|
#include "osl/detail/file.h"
|
|
|
|
#include "osl/diagnose.h"
|
|
#include "osl/thread.h"
|
|
#include <osl/signal.h>
|
|
#include "rtl/alloc.h"
|
|
#include <rtl/string.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 <sys/types.h>
|
|
#include <errno.h>
|
|
#include <dirent.h>
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/mman.h>
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
|
|
#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
|
|
***********************************************************************/
|
|
|
|
typedef struct
|
|
{
|
|
rtl_uString* ustrPath; /* holds native directory path */
|
|
DIR* pDirStruct;
|
|
#ifdef ANDROID
|
|
enum Kind
|
|
{
|
|
KIND_DIRENT = 1,
|
|
KIND_ASSETS = 2
|
|
};
|
|
int eKind;
|
|
lo_apk_dir* pApkDirStruct;
|
|
#endif
|
|
} oslDirectoryImpl;
|
|
|
|
DirectoryItem_Impl::DirectoryItem_Impl(
|
|
rtl_uString * ustrFilePath, unsigned char DType)
|
|
: m_RefCount (1),
|
|
m_ustrFilePath (ustrFilePath),
|
|
m_DType (DType)
|
|
{
|
|
if (m_ustrFilePath != nullptr)
|
|
rtl_uString_acquire(m_ustrFilePath);
|
|
}
|
|
DirectoryItem_Impl::~DirectoryItem_Impl()
|
|
{
|
|
if (m_ustrFilePath != nullptr)
|
|
rtl_uString_release(m_ustrFilePath);
|
|
}
|
|
|
|
void * DirectoryItem_Impl::operator new(size_t n)
|
|
{
|
|
return rtl_allocateMemory(n);
|
|
}
|
|
void DirectoryItem_Impl::operator delete(void * p)
|
|
{
|
|
rtl_freeMemory(p);
|
|
}
|
|
|
|
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 sal_Char* pszPath);
|
|
|
|
oslFileError SAL_CALL osl_openDirectory(rtl_uString* ustrDirectoryURL, oslDirectory* pDirectory)
|
|
{
|
|
rtl_uString* ustrSystemPath = nullptr;
|
|
oslFileError eRet;
|
|
|
|
char path[PATH_MAX];
|
|
|
|
if ((ustrDirectoryURL == nullptr) || (ustrDirectoryURL->length == 0) || (pDirectory == nullptr))
|
|
return osl_File_E_INVAL;
|
|
|
|
/* convert file URL to system path */
|
|
eRet = osl_getSystemPathFromFileURL_Ex(ustrDirectoryURL, &ustrSystemPath);
|
|
|
|
if( eRet != osl_File_E_None )
|
|
return eRet;
|
|
|
|
osl_systemPathRemoveSeparator(ustrSystemPath);
|
|
|
|
/* convert unicode path to text */
|
|
if ( UnicodeToText( path, PATH_MAX, ustrSystemPath->buffer, ustrSystemPath->length )
|
|
#ifdef MACOSX
|
|
&& macxp_resolveAlias( path, PATH_MAX ) == 0
|
|
#endif /* MACOSX */
|
|
)
|
|
{
|
|
#ifdef ANDROID
|
|
if( strncmp( path, "/assets/", sizeof( "/assets/" ) - 1) == 0 )
|
|
{
|
|
lo_apk_dir *pdir = lo_apk_opendir( path );
|
|
|
|
if( pdir )
|
|
{
|
|
oslDirectoryImpl* pDirImpl = (oslDirectoryImpl*) rtl_allocateMemory( sizeof(oslDirectoryImpl) );
|
|
|
|
if( pDirImpl )
|
|
{
|
|
pDirImpl->eKind = oslDirectoryImpl::KIND_ASSETS;
|
|
pDirImpl->pApkDirStruct = pdir;
|
|
pDirImpl->ustrPath = ustrSystemPath;
|
|
|
|
*pDirectory = (oslDirectory) pDirImpl;
|
|
return osl_File_E_None;
|
|
}
|
|
else
|
|
{
|
|
errno = ENOMEM;
|
|
lo_apk_closedir( pdir );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* open directory */
|
|
DIR *pdir = opendir( path );
|
|
|
|
if( pdir )
|
|
{
|
|
/* create and initialize impl structure */
|
|
oslDirectoryImpl* pDirImpl = static_cast<oslDirectoryImpl*>(rtl_allocateMemory( sizeof(oslDirectoryImpl) ));
|
|
|
|
if( pDirImpl )
|
|
{
|
|
pDirImpl->pDirStruct = pdir;
|
|
pDirImpl->ustrPath = ustrSystemPath;
|
|
#ifdef ANDROID
|
|
pDirImpl->eKind = oslDirectoryImpl::KIND_DIRENT;
|
|
#endif
|
|
*pDirectory = static_cast<oslDirectory>(pDirImpl);
|
|
return osl_File_E_None;
|
|
}
|
|
errno = ENOMEM;
|
|
closedir( pdir );
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG_OSL_FILE
|
|
perror ("osl_openDirectory"); fprintf (stderr, path);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
rtl_uString_release( ustrSystemPath );
|
|
|
|
return oslTranslateFileError(OSL_FET_ERROR, errno);
|
|
}
|
|
|
|
oslFileError SAL_CALL osl_closeDirectory(oslDirectory pDirectory)
|
|
{
|
|
SAL_WARN_IF(!pDirectory, "sal.file", "pDirectory is nullptr");
|
|
oslDirectoryImpl* pDirImpl = static_cast<oslDirectoryImpl*>(pDirectory);
|
|
oslFileError err = osl_File_E_None;
|
|
|
|
if (!pDirImpl)
|
|
return osl_File_E_INVAL;
|
|
|
|
#ifdef ANDROID
|
|
if (pDirImpl->eKind == oslDirectoryImpl::KIND_ASSETS)
|
|
{
|
|
if (lo_apk_closedir(pDirImpl->pApkDirStruct))
|
|
err = osl_File_E_IO;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (closedir( pDirImpl->pDirStruct))
|
|
err = oslTranslateFileError(OSL_FET_ERROR, errno);
|
|
}
|
|
|
|
/* cleanup members */
|
|
rtl_uString_release(pDirImpl->ustrPath);
|
|
|
|
rtl_freeMemory(pDirImpl);
|
|
|
|
return err;
|
|
}
|
|
|
|
/**********************************************
|
|
* osl_readdir_impl_
|
|
*
|
|
* readdir wrapper, filters out "." and ".."
|
|
* on request
|
|
*********************************************/
|
|
|
|
static struct dirent* osl_readdir_impl_(DIR* pdir, bool bFilterLocalAndParentDir)
|
|
{
|
|
struct dirent* pdirent;
|
|
|
|
while ((pdirent = readdir(pdir)) != nullptr)
|
|
{
|
|
if (bFilterLocalAndParentDir &&
|
|
((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");
|
|
|
|
oslDirectoryImpl* pDirImpl = static_cast<oslDirectoryImpl*>(pDirectory);
|
|
rtl_uString* ustrFileName = nullptr;
|
|
rtl_uString* ustrFilePath = nullptr;
|
|
struct dirent* pEntry;
|
|
|
|
if ((pDirectory == nullptr) || (pItem == nullptr))
|
|
return osl_File_E_INVAL;
|
|
|
|
#ifdef ANDROID
|
|
if(pDirImpl->eKind == oslDirectoryImpl::KIND_ASSETS)
|
|
{
|
|
pEntry = lo_apk_readdir(pDirImpl->pApkDirStruct);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
pEntry = osl_readdir_impl_(pDirImpl->pDirStruct, true);
|
|
}
|
|
|
|
if (!pEntry)
|
|
return osl_File_E_NOENT;
|
|
|
|
#if defined(MACOSX)
|
|
|
|
// convert decomposed filename to precomposed unicode
|
|
char composed_name[BUFSIZ];
|
|
CFMutableStringRef strRef = CFStringCreateMutable(nullptr, 0 );
|
|
CFStringAppendCString(strRef, pEntry->d_name, kCFStringEncodingUTF8); // UTF8 is default on Mac OSX
|
|
CFStringNormalize(strRef, kCFStringNormalizationFormC);
|
|
CFStringGetCString(strRef, composed_name, BUFSIZ, kCFStringEncodingUTF8);
|
|
CFRelease(strRef);
|
|
rtl_string2UString(&ustrFileName, composed_name, strlen(composed_name),
|
|
osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS);
|
|
|
|
#else // not MACOSX
|
|
/* convert file name to unicode */
|
|
rtl_string2UString(&ustrFileName, pEntry->d_name, strlen(pEntry->d_name),
|
|
osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS);
|
|
assert(ustrFileName);
|
|
|
|
#endif
|
|
|
|
osl_systemPathMakeAbsolutePath(pDirImpl->ustrPath, ustrFileName, &ustrFilePath);
|
|
rtl_uString_release(ustrFileName);
|
|
|
|
DirectoryItem_Impl* pImpl = static_cast< DirectoryItem_Impl* >(*pItem);
|
|
if (pImpl)
|
|
{
|
|
pImpl->release();
|
|
pImpl = nullptr;
|
|
}
|
|
#ifdef _DIRENT_HAVE_D_TYPE
|
|
pImpl = new DirectoryItem_Impl(ustrFilePath, pEntry->d_type);
|
|
#else
|
|
pImpl = new DirectoryItem_Impl(ustrFilePath);
|
|
#endif /* _DIRENT_HAVE_D_TYPE */
|
|
*pItem = pImpl;
|
|
rtl_uString_release(ustrFilePath);
|
|
|
|
return osl_File_E_None;
|
|
}
|
|
|
|
oslFileError SAL_CALL osl_getDirectoryItem(rtl_uString* ustrFileURL, oslDirectoryItem* pItem)
|
|
{
|
|
SAL_WARN_IF((!ustrFileURL) || (ustrFileURL->length == 0), "sal.file",
|
|
"Invalid file URL " << (ustrFileURL ? ("'" + OUString(ustrFileURL) + "'") : OUString("(null)")));
|
|
SAL_WARN_IF(!pItem, "sal.file", "pItem is null");
|
|
|
|
rtl_uString* ustrSystemPath = nullptr;
|
|
oslFileError osl_error = osl_File_E_INVAL;
|
|
|
|
if ((!ustrFileURL) || (ustrFileURL->length == 0) || (!pItem))
|
|
return osl_File_E_INVAL;
|
|
|
|
osl_error = osl_getSystemPathFromFileURL_Ex(ustrFileURL, &ustrSystemPath);
|
|
if (osl_error != osl_File_E_None)
|
|
return osl_error;
|
|
|
|
osl_systemPathRemoveSeparator(ustrSystemPath);
|
|
|
|
if (access_u(ustrSystemPath, F_OK) == -1)
|
|
{
|
|
osl_error = oslTranslateFileError(OSL_FET_ERROR, errno);
|
|
}
|
|
else
|
|
{
|
|
*pItem = new DirectoryItem_Impl(ustrSystemPath);
|
|
}
|
|
rtl_uString_release(ustrSystemPath);
|
|
|
|
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( OSL_FET_ERROR, 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( OSL_FET_ERROR, 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;
|
|
return oslTranslateFileError(OSL_FET_ERROR, nRet);
|
|
}
|
|
|
|
return osl_File_E_None;
|
|
}
|
|
|
|
static oslFileError osl_psz_removeDirectory( const sal_Char* pszPath )
|
|
{
|
|
int nRet=0;
|
|
|
|
nRet = rmdir(pszPath);
|
|
|
|
if ( nRet < 0 )
|
|
{
|
|
nRet=errno;
|
|
return oslTranslateFileError(OSL_FET_ERROR, nRet);
|
|
}
|
|
|
|
return osl_File_E_None;
|
|
}
|
|
|
|
static int path_make_parent(sal_Unicode* path)
|
|
{
|
|
int i = rtl_ustr_lastIndexOfChar(path, '/');
|
|
|
|
if (i > 0)
|
|
{
|
|
*(path + i) = 0;
|
|
return i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int create_dir_with_callback(
|
|
sal_Unicode* directory_path,
|
|
oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
|
|
void* pData)
|
|
{
|
|
if (osl::mkdir(directory_path, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
|
|
{
|
|
if (aDirectoryCreationCallbackFunc)
|
|
{
|
|
rtl::OUString url;
|
|
osl::FileBase::getFileURLFromSystemPath(directory_path, url);
|
|
aDirectoryCreationCallbackFunc(pData, url.pData);
|
|
}
|
|
return 0;
|
|
}
|
|
return errno;
|
|
}
|
|
|
|
static oslFileError create_dir_recursively_(
|
|
sal_Unicode* dir_path,
|
|
oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
|
|
void* pData)
|
|
{
|
|
OSL_PRECOND((rtl_ustr_getLength(dir_path) > 0) && ((dir_path + (rtl_ustr_getLength(dir_path) - 1)) != (dir_path + rtl_ustr_lastIndexOfChar(dir_path, '/'))),
|
|
"Path must not end with a slash");
|
|
|
|
int native_err = create_dir_with_callback(
|
|
dir_path, aDirectoryCreationCallbackFunc, pData);
|
|
|
|
if (native_err == 0)
|
|
return osl_File_E_None;
|
|
|
|
if (native_err != ENOENT)
|
|
return oslTranslateFileError(OSL_FET_ERROR, native_err);
|
|
|
|
// 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);
|
|
|
|
oslFileError osl_error = create_dir_recursively_(
|
|
dir_path, aDirectoryCreationCallbackFunc, pData);
|
|
|
|
if (osl_error != osl_File_E_None)
|
|
return osl_error;
|
|
|
|
dir_path[pos] = '/';
|
|
|
|
return create_dir_recursively_(dir_path, aDirectoryCreationCallbackFunc, pData);
|
|
}
|
|
|
|
oslFileError SAL_CALL osl_createDirectoryPath(
|
|
rtl_uString* aDirectoryUrl,
|
|
oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
|
|
void* pData)
|
|
{
|
|
if (aDirectoryUrl == nullptr)
|
|
return osl_File_E_INVAL;
|
|
|
|
rtl::OUString sys_path;
|
|
oslFileError osl_error = osl_getSystemPathFromFileURL_Ex(aDirectoryUrl, &sys_path.pData);
|
|
|
|
if (osl_error != osl_File_E_None)
|
|
return osl_error;
|
|
|
|
osl::systemPathRemoveSeparator(sys_path);
|
|
|
|
// const_cast because sys_path is a local copy which we want to modify inplace instead of
|
|
// copy it into another buffer on the heap again
|
|
return create_dir_recursively_(sys_path.pData->buffer, aDirectoryCreationCallbackFunc, pData);
|
|
}
|
|
|
|
static oslFileError osl_psz_removeFile(const sal_Char* pszPath);
|
|
static oslFileError osl_psz_copyFile(const sal_Char* pszPath, const sal_Char* pszDestPath, bool preserveMetadata);
|
|
static oslFileError osl_psz_moveFile(const sal_Char* pszPath, const sal_Char* pszDestPath);
|
|
|
|
static oslFileError oslDoCopy(const sal_Char* pszSourceFileName, const sal_Char* pszDestFileName, mode_t nMode, size_t nSourceSize, int DestFileExists);
|
|
static void attemptChangeMetadata(const sal_Char* pszFileName, mode_t nMode, time_t nAcTime, time_t nModTime, uid_t nUID, gid_t nGID);
|
|
static int oslDoCopyLink(const sal_Char* pszSourceFileName, const sal_Char* pszDestFileName);
|
|
static int oslDoCopyFile(const sal_Char* pszSourceFileName, const sal_Char* pszDestFileName, size_t nSourceSize, mode_t mode);
|
|
static oslFileError oslDoMoveFile(const sal_Char* pszPath, const sal_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( OSL_FET_ERROR, errno );
|
|
#endif/* MACOSX */
|
|
|
|
return oslDoMoveFile( srcPath, destPath );
|
|
}
|
|
|
|
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( OSL_FET_ERROR, 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( OSL_FET_ERROR, errno );
|
|
#endif/* MACOSX */
|
|
|
|
return osl_psz_removeFile( path );
|
|
}
|
|
|
|
static oslFileError oslDoMoveFile( const sal_Char* pszPath, const sal_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_psz_removeFile(pszDestPath);
|
|
return tErr;
|
|
}
|
|
|
|
tErr=osl_psz_removeFile(pszPath);
|
|
|
|
return tErr;
|
|
}
|
|
|
|
static oslFileError osl_psz_removeFile( const sal_Char* pszPath )
|
|
{
|
|
int nRet=0;
|
|
struct stat aStat;
|
|
|
|
nRet = lstat_c(pszPath,&aStat);
|
|
if ( nRet < 0 )
|
|
{
|
|
nRet=errno;
|
|
return oslTranslateFileError(OSL_FET_ERROR, nRet);
|
|
}
|
|
|
|
if ( S_ISDIR(aStat.st_mode) )
|
|
{
|
|
return osl_File_E_ISDIR;
|
|
}
|
|
|
|
nRet = unlink(pszPath);
|
|
if ( nRet < 0 )
|
|
{
|
|
nRet=errno;
|
|
return oslTranslateFileError(OSL_FET_ERROR, nRet);
|
|
}
|
|
|
|
return osl_File_E_None;
|
|
}
|
|
|
|
static oslFileError osl_psz_moveFile(const sal_Char* pszPath, const sal_Char* pszDestPath)
|
|
{
|
|
int nRet = 0;
|
|
|
|
nRet = rename(pszPath,pszDestPath);
|
|
|
|
if ( nRet < 0 )
|
|
{
|
|
nRet=errno;
|
|
return oslTranslateFileError(OSL_FET_ERROR, nRet);
|
|
}
|
|
|
|
return osl_File_E_None;
|
|
}
|
|
|
|
static oslFileError osl_psz_copyFile( const sal_Char* pszPath, const sal_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;
|
|
int DestFileExists=1;
|
|
|
|
/* mfe: does the source file really exists? */
|
|
nRet = lstat_c(pszPath,&aFileStat);
|
|
|
|
if ( nRet < 0 )
|
|
{
|
|
nRet=errno;
|
|
return oslTranslateFileError(OSL_FET_ERROR, nRet);
|
|
}
|
|
|
|
/* mfe: we do only copy files here! */
|
|
if ( S_ISDIR(aFileStat.st_mode) )
|
|
{
|
|
return osl_File_E_ISDIR;
|
|
}
|
|
|
|
nSourceSize=(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;
|
|
|
|
if ( nRet == ENOENT )
|
|
{
|
|
DestFileExists=0;
|
|
}
|
|
}
|
|
|
|
/* 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 sal_Char* pszSourceFileName, const sal_Char* pszDestFileName, mode_t nMode, size_t nSourceSize, int DestFileExists)
|
|
{
|
|
int nRet=0;
|
|
|
|
rtl::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 = rtl::OString(pszDestFileName) + ".osl-tmp";
|
|
if (rename(pszDestFileName, tmpDestFile.getStr()) != 0)
|
|
{
|
|
if (errno == ENOENT)
|
|
{
|
|
DestFileExists = 0;
|
|
}
|
|
else
|
|
{
|
|
int e = errno;
|
|
SAL_INFO(
|
|
"sal.osl",
|
|
"rename(" << pszDestFileName << ", " << tmpDestFile
|
|
<< ") failed with errno " << e);
|
|
return osl_File_E_EXIST; // for want of a better error code
|
|
}
|
|
}
|
|
}
|
|
|
|
/* mfe: should be S_ISREG */
|
|
if ( !S_ISLNK(nMode) )
|
|
{
|
|
/* copy SourceFile to DestFile */
|
|
nRet = oslDoCopyFile(pszSourceFileName,pszDestFileName,nSourceSize, nMode);
|
|
}
|
|
/* mfe: OK redundant at the moment */
|
|
else if ( S_ISLNK(nMode) )
|
|
{
|
|
nRet = oslDoCopyLink(pszSourceFileName,pszDestFileName);
|
|
}
|
|
else
|
|
{
|
|
/* mfe: what to do here? */
|
|
nRet=ENOSYS;
|
|
}
|
|
|
|
if ( nRet > 0 && DestFileExists == 1 )
|
|
{
|
|
unlink(pszDestFileName);
|
|
if (rename(tmpDestFile.getStr(), pszDestFileName) != 0)
|
|
{
|
|
int e = errno;
|
|
SAL_WARN(
|
|
"sal.osl",
|
|
"rename(" << tmpDestFile << ", " << pszDestFileName
|
|
<< ") failed with errno " << e);
|
|
}
|
|
}
|
|
|
|
if ( nRet > 0 )
|
|
{
|
|
return oslTranslateFileError(OSL_FET_ERROR, nRet);
|
|
}
|
|
|
|
if ( DestFileExists == 1 )
|
|
{
|
|
unlink(tmpDestFile.getStr());
|
|
}
|
|
|
|
return osl_File_E_None;
|
|
}
|
|
|
|
void attemptChangeMetadata( const sal_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.osl", "chmod(" << pszFileName << ") failed with errno " << e);
|
|
}
|
|
|
|
// 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.osl",
|
|
"utime(" << pszFileName << ") failed with errno " << e);
|
|
}
|
|
}
|
|
|
|
if ( nUID != getuid() )
|
|
{
|
|
nUID=getuid();
|
|
}
|
|
if ( lchown(pszFileName,nUID,nGID) < 0 )
|
|
{
|
|
int e = errno;
|
|
SAL_INFO(
|
|
"sal.osl", "lchown(" << pszFileName << ") failed with errno " << e);
|
|
}
|
|
}
|
|
|
|
static int oslDoCopyLink(const sal_Char* pszSourceFileName, const sal_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) */
|
|
sal_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 sal_Char* pszSourceFileName, const sal_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;
|
|
osl_closeFile(SourceFileFH);
|
|
return nRet;
|
|
}
|
|
|
|
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 -= (size_t) nRead;
|
|
}
|
|
while( nRemains );
|
|
}
|
|
|
|
if ( nRemains )
|
|
{
|
|
if ( errno )
|
|
nRet = errno;
|
|
else
|
|
nRet = ENOSPC;
|
|
}
|
|
|
|
osl_closeFile( SourceFileFH );
|
|
if ( close( DestFileFD ) == -1 && nRet == 0 )
|
|
nRet = errno;
|
|
|
|
return nRet;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|