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