xref: /AOO41X/main/sal/osl/unx/file_stat.cxx (revision 87d2adbc9cadf14644c3679b041b9226f7630199)
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