Coverage Report

Created: 2025-11-16 09:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sal/osl/unx/file_stat.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
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/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
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 .
18
 */
19
20
#include <osl/file.h>
21
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>
28
29
#include <osl/diagnose.h>
30
#include <osl/thread.h>
31
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"
38
39
namespace
40
{
41
    void set_file_type(const struct stat& file_stat, oslFileStatus* pStat)
42
133k
    {
43
        /* links to directories state also to be a directory */
44
133k
       if (S_ISLNK(file_stat.st_mode))
45
0
           pStat->eType = osl_File_Type_Link;
46
133k
       else if (S_ISDIR(file_stat.st_mode))
47
9.82k
           pStat->eType = osl_File_Type_Directory;
48
123k
       else if (S_ISREG(file_stat.st_mode))
49
123k
           pStat->eType = osl_File_Type_Regular;
50
0
       else if (S_ISFIFO(file_stat.st_mode))
51
0
           pStat->eType = osl_File_Type_Fifo;
52
0
       else if (S_ISSOCK(file_stat.st_mode))
53
0
           pStat->eType = osl_File_Type_Socket;
54
0
       else if (S_ISCHR(file_stat.st_mode) || S_ISBLK(file_stat.st_mode))
55
0
           pStat->eType = osl_File_Type_Special;
56
0
       else
57
0
           pStat->eType = osl_File_Type_Unknown;
58
59
133k
       pStat->uValidFields |= osl_FileStatus_Mask_Type;
60
133k
    }
61
62
    void set_file_access_mask(const struct stat& file_stat, oslFileStatus* pStat)
63
133k
    {
64
        // user permissions
65
133k
        if (S_IRUSR & file_stat.st_mode)
66
133k
            pStat->uAttributes |= osl_File_Attribute_OwnRead;
67
68
133k
        if (S_IWUSR & file_stat.st_mode)
69
133k
            pStat->uAttributes |= osl_File_Attribute_OwnWrite;
70
71
133k
        if (S_IXUSR & file_stat.st_mode)
72
9.82k
            pStat->uAttributes |= osl_File_Attribute_OwnExe;
73
74
        // group permissions
75
133k
        if (S_IRGRP & file_stat.st_mode)
76
258
            pStat->uAttributes |= osl_File_Attribute_GrpRead;
77
78
133k
        if (S_IWGRP & file_stat.st_mode)
79
9
            pStat->uAttributes |= osl_File_Attribute_GrpWrite;
80
81
133k
        if (S_IXGRP & file_stat.st_mode)
82
42
            pStat->uAttributes |= osl_File_Attribute_GrpExe;
83
84
        // others permissions
85
133k
        if (S_IROTH & file_stat.st_mode)
86
258
            pStat->uAttributes |= osl_File_Attribute_OthRead;
87
88
133k
        if (S_IWOTH & file_stat.st_mode)
89
9
            pStat->uAttributes |= osl_File_Attribute_OthWrite;
90
91
133k
        if (S_IXOTH & file_stat.st_mode)
92
42
            pStat->uAttributes |= osl_File_Attribute_OthExe;
93
94
133k
        pStat->uValidFields |= osl_FileStatus_Mask_Attributes;
95
133k
    }
96
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).
103
    */
104
    void set_file_access_rights(const OString& file_path, oslFileStatus* pStat)
105
9.82k
    {
106
9.82k
        pStat->uValidFields |= osl_FileStatus_Mask_Attributes;
107
108
9.82k
        if (osl::access(file_path, W_OK) < 0)
109
0
            pStat->uAttributes |= osl_File_Attribute_ReadOnly;
110
111
9.82k
        if (osl::access(file_path, X_OK) == 0)
112
42
            pStat->uAttributes |= osl_File_Attribute_Executable;
113
9.82k
    }
114
115
    void set_file_hidden_status(const OString& file_path, oslFileStatus* pStat)
116
133k
    {
117
133k
        pStat->uAttributes   = osl::systemPathIsHiddenFileOrDirectoryEntry(file_path) ? osl_File_Attribute_Hidden : 0;
118
133k
        pStat->uValidFields |= osl_FileStatus_Mask_Attributes;
119
133k
    }
120
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)
125
133k
    {
126
133k
        set_file_hidden_status(file_path, pStat);
127
133k
        set_file_access_mask(file_stat, pStat);
128
129
        // we set the file access rights only on demand
130
        // because it's potentially expensive
131
133k
        if (uFieldMask & osl_FileStatus_Mask_Attributes)
132
9.82k
            set_file_access_rights(file_path, pStat);
133
133k
    }
134
135
    void set_file_access_time(const struct stat& file_stat, oslFileStatus* pStat)
136
133k
    {
137
133k
        pStat->aAccessTime.Seconds  = file_stat.st_atime;
138
133k
        pStat->aAccessTime.Nanosec  = 0;
139
133k
        pStat->uValidFields        |= osl_FileStatus_Mask_AccessTime;
140
133k
    }
141
142
    void set_file_modify_time(const struct stat& file_stat, oslFileStatus* pStat)
143
133k
    {
144
133k
        pStat->aModifyTime.Seconds  = file_stat.st_mtime;
145
133k
        pStat->aModifyTime.Nanosec  = 0;
146
133k
        pStat->uValidFields        |= osl_FileStatus_Mask_ModifyTime;
147
133k
    }
148
149
    void set_file_size(const struct stat& file_stat, oslFileStatus* pStat)
150
133k
    {
151
133k
        if (S_ISREG(file_stat.st_mode))
152
123k
        {
153
123k
            pStat->uFileSize     = file_stat.st_size;
154
123k
            pStat->uValidFields |= osl_FileStatus_Mask_FileSize;
155
123k
        }
156
133k
    }
157
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)
161
133k
    {
162
133k
        return (
163
133k
                ((field_mask & osl_FileStatus_Mask_Type) && (file_type == osl_File_Type_Unknown)) ||
164
106k
                (field_mask & osl_FileStatus_Mask_Attributes) ||
165
96.7k
                (field_mask & osl_FileStatus_Mask_CreationTime) ||
166
96.7k
                (field_mask & osl_FileStatus_Mask_AccessTime) ||
167
96.7k
                (field_mask & osl_FileStatus_Mask_ModifyTime) ||
168
96.7k
                (field_mask & osl_FileStatus_Mask_FileSize) ||
169
96.7k
                (field_mask & osl_FileStatus_Mask_LinkTargetURL) ||
170
205
                (field_mask & osl_FileStatus_Mask_Validate));
171
133k
    }
172
173
    oslFileError set_link_target_url(const OString& file_path, oslFileStatus* pStat)
174
0
    {
175
0
        OString link_target;
176
0
        if (!osl::realpath(file_path, link_target))
177
0
            return oslTranslateFileError(errno);
178
179
0
        OUString url;
180
0
        oslFileError osl_error = osl::detail::convertPathnameToUrl(link_target, &url);
181
0
        if (osl_error != osl_File_E_None)
182
0
            return osl_error;
183
0
        rtl_uString_assign(&pStat->ustrLinkTargetURL, url.pData);
184
185
0
        pStat->uValidFields |= osl_FileStatus_Mask_LinkTargetURL;
186
0
        return osl_File_E_None;
187
0
    }
188
189
    oslFileError setup_osl_getFileStatus(
190
        DirectoryItem_Impl * pImpl, oslFileStatus* pStat, OString& file_path)
191
133k
    {
192
133k
        if ((pImpl == nullptr) || (pStat == nullptr))
193
0
            return osl_File_E_INVAL;
194
195
133k
        file_path = pImpl->m_strFilePath;
196
133k
        OSL_ASSERT(!file_path.isEmpty());
197
133k
        if (file_path.isEmpty())
198
0
            return osl_File_E_INVAL;
199
200
133k
        pStat->uValidFields = 0;
201
133k
        return osl_File_E_None;
202
133k
    }
203
204
}
205
206
oslFileError SAL_CALL osl_getFileStatus(oslDirectoryItem Item, oslFileStatus* pStat, sal_uInt32 uFieldMask)
207
133k
{
208
133k
    DirectoryItem_Impl * pImpl = static_cast< DirectoryItem_Impl* >(Item);
209
210
133k
    OString file_path;
211
133k
    oslFileError  osl_error = setup_osl_getFileStatus(pImpl, pStat, file_path);
212
133k
    if (osl_error != osl_File_E_None)
213
0
        return osl_error;
214
215
133k
    struct stat file_stat;
216
217
133k
    bool bStatNeeded = is_stat_call_necessary(uFieldMask, pImpl->getFileType());
218
133k
    if (bStatNeeded && (osl::lstat(file_path, file_stat) != 0))
219
0
        return oslTranslateFileError(errno);
220
221
133k
    if (bStatNeeded)
222
133k
    {
223
        // we set all these attributes because it's cheap
224
133k
        set_file_type(file_stat, pStat);
225
133k
        set_file_access_time(file_stat, pStat);
226
133k
        set_file_modify_time(file_stat, pStat);
227
133k
        set_file_size(file_stat, pStat);
228
133k
        set_file_attributes(file_path, file_stat, uFieldMask, pStat);
229
230
        // file exists semantic of osl_FileStatus_Mask_Validate
231
133k
        if ((uFieldMask & osl_FileStatus_Mask_LinkTargetURL) && S_ISLNK(file_stat.st_mode))
232
0
        {
233
0
            osl_error = set_link_target_url(file_path, pStat);
234
0
            if (osl_error != osl_File_E_None)
235
0
                return osl_error;
236
0
        }
237
133k
    }
238
205
#ifdef _DIRENT_HAVE_D_TYPE
239
205
    else if (uFieldMask & osl_FileStatus_Mask_Type)
240
0
    {
241
0
        pStat->eType = pImpl->getFileType();
242
0
        pStat->uValidFields |= osl_FileStatus_Mask_Type;
243
0
    }
244
133k
#endif /* _DIRENT_HAVE_D_TYPE */
245
246
133k
    if (uFieldMask & osl_FileStatus_Mask_FileURL)
247
96.7k
    {
248
96.7k
        OUString url;
249
96.7k
        if ((osl_error = osl::detail::convertPathnameToUrl(file_path, &url)) != osl_File_E_None)
250
0
            return osl_error;
251
96.7k
        rtl_uString_assign(&pStat->ustrFileURL, url.pData);
252
253
96.7k
        pStat->uValidFields |= osl_FileStatus_Mask_FileURL;
254
96.7k
    }
255
256
133k
    if (uFieldMask & osl_FileStatus_Mask_FileName)
257
96.5k
    {
258
96.5k
        OString name;
259
96.5k
        osl_systemPathGetFileNameOrLastDirectoryPart(file_path.pData, &name.pData);
260
96.5k
        bool ok = rtl_convertStringToUString(
261
96.5k
            &pStat->ustrFileName, name.getStr(), name.getLength(), osl_getThreadTextEncoding(),
262
96.5k
            (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT
263
96.5k
             | RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT));
264
96.5k
        assert(ok); (void)ok;
265
96.5k
        pStat->uValidFields |= osl_FileStatus_Mask_FileName;
266
96.5k
    }
267
133k
    return osl_File_E_None;
268
133k
}
269
270
static oslFileError osl_psz_setFileAttributes( const char* pszFilePath, sal_uInt64 uAttributes )
271
0
{
272
0
    oslFileError osl_error = osl_File_E_None;
273
0
    mode_t       nNewMode  = 0;
274
275
0
    OSL_ENSURE(!(osl_File_Attribute_Hidden & uAttributes), "osl_File_Attribute_Hidden doesn't work under Unix");
276
277
0
    if (isForbidden(pszFilePath, osl_File_OpenFlag_Write))
278
0
        return osl_File_E_ACCES;
279
280
0
    if (uAttributes & osl_File_Attribute_OwnRead)
281
0
        nNewMode |= S_IRUSR;
282
283
0
    if (uAttributes & osl_File_Attribute_OwnWrite)
284
0
        nNewMode|=S_IWUSR;
285
286
0
    if  (uAttributes & osl_File_Attribute_OwnExe)
287
0
        nNewMode|=S_IXUSR;
288
289
0
    if (uAttributes & osl_File_Attribute_GrpRead)
290
0
        nNewMode|=S_IRGRP;
291
292
0
    if (uAttributes & osl_File_Attribute_GrpWrite)
293
0
        nNewMode|=S_IWGRP;
294
295
0
    if (uAttributes & osl_File_Attribute_GrpExe)
296
0
        nNewMode|=S_IXGRP;
297
298
0
    if (uAttributes & osl_File_Attribute_OthRead)
299
0
        nNewMode|=S_IROTH;
300
301
0
    if (uAttributes & osl_File_Attribute_OthWrite)
302
0
        nNewMode|=S_IWOTH;
303
304
0
    if (uAttributes & osl_File_Attribute_OthExe)
305
0
        nNewMode|=S_IXOTH;
306
307
0
    if (chmod(pszFilePath, nNewMode) < 0)
308
0
        osl_error = oslTranslateFileError(errno);
309
310
0
    return osl_error;
311
0
}
312
313
oslFileError SAL_CALL osl_setFileAttributes( rtl_uString* ustrFileURL, sal_uInt64 uAttributes )
314
0
{
315
0
    char path[PATH_MAX];
316
0
    oslFileError eRet;
317
318
0
    OSL_ASSERT( ustrFileURL );
319
320
    /* convert file url to system path */
321
0
    eRet = FileURLToPath( path, PATH_MAX, ustrFileURL );
322
0
    if( eRet != osl_File_E_None )
323
0
        return eRet;
324
325
#ifdef MACOSX
326
    if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
327
      return oslTranslateFileError( errno );
328
#endif/* MACOSX */
329
330
0
    return osl_psz_setFileAttributes( path, uAttributes );
331
0
}
332
333
static oslFileError osl_psz_setFileTime (
334
    const char* pszFilePath,
335
    const TimeValue* pLastAccessTime,
336
    const TimeValue* pLastWriteTime )
337
0
{
338
0
    int nRet=0;
339
0
    struct utimbuf aTimeBuffer;
340
0
    struct stat aFileStat;
341
#ifdef DEBUG_OSL_FILE
342
    struct tm* pTM=0;
343
#endif
344
345
0
    if (isForbidden(pszFilePath, osl_File_OpenFlag_Write))
346
0
        return osl_File_E_ACCES;
347
348
0
    nRet = lstat_c(pszFilePath,&aFileStat);
349
350
0
    if ( nRet < 0 )
351
0
    {
352
0
        nRet=errno;
353
0
        return oslTranslateFileError(nRet);
354
0
    }
355
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));
364
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
370
371
0
    if ( pLastAccessTime != nullptr )
372
0
    {
373
0
        aTimeBuffer.actime=pLastAccessTime->Seconds;
374
0
    }
375
0
    else
376
0
    {
377
0
        aTimeBuffer.actime=aFileStat.st_atime;
378
0
    }
379
380
0
    if ( pLastWriteTime != nullptr )
381
0
    {
382
0
        aTimeBuffer.modtime=pLastWriteTime->Seconds;
383
0
    }
384
0
    else
385
0
    {
386
0
        aTimeBuffer.modtime=aFileStat.st_mtime;
387
0
    }
388
389
    /* mfe: Creation time not used here! */
390
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));
399
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
405
406
0
    nRet = utime_c(pszFilePath,&aTimeBuffer);
407
0
    if ( nRet < 0 )
408
0
    {
409
0
        nRet=errno;
410
0
        return oslTranslateFileError(nRet);
411
0
    }
412
413
0
    return osl_File_E_None;
414
0
}
415
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 )
421
0
{
422
0
    char path[PATH_MAX];
423
0
    oslFileError eRet;
424
425
0
    OSL_ASSERT( ustrFileURL );
426
427
    /* convert file url to system path */
428
0
    eRet = FileURLToPath( path, PATH_MAX, ustrFileURL );
429
0
    if( eRet != osl_File_E_None )
430
0
        return eRet;
431
432
#ifdef MACOSX
433
    if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
434
      return oslTranslateFileError( errno );
435
#endif/* MACOSX */
436
437
0
    return osl_psz_setFileTime( path, pLastAccessTime, pLastWriteTime );
438
0
}
439
440
sal_Bool
441
SAL_CALL osl_identicalDirectoryItem( oslDirectoryItem a, oslDirectoryItem b)
442
0
{
443
0
    DirectoryItem_Impl *pA = static_cast<DirectoryItem_Impl *>(a);
444
0
    DirectoryItem_Impl *pB = static_cast<DirectoryItem_Impl *>(b);
445
0
    if (a == b)
446
0
        return true;
447
    /* same name => same item, unless renaming / moving madness has occurred */
448
0
    if (pA->m_strFilePath == pB->m_strFilePath)
449
0
        return true;
450
451
0
    struct stat a_stat, b_stat;
452
453
0
    if (osl::lstat(pA->m_strFilePath, a_stat) != 0 ||
454
0
        osl::lstat(pB->m_strFilePath, b_stat) != 0)
455
0
        return false;
456
457
0
    return (a_stat.st_ino == b_stat.st_ino);
458
0
}
459
460
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */