Avoid potential negative array index access to cached text.
[LibreOffice.git] / sal / osl / unx / file_stat.cxx
blob3d0754883a118168e11022e6c4127275e7225ac6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <osl/file.h>
22 #include <sys/stat.h>
23 #include <dirent.h>
24 #include <errno.h>
25 #include <limits.h>
26 #include <unistd.h>
27 #include <utime.h>
29 #include <osl/diagnose.h>
30 #include <osl/thread.h>
32 #include "system.hxx"
33 #include "file_impl.hxx"
34 #include "file_error_transl.hxx"
35 #include "file_path_helper.hxx"
36 #include "file_url.hxx"
37 #include "uunxapi.hxx"
39 namespace
41 void set_file_type(const struct stat& file_stat, oslFileStatus* pStat)
43 /* links to directories state also to be a directory */
44 if (S_ISLNK(file_stat.st_mode))
45 pStat->eType = osl_File_Type_Link;
46 else if (S_ISDIR(file_stat.st_mode))
47 pStat->eType = osl_File_Type_Directory;
48 else if (S_ISREG(file_stat.st_mode))
49 pStat->eType = osl_File_Type_Regular;
50 else if (S_ISFIFO(file_stat.st_mode))
51 pStat->eType = osl_File_Type_Fifo;
52 else if (S_ISSOCK(file_stat.st_mode))
53 pStat->eType = osl_File_Type_Socket;
54 else if (S_ISCHR(file_stat.st_mode) || S_ISBLK(file_stat.st_mode))
55 pStat->eType = osl_File_Type_Special;
56 else
57 pStat->eType = osl_File_Type_Unknown;
59 pStat->uValidFields |= osl_FileStatus_Mask_Type;
62 void set_file_access_mask(const struct stat& file_stat, oslFileStatus* pStat)
64 // user permissions
65 if (S_IRUSR & file_stat.st_mode)
66 pStat->uAttributes |= osl_File_Attribute_OwnRead;
68 if (S_IWUSR & file_stat.st_mode)
69 pStat->uAttributes |= osl_File_Attribute_OwnWrite;
71 if (S_IXUSR & file_stat.st_mode)
72 pStat->uAttributes |= osl_File_Attribute_OwnExe;
74 // group permissions
75 if (S_IRGRP & file_stat.st_mode)
76 pStat->uAttributes |= osl_File_Attribute_GrpRead;
78 if (S_IWGRP & file_stat.st_mode)
79 pStat->uAttributes |= osl_File_Attribute_GrpWrite;
81 if (S_IXGRP & file_stat.st_mode)
82 pStat->uAttributes |= osl_File_Attribute_GrpExe;
84 // others permissions
85 if (S_IROTH & file_stat.st_mode)
86 pStat->uAttributes |= osl_File_Attribute_OthRead;
88 if (S_IWOTH & file_stat.st_mode)
89 pStat->uAttributes |= osl_File_Attribute_OthWrite;
91 if (S_IXOTH & file_stat.st_mode)
92 pStat->uAttributes |= osl_File_Attribute_OthExe;
94 pStat->uValidFields |= osl_FileStatus_Mask_Attributes;
97 /* This code used not to use access(...) because access follows links which
98 may cause performance problems see #97133. (That apparently references a
99 no-longer accessible Hamburg-internal bug-tracking system.)
100 However, contrary to what is stated above the use of access calls is
101 required on network file systems not using unix semantics (AFS, see
102 fdo#43095).
104 void set_file_access_rights(const OString& file_path, oslFileStatus* pStat)
106 pStat->uValidFields |= osl_FileStatus_Mask_Attributes;
108 if (osl::access(file_path, W_OK) < 0)
109 pStat->uAttributes |= osl_File_Attribute_ReadOnly;
111 if (osl::access(file_path, X_OK) == 0)
112 pStat->uAttributes |= osl_File_Attribute_Executable;
115 void set_file_hidden_status(const OString& file_path, oslFileStatus* pStat)
117 pStat->uAttributes = osl::systemPathIsHiddenFileOrDirectoryEntry(file_path) ? osl_File_Attribute_Hidden : 0;
118 pStat->uValidFields |= osl_FileStatus_Mask_Attributes;
121 /* the set_file_access_rights must be called after set_file_hidden_status(...) and
122 set_file_access_mask(...) because of the hack in set_file_access_rights(...) */
123 void set_file_attributes(
124 const OString& file_path, const struct stat& file_stat, const sal_uInt32 uFieldMask, oslFileStatus* pStat)
126 set_file_hidden_status(file_path, pStat);
127 set_file_access_mask(file_stat, pStat);
129 // we set the file access rights only on demand
130 // because it's potentially expensive
131 if (uFieldMask & osl_FileStatus_Mask_Attributes)
132 set_file_access_rights(file_path, pStat);
135 void set_file_access_time(const struct stat& file_stat, oslFileStatus* pStat)
137 pStat->aAccessTime.Seconds = file_stat.st_atime;
138 pStat->aAccessTime.Nanosec = 0;
139 pStat->uValidFields |= osl_FileStatus_Mask_AccessTime;
142 void set_file_modify_time(const struct stat& file_stat, oslFileStatus* pStat)
144 pStat->aModifyTime.Seconds = file_stat.st_mtime;
145 pStat->aModifyTime.Nanosec = 0;
146 pStat->uValidFields |= osl_FileStatus_Mask_ModifyTime;
149 void set_file_size(const struct stat& file_stat, oslFileStatus* pStat)
151 if (S_ISREG(file_stat.st_mode))
153 pStat->uFileSize = file_stat.st_size;
154 pStat->uValidFields |= osl_FileStatus_Mask_FileSize;
158 /* we only need to call stat or lstat if one of the
159 following flags is set */
160 bool is_stat_call_necessary(sal_uInt32 field_mask, oslFileType file_type)
162 return (
163 ((field_mask & osl_FileStatus_Mask_Type) && (file_type == osl_File_Type_Unknown)) ||
164 (field_mask & osl_FileStatus_Mask_Attributes) ||
165 (field_mask & osl_FileStatus_Mask_CreationTime) ||
166 (field_mask & osl_FileStatus_Mask_AccessTime) ||
167 (field_mask & osl_FileStatus_Mask_ModifyTime) ||
168 (field_mask & osl_FileStatus_Mask_FileSize) ||
169 (field_mask & osl_FileStatus_Mask_LinkTargetURL) ||
170 (field_mask & osl_FileStatus_Mask_Validate));
173 oslFileError set_link_target_url(const OString& file_path, oslFileStatus* pStat)
175 OString link_target;
176 if (!osl::realpath(file_path, link_target))
177 return oslTranslateFileError(errno);
179 OUString url;
180 oslFileError osl_error = osl::detail::convertPathnameToUrl(link_target, &url);
181 if (osl_error != osl_File_E_None)
182 return osl_error;
183 rtl_uString_assign(&pStat->ustrLinkTargetURL, url.pData);
185 pStat->uValidFields |= osl_FileStatus_Mask_LinkTargetURL;
186 return osl_File_E_None;
189 oslFileError setup_osl_getFileStatus(
190 DirectoryItem_Impl * pImpl, oslFileStatus* pStat, OString& file_path)
192 if ((pImpl == nullptr) || (pStat == nullptr))
193 return osl_File_E_INVAL;
195 file_path = pImpl->m_strFilePath;
196 OSL_ASSERT(!file_path.isEmpty());
197 if (file_path.isEmpty())
198 return osl_File_E_INVAL;
200 pStat->uValidFields = 0;
201 return osl_File_E_None;
206 oslFileError SAL_CALL osl_getFileStatus(oslDirectoryItem Item, oslFileStatus* pStat, sal_uInt32 uFieldMask)
208 DirectoryItem_Impl * pImpl = static_cast< DirectoryItem_Impl* >(Item);
210 OString file_path;
211 oslFileError osl_error = setup_osl_getFileStatus(pImpl, pStat, file_path);
212 if (osl_error != osl_File_E_None)
213 return osl_error;
215 struct stat file_stat;
217 bool bStatNeeded = is_stat_call_necessary(uFieldMask, pImpl->getFileType());
218 if (bStatNeeded && (osl::lstat(file_path, file_stat) != 0))
219 return oslTranslateFileError(errno);
221 if (bStatNeeded)
223 // we set all these attributes because it's cheap
224 set_file_type(file_stat, pStat);
225 set_file_access_time(file_stat, pStat);
226 set_file_modify_time(file_stat, pStat);
227 set_file_size(file_stat, pStat);
228 set_file_attributes(file_path, file_stat, uFieldMask, pStat);
230 // file exists semantic of osl_FileStatus_Mask_Validate
231 if ((uFieldMask & osl_FileStatus_Mask_LinkTargetURL) && S_ISLNK(file_stat.st_mode))
233 osl_error = set_link_target_url(file_path, pStat);
234 if (osl_error != osl_File_E_None)
235 return osl_error;
238 #ifdef _DIRENT_HAVE_D_TYPE
239 else if (uFieldMask & osl_FileStatus_Mask_Type)
241 pStat->eType = pImpl->getFileType();
242 pStat->uValidFields |= osl_FileStatus_Mask_Type;
244 #endif /* _DIRENT_HAVE_D_TYPE */
246 if (uFieldMask & osl_FileStatus_Mask_FileURL)
248 OUString url;
249 if ((osl_error = osl::detail::convertPathnameToUrl(file_path, &url)) != osl_File_E_None)
250 return osl_error;
251 rtl_uString_assign(&pStat->ustrFileURL, url.pData);
253 pStat->uValidFields |= osl_FileStatus_Mask_FileURL;
256 if (uFieldMask & osl_FileStatus_Mask_FileName)
258 OString name;
259 osl_systemPathGetFileNameOrLastDirectoryPart(file_path.pData, &name.pData);
260 bool ok = rtl_convertStringToUString(
261 &pStat->ustrFileName, name.getStr(), name.getLength(), osl_getThreadTextEncoding(),
262 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT
263 | RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT));
264 assert(ok); (void)ok;
265 pStat->uValidFields |= osl_FileStatus_Mask_FileName;
267 return osl_File_E_None;
270 static oslFileError osl_psz_setFileAttributes( const char* pszFilePath, sal_uInt64 uAttributes )
272 oslFileError osl_error = osl_File_E_None;
273 mode_t nNewMode = 0;
275 OSL_ENSURE(!(osl_File_Attribute_Hidden & uAttributes), "osl_File_Attribute_Hidden doesn't work under Unix");
277 if (isForbidden(pszFilePath, osl_File_OpenFlag_Write))
278 return osl_File_E_ACCES;
280 if (uAttributes & osl_File_Attribute_OwnRead)
281 nNewMode |= S_IRUSR;
283 if (uAttributes & osl_File_Attribute_OwnWrite)
284 nNewMode|=S_IWUSR;
286 if (uAttributes & osl_File_Attribute_OwnExe)
287 nNewMode|=S_IXUSR;
289 if (uAttributes & osl_File_Attribute_GrpRead)
290 nNewMode|=S_IRGRP;
292 if (uAttributes & osl_File_Attribute_GrpWrite)
293 nNewMode|=S_IWGRP;
295 if (uAttributes & osl_File_Attribute_GrpExe)
296 nNewMode|=S_IXGRP;
298 if (uAttributes & osl_File_Attribute_OthRead)
299 nNewMode|=S_IROTH;
301 if (uAttributes & osl_File_Attribute_OthWrite)
302 nNewMode|=S_IWOTH;
304 if (uAttributes & osl_File_Attribute_OthExe)
305 nNewMode|=S_IXOTH;
307 if (chmod(pszFilePath, nNewMode) < 0)
308 osl_error = oslTranslateFileError(errno);
310 return osl_error;
313 oslFileError SAL_CALL osl_setFileAttributes( rtl_uString* ustrFileURL, sal_uInt64 uAttributes )
315 char path[PATH_MAX];
316 oslFileError eRet;
318 OSL_ASSERT( ustrFileURL );
320 /* convert file url to system path */
321 eRet = FileURLToPath( path, PATH_MAX, ustrFileURL );
322 if( eRet != osl_File_E_None )
323 return eRet;
325 #ifdef MACOSX
326 if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
327 return oslTranslateFileError( errno );
328 #endif/* MACOSX */
330 return osl_psz_setFileAttributes( path, uAttributes );
333 static oslFileError osl_psz_setFileTime (
334 const char* pszFilePath,
335 const TimeValue* pLastAccessTime,
336 const TimeValue* pLastWriteTime )
338 int nRet=0;
339 struct utimbuf aTimeBuffer;
340 struct stat aFileStat;
341 #ifdef DEBUG_OSL_FILE
342 struct tm* pTM=0;
343 #endif
345 if (isForbidden(pszFilePath, osl_File_OpenFlag_Write))
346 return osl_File_E_ACCES;
348 nRet = lstat_c(pszFilePath,&aFileStat);
350 if ( nRet < 0 )
352 nRet=errno;
353 return oslTranslateFileError(nRet);
356 #ifdef DEBUG_OSL_FILE
357 fprintf(stderr,"File Times are (in localtime):\n");
358 pTM=localtime(&aFileStat.st_ctime);
359 fprintf(stderr,"CreationTime is '%s'\n",asctime(pTM));
360 pTM=localtime(&aFileStat.st_atime);
361 fprintf(stderr,"AccessTime is '%s'\n",asctime(pTM));
362 pTM=localtime(&aFileStat.st_mtime);
363 fprintf(stderr,"Modification is '%s'\n",asctime(pTM));
365 fprintf(stderr,"File Times are (in UTC):\n");
366 fprintf(stderr,"CreationTime is '%s'\n",ctime(&aFileStat.st_ctime));
367 fprintf(stderr,"AccessTime is '%s'\n",ctime(&aTimeBuffer.actime));
368 fprintf(stderr,"Modification is '%s'\n",ctime(&aTimeBuffer.modtime));
369 #endif
371 if ( pLastAccessTime != nullptr )
373 aTimeBuffer.actime=pLastAccessTime->Seconds;
375 else
377 aTimeBuffer.actime=aFileStat.st_atime;
380 if ( pLastWriteTime != nullptr )
382 aTimeBuffer.modtime=pLastWriteTime->Seconds;
384 else
386 aTimeBuffer.modtime=aFileStat.st_mtime;
389 /* mfe: Creation time not used here! */
391 #ifdef DEBUG_OSL_FILE
392 fprintf(stderr,"File Times are (in localtime):\n");
393 pTM=localtime(&aFileStat.st_ctime);
394 fprintf(stderr,"CreationTime now '%s'\n",asctime(pTM));
395 pTM=localtime(&aTimeBuffer.actime);
396 fprintf(stderr,"AccessTime now '%s'\n",asctime(pTM));
397 pTM=localtime(&aTimeBuffer.modtime);
398 fprintf(stderr,"Modification now '%s'\n",asctime(pTM));
400 fprintf(stderr,"File Times are (in UTC):\n");
401 fprintf(stderr,"CreationTime now '%s'\n",ctime(&aFileStat.st_ctime));
402 fprintf(stderr,"AccessTime now '%s'\n",ctime(&aTimeBuffer.actime));
403 fprintf(stderr,"Modification now '%s'\n",ctime(&aTimeBuffer.modtime));
404 #endif
406 nRet = utime_c(pszFilePath,&aTimeBuffer);
407 if ( nRet < 0 )
409 nRet=errno;
410 return oslTranslateFileError(nRet);
413 return osl_File_E_None;
416 oslFileError SAL_CALL osl_setFileTime (
417 rtl_uString* ustrFileURL,
418 SAL_UNUSED_PARAMETER const TimeValue* /* pCreationTime */,
419 const TimeValue* pLastAccessTime,
420 const TimeValue* pLastWriteTime )
422 char path[PATH_MAX];
423 oslFileError eRet;
425 OSL_ASSERT( ustrFileURL );
427 /* convert file url to system path */
428 eRet = FileURLToPath( path, PATH_MAX, ustrFileURL );
429 if( eRet != osl_File_E_None )
430 return eRet;
432 #ifdef MACOSX
433 if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
434 return oslTranslateFileError( errno );
435 #endif/* MACOSX */
437 return osl_psz_setFileTime( path, pLastAccessTime, pLastWriteTime );
440 sal_Bool
441 SAL_CALL osl_identicalDirectoryItem( oslDirectoryItem a, oslDirectoryItem b)
443 DirectoryItem_Impl *pA = static_cast<DirectoryItem_Impl *>(a);
444 DirectoryItem_Impl *pB = static_cast<DirectoryItem_Impl *>(b);
445 if (a == b)
446 return true;
447 /* same name => same item, unless renaming / moving madness has occurred */
448 if (pA->m_strFilePath == pB->m_strFilePath)
449 return true;
451 struct stat a_stat, b_stat;
453 if (osl::lstat(pA->m_strFilePath, a_stat) != 0 ||
454 osl::lstat(pB->m_strFilePath, b_stat) != 0)
455 return false;
457 return (a_stat.st_ino == b_stat.st_ino);
460 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */