| /************************************************************** |
| * |
| * 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 ); |
| } |