Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  file_misc.cxx   Sprache: C

 
/* -*- 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.24 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge