1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_sal.hxx" 30 31 #include "osl/file.h" 32 33 #include "system.h" 34 #include <sys/types.h> 35 #include <dirent.h> 36 #include <errno.h> 37 #include <limits.h> 38 #include <unistd.h> 39 40 #include "file_impl.hxx" 41 #include "file_error_transl.h" 42 #include "file_path_helper.hxx" 43 #include "file_url.h" 44 #include "uunxapi.hxx" 45 46 namespace /* private */ 47 { 48 49 inline void set_file_type(const struct stat& file_stat, oslFileStatus* pStat) 50 { 51 /* links to directories state also to be a directory */ 52 if (S_ISLNK(file_stat.st_mode)) 53 pStat->eType = osl_File_Type_Link; 54 else if (S_ISDIR(file_stat.st_mode)) 55 pStat->eType = osl_File_Type_Directory; 56 else if (S_ISREG(file_stat.st_mode)) 57 pStat->eType = osl_File_Type_Regular; 58 else if (S_ISFIFO(file_stat.st_mode)) 59 pStat->eType = osl_File_Type_Fifo; 60 else if (S_ISSOCK(file_stat.st_mode)) 61 pStat->eType = osl_File_Type_Socket; 62 else if (S_ISCHR(file_stat.st_mode) || S_ISBLK(file_stat.st_mode)) 63 pStat->eType = osl_File_Type_Special; 64 else 65 pStat->eType = osl_File_Type_Unknown; 66 67 pStat->uValidFields |= osl_FileStatus_Mask_Type; 68 } 69 70 inline void set_file_access_mask(const struct stat& file_stat, oslFileStatus* pStat) 71 { 72 // user permissions 73 if (S_IRUSR & file_stat.st_mode) 74 pStat->uAttributes |= osl_File_Attribute_OwnRead; 75 76 if (S_IWUSR & file_stat.st_mode) 77 pStat->uAttributes |= osl_File_Attribute_OwnWrite; 78 79 if (S_IXUSR & file_stat.st_mode) 80 pStat->uAttributes |= osl_File_Attribute_OwnExe; 81 82 // group permissions 83 if (S_IRGRP & file_stat.st_mode) 84 pStat->uAttributes |= osl_File_Attribute_GrpRead; 85 86 if (S_IWGRP & file_stat.st_mode) 87 pStat->uAttributes |= osl_File_Attribute_GrpWrite; 88 89 if (S_IXGRP & file_stat.st_mode) 90 pStat->uAttributes |= osl_File_Attribute_GrpExe; 91 92 // others permissions 93 if (S_IROTH & file_stat.st_mode) 94 pStat->uAttributes |= osl_File_Attribute_OthRead; 95 96 if (S_IWOTH & file_stat.st_mode) 97 pStat->uAttributes |= osl_File_Attribute_OthWrite; 98 99 if (S_IXOTH & file_stat.st_mode) 100 pStat->uAttributes |= osl_File_Attribute_OthExe; 101 102 pStat->uValidFields |= osl_FileStatus_Mask_Attributes; 103 } 104 105 inline void set_file_access_rights(const struct stat& file_stat, int S_IR, int S_IW, int S_IX, oslFileStatus* pStat) 106 { 107 /* we cannot really map osl_File_Attribute_ReadOnly to 108 the Unix access rights, it's a Windows only flag 109 that's why the following hack. We set osl_FileStatus_Mask_Attributes 110 but if there is no read access for a file we clear the flag 111 again to signal to the caller that there are no file attributes 112 to read because that's better than to give them incorrect one. 113 */ 114 pStat->uValidFields |= osl_FileStatus_Mask_Attributes; 115 116 if ((0 == (S_IW & file_stat.st_mode)) && (S_IR & file_stat.st_mode)) 117 pStat->uAttributes |= osl_File_Attribute_ReadOnly; 118 119 if (S_IX & file_stat.st_mode) 120 pStat->uAttributes |= osl_File_Attribute_Executable; 121 } 122 123 /* a process may belong to up to NGROUPS_MAX groups, so when 124 checking group access rights, we have to check all belonging 125 groups */ 126 inline bool is_in_process_grouplist(const gid_t file_group) 127 { 128 // check primary process group 129 130 if (getgid() == file_group) 131 return true; 132 133 // check supplementary process groups 134 135 gid_t grplist[NGROUPS_MAX]; 136 int grp_number = getgroups(NGROUPS_MAX, grplist); 137 138 for (int i = 0; i < grp_number; i++) 139 { 140 if (grplist[i] == file_group) 141 return true; 142 } 143 return false; 144 } 145 146 /* Currently we are determining the file access right based 147 on the real user ID not the effective user ID! 148 We don't use access(...) because access follows links which 149 may cause performance problems see #97133. 150 */ 151 inline void set_file_access_rights(const struct stat& file_stat, oslFileStatus* pStat) 152 { 153 if (getuid() == file_stat.st_uid) 154 { 155 set_file_access_rights(file_stat, S_IRUSR, S_IWUSR, S_IXUSR, pStat); 156 } 157 else if (is_in_process_grouplist(file_stat.st_gid)) 158 { 159 set_file_access_rights(file_stat, S_IRGRP, S_IWGRP, S_IXGRP, pStat); 160 } 161 else 162 { 163 set_file_access_rights(file_stat, S_IROTH, S_IWOTH, S_IXOTH, pStat); 164 } 165 } 166 167 inline void set_file_hidden_status(const rtl::OUString& file_path, oslFileStatus* pStat) 168 { 169 pStat->uAttributes = osl::systemPathIsHiddenFileOrDirectoryEntry(file_path) ? osl_File_Attribute_Hidden : 0; 170 pStat->uValidFields |= osl_FileStatus_Mask_Attributes; 171 } 172 173 /* the set_file_access_rights must be called after set_file_hidden_status(...) and 174 set_file_access_mask(...) because of the hack in set_file_access_rights(...) */ 175 inline void set_file_attributes( 176 const rtl::OUString& file_path, const struct stat& file_stat, const sal_uInt32 uFieldMask, oslFileStatus* pStat) 177 { 178 set_file_hidden_status(file_path, pStat); 179 set_file_access_mask(file_stat, pStat); 180 181 // we set the file access rights only on demand 182 // because it's potentially expensive 183 if (uFieldMask & osl_FileStatus_Mask_Attributes) 184 set_file_access_rights(file_stat, pStat); 185 } 186 187 inline void set_file_access_time(const struct stat& file_stat, oslFileStatus* pStat) 188 { 189 pStat->aAccessTime.Seconds = file_stat.st_atime; 190 pStat->aAccessTime.Nanosec = 0; 191 pStat->uValidFields |= osl_FileStatus_Mask_AccessTime; 192 } 193 194 inline void set_file_modify_time(const struct stat& file_stat, oslFileStatus* pStat) 195 { 196 pStat->aModifyTime.Seconds = file_stat.st_mtime; 197 pStat->aModifyTime.Nanosec = 0; 198 pStat->uValidFields |= osl_FileStatus_Mask_ModifyTime; 199 } 200 201 inline void set_file_size(const struct stat& file_stat, oslFileStatus* pStat) 202 { 203 if (S_ISREG(file_stat.st_mode)) 204 { 205 pStat->uFileSize = file_stat.st_size; 206 pStat->uValidFields |= osl_FileStatus_Mask_FileSize; 207 } 208 } 209 210 /* we only need to call stat or lstat if one of the 211 following flags is set */ 212 inline bool is_stat_call_necessary(sal_uInt32 field_mask, oslFileType file_type = osl_File_Type_Unknown) 213 { 214 return ( 215 ((field_mask & osl_FileStatus_Mask_Type) && (file_type == osl_File_Type_Unknown)) || 216 (field_mask & osl_FileStatus_Mask_Attributes) || 217 (field_mask & osl_FileStatus_Mask_CreationTime) || 218 (field_mask & osl_FileStatus_Mask_AccessTime) || 219 (field_mask & osl_FileStatus_Mask_ModifyTime) || 220 (field_mask & osl_FileStatus_Mask_FileSize) || 221 (field_mask & osl_FileStatus_Mask_LinkTargetURL) || 222 (field_mask & osl_FileStatus_Mask_Validate)); 223 } 224 225 inline oslFileError set_link_target_url(const rtl::OUString& file_path, oslFileStatus* pStat) 226 { 227 rtl::OUString link_target; 228 if (!osl::realpath(file_path, link_target)) 229 return oslTranslateFileError(OSL_FET_ERROR, errno); 230 231 oslFileError osl_error = osl_getFileURLFromSystemPath(link_target.pData, &pStat->ustrLinkTargetURL); 232 if (osl_error != osl_File_E_None) 233 return osl_error; 234 235 pStat->uValidFields |= osl_FileStatus_Mask_LinkTargetURL; 236 return osl_File_E_None; 237 } 238 239 inline oslFileError setup_osl_getFileStatus( 240 DirectoryItem_Impl * pImpl, oslFileStatus* pStat, rtl::OUString& file_path) 241 { 242 if ((NULL == pImpl) || (NULL == pStat)) 243 return osl_File_E_INVAL; 244 245 file_path = rtl::OUString(pImpl->m_ustrFilePath); 246 OSL_ASSERT(file_path.getLength() > 0); 247 if (file_path.getLength() <= 0) 248 return osl_File_E_INVAL; 249 250 pStat->uValidFields = 0; 251 return osl_File_E_None; 252 } 253 254 } // end namespace private 255 256 257 /**************************************************************************** 258 * osl_getFileStatus 259 ****************************************************************************/ 260 261 oslFileError SAL_CALL osl_getFileStatus(oslDirectoryItem Item, oslFileStatus* pStat, sal_uInt32 uFieldMask) 262 { 263 DirectoryItem_Impl * pImpl = static_cast< DirectoryItem_Impl* >(Item); 264 265 rtl::OUString file_path; 266 oslFileError osl_error = setup_osl_getFileStatus(pImpl, pStat, file_path); 267 if (osl_File_E_None != osl_error) 268 return osl_error; 269 270 #if defined(__GNUC__) && (__GNUC__ < 3) 271 struct ::stat file_stat; 272 #else 273 struct stat file_stat; 274 #endif 275 276 bool bStatNeeded = is_stat_call_necessary(uFieldMask, pImpl->getFileType()); 277 if (bStatNeeded && (0 != osl::lstat(file_path, file_stat))) 278 return oslTranslateFileError(OSL_FET_ERROR, errno); 279 280 if (bStatNeeded) 281 { 282 // we set all these attributes because it's cheap 283 set_file_type(file_stat, pStat); 284 set_file_access_time(file_stat, pStat); 285 set_file_modify_time(file_stat, pStat); 286 set_file_size(file_stat, pStat); 287 set_file_attributes(file_path, file_stat, uFieldMask, pStat); 288 289 // file exists semantic of osl_FileStatus_Mask_Validate 290 if ((uFieldMask & osl_FileStatus_Mask_LinkTargetURL) && S_ISLNK(file_stat.st_mode)) 291 { 292 osl_error = set_link_target_url(file_path, pStat); 293 if (osl_error != osl_File_E_None) 294 return osl_error; 295 } 296 } 297 #ifdef _DIRENT_HAVE_D_TYPE 298 else if (uFieldMask & osl_FileStatus_Mask_Type) 299 { 300 pStat->eType = pImpl->getFileType(); 301 pStat->uValidFields |= osl_FileStatus_Mask_Type; 302 } 303 #endif /* _DIRENT_HAVE_D_TYPE */ 304 305 if (uFieldMask & osl_FileStatus_Mask_FileURL) 306 { 307 if ((osl_error = osl_getFileURLFromSystemPath(file_path.pData, &pStat->ustrFileURL)) != osl_File_E_None) 308 return osl_error; 309 310 pStat->uValidFields |= osl_FileStatus_Mask_FileURL; 311 } 312 313 if (uFieldMask & osl_FileStatus_Mask_FileName) 314 { 315 osl_systemPathGetFileNameOrLastDirectoryPart(file_path.pData, &pStat->ustrFileName); 316 pStat->uValidFields |= osl_FileStatus_Mask_FileName; 317 } 318 return osl_File_E_None; 319 } 320 321 /****************************************************************************/ 322 /* osl_setFileAttributes */ 323 /****************************************************************************/ 324 325 static oslFileError osl_psz_setFileAttributes( const sal_Char* pszFilePath, sal_uInt64 uAttributes ) 326 { 327 oslFileError osl_error = osl_File_E_None; 328 mode_t nNewMode = 0; 329 330 OSL_ENSURE(!(osl_File_Attribute_Hidden & uAttributes), "osl_File_Attribute_Hidden doesn't work under Unix"); 331 332 if (uAttributes & osl_File_Attribute_OwnRead) 333 nNewMode |= S_IRUSR; 334 335 if (uAttributes & osl_File_Attribute_OwnWrite) 336 nNewMode|=S_IWUSR; 337 338 if (uAttributes & osl_File_Attribute_OwnExe) 339 nNewMode|=S_IXUSR; 340 341 if (uAttributes & osl_File_Attribute_GrpRead) 342 nNewMode|=S_IRGRP; 343 344 if (uAttributes & osl_File_Attribute_GrpWrite) 345 nNewMode|=S_IWGRP; 346 347 if (uAttributes & osl_File_Attribute_GrpExe) 348 nNewMode|=S_IXGRP; 349 350 if (uAttributes & osl_File_Attribute_OthRead) 351 nNewMode|=S_IROTH; 352 353 if (uAttributes & osl_File_Attribute_OthWrite) 354 nNewMode|=S_IWOTH; 355 356 if (uAttributes & osl_File_Attribute_OthExe) 357 nNewMode|=S_IXOTH; 358 359 if (chmod(pszFilePath, nNewMode) < 0) 360 osl_error = oslTranslateFileError(OSL_FET_ERROR, errno); 361 362 return osl_error; 363 } 364 365 oslFileError SAL_CALL osl_setFileAttributes( rtl_uString* ustrFileURL, sal_uInt64 uAttributes ) 366 { 367 char path[PATH_MAX]; 368 oslFileError eRet; 369 370 OSL_ASSERT( ustrFileURL ); 371 372 /* convert file url to system path */ 373 eRet = FileURLToPath( path, PATH_MAX, ustrFileURL ); 374 if( eRet != osl_File_E_None ) 375 return eRet; 376 377 #ifdef MACOSX 378 if ( macxp_resolveAlias( path, PATH_MAX ) != 0 ) 379 return oslTranslateFileError( OSL_FET_ERROR, errno ); 380 #endif/* MACOSX */ 381 382 return osl_psz_setFileAttributes( path, uAttributes ); 383 } 384 385 /****************************************************************************/ 386 /* osl_setFileTime */ 387 /****************************************************************************/ 388 389 static oslFileError osl_psz_setFileTime ( 390 const sal_Char* pszFilePath, 391 const TimeValue* /*pCreationTime*/, 392 const TimeValue* pLastAccessTime, 393 const TimeValue* pLastWriteTime ) 394 { 395 int nRet=0; 396 struct utimbuf aTimeBuffer; 397 struct stat aFileStat; 398 #ifdef DEBUG_OSL_FILE 399 struct tm* pTM=0; 400 #endif 401 402 nRet = lstat(pszFilePath,&aFileStat); 403 404 if ( nRet < 0 ) 405 { 406 nRet=errno; 407 return oslTranslateFileError(OSL_FET_ERROR, nRet); 408 } 409 410 #ifdef DEBUG_OSL_FILE 411 fprintf(stderr,"File Times are (in localtime):\n"); 412 pTM=localtime(&aFileStat.st_ctime); 413 fprintf(stderr,"CreationTime is '%s'\n",asctime(pTM)); 414 pTM=localtime(&aFileStat.st_atime); 415 fprintf(stderr,"AccessTime is '%s'\n",asctime(pTM)); 416 pTM=localtime(&aFileStat.st_mtime); 417 fprintf(stderr,"Modification is '%s'\n",asctime(pTM)); 418 419 fprintf(stderr,"File Times are (in UTC):\n"); 420 fprintf(stderr,"CreationTime is '%s'\n",ctime(&aFileStat.st_ctime)); 421 fprintf(stderr,"AccessTime is '%s'\n",ctime(&aTimeBuffer.actime)); 422 fprintf(stderr,"Modification is '%s'\n",ctime(&aTimeBuffer.modtime)); 423 #endif 424 425 if ( pLastAccessTime != 0 ) 426 { 427 aTimeBuffer.actime=pLastAccessTime->Seconds; 428 } 429 else 430 { 431 aTimeBuffer.actime=aFileStat.st_atime; 432 } 433 434 if ( pLastWriteTime != 0 ) 435 { 436 aTimeBuffer.modtime=pLastWriteTime->Seconds; 437 } 438 else 439 { 440 aTimeBuffer.modtime=aFileStat.st_mtime; 441 } 442 443 /* mfe: Creation time not used here! */ 444 445 #ifdef DEBUG_OSL_FILE 446 fprintf(stderr,"File Times are (in localtime):\n"); 447 pTM=localtime(&aFileStat.st_ctime); 448 fprintf(stderr,"CreationTime now '%s'\n",asctime(pTM)); 449 pTM=localtime(&aTimeBuffer.actime); 450 fprintf(stderr,"AccessTime now '%s'\n",asctime(pTM)); 451 pTM=localtime(&aTimeBuffer.modtime); 452 fprintf(stderr,"Modification now '%s'\n",asctime(pTM)); 453 454 fprintf(stderr,"File Times are (in UTC):\n"); 455 fprintf(stderr,"CreationTime now '%s'\n",ctime(&aFileStat.st_ctime)); 456 fprintf(stderr,"AccessTime now '%s'\n",ctime(&aTimeBuffer.actime)); 457 fprintf(stderr,"Modification now '%s'\n",ctime(&aTimeBuffer.modtime)); 458 #endif 459 460 nRet=utime(pszFilePath,&aTimeBuffer); 461 if ( nRet < 0 ) 462 { 463 nRet=errno; 464 return oslTranslateFileError(OSL_FET_ERROR, nRet); 465 } 466 467 return osl_File_E_None; 468 } 469 470 oslFileError SAL_CALL osl_setFileTime ( 471 rtl_uString* ustrFileURL, 472 const TimeValue* pCreationTime, 473 const TimeValue* pLastAccessTime, 474 const TimeValue* pLastWriteTime ) 475 { 476 char path[PATH_MAX]; 477 oslFileError eRet; 478 479 OSL_ASSERT( ustrFileURL ); 480 481 /* convert file url to system path */ 482 eRet = FileURLToPath( path, PATH_MAX, ustrFileURL ); 483 if( eRet != osl_File_E_None ) 484 return eRet; 485 486 #ifdef MACOSX 487 if ( macxp_resolveAlias( path, PATH_MAX ) != 0 ) 488 return oslTranslateFileError( OSL_FET_ERROR, errno ); 489 #endif/* MACOSX */ 490 491 return osl_psz_setFileTime( path, pCreationTime, pLastAccessTime, pLastWriteTime ); 492 } 493