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
set_file_type(const struct stat & file_stat,oslFileStatus * pStat)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
set_file_access_mask(const struct stat & file_stat,oslFileStatus * pStat)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
set_file_access_rights(const struct stat & file_stat,int S_IR,int S_IW,int S_IX,oslFileStatus * pStat)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 */
is_in_process_grouplist(const gid_t file_group)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 */
set_file_access_rights(const struct stat & file_stat,oslFileStatus * pStat)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
set_file_hidden_status(const rtl::OUString & file_path,oslFileStatus * pStat)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(...) */
set_file_attributes(const rtl::OUString & file_path,const struct stat & file_stat,const sal_uInt32 uFieldMask,oslFileStatus * pStat)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
set_file_access_time(const struct stat & file_stat,oslFileStatus * pStat)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
set_file_modify_time(const struct stat & file_stat,oslFileStatus * pStat)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
set_file_size(const struct stat & file_stat,oslFileStatus * pStat)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 */
is_stat_call_necessary(sal_uInt32 field_mask,oslFileType file_type=osl_File_Type_Unknown)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
set_link_target_url(const rtl::OUString & file_path,oslFileStatus * pStat)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
setup_osl_getFileStatus(DirectoryItem_Impl * pImpl,oslFileStatus * pStat,rtl::OUString & file_path)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
osl_getFileStatus(oslDirectoryItem Item,oslFileStatus * pStat,sal_uInt32 uFieldMask)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
osl_psz_setFileAttributes(const sal_Char * pszFilePath,sal_uInt64 uAttributes)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
osl_setFileAttributes(rtl_uString * ustrFileURL,sal_uInt64 uAttributes)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
osl_psz_setFileTime(const sal_Char * pszFilePath,const TimeValue *,const TimeValue * pLastAccessTime,const TimeValue * pLastWriteTime)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
osl_setFileTime(rtl_uString * ustrFileURL,const TimeValue * pCreationTime,const TimeValue * pLastAccessTime,const TimeValue * pLastWriteTime)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