Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sal/osl/unx/file_misc.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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/detail/file.h>
21
22
#include <osl/diagnose.h>
23
#include <rtl/string.hxx>
24
#include <sal/log.hxx>
25
26
#include "system.hxx"
27
#include "file_impl.hxx"
28
#include "file_error_transl.hxx"
29
#include "file_path_helper.hxx"
30
#include "file_url.hxx"
31
#include "uunxapi.hxx"
32
#include "readwrite_helper.hxx"
33
#include "unixerrnostring.hxx"
34
35
#include <errno.h>
36
#include <dirent.h>
37
#include <fcntl.h>
38
#include <limits.h>
39
#include <stdio.h>
40
#include <string.h>
41
#include <unistd.h>
42
#include <utime.h>
43
#include <sys/stat.h>
44
45
#include <algorithm>
46
#include <new>
47
48
#ifdef ANDROID
49
#include <osl/detail/android-bootstrap.h>
50
#endif
51
52
/************************************************************************
53
 *   TODO
54
 *
55
 *   - Fix: check for corresponding struct sizes in exported functions
56
 *   - check size/use of oslDirectory
57
 *   - check size/use of oslDirectoryItem
58
 ***********************************************************************/
59
60
namespace {
61
62
struct DirectoryImpl
63
{
64
    OString strPath;           /* holds native directory path */
65
    DIR*         pDirStruct;
66
#ifdef ANDROID
67
    enum Kind
68
    {
69
        KIND_DIRENT = 1,
70
        KIND_ASSETS = 2
71
    };
72
    int eKind;
73
    lo_apk_dir*  pApkDirStruct;
74
#endif
75
};
76
77
}
78
79
DirectoryItem_Impl::DirectoryItem_Impl(
80
    OString strFilePath, unsigned char DType)
81
146k
    : m_strFilePath (std::move(strFilePath)),
82
146k
      m_RefCount     (1),
83
146k
      m_DType        (DType)
84
146k
{
85
146k
}
86
DirectoryItem_Impl::~DirectoryItem_Impl()
87
146k
{
88
146k
}
89
90
void DirectoryItem_Impl::acquire()
91
0
{
92
0
    ++m_RefCount;
93
0
}
94
void DirectoryItem_Impl::release()
95
146k
{
96
146k
    if (--m_RefCount == 0)
97
146k
        delete this;
98
146k
}
99
100
oslFileType DirectoryItem_Impl::getFileType() const
101
135k
{
102
135k
    switch (m_DType)
103
135k
    {
104
0
#ifdef _DIRENT_HAVE_D_TYPE
105
0
        case DT_LNK:
106
0
            return osl_File_Type_Link;
107
0
        case DT_DIR:
108
0
            return osl_File_Type_Directory;
109
102k
        case DT_REG:
110
102k
            return osl_File_Type_Regular;
111
0
        case DT_FIFO:
112
0
            return osl_File_Type_Fifo;
113
0
        case DT_SOCK:
114
0
            return osl_File_Type_Socket;
115
0
        case DT_CHR:
116
0
        case DT_BLK:
117
0
            return osl_File_Type_Special;
118
0
#endif /* _DIRENT_HAVE_D_TYPE */
119
33.4k
        default:
120
33.4k
            break;
121
135k
    }
122
33.4k
    return osl_File_Type_Unknown;
123
135k
}
124
125
static oslFileError osl_psz_createDirectory(
126
    char const * pszPath, sal_uInt32 flags);
127
static oslFileError osl_psz_removeDirectory(const char* pszPath);
128
129
oslFileError SAL_CALL osl_openDirectory(rtl_uString* ustrDirectoryURL, oslDirectory* pDirectory)
130
123k
{
131
123k
    oslFileError eRet;
132
133
123k
    OString path;
134
135
123k
    if ((ustrDirectoryURL == nullptr) || (ustrDirectoryURL->length == 0) || (pDirectory == nullptr))
136
0
        return osl_File_E_INVAL;
137
138
    /* convert file URL to system path */
139
123k
    eRet = osl::detail::convertUrlToPathname(OUString::unacquired(&ustrDirectoryURL), &path);
140
141
123k
    if( eRet != osl_File_E_None )
142
0
        return eRet;
143
144
123k
    osl_systemPathRemoveSeparator(path.pData);
145
146
123k
    if (isForbidden(path, osl_File_OpenFlag_Read))
147
0
        return osl_File_E_ACCES;
148
149
#ifdef MACOSX
150
    {
151
        auto const n = std::max(int(path.getLength() + 1), int(PATH_MAX));
152
        auto const tmp = std::make_unique<char[]>(n);
153
        std::strcpy(tmp.get(), path.getStr());
154
        if (macxp_resolveAlias(tmp.get(), n) != 0) {
155
            return oslTranslateFileError(errno);
156
        }
157
        path = OString(tmp.get(), std::strlen(tmp.get()));
158
    }
159
#endif /* MACOSX */
160
161
#ifdef ANDROID
162
    if( strncmp( path.getStr(), "/assets/", sizeof( "/assets/" ) - 1) == 0 )
163
    {
164
        lo_apk_dir *pdir = lo_apk_opendir( path.getStr() );
165
166
        if( pdir )
167
        {
168
            DirectoryImpl* pDirImpl = new(std::nothrow) DirectoryImpl;
169
170
            if( pDirImpl )
171
            {
172
                pDirImpl->eKind = DirectoryImpl::KIND_ASSETS;
173
                pDirImpl->pApkDirStruct = pdir;
174
                pDirImpl->strPath = path;
175
176
                *pDirectory = (oslDirectory) pDirImpl;
177
                return osl_File_E_None;
178
            }
179
            else
180
            {
181
                errno = ENOMEM;
182
                lo_apk_closedir( pdir );
183
            }
184
        }
185
    }
186
    else
187
#endif
188
123k
    {
189
        /* open directory */
190
123k
        DIR *pdir = opendir( path.getStr() );
191
192
123k
        if( pdir )
193
123k
        {
194
123k
            SAL_INFO("sal.file", "opendir(" << path << ") => " << pdir);
195
196
            /* create and initialize impl structure */
197
123k
            DirectoryImpl* pDirImpl = new(std::nothrow) DirectoryImpl;
198
199
123k
            if( pDirImpl )
200
123k
            {
201
123k
                pDirImpl->pDirStruct = pdir;
202
123k
                pDirImpl->strPath = path;
203
#ifdef ANDROID
204
                pDirImpl->eKind = DirectoryImpl::KIND_DIRENT;
205
#endif
206
123k
                *pDirectory = static_cast<oslDirectory>(pDirImpl);
207
123k
                return osl_File_E_None;
208
123k
            }
209
123k
            errno = ENOMEM;
210
0
            closedir( pdir );
211
0
        }
212
50
        else
213
50
        {
214
50
            int e = errno;
215
50
            SAL_INFO("sal.file", "opendir(" << path << "): " << UnixErrnoString(e));
216
            // Restore errno after possible modification by SAL_INFO above
217
50
            errno = e;
218
50
        }
219
123k
    }
220
221
50
    return oslTranslateFileError(errno);
222
123k
}
223
224
oslFileError SAL_CALL osl_closeDirectory(oslDirectory pDirectory)
225
123k
{
226
123k
    SAL_WARN_IF(!pDirectory, "sal.file", "pDirectory is nullptr");
227
123k
    DirectoryImpl* pDirImpl = static_cast<DirectoryImpl*>(pDirectory);
228
123k
    oslFileError err = osl_File_E_None;
229
230
123k
    if (!pDirImpl)
231
0
        return osl_File_E_INVAL;
232
233
#ifdef ANDROID
234
    if (pDirImpl->eKind == DirectoryImpl::KIND_ASSETS)
235
    {
236
        if (lo_apk_closedir(pDirImpl->pApkDirStruct))
237
            err = osl_File_E_IO;
238
    }
239
    else
240
#endif
241
123k
    {
242
123k
        if (closedir( pDirImpl->pDirStruct) != 0)
243
0
        {
244
0
            int e = errno;
245
0
            SAL_INFO("sal.file", "closedir(" << pDirImpl->pDirStruct << "): " << UnixErrnoString(e));
246
0
            err = oslTranslateFileError(e);
247
0
        }
248
123k
        else
249
123k
            SAL_INFO("sal.file", "closedir(" << pDirImpl->pDirStruct << "): OK");
250
123k
    }
251
252
123k
    delete pDirImpl;
253
254
123k
    return err;
255
123k
}
256
257
/**********************************************
258
 * osl_readdir_impl_
259
 *
260
 * readdir wrapper, filters out "." and ".."
261
 * on request
262
 *********************************************/
263
264
static struct dirent* osl_readdir_impl_(DIR* pdir)
265
174k
{
266
174k
    struct dirent* pdirent;
267
268
400k
    while ((pdirent = readdir(pdir)) != nullptr)
269
328k
    {
270
328k
        if ((strcmp(pdirent->d_name, ".") == 0) || (strcmp(pdirent->d_name, "..") == 0))
271
225k
            continue;
272
102k
        break;
273
328k
    }
274
275
174k
    return pdirent;
276
174k
}
277
278
oslFileError SAL_CALL osl_getNextDirectoryItem(oslDirectory pDirectory,
279
        oslDirectoryItem* pItem, SAL_UNUSED_PARAMETER sal_uInt32 /*uHint*/)
280
174k
{
281
174k
    SAL_WARN_IF(!pDirectory, "sal.file", "pDirectory is nullptr");
282
174k
    SAL_WARN_IF(!pItem, "sal.file", "pItem is nullptr");
283
284
174k
    DirectoryImpl* pDirImpl = static_cast<DirectoryImpl*>(pDirectory);
285
174k
    OString strFileName;
286
174k
    struct dirent* pEntry;
287
288
174k
    if ((pDirectory == nullptr) || (pItem == nullptr))
289
0
        return osl_File_E_INVAL;
290
291
#ifdef ANDROID
292
    if(pDirImpl->eKind == DirectoryImpl::KIND_ASSETS)
293
    {
294
        pEntry = lo_apk_readdir(pDirImpl->pApkDirStruct);
295
    }
296
    else
297
#endif
298
174k
    {
299
174k
        pEntry = osl_readdir_impl_(pDirImpl->pDirStruct);
300
174k
    }
301
302
174k
    if (!pEntry)
303
71.9k
        return osl_File_E_NOENT;
304
305
102k
    char const * filename = pEntry->d_name;
306
307
#if defined(MACOSX)
308
    // convert decomposed filename to precomposed UTF-8
309
    char composed_name[BUFSIZ];
310
    CFMutableStringRef strRef = CFStringCreateMutable(nullptr, 0 );
311
    CFStringAppendCString(strRef, filename, kCFStringEncodingUTF8);  // UTF8 is default on Mac OSX
312
    CFStringNormalize(strRef, kCFStringNormalizationFormC);
313
    CFStringGetCString(strRef, composed_name, BUFSIZ, kCFStringEncodingUTF8);
314
    CFRelease(strRef);
315
    filename = composed_name;
316
#endif
317
318
102k
    strFileName = OString(filename, strlen(filename));
319
320
102k
    auto const strFilePath = osl::systemPathMakeAbsolutePath(pDirImpl->strPath, strFileName);
321
322
102k
    DirectoryItem_Impl* pImpl = static_cast< DirectoryItem_Impl* >(*pItem);
323
102k
    if (pImpl)
324
0
        pImpl->release();
325
102k
#ifdef _DIRENT_HAVE_D_TYPE
326
102k
    pImpl = new DirectoryItem_Impl(strFilePath, pEntry->d_type);
327
#else
328
    pImpl = new DirectoryItem_Impl(strFilePath);
329
#endif /* _DIRENT_HAVE_D_TYPE */
330
102k
    *pItem = pImpl;
331
332
102k
    return osl_File_E_None;
333
174k
}
334
335
oslFileError SAL_CALL osl_getDirectoryItem(rtl_uString* ustrFileURL, oslDirectoryItem* pItem)
336
546k
{
337
546k
    OString strSystemPath;
338
546k
    oslFileError osl_error = osl_File_E_INVAL;
339
340
546k
    if ((!ustrFileURL) || (ustrFileURL->length == 0) || (!pItem))
341
136k
        return osl_File_E_INVAL;
342
343
409k
    osl_error = osl::detail::convertUrlToPathname(OUString::unacquired(&ustrFileURL), &strSystemPath);
344
409k
    if (osl_error != osl_File_E_None)
345
345k
        return osl_error;
346
347
64.7k
    osl_systemPathRemoveSeparator(strSystemPath.pData);
348
349
64.7k
    if (isForbidden(strSystemPath, osl_File_OpenFlag_Read))
350
0
        return osl_File_E_ACCES;
351
352
64.7k
    if (osl::access(strSystemPath, F_OK) == -1)
353
21.0k
    {
354
21.0k
        osl_error = oslTranslateFileError(errno);
355
21.0k
    }
356
43.7k
    else
357
43.7k
    {
358
43.7k
        *pItem = new DirectoryItem_Impl(std::move(strSystemPath));
359
43.7k
    }
360
361
64.7k
    return osl_error;
362
64.7k
}
363
364
oslFileError SAL_CALL osl_acquireDirectoryItem( oslDirectoryItem Item )
365
0
{
366
0
    DirectoryItem_Impl * pImpl = static_cast< DirectoryItem_Impl* >(Item);
367
0
    if (pImpl == nullptr)
368
0
        return osl_File_E_INVAL;
369
370
0
    pImpl->acquire();
371
0
    return osl_File_E_None;
372
0
}
373
374
oslFileError SAL_CALL osl_releaseDirectoryItem( oslDirectoryItem Item )
375
146k
{
376
146k
    DirectoryItem_Impl * pImpl = static_cast< DirectoryItem_Impl* >(Item);
377
146k
    if (pImpl == nullptr)
378
0
        return osl_File_E_INVAL;
379
380
146k
    pImpl->release();
381
146k
    return osl_File_E_None;
382
146k
}
383
384
oslFileError SAL_CALL osl_createDirectory( rtl_uString* ustrDirectoryURL )
385
0
{
386
0
    return osl_createDirectoryWithFlags(
387
0
        ustrDirectoryURL, osl_File_OpenFlag_Read | osl_File_OpenFlag_Write);
388
0
}
389
390
oslFileError osl_createDirectoryWithFlags(
391
    rtl_uString * ustrDirectoryURL, sal_uInt32 flags)
392
10.3k
{
393
10.3k
    char path[PATH_MAX];
394
10.3k
    oslFileError eRet;
395
396
10.3k
    SAL_WARN_IF((!ustrDirectoryURL) || (ustrDirectoryURL->length == 0),
397
10.3k
                "sal.file", "Invalid directory URL");
398
399
    /* convert directory url to system path */
400
10.3k
    eRet = FileURLToPath( path, PATH_MAX, ustrDirectoryURL );
401
10.3k
    if( eRet != osl_File_E_None )
402
0
        return eRet;
403
404
#ifdef MACOSX
405
    if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
406
      return oslTranslateFileError( errno );
407
#endif/* MACOSX */
408
409
10.3k
    return osl_psz_createDirectory( path, flags );
410
10.3k
}
411
412
oslFileError SAL_CALL osl_removeDirectory( rtl_uString* ustrDirectoryURL )
413
10.3k
{
414
10.3k
    char path[PATH_MAX];
415
10.3k
    oslFileError eRet;
416
417
10.3k
    SAL_WARN_IF((!ustrDirectoryURL) || (ustrDirectoryURL->length == 0),
418
10.3k
                "sal.file", "Invalid directory URL");
419
420
    /* convert directory url to system path */
421
10.3k
    eRet = FileURLToPath( path, PATH_MAX, ustrDirectoryURL );
422
10.3k
    if( eRet != osl_File_E_None )
423
0
        return eRet;
424
425
#ifdef MACOSX
426
    if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
427
      return oslTranslateFileError( errno );
428
#endif/* MACOSX */
429
430
10.3k
    return osl_psz_removeDirectory( path );
431
10.3k
}
432
433
oslFileError osl_psz_createDirectory(char const * pszPath, sal_uInt32 flags)
434
10.3k
{
435
10.3k
    if (isForbidden(pszPath, osl_File_OpenFlag_Create))
436
0
        return osl_File_E_ACCES;
437
438
10.3k
    int nRet=0;
439
10.3k
    int mode
440
10.3k
        = (((flags & osl_File_OpenFlag_Read) == 0
441
10.3k
            ? 0
442
10.3k
            : ((flags & osl_File_OpenFlag_Private) == 0
443
10.3k
               ? S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
444
10.3k
               : S_IRUSR | S_IXUSR))
445
10.3k
           | ((flags & osl_File_OpenFlag_Write) == 0
446
10.3k
              ? 0
447
10.3k
              : ((flags & osl_File_OpenFlag_Private) == 0
448
10.3k
                 ? S_IWUSR | S_IWGRP | S_IWOTH
449
10.3k
                 : S_IWUSR)));
450
451
10.3k
    nRet = mkdir(pszPath,mode);
452
453
10.3k
    if ( nRet < 0 )
454
0
    {
455
0
        nRet=errno;
456
0
        SAL_INFO("sal.file", "mkdir(" << pszPath << ",0" << std::oct << mode << std::dec << "): " << UnixErrnoString(nRet));
457
0
        return oslTranslateFileError(nRet);
458
0
    }
459
10.3k
    else
460
10.3k
        SAL_INFO("sal.file", "mkdir(" << pszPath << ",0" << std::oct << mode << std::dec << "): OK");
461
462
10.3k
    return osl_File_E_None;
463
10.3k
}
464
465
static oslFileError osl_psz_removeDirectory( const char* pszPath )
466
10.3k
{
467
10.3k
    if (isForbidden(pszPath, osl_File_OpenFlag_Write))
468
0
        return osl_File_E_ACCES;
469
470
10.3k
    int nRet = rmdir(pszPath);
471
472
10.3k
    if ( nRet < 0 )
473
0
    {
474
0
        nRet=errno;
475
0
        SAL_INFO("sal.file", "rmdir(" << pszPath << "): " << UnixErrnoString(nRet));
476
0
        return oslTranslateFileError(nRet);
477
0
    }
478
10.3k
    else
479
10.3k
        SAL_INFO("sal.file", "rmdir(" << pszPath << "): OK");
480
481
10.3k
    return osl_File_E_None;
482
10.3k
}
483
484
static int path_make_parent(char* path)
485
0
{
486
0
    int i = rtl_str_lastIndexOfChar(path, '/');
487
488
0
    if (i > 0)
489
0
    {
490
0
        *(path + i) = 0;
491
0
        return i;
492
0
    }
493
0
    return 0;
494
0
}
495
496
static oslFileError create_dir_with_callback(
497
    char* directory_path,
498
    oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
499
    void* pData)
500
367k
{
501
367k
    if (osl::mkdir(directory_path, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
502
0
    {
503
0
        if (aDirectoryCreationCallbackFunc)
504
0
        {
505
0
            OUString url;
506
0
            osl::detail::convertPathnameToUrl(directory_path, &url);
507
0
            aDirectoryCreationCallbackFunc(pData, url.pData);
508
0
        }
509
0
        return osl_File_E_None;
510
0
    }
511
367k
    return oslTranslateFileError(errno);
512
367k
}
513
514
static oslFileError create_dir_recursively_(
515
    char* dir_path,
516
    oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
517
    void* pData)
518
367k
{
519
367k
    OSL_PRECOND((rtl_str_getLength(dir_path) > 0) && ((dir_path + (rtl_str_getLength(dir_path) - 1)) != (dir_path + rtl_str_lastIndexOfChar(dir_path, '/'))),
520
367k
    "Path must not end with a slash");
521
522
367k
    oslFileError osl_error = create_dir_with_callback(
523
367k
        dir_path, aDirectoryCreationCallbackFunc, pData);
524
525
367k
    if (osl_error != osl_File_E_NOENT)
526
367k
        return osl_error;
527
528
    // we step back until '/a_dir' at maximum because
529
    // we should get an error unequal ENOENT when
530
    // we try to create 'a_dir' at '/' and would so
531
    // return before
532
0
    int pos = path_make_parent(dir_path);
533
534
0
    osl_error = create_dir_recursively_(
535
0
        dir_path, aDirectoryCreationCallbackFunc, pData);
536
537
0
    if (osl_error != osl_File_E_None && osl_error != osl_File_E_EXIST)
538
0
        return osl_error;
539
540
0
    dir_path[pos] = '/';
541
542
0
    return create_dir_with_callback(dir_path, aDirectoryCreationCallbackFunc, pData);
543
0
}
544
545
oslFileError SAL_CALL osl_createDirectoryPath(
546
    rtl_uString* aDirectoryUrl,
547
    oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
548
    void* pData)
549
367k
{
550
367k
    if (aDirectoryUrl == nullptr)
551
0
        return osl_File_E_INVAL;
552
553
367k
    OString sys_path;
554
367k
    oslFileError osl_error = osl::detail::convertUrlToPathname(
555
367k
        OUString::unacquired(&aDirectoryUrl), &sys_path);
556
557
367k
    if (osl_error != osl_File_E_None)
558
106
        return osl_error;
559
560
367k
    osl::systemPathRemoveSeparator(sys_path);
561
562
367k
    if (isForbidden(sys_path, osl_File_OpenFlag_Create))
563
0
        return osl_File_E_ACCES;
564
565
367k
    return create_dir_recursively_(sys_path.pData->buffer, aDirectoryCreationCallbackFunc, pData);
566
367k
}
567
568
static oslFileError osl_unlinkFile(const char* pszPath);
569
static oslFileError osl_psz_copyFile(const char* pszPath, const char* pszDestPath, bool preserveMetadata);
570
static oslFileError osl_psz_moveFile(const char* pszPath, const char* pszDestPath);
571
572
static oslFileError  oslDoCopy(const char* pszSourceFileName, const char* pszDestFileName, mode_t nMode, size_t nSourceSize, bool DestFileExists);
573
static void attemptChangeMetadata(const char* pszFileName, mode_t nMode, time_t nAcTime, time_t nModTime, uid_t nUID, gid_t nGID);
574
static int           oslDoCopyLink(const char* pszSourceFileName, const char* pszDestFileName);
575
static int           oslDoCopyFile(const char* pszSourceFileName, const char* pszDestFileName, size_t nSourceSize, mode_t mode);
576
static oslFileError  oslDoMoveFile(const char* pszPath, const char* pszDestPath);
577
578
oslFileError SAL_CALL osl_moveFile( rtl_uString* ustrFileURL, rtl_uString* ustrDestURL )
579
0
{
580
0
    char srcPath[PATH_MAX];
581
0
    char destPath[PATH_MAX];
582
0
    oslFileError eRet;
583
584
0
    SAL_WARN_IF((!ustrFileURL) || (ustrFileURL->length == 0), "sal.file", "Invalid source file URL");
585
0
    SAL_WARN_IF((!ustrDestURL) || (ustrDestURL->length == 0), "sal.file", "Invalid destination file URL");
586
587
    /* convert source url to system path */
588
0
    eRet = FileURLToPath( srcPath, PATH_MAX, ustrFileURL );
589
0
    if( eRet != osl_File_E_None )
590
0
        return eRet;
591
592
    /* convert destination url to system path */
593
0
    eRet = FileURLToPath( destPath, PATH_MAX, ustrDestURL );
594
0
    if( eRet != osl_File_E_None )
595
0
        return eRet;
596
597
0
    if (isForbidden(srcPath, osl_File_OpenFlag_Read) ||
598
0
        isForbidden(destPath, osl_File_OpenFlag_Create))
599
0
        return osl_File_E_ACCES;
600
601
#ifdef MACOSX
602
    if ( macxp_resolveAlias( srcPath, PATH_MAX ) != 0 || macxp_resolveAlias( destPath, PATH_MAX ) != 0 )
603
      return oslTranslateFileError( errno );
604
#endif/* MACOSX */
605
606
0
    return oslDoMoveFile( srcPath, destPath );
607
0
}
608
609
oslFileError SAL_CALL osl_replaceFile(rtl_uString* ustrFileURL, rtl_uString* ustrDestURL)
610
0
{
611
0
    int nGid = -1;
612
0
    char destPath[PATH_MAX];
613
0
    oslFileError eRet = FileURLToPath(destPath, PATH_MAX, ustrDestURL);
614
615
0
    if (isForbidden(destPath, osl_File_OpenFlag_Create))
616
0
        return osl_File_E_ACCES;
617
618
0
    if (eRet == osl_File_E_None)
619
0
    {
620
0
        struct stat aFileStat;
621
        // coverity[fs_check_call] - unavoidable TOCTOU
622
0
        int nRet = stat(destPath, &aFileStat);
623
0
        if (nRet == -1)
624
0
        {
625
0
            nRet = errno;
626
0
            SAL_INFO("sal.file", "stat(" << destPath << "): " << UnixErrnoString(nRet));
627
0
        }
628
0
        else
629
0
        {
630
0
            nGid = aFileStat.st_gid;
631
0
        }
632
0
    }
633
634
0
    eRet = osl_moveFile(ustrFileURL, ustrDestURL);
635
636
0
    if (eRet == osl_File_E_None && nGid != -1)
637
0
    {
638
0
        int nRet = chown(destPath, -1, nGid);
639
0
        if (nRet == -1)
640
0
        {
641
0
            nRet = errno;
642
0
            SAL_INFO("sal.file",
643
0
                     "chown(" << destPath << "-1, " << nGid << "): " << UnixErrnoString(nRet));
644
0
        }
645
0
    }
646
647
0
    return eRet;
648
0
}
649
650
oslFileError SAL_CALL osl_copyFile( rtl_uString* ustrFileURL, rtl_uString* ustrDestURL )
651
0
{
652
0
    char srcPath[PATH_MAX];
653
0
    char destPath[PATH_MAX];
654
0
    oslFileError eRet;
655
656
0
    SAL_WARN_IF((!ustrFileURL) || (ustrFileURL->length == 0), "sal.file", "Invalid source file URL");
657
0
    SAL_WARN_IF((!ustrDestURL) || (ustrDestURL->length == 0), "sal.file", "Invalid destination file URL");
658
659
    /* convert source url to system path */
660
0
    eRet = FileURLToPath( srcPath, PATH_MAX, ustrFileURL );
661
0
    if( eRet != osl_File_E_None )
662
0
        return eRet;
663
664
    /* convert destination url to system path */
665
0
    eRet = FileURLToPath( destPath, PATH_MAX, ustrDestURL );
666
0
    if( eRet != osl_File_E_None )
667
0
        return eRet;
668
669
#ifdef MACOSX
670
    if ( macxp_resolveAlias( srcPath, PATH_MAX ) != 0 || macxp_resolveAlias( destPath, PATH_MAX ) != 0 )
671
      return oslTranslateFileError( errno );
672
#endif/* MACOSX */
673
674
0
    return osl_psz_copyFile( srcPath, destPath, false );
675
0
}
676
677
oslFileError SAL_CALL osl_removeFile(rtl_uString* ustrFileURL)
678
467k
{
679
467k
    char path[PATH_MAX];
680
467k
    oslFileError eRet;
681
682
467k
    SAL_WARN_IF(!ustrFileURL || ustrFileURL->length == 0, "sal.file", "Invalid file URL");
683
684
    /* convert file url to system path */
685
467k
    eRet = FileURLToPath(path, PATH_MAX, ustrFileURL);
686
467k
    if (eRet != osl_File_E_None)
687
0
        return eRet;
688
689
#ifdef MACOSX
690
    if (macxp_resolveAlias(path, PATH_MAX) != 0)
691
      return oslTranslateFileError(errno);
692
#endif/* MACOSX */
693
694
467k
    if (isForbidden(path, osl_File_OpenFlag_Write))
695
0
        return osl_File_E_ACCES;
696
697
467k
    return osl_unlinkFile(path);
698
467k
}
699
700
static oslFileError oslDoMoveFile(const char* pszPath, const char* pszDestPath)
701
0
{
702
0
    oslFileError tErr = osl_psz_moveFile(pszPath,pszDestPath);
703
0
    if (tErr == osl_File_E_None)
704
0
        return tErr;
705
706
0
    if (tErr != osl_File_E_XDEV)
707
0
        return tErr;
708
709
0
    tErr = osl_psz_copyFile(pszPath,pszDestPath, true);
710
711
0
    if (tErr != osl_File_E_None)
712
0
    {
713
0
        osl_unlinkFile(pszDestPath);
714
0
        return tErr;
715
0
    }
716
717
0
    tErr = osl_unlinkFile(pszPath);
718
719
0
    return tErr;
720
0
}
721
722
static oslFileError osl_unlinkFile(const char* pszPath)
723
467k
{
724
467k
    int nRet=0;
725
467k
    struct stat aStat;
726
727
467k
    nRet = lstat_c(pszPath,&aStat);
728
467k
    if (nRet < 0)
729
0
    {
730
0
        nRet=errno;
731
0
        return oslTranslateFileError(nRet);
732
0
    }
733
734
467k
    if (S_ISDIR(aStat.st_mode))
735
0
        return osl_File_E_ISDIR;
736
737
467k
    nRet = unlink(pszPath);
738
467k
    if (nRet < 0)
739
0
    {
740
0
        nRet=errno;
741
0
        SAL_INFO("sal.file", "unlink(" << pszPath << "): " << UnixErrnoString(nRet));
742
0
        return oslTranslateFileError(nRet);
743
0
    }
744
467k
    else
745
467k
        SAL_INFO("sal.file", "unlink(" << pszPath << "): OK");
746
747
467k
    return osl_File_E_None;
748
467k
}
749
750
static oslFileError osl_psz_moveFile(const char* pszPath, const char* pszDestPath)
751
0
{
752
0
    if (isForbidden(pszPath, osl_File_OpenFlag_Read) ||
753
0
        isForbidden(pszDestPath, osl_File_OpenFlag_Create))
754
0
        return osl_File_E_ACCES;
755
756
0
    int nRet = rename(pszPath,pszDestPath);
757
758
0
    if (nRet < 0)
759
0
    {
760
0
        nRet=errno;
761
0
        SAL_INFO("sal.file", "rename(" << pszPath << "," << pszDestPath << "): " << UnixErrnoString(nRet));
762
0
        return oslTranslateFileError(nRet);
763
0
    }
764
0
    else
765
0
        SAL_INFO("sal.file", "rename(" << pszPath << "," << pszDestPath << "): OK");
766
767
0
    return osl_File_E_None;
768
0
}
769
770
static oslFileError osl_psz_copyFile( const char* pszPath, const char* pszDestPath, bool preserveMetadata )
771
0
{
772
0
    time_t nAcTime=0;
773
0
    time_t nModTime=0;
774
0
    uid_t nUID=0;
775
0
    gid_t nGID=0;
776
0
    int nRet=0;
777
0
    mode_t nMode=0;
778
0
    struct stat aFileStat;
779
0
    oslFileError tErr=osl_File_E_invalidError;
780
0
    size_t nSourceSize=0;
781
0
    bool DestFileExists=true;
782
783
0
    if (isForbidden(pszPath, osl_File_OpenFlag_Read) ||
784
0
        isForbidden(pszDestPath, osl_File_OpenFlag_Create))
785
0
        return osl_File_E_ACCES;
786
787
    /* mfe: does the source file really exists? */
788
0
    nRet = lstat_c(pszPath,&aFileStat);
789
790
0
    if (nRet < 0)
791
0
    {
792
0
        nRet=errno;
793
0
        return oslTranslateFileError(nRet);
794
0
    }
795
796
    /* we do only copy files here */
797
0
    if (S_ISDIR(aFileStat.st_mode))
798
0
        return osl_File_E_ISDIR;
799
800
0
    nSourceSize = static_cast< size_t >(aFileStat.st_size);
801
0
    nMode = aFileStat.st_mode;
802
0
    nAcTime = aFileStat.st_atime;
803
0
    nModTime = aFileStat.st_mtime;
804
0
    nUID = aFileStat.st_uid;
805
0
    nGID = aFileStat.st_gid;
806
807
0
    nRet = stat_c(pszDestPath,&aFileStat);
808
0
    if (nRet < 0)
809
0
    {
810
0
        nRet=errno;
811
812
#ifdef IOS
813
        // Checking for nonexistent files at least in the iCloud cache directory (like
814
        // "/private/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/helloodt0.odt" fails
815
        // with EPERM, not ENOENT.
816
        if (nRet == EPERM)
817
            DestFileExists=false;
818
#endif
819
820
0
        if (nRet == ENOENT)
821
0
            DestFileExists=false;
822
0
    }
823
824
    /* mfe: the destination file must not be a directory! */
825
0
    if (nRet == 0 && S_ISDIR(aFileStat.st_mode))
826
0
        return osl_File_E_ISDIR;
827
828
    /* mfe: file does not exists or is no dir */
829
830
0
    tErr = oslDoCopy(pszPath, pszDestPath, nMode, nSourceSize, DestFileExists);
831
832
0
    if (tErr != osl_File_E_None)
833
0
        return tErr;
834
835
0
    if (preserveMetadata)
836
0
        attemptChangeMetadata(pszDestPath, nMode, nAcTime, nModTime, nUID, nGID);
837
838
0
    return tErr;
839
0
}
840
841
static oslFileError oslDoCopy(const char* pszSourceFileName, const char* pszDestFileName, mode_t nMode, size_t nSourceSize, bool DestFileExists)
842
0
{
843
0
    int      nRet=0;
844
845
0
    OString tmpDestFile;
846
0
    if ( DestFileExists )
847
0
    {
848
        //TODO: better pick a temp file name instead of adding .osl-tmp:
849
        // use the destination file to avoid EXDEV /* Cross-device link */
850
0
        tmpDestFile = pszDestFileName + OString::Concat(".osl-tmp");
851
0
        if (rename(pszDestFileName, tmpDestFile.getStr()) != 0)
852
0
        {
853
0
            int e = errno;
854
0
            SAL_INFO("sal.file", "rename(" << pszDestFileName << ", " << tmpDestFile
855
0
                     << "): " << UnixErrnoString(e));
856
0
            if (e == ENOENT)
857
0
            {
858
0
                DestFileExists = false;
859
0
            }
860
0
            else
861
0
            {
862
0
                return osl_File_E_EXIST; // for want of a better error code
863
0
            }
864
0
        }
865
0
        else
866
0
        {
867
0
            SAL_INFO("sal.file", "rename(" << pszDestFileName << ", " << tmpDestFile
868
0
                     << "): OK");
869
0
        }
870
0
    }
871
872
0
    if ( S_ISREG(nMode) )
873
0
    {
874
        /* copy SourceFile to DestFile */
875
0
        nRet = oslDoCopyFile(pszSourceFileName,pszDestFileName,nSourceSize, nMode);
876
0
    }
877
0
    else if ( S_ISLNK(nMode) )
878
0
    {
879
0
        nRet = oslDoCopyLink(pszSourceFileName,pszDestFileName);
880
0
    }
881
0
    else
882
0
    {
883
0
        nRet = EINVAL;
884
0
    }
885
886
0
    if ( nRet > 0 && DestFileExists )
887
0
    {
888
0
        if (unlink(pszDestFileName) != 0)
889
0
        {
890
0
            int e = errno;
891
0
            SAL_INFO("sal.file", "unlink(" << pszDestFileName << "): " << UnixErrnoString(e));
892
0
        }
893
0
        else
894
0
            SAL_INFO("sal.file", "unlink(" << pszDestFileName << "): OK");
895
896
0
        if (rename(tmpDestFile.getStr(), pszDestFileName) != 0)
897
0
        {
898
0
            int e = errno;
899
0
            SAL_INFO("sal.file", "rename(" << tmpDestFile << ", " << pszDestFileName
900
0
                     << "): " << UnixErrnoString(e));
901
0
        }
902
0
        else
903
0
            SAL_INFO("sal.file", "rename(" << tmpDestFile << ", " << pszDestFileName << "): OK");
904
0
    }
905
906
0
    if ( nRet > 0 )
907
0
    {
908
0
        return oslTranslateFileError(nRet);
909
0
    }
910
911
0
    if ( DestFileExists )
912
0
    {
913
0
        unlink(tmpDestFile.getStr());
914
0
    }
915
916
0
    return osl_File_E_None;
917
0
}
918
919
void attemptChangeMetadata( const char* pszFileName, mode_t nMode, time_t nAcTime, time_t nModTime, uid_t nUID, gid_t nGID)
920
0
{
921
0
    struct utimbuf aTimeBuffer;
922
923
#if !defined AT_FDCWD
924
    if (!S_ISLNK(nMode) && chmod(pszFileName, nMode) < 0)
925
#else
926
0
    if ( fchmodat(AT_FDCWD, pszFileName, nMode, AT_SYMLINK_NOFOLLOW) < 0 )
927
0
#endif
928
0
    {
929
0
        int e = errno;
930
0
        SAL_INFO("sal.file", "chmod(" << pszFileName << ",0" << std::oct << nMode << std::dec <<"): " << UnixErrnoString(e));
931
0
    }
932
0
    else
933
0
        SAL_INFO("sal.file", "chmod(" << pszFileName << ",0" << std::oct << nMode << std::dec <<"): OK");
934
935
    // No way to change utime of a symlink itself:
936
0
    if (!S_ISLNK(nMode))
937
0
    {
938
0
        aTimeBuffer.actime=nAcTime;
939
0
        aTimeBuffer.modtime=nModTime;
940
0
        if ( utime(pszFileName,&aTimeBuffer) < 0 )
941
0
        {
942
0
            int e = errno;
943
0
            SAL_INFO("sal.file", "utime(" << pszFileName << "): errno " << e);
944
0
        }
945
0
    }
946
947
0
    if ( nUID != getuid() )
948
0
    {
949
0
        nUID=getuid();
950
0
    }
951
0
    if ( lchown(pszFileName,nUID,nGID) < 0 )
952
0
    {
953
0
        int e = errno;
954
0
        SAL_INFO("sal.file", "lchown(" << pszFileName << "): errno " << e);
955
0
    }
956
0
    else
957
0
        SAL_INFO("sal.file", "lchown(" << pszFileName << "): OK");
958
0
}
959
960
static int oslDoCopyLink(const char* pszSourceFileName, const char* pszDestFileName)
961
0
{
962
0
    int nRet=0;
963
964
    /* mfe: if dest file is symbolic link remove the link and place the file instead (hro says so) */
965
    /* mfe: if source is a link copy the link and not the file it points to (hro says so) */
966
0
    char pszLinkContent[PATH_MAX+1];
967
968
0
    pszLinkContent[0] = '\0';
969
970
0
    nRet = readlink(pszSourceFileName,pszLinkContent,PATH_MAX);
971
972
0
    if ( nRet < 0 )
973
0
    {
974
0
        nRet=errno;
975
0
        return nRet;
976
0
    }
977
978
0
    pszLinkContent[ nRet ] = 0;
979
980
0
    nRet = symlink(pszLinkContent,pszDestFileName);
981
982
0
    if ( nRet < 0 )
983
0
    {
984
0
        nRet=errno;
985
0
        return nRet;
986
0
    }
987
988
0
    return 0;
989
0
}
990
991
static int oslDoCopyFile(const char* pszSourceFileName, const char* pszDestFileName, size_t nSourceSize, mode_t mode)
992
0
{
993
0
    oslFileHandle SourceFileFH=nullptr;
994
0
    int DestFileFD=0;
995
0
    int nRet=0;
996
997
0
    if (openFilePath(pszSourceFileName,
998
0
                         &SourceFileFH,
999
0
                         osl_File_OpenFlag_Read|osl_File_OpenFlag_NoLock|osl_File_OpenFlag_NoExcl, mode_t(-1)) != osl_File_E_None)
1000
0
    {
1001
        // Let's hope errno is still set relevantly after openFilePath...
1002
0
        nRet=errno;
1003
0
        return nRet;
1004
0
    }
1005
1006
0
    DestFileFD=open(pszDestFileName, O_WRONLY | O_CREAT, mode);
1007
1008
0
    if ( DestFileFD < 0 )
1009
0
    {
1010
0
        nRet=errno;
1011
0
        SAL_INFO("sal.file", "open(" << pszDestFileName << ",O_WRONLY|O_CREAT,0" << std::oct << mode << std::dec << "): " << UnixErrnoString(nRet));
1012
0
        osl_closeFile(SourceFileFH);
1013
0
        return nRet;
1014
0
    }
1015
0
    else
1016
0
        SAL_INFO("sal.file", "open(" << pszDestFileName << ",O_WRONLY|O_CREAT,0" << std::oct << mode << std::dec << "): OK");
1017
1018
0
    size_t nRemains = nSourceSize;
1019
1020
0
    if ( nRemains )
1021
0
    {
1022
        /* mmap has problems, try the direct streaming */
1023
0
        char pBuffer[0x7FFF];
1024
1025
0
        do
1026
0
        {
1027
0
            size_t nToRead = std::min( sizeof(pBuffer), nRemains );
1028
0
            sal_uInt64 nRead;
1029
0
            bool succeeded;
1030
0
            if ( osl_readFile( SourceFileFH, pBuffer, nToRead, &nRead ) != osl_File_E_None || nRead > nToRead || nRead == 0 )
1031
0
                break;
1032
1033
0
            succeeded = safeWrite( DestFileFD, pBuffer, nRead );
1034
0
            if ( !succeeded )
1035
0
                break;
1036
1037
            // We know nRead <= nToRead, so it must fit in a size_t
1038
0
            nRemains -= static_cast<size_t>(nRead);
1039
0
        }
1040
0
        while( nRemains );
1041
0
    }
1042
1043
0
    if ( nRemains )
1044
0
    {
1045
0
        if ( errno )
1046
0
            nRet = errno;
1047
0
        else
1048
0
            nRet = ENOSPC;
1049
0
    }
1050
1051
0
    osl_closeFile( SourceFileFH );
1052
0
    if ( close( DestFileFD ) == -1 )
1053
0
    {
1054
0
        int e = errno;
1055
0
        SAL_INFO("sal.file", "close(" << DestFileFD << "): " << UnixErrnoString(e));
1056
0
        if ( nRet == 0 )
1057
0
            nRet = e;
1058
0
    }
1059
0
    else
1060
0
        SAL_INFO("sal.file", "close(" << DestFileFD << "): OK");
1061
1062
0
    return nRet;
1063
0
}
1064
1065
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */