blob: c56ff7de4056c3649de2f47441712d18c6c0e731 [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sal.hxx"
#include "osl/file.h"
#include "system.h"
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <unistd.h>
#include "file_impl.hxx"
#include "file_error_transl.h"
#include "file_path_helper.hxx"
#include "file_url.h"
#include "uunxapi.hxx"
namespace /* private */
{
inline 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;
}
inline 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;
}
inline void set_file_access_rights(const struct stat& file_stat, int S_IR, int S_IW, int S_IX, oslFileStatus* pStat)
{
/* we cannot really map osl_File_Attribute_ReadOnly to
the Unix access rights, it's a Windows only flag
that's why the following hack. We set osl_FileStatus_Mask_Attributes
but if there is no read access for a file we clear the flag
again to signal to the caller that there are no file attributes
to read because that's better than to give them incorrect one.
*/
pStat->uValidFields |= osl_FileStatus_Mask_Attributes;
if ((0 == (S_IW & file_stat.st_mode)) && (S_IR & file_stat.st_mode))
pStat->uAttributes |= osl_File_Attribute_ReadOnly;
if (S_IX & file_stat.st_mode)
pStat->uAttributes |= osl_File_Attribute_Executable;
}
/* a process may belong to up to NGROUPS_MAX groups, so when
checking group access rights, we have to check all belonging
groups */
inline bool is_in_process_grouplist(const gid_t file_group)
{
// check primary process group
if (getgid() == file_group)
return true;
// check supplementary process groups
gid_t grplist[NGROUPS_MAX];
int grp_number = getgroups(NGROUPS_MAX, grplist);
for (int i = 0; i < grp_number; i++)
{
if (grplist[i] == file_group)
return true;
}
return false;
}
/* Currently we are determining the file access right based
on the real user ID not the effective user ID!
We don't use access(...) because access follows links which
may cause performance problems see #97133.
*/
inline void set_file_access_rights(const struct stat& file_stat, oslFileStatus* pStat)
{
if (getuid() == file_stat.st_uid)
{
set_file_access_rights(file_stat, S_IRUSR, S_IWUSR, S_IXUSR, pStat);
}
else if (is_in_process_grouplist(file_stat.st_gid))
{
set_file_access_rights(file_stat, S_IRGRP, S_IWGRP, S_IXGRP, pStat);
}
else
{
set_file_access_rights(file_stat, S_IROTH, S_IWOTH, S_IXOTH, pStat);
}
}
inline void set_file_hidden_status(const rtl::OUString& 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(...) */
inline void set_file_attributes(
const rtl::OUString& 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_stat, pStat);
}
inline 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;
}
inline 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;
}
inline 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 */
inline bool is_stat_call_necessary(sal_uInt32 field_mask, oslFileType file_type = osl_File_Type_Unknown)
{
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));
}
inline oslFileError set_link_target_url(const rtl::OUString& file_path, oslFileStatus* pStat)
{
rtl::OUString link_target;
if (!osl::realpath(file_path, link_target))
return oslTranslateFileError(OSL_FET_ERROR, errno);
oslFileError osl_error = osl_getFileURLFromSystemPath(link_target.pData, &pStat->ustrLinkTargetURL);
if (osl_error != osl_File_E_None)
return osl_error;
pStat->uValidFields |= osl_FileStatus_Mask_LinkTargetURL;
return osl_File_E_None;
}
inline oslFileError setup_osl_getFileStatus(
DirectoryItem_Impl * pImpl, oslFileStatus* pStat, rtl::OUString& file_path)
{
if ((NULL == pImpl) || (NULL == pStat))
return osl_File_E_INVAL;
file_path = rtl::OUString(pImpl->m_ustrFilePath);
OSL_ASSERT(file_path.getLength() > 0);
if (file_path.getLength() <= 0)
return osl_File_E_INVAL;
pStat->uValidFields = 0;
return osl_File_E_None;
}
} // end namespace private
/****************************************************************************
* osl_getFileStatus
****************************************************************************/
oslFileError SAL_CALL osl_getFileStatus(oslDirectoryItem Item, oslFileStatus* pStat, sal_uInt32 uFieldMask)
{
DirectoryItem_Impl * pImpl = static_cast< DirectoryItem_Impl* >(Item);
rtl::OUString file_path;
oslFileError osl_error = setup_osl_getFileStatus(pImpl, pStat, file_path);
if (osl_File_E_None != osl_error)
return osl_error;
#if defined(__GNUC__) && (__GNUC__ < 3)
struct ::stat file_stat;
#else
struct stat file_stat;
#endif
bool bStatNeeded = is_stat_call_necessary(uFieldMask, pImpl->getFileType());
if (bStatNeeded && (0 != osl::lstat(file_path, file_stat)))
return oslTranslateFileError(OSL_FET_ERROR, 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)
{
if ((osl_error = osl_getFileURLFromSystemPath(file_path.pData, &pStat->ustrFileURL)) != osl_File_E_None)
return osl_error;
pStat->uValidFields |= osl_FileStatus_Mask_FileURL;
}
if (uFieldMask & osl_FileStatus_Mask_FileName)
{
osl_systemPathGetFileNameOrLastDirectoryPart(file_path.pData, &pStat->ustrFileName);
pStat->uValidFields |= osl_FileStatus_Mask_FileName;
}
return osl_File_E_None;
}
/****************************************************************************/
/* osl_setFileAttributes */
/****************************************************************************/
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(OSL_FET_ERROR, 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( OSL_FET_ERROR, errno );
#endif/* MACOSX */
return osl_psz_setFileAttributes( path, uAttributes );
}
/****************************************************************************/
/* osl_setFileTime */
/****************************************************************************/
static oslFileError osl_psz_setFileTime (
const sal_Char* pszFilePath,
const TimeValue* /*pCreationTime*/,
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(pszFilePath,&aFileStat);
if ( nRet < 0 )
{
nRet=errno;
return oslTranslateFileError(OSL_FET_ERROR, 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 != 0 )
{
aTimeBuffer.actime=pLastAccessTime->Seconds;
}
else
{
aTimeBuffer.actime=aFileStat.st_atime;
}
if ( pLastWriteTime != 0 )
{
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(pszFilePath,&aTimeBuffer);
if ( nRet < 0 )
{
nRet=errno;
return oslTranslateFileError(OSL_FET_ERROR, nRet);
}
return osl_File_E_None;
}
oslFileError SAL_CALL osl_setFileTime (
rtl_uString* ustrFileURL,
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( OSL_FET_ERROR, errno );
#endif/* MACOSX */
return osl_psz_setFileTime( path, pCreationTime, pLastAccessTime, pLastWriteTime );
}