/* -*- 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 #include "system.hxx" #include #include #include #include #include #include #include #include "file_impl.hxx" #include "file_error_transl.hxx" #include "file_path_helper.hxx" #include "file_url.hxx" #include "uunxapi.hxx" namespace { void set_file_type(const struct stat& file_stat, oslFileStatus* pStat) { /* links to directories state also to be a directory */ if (S_ISLNK(file_stat.st_mode)) pStat->eType = osl_File_Type_Link; else if (S_ISDIR(file_stat.st_mode)) pStat->eType = osl_File_Type_Directory; else if (S_ISREG(file_stat.st_mode)) pStat->eType = osl_File_Type_Regular; else if (S_ISFIFO(file_stat.st_mode)) pStat->eType = osl_File_Type_Fifo; else if (S_ISSOCK(file_stat.st_mode)) pStat->eType = osl_File_Type_Socket; else if (S_ISCHR(file_stat.st_mode) || S_ISBLK(file_stat.st_mode)) pStat->eType = osl_File_Type_Special; else pStat->eType = osl_File_Type_Unknown; pStat->uValidFields |= osl_FileStatus_Mask_Type; } void set_file_access_mask(const struct stat& file_stat, oslFileStatus* pStat) { // user permissions if (S_IRUSR & file_stat.st_mode) pStat->uAttributes |= osl_File_Attribute_OwnRead; if (S_IWUSR & file_stat.st_mode) pStat->uAttributes |= osl_File_Attribute_OwnWrite; if (S_IXUSR & file_stat.st_mode) pStat->uAttributes |= osl_File_Attribute_OwnExe; // group permissions if (S_IRGRP & file_stat.st_mode) pStat->uAttributes |= osl_File_Attribute_GrpRead; if (S_IWGRP & file_stat.st_mode) pStat->uAttributes |= osl_File_Attribute_GrpWrite; if (S_IXGRP & file_stat.st_mode) pStat->uAttributes |= osl_File_Attribute_GrpExe; // others permissions if (S_IROTH & file_stat.st_mode) pStat->uAttributes |= osl_File_Attribute_OthRead; if (S_IWOTH & file_stat.st_mode) pStat->uAttributes |= osl_File_Attribute_OthWrite; if (S_IXOTH & file_stat.st_mode) pStat->uAttributes |= osl_File_Attribute_OthExe; pStat->uValidFields |= osl_FileStatus_Mask_Attributes; } /* This code used not to use access(...) because access follows links which may cause performance problems see #97133. (That apparently references a no-longer accessible Hamburg-internal bug-tracking system.) However, contrary to what is stated above the use of access calls is required on network file systems not using unix semantics (AFS, see fdo#43095). */ void set_file_access_rights(const OString& file_path, oslFileStatus* pStat) { pStat->uValidFields |= osl_FileStatus_Mask_Attributes; if (osl::access(file_path, W_OK) < 0) pStat->uAttributes |= osl_File_Attribute_ReadOnly; if (osl::access(file_path, X_OK) == 0) pStat->uAttributes |= osl_File_Attribute_Executable; } void set_file_hidden_status(const OString& file_path, oslFileStatus* pStat) { pStat->uAttributes = osl::systemPathIsHiddenFileOrDirectoryEntry(file_path) ? osl_File_Attribute_Hidden : 0; pStat->uValidFields |= osl_FileStatus_Mask_Attributes; } /* the set_file_access_rights must be called after set_file_hidden_status(...) and set_file_access_mask(...) because of the hack in set_file_access_rights(...) */ void set_file_attributes( const OString& file_path, const struct stat& file_stat, const sal_uInt32 uFieldMask, oslFileStatus* pStat) { set_file_hidden_status(file_path, pStat); set_file_access_mask(file_stat, pStat); // we set the file access rights only on demand // because it's potentially expensive if (uFieldMask & osl_FileStatus_Mask_Attributes) set_file_access_rights(file_path, pStat); } void set_file_access_time(const struct stat& file_stat, oslFileStatus* pStat) { pStat->aAccessTime.Seconds = file_stat.st_atime; pStat->aAccessTime.Nanosec = 0; pStat->uValidFields |= osl_FileStatus_Mask_AccessTime; } void set_file_modify_time(const struct stat& file_stat, oslFileStatus* pStat) { pStat->aModifyTime.Seconds = file_stat.st_mtime; pStat->aModifyTime.Nanosec = 0; pStat->uValidFields |= osl_FileStatus_Mask_ModifyTime; } void set_file_size(const struct stat& file_stat, oslFileStatus* pStat) { if (S_ISREG(file_stat.st_mode)) { pStat->uFileSize = file_stat.st_size; pStat->uValidFields |= osl_FileStatus_Mask_FileSize; } } /* we only need to call stat or lstat if one of the following flags is set */ bool is_stat_call_necessary(sal_uInt32 field_mask, oslFileType file_type) { return ( ((field_mask & osl_FileStatus_Mask_Type) && (file_type == osl_File_Type_Unknown)) || (field_mask & osl_FileStatus_Mask_Attributes) || (field_mask & osl_FileStatus_Mask_CreationTime) || (field_mask & osl_FileStatus_Mask_AccessTime) || (field_mask & osl_FileStatus_Mask_ModifyTime) || (field_mask & osl_FileStatus_Mask_FileSize) || (field_mask & osl_FileStatus_Mask_LinkTargetURL) || (field_mask & osl_FileStatus_Mask_Validate)); } oslFileError set_link_target_url(const OString& file_path, oslFileStatus* pStat) { OString link_target; if (!osl::realpath(file_path, link_target)) return oslTranslateFileError(errno); OUString url; oslFileError osl_error = osl::detail::convertPathnameToUrl(link_target, &url); if (osl_error != osl_File_E_None) return osl_error; rtl_uString_assign(&pStat->ustrLinkTargetURL, url.pData); pStat->uValidFields |= osl_FileStatus_Mask_LinkTargetURL; return osl_File_E_None; } oslFileError setup_osl_getFileStatus( DirectoryItem_Impl * pImpl, oslFileStatus* pStat, OString& file_path) { if ((pImpl == nullptr) || (pStat == nullptr)) return osl_File_E_INVAL; file_path = OString(pImpl->m_strFilePath); OSL_ASSERT(!file_path.isEmpty()); if (file_path.isEmpty()) return osl_File_E_INVAL; pStat->uValidFields = 0; return osl_File_E_None; } } oslFileError SAL_CALL osl_getFileStatus(oslDirectoryItem Item, oslFileStatus* pStat, sal_uInt32 uFieldMask) { DirectoryItem_Impl * pImpl = static_cast< DirectoryItem_Impl* >(Item); OString file_path; oslFileError osl_error = setup_osl_getFileStatus(pImpl, pStat, file_path); if (osl_error != osl_File_E_None) return osl_error; struct stat file_stat; bool bStatNeeded = is_stat_call_necessary(uFieldMask, pImpl->getFileType()); if (bStatNeeded && (osl::lstat(file_path, file_stat) != 0)) return oslTranslateFileError(errno); if (bStatNeeded) { // we set all these attributes because it's cheap set_file_type(file_stat, pStat); set_file_access_time(file_stat, pStat); set_file_modify_time(file_stat, pStat); set_file_size(file_stat, pStat); set_file_attributes(file_path, file_stat, uFieldMask, pStat); // file exists semantic of osl_FileStatus_Mask_Validate if ((uFieldMask & osl_FileStatus_Mask_LinkTargetURL) && S_ISLNK(file_stat.st_mode)) { osl_error = set_link_target_url(file_path, pStat); if (osl_error != osl_File_E_None) return osl_error; } } #ifdef _DIRENT_HAVE_D_TYPE else if (uFieldMask & osl_FileStatus_Mask_Type) { pStat->eType = pImpl->getFileType(); pStat->uValidFields |= osl_FileStatus_Mask_Type; } #endif /* _DIRENT_HAVE_D_TYPE */ if (uFieldMask & osl_FileStatus_Mask_FileURL) { OUString url; if ((osl_error = osl::detail::convertPathnameToUrl(file_path, &url)) != osl_File_E_None) return osl_error; rtl_uString_assign(&pStat->ustrFileURL, url.pData); pStat->uValidFields |= osl_FileStatus_Mask_FileURL; } if (uFieldMask & osl_FileStatus_Mask_FileName) { OString name; osl_systemPathGetFileNameOrLastDirectoryPart(file_path.pData, &name.pData); bool ok = rtl_convertStringToUString( &pStat->ustrFileName, name.getStr(), name.getLength(), osl_getThreadTextEncoding(), (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT | RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT)); assert(ok); (void)ok; pStat->uValidFields |= osl_FileStatus_Mask_FileName; } return osl_File_E_None; } static oslFileError osl_psz_setFileAttributes( const sal_Char* pszFilePath, sal_uInt64 uAttributes ) { oslFileError osl_error = osl_File_E_None; mode_t nNewMode = 0; OSL_ENSURE(!(osl_File_Attribute_Hidden & uAttributes), "osl_File_Attribute_Hidden doesn't work under Unix"); if (uAttributes & osl_File_Attribute_OwnRead) nNewMode |= S_IRUSR; if (uAttributes & osl_File_Attribute_OwnWrite) nNewMode|=S_IWUSR; if (uAttributes & osl_File_Attribute_OwnExe) nNewMode|=S_IXUSR; if (uAttributes & osl_File_Attribute_GrpRead) nNewMode|=S_IRGRP; if (uAttributes & osl_File_Attribute_GrpWrite) nNewMode|=S_IWGRP; if (uAttributes & osl_File_Attribute_GrpExe) nNewMode|=S_IXGRP; if (uAttributes & osl_File_Attribute_OthRead) nNewMode|=S_IROTH; if (uAttributes & osl_File_Attribute_OthWrite) nNewMode|=S_IWOTH; if (uAttributes & osl_File_Attribute_OthExe) nNewMode|=S_IXOTH; if (chmod(pszFilePath, nNewMode) < 0) osl_error = oslTranslateFileError(errno); return osl_error; } oslFileError SAL_CALL osl_setFileAttributes( rtl_uString* ustrFileURL, sal_uInt64 uAttributes ) { char path[PATH_MAX]; oslFileError eRet; OSL_ASSERT( ustrFileURL ); /* 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_psz_setFileAttributes( path, uAttributes ); } static oslFileError osl_psz_setFileTime ( const sal_Char* pszFilePath, const TimeValue* pLastAccessTime, const TimeValue* pLastWriteTime ) { int nRet=0; struct utimbuf aTimeBuffer; struct stat aFileStat; #ifdef DEBUG_OSL_FILE struct tm* pTM=0; #endif nRet = lstat_c(pszFilePath,&aFileStat); if ( nRet < 0 ) { nRet=errno; return oslTranslateFileError(nRet); } #ifdef DEBUG_OSL_FILE fprintf(stderr,"File Times are (in localtime):\n"); pTM=localtime(&aFileStat.st_ctime); fprintf(stderr,"CreationTime is '%s'\n",asctime(pTM)); pTM=localtime(&aFileStat.st_atime); fprintf(stderr,"AccessTime is '%s'\n",asctime(pTM)); pTM=localtime(&aFileStat.st_mtime); fprintf(stderr,"Modification is '%s'\n",asctime(pTM)); fprintf(stderr,"File Times are (in UTC):\n"); fprintf(stderr,"CreationTime is '%s'\n",ctime(&aFileStat.st_ctime)); fprintf(stderr,"AccessTime is '%s'\n",ctime(&aTimeBuffer.actime)); fprintf(stderr,"Modification is '%s'\n",ctime(&aTimeBuffer.modtime)); #endif if ( pLastAccessTime != nullptr ) { aTimeBuffer.actime=pLastAccessTime->Seconds; } else { aTimeBuffer.actime=aFileStat.st_atime; } if ( pLastWriteTime != nullptr ) { aTimeBuffer.modtime=pLastWriteTime->Seconds; } else { aTimeBuffer.modtime=aFileStat.st_mtime; } /* mfe: Creation time not used here! */ #ifdef DEBUG_OSL_FILE fprintf(stderr,"File Times are (in localtime):\n"); pTM=localtime(&aFileStat.st_ctime); fprintf(stderr,"CreationTime now '%s'\n",asctime(pTM)); pTM=localtime(&aTimeBuffer.actime); fprintf(stderr,"AccessTime now '%s'\n",asctime(pTM)); pTM=localtime(&aTimeBuffer.modtime); fprintf(stderr,"Modification now '%s'\n",asctime(pTM)); fprintf(stderr,"File Times are (in UTC):\n"); fprintf(stderr,"CreationTime now '%s'\n",ctime(&aFileStat.st_ctime)); fprintf(stderr,"AccessTime now '%s'\n",ctime(&aTimeBuffer.actime)); fprintf(stderr,"Modification now '%s'\n",ctime(&aTimeBuffer.modtime)); #endif nRet = utime_c(pszFilePath,&aTimeBuffer); if ( nRet < 0 ) { nRet=errno; return oslTranslateFileError(nRet); } return osl_File_E_None; } oslFileError SAL_CALL osl_setFileTime ( rtl_uString* ustrFileURL, SAL_UNUSED_PARAMETER const TimeValue* /* pCreationTime */, const TimeValue* pLastAccessTime, const TimeValue* pLastWriteTime ) { char path[PATH_MAX]; oslFileError eRet; OSL_ASSERT( ustrFileURL ); /* 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_psz_setFileTime( path, pLastAccessTime, pLastWriteTime ); } sal_Bool SAL_CALL osl_identicalDirectoryItem( oslDirectoryItem a, oslDirectoryItem b) { DirectoryItem_Impl *pA = static_cast(a); DirectoryItem_Impl *pB = static_cast(b); if (a == b) return true; /* same name => same item, unless renaming / moving madness has occurred */ if (pA->m_strFilePath == pB->m_strFilePath) return true; struct stat a_stat, b_stat; if (osl::lstat(OString(pA->m_strFilePath), a_stat) != 0 || osl::lstat(OString(pB->m_strFilePath), b_stat) != 0) return false; return (a_stat.st_ino == b_stat.st_ino); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */