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