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