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.hxx"
21#include "osl/detail/file.h"
22
23#include "osl/diagnose.h"
24#include "osl/thread.h"
25#include <osl/signal.h>
26#include "rtl/alloc.h"
27
28#include "system.h"
29#include "file_impl.hxx"
30#include "file_error_transl.h"
31#include "file_path_helper.hxx"
32#include "file_url.h"
33#include "uunxapi.hxx"
34#include "readwrite_helper.h"
35
36#include <sys/types.h>
37#include <errno.h>
38#include <dirent.h>
39#include <limits.h>
40#include <stdio.h>
41#include <string.h>
42#include <unistd.h>
43#include <sys/stat.h>
44#include <sys/mman.h>
45
46#include <algorithm>
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
60typedef struct
61{
62 rtl_uString* ustrPath; /* holds native directory path */
63 DIR* pDirStruct;
64#ifdef ANDROID
65 enum Kind
66 {
67 KIND_DIRENT = 1,
68 KIND_ASSETS = 2
69 };
70 int eKind;
71 lo_apk_dir* pApkDirStruct;
72#endif
73} oslDirectoryImpl;
74
75DirectoryItem_Impl::DirectoryItem_Impl(
76 rtl_uString * ustrFilePath, unsigned char DType)
77 : m_RefCount (1),
78 m_ustrFilePath (ustrFilePath),
79 m_DType (DType)
80{
81 if (m_ustrFilePath != 0)
82 rtl_uString_acquire(m_ustrFilePath);
83}
84DirectoryItem_Impl::~DirectoryItem_Impl()
85{
86 if (m_ustrFilePath != 0)
87 rtl_uString_release(m_ustrFilePath);
88}
89
90void * DirectoryItem_Impl::operator new(size_t n)
91{
92 return rtl_allocateMemory(n);
93}
94void DirectoryItem_Impl::operator delete(void * p)
95{
96 rtl_freeMemory(p);
97}
98
99void DirectoryItem_Impl::acquire()
100{
101 ++m_RefCount;
102}
103void DirectoryItem_Impl::release()
104{
105 if (0 == --m_RefCount)
106 delete this;
107}
108
109oslFileType DirectoryItem_Impl::getFileType() const
110{
111 switch (m_DType)
112 {
113#ifdef _DIRENT_HAVE_D_TYPE
114 case DT_LNK:
115 return osl_File_Type_Link;
116 case DT_DIR:
117 return osl_File_Type_Directory;
118 case DT_REG:
119 return osl_File_Type_Regular;
120 case DT_FIFO:
121 return osl_File_Type_Fifo;
122 case DT_SOCK:
123 return osl_File_Type_Socket;
124 case DT_CHR:
125 case DT_BLK:
126 return osl_File_Type_Special;
127#endif /* _DIRENT_HAVE_D_TYPE */
128 default:
129 break;
130 }
131 return osl_File_Type_Unknown;
132}
133
134static oslFileError osl_psz_createDirectory(const sal_Char* pszPath);
135static oslFileError osl_psz_removeDirectory(const sal_Char* pszPath);
136
137oslFileError SAL_CALL osl_openDirectory(rtl_uString* ustrDirectoryURL, oslDirectory* pDirectory)
138{
139 rtl_uString* ustrSystemPath = NULL;
140 oslFileError eRet;
141
142 char path[PATH_MAX];
143
144 if ((0 == ustrDirectoryURL) || (0 == ustrDirectoryURL->length) || (0 == pDirectory))
145 return osl_File_E_INVAL;
146
147 /* convert file URL to system path */
148 eRet = osl_getSystemPathFromFileURL_Ex(ustrDirectoryURL, &ustrSystemPath);
149
150 if( osl_File_E_None != eRet )
151 return eRet;
152
153 osl_systemPathRemoveSeparator(ustrSystemPath);
154
155 /* convert unicode path to text */
156 if ( UnicodeToText( path, PATH_MAX, ustrSystemPath->buffer, ustrSystemPath->length )
157#ifdef MACOSX
158 && macxp_resolveAlias( path, PATH_MAX ) == 0
159#endif /* MACOSX */
160 )
161 {
162#ifdef ANDROID
163 if( strncmp( path, "/assets/", sizeof( "/assets/" ) - 1) == 0 )
164 {
165 lo_apk_dir *pdir = lo_apk_opendir( path );
166
167 if( pdir )
168 {
169 oslDirectoryImpl* pDirImpl = (oslDirectoryImpl*) rtl_allocateMemory( sizeof(oslDirectoryImpl) );
170
171 if( pDirImpl )
172 {
173 pDirImpl->eKind = oslDirectoryImpl::KIND_ASSETS;
174 pDirImpl->pApkDirStruct = pdir;
175 pDirImpl->ustrPath = ustrSystemPath;
176
177 *pDirectory = (oslDirectory) pDirImpl;
178 return osl_File_E_None;
179 }
180 else
181 {
182 errno = ENOMEM;
183 lo_apk_closedir( pdir );
184 }
185 }
186 }
187 else
188#endif
189 {
190 /* open directory */
191 DIR *pdir = opendir( path );
192
193 if( pdir )
194 {
195 /* create and initialize impl structure */
196 oslDirectoryImpl* pDirImpl = (oslDirectoryImpl*) rtl_allocateMemory( sizeof(oslDirectoryImpl) );
197
198 if( pDirImpl )
199 {
200 pDirImpl->pDirStruct = pdir;
201 pDirImpl->ustrPath = ustrSystemPath;
202#ifdef ANDROID
203 pDirImpl->eKind = oslDirectoryImpl::KIND_DIRENT;
204#endif
205 *pDirectory = (oslDirectory) pDirImpl;
206 return osl_File_E_None;
207 }
208 else
209 {
210 errno = ENOMEM;
211 closedir( pdir );
212 }
213 }
214 else
215 {
216#ifdef DEBUG_OSL_FILE
217 perror ("osl_openDirectory"); fprintf (stderr, path);
218#endif
219 }
220 }
221 }
222
223 rtl_uString_release( ustrSystemPath );
224
225 return oslTranslateFileError(OSL_FET_ERROR, errno);
226}
227
228oslFileError SAL_CALL osl_closeDirectory( oslDirectory Directory )
229{
230 oslDirectoryImpl* pDirImpl = (oslDirectoryImpl*) Directory;
231 oslFileError err = osl_File_E_None;
232
233 OSL_ASSERT( Directory );
234
235 if( NULL == pDirImpl )
236 return osl_File_E_INVAL;
237
238#ifdef ANDROID
239 if( pDirImpl->eKind == oslDirectoryImpl::KIND_ASSETS )
240 {
241 if (lo_apk_closedir( pDirImpl->pApkDirStruct ))
242 err = osl_File_E_IO;
243 }
244 else
245#endif
246 {
247 if( closedir( pDirImpl->pDirStruct ) )
248 err = oslTranslateFileError(OSL_FET_ERROR, errno);
249 }
250
251 /* cleanup members */
252 rtl_uString_release( pDirImpl->ustrPath );
253
254 rtl_freeMemory( pDirImpl );
255
256 return err;
257}
258
259/**********************************************
260 * osl_readdir_impl_
261 *
262 * readdir wrapper, filters out "." and ".."
263 * on request
264 *********************************************/
265
266static struct dirent* osl_readdir_impl_(DIR* pdir, sal_Bool bFilterLocalAndParentDir)
267{
268 struct dirent* pdirent;
269
270 while ((pdirent = readdir(pdir)) != NULL)
271 {
272 if (bFilterLocalAndParentDir &&
273 ((0 == strcmp(pdirent->d_name, ".")) || (0 == strcmp(pdirent->d_name, ".."))))
274 continue;
275 else
276 break;
277 }
278
279 return pdirent;
280}
281
282oslFileError SAL_CALL osl_getNextDirectoryItem(oslDirectory Directory, oslDirectoryItem* pItem, SAL_UNUSED_PARAMETER sal_uInt32 /*uHint*/)
283{
284 oslDirectoryImpl* pDirImpl = (oslDirectoryImpl*)Directory;
285 rtl_uString* ustrFileName = NULL;
286 rtl_uString* ustrFilePath = NULL;
287 struct dirent* pEntry;
288
289 OSL_ASSERT(Directory);
290 OSL_ASSERT(pItem);
291
292 if ((NULL == Directory) || (NULL == pItem))
293 return osl_File_E_INVAL;
294
295#ifdef ANDROID
296 if( pDirImpl->eKind == oslDirectoryImpl::KIND_ASSETS )
297 {
298 pEntry = lo_apk_readdir(pDirImpl->pApkDirStruct);
299 }
300 else
301#endif
302 {
303 pEntry = osl_readdir_impl_(pDirImpl->pDirStruct, sal_True);
304 }
305
306 if (NULL == pEntry)
307 return osl_File_E_NOENT;
308
309
310#if defined(MACOSX)
311
312 // convert decomposed filename to precomposed unicode
313 char composed_name[BUFSIZ];
314 CFMutableStringRef strRef = CFStringCreateMutable (NULL, 0 );
315 CFStringAppendCString( strRef, pEntry->d_name, kCFStringEncodingUTF8 ); //UTF8 is default on Mac OSX
316 CFStringNormalize( strRef, kCFStringNormalizationFormC );
317 CFStringGetCString( strRef, composed_name, BUFSIZ, kCFStringEncodingUTF8 );
318 CFRelease( strRef );
319 rtl_string2UString( &ustrFileName, composed_name, strlen( composed_name),
320 osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
321
322#else // not MACOSX
323 /* convert file name to unicode */
324 rtl_string2UString( &ustrFileName, pEntry->d_name, strlen( pEntry->d_name ),
325 osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
326 OSL_ASSERT(ustrFileName != 0);
327
328#endif
329
330 osl_systemPathMakeAbsolutePath(pDirImpl->ustrPath, ustrFileName, &ustrFilePath);
331 rtl_uString_release( ustrFileName );
332
333 DirectoryItem_Impl * pImpl = static_cast< DirectoryItem_Impl* >(*pItem);
334 if (0 != pImpl)
335 {
336 pImpl->release(), pImpl = 0;
337 }
338#ifdef _DIRENT_HAVE_D_TYPE
339 pImpl = new DirectoryItem_Impl(ustrFilePath, pEntry->d_type);
340#else
341 pImpl = new DirectoryItem_Impl(ustrFilePath);
342#endif /* _DIRENT_HAVE_D_TYPE */
343 *pItem = pImpl;
344 rtl_uString_release( ustrFilePath );
345
346 return osl_File_E_None;
347}
348
349oslFileError SAL_CALL osl_getDirectoryItem( rtl_uString* ustrFileURL, oslDirectoryItem* pItem )
350{
351 rtl_uString* ustrSystemPath = NULL;
352 oslFileError osl_error = osl_File_E_INVAL;
353
354 OSL_ASSERT((0 != ustrFileURL) && (0 != pItem));
355 if ((0 == ustrFileURL) || (0 == ustrFileURL->length) || (0 == pItem))
356 return osl_File_E_INVAL;
357
358 osl_error = osl_getSystemPathFromFileURL_Ex(ustrFileURL, &ustrSystemPath);
359 if (osl_File_E_None != osl_error)
360 return osl_error;
361
362 osl_systemPathRemoveSeparator(ustrSystemPath);
363
364 if (-1 == access_u(ustrSystemPath, F_OK))
365 {
366 osl_error = oslTranslateFileError(OSL_FET_ERROR, errno);
367 }
368 else
369 {
370 *pItem = new DirectoryItem_Impl(ustrSystemPath);
371 }
372 rtl_uString_release(ustrSystemPath);
373
374 return osl_error;
375}
376
377oslFileError SAL_CALL osl_acquireDirectoryItem( oslDirectoryItem Item )
378{
379 DirectoryItem_Impl * pImpl = static_cast< DirectoryItem_Impl* >(Item);
380 if (0 == pImpl)
381 return osl_File_E_INVAL;
382
383 pImpl->acquire();
384 return osl_File_E_None;
385}
386
387oslFileError SAL_CALL osl_releaseDirectoryItem( oslDirectoryItem Item )
388{
389 DirectoryItem_Impl * pImpl = static_cast< DirectoryItem_Impl* >(Item);
390 if (0 == pImpl)
391 return osl_File_E_INVAL;
392
393 pImpl->release();
394 return osl_File_E_None;
395}
396
397oslFileError SAL_CALL osl_createDirectory( rtl_uString* ustrDirectoryURL )
398{
399 char path[PATH_MAX];
400 oslFileError eRet;
401
402 OSL_ASSERT( ustrDirectoryURL );
403
404 /* convert directory url to system path */
405 eRet = FileURLToPath( path, PATH_MAX, ustrDirectoryURL );
406 if( eRet != osl_File_E_None )
407 return eRet;
408
409#ifdef MACOSX
410 if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
411 return oslTranslateFileError( OSL_FET_ERROR, errno );
412#endif/* MACOSX */
413
414 return osl_psz_createDirectory( path );
415}
416
417oslFileError SAL_CALL osl_removeDirectory( rtl_uString* ustrDirectoryURL )
418{
419 char path[PATH_MAX];
420 oslFileError eRet;
421
422 OSL_ASSERT( ustrDirectoryURL );
423
424 /* convert directory url to system path */
425 eRet = FileURLToPath( path, PATH_MAX, ustrDirectoryURL );
426 if( eRet != osl_File_E_None )
427 return eRet;
428
429#ifdef MACOSX
430 if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
431 return oslTranslateFileError( OSL_FET_ERROR, errno );
432#endif/* MACOSX */
433
434 return osl_psz_removeDirectory( path );
435}
436
437static oslFileError osl_psz_createDirectory( const sal_Char* pszPath )
438{
439 int nRet=0;
440 int mode = S_IRWXU | S_IRWXG | S_IRWXO;
441
442 nRet = mkdir(pszPath,mode);
443
444 if ( nRet < 0 )
445 {
446 nRet=errno;
447 return oslTranslateFileError(OSL_FET_ERROR, nRet);
448 }
449
450 return osl_File_E_None;
451}
452
453static oslFileError osl_psz_removeDirectory( const sal_Char* pszPath )
454{
455 int nRet=0;
456
457 nRet = rmdir(pszPath);
458
459 if ( nRet < 0 )
460 {
461 nRet=errno;
462 return oslTranslateFileError(OSL_FET_ERROR, nRet);
463 }
464
465 return osl_File_E_None;
466}
467
468static int path_make_parent(sal_Unicode* path)
469{
470 int i = rtl_ustr_lastIndexOfChar(path, '/');
471
472 if (i > 0)
473 {
474 *(path + i) = 0;
475 return i;
476 }
477 else
478 return 0;
479}
480
481static int create_dir_with_callback(
482 sal_Unicode* directory_path,
483 oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
484 void* pData)
485{
486 int mode = S_IRWXU | S_IRWXG | S_IRWXO;
487
488 if (osl::mkdir(directory_path, mode) == 0)
489 {
490 if (aDirectoryCreationCallbackFunc)
491 {
492 rtl::OUString url;
493 osl::FileBase::getFileURLFromSystemPath(directory_path, url);
494 aDirectoryCreationCallbackFunc(pData, url.pData);
495 }
496 return 0;
497 }
498 return errno;
499}
500
501static oslFileError create_dir_recursively_(
502 sal_Unicode* dir_path,
503 oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
504 void* pData)
505{
506 OSL_PRECOND((rtl_ustr_getLength(dir_path) > 0) && ((dir_path + (rtl_ustr_getLength(dir_path) - 1)) != (dir_path + rtl_ustr_lastIndexOfChar(dir_path, '/'))), \
507 "Path must not end with a slash");
508
509 int native_err = create_dir_with_callback(
510 dir_path, aDirectoryCreationCallbackFunc, pData);
511
512 if (native_err == 0)
513 return osl_File_E_None;
514
515 if (native_err != ENOENT)
516 return oslTranslateFileError(OSL_FET_ERROR, native_err);
517
518 // we step back until '/a_dir' at maximum because
519 // we should get an error unequal ENOENT when
520 // we try to create 'a_dir' at '/' and would so
521 // return before
522 int pos = path_make_parent(dir_path);
523
524 oslFileError osl_error = create_dir_recursively_(
525 dir_path, aDirectoryCreationCallbackFunc, pData);
526
527 if (osl_File_E_None != osl_error)
528 return osl_error;
529
530 dir_path[pos] = '/';
531
532 return create_dir_recursively_(dir_path, aDirectoryCreationCallbackFunc, pData);
533}
534
535oslFileError SAL_CALL osl_createDirectoryPath(
536 rtl_uString* aDirectoryUrl,
537 oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
538 void* pData)
539{
540 if (aDirectoryUrl == NULL)
541 return osl_File_E_INVAL;
542
543 rtl::OUString sys_path;
544 oslFileError osl_error = osl_getSystemPathFromFileURL_Ex(aDirectoryUrl, &sys_path.pData);
545
546 if (osl_error != osl_File_E_None)
547 return osl_error;
548
549 osl::systemPathRemoveSeparator(sys_path);
550
551 // const_cast because sys_path is a local copy which we want to modify inplace instead of
552 // coyp it into another buffer on the heap again
553 return create_dir_recursively_(sys_path.pData->buffer, aDirectoryCreationCallbackFunc, pData);
554}
555
556static oslFileError osl_psz_removeFile(const sal_Char* pszPath);
557static oslFileError osl_psz_copyFile(const sal_Char* pszPath, const sal_Char* pszDestPath);
558static oslFileError osl_psz_moveFile(const sal_Char* pszPath, const sal_Char* pszDestPath);
559
560static oslFileError oslDoCopy(const sal_Char* pszSourceFileName, const sal_Char* pszDestFileName, mode_t nMode, size_t nSourceSize, int DestFileExists);
561static oslFileError oslChangeFileModes(const sal_Char* pszFileName, mode_t nMode, time_t nAcTime, time_t nModTime, uid_t nUID, gid_t nGID);
562static int oslDoCopyLink(const sal_Char* pszSourceFileName, const sal_Char* pszDestFileName);
563static int oslDoCopyFile(const sal_Char* pszSourceFileName, const sal_Char* pszDestFileName, size_t nSourceSize, mode_t mode);
564static oslFileError oslDoMoveFile(const sal_Char* pszPath, const sal_Char* pszDestPath);
565
566oslFileError SAL_CALL osl_moveFile( rtl_uString* ustrFileURL, rtl_uString* ustrDestURL )
567{
568 char srcPath[PATH_MAX];
569 char destPath[PATH_MAX];
570 oslFileError eRet;
571
572 OSL_ASSERT( ustrFileURL );
573 OSL_ASSERT( ustrDestURL );
574
575 /* convert source url to system path */
576 eRet = FileURLToPath( srcPath, PATH_MAX, ustrFileURL );
577 if( eRet != osl_File_E_None )
578 return eRet;
579
580 /* convert destination url to system path */
581 eRet = FileURLToPath( destPath, PATH_MAX, ustrDestURL );
582 if( eRet != osl_File_E_None )
583 return eRet;
584
585#ifdef MACOSX
586 if ( macxp_resolveAlias( srcPath, PATH_MAX ) != 0 || macxp_resolveAlias( destPath, PATH_MAX ) != 0 )
587 return oslTranslateFileError( OSL_FET_ERROR, errno );
588#endif/* MACOSX */
589
590 return oslDoMoveFile( srcPath, destPath );
591}
592
593oslFileError SAL_CALL osl_copyFile( rtl_uString* ustrFileURL, rtl_uString* ustrDestURL )
594{
595 char srcPath[PATH_MAX];
596 char destPath[PATH_MAX];
597 oslFileError eRet;
598
599 OSL_ASSERT( ustrFileURL );
600 OSL_ASSERT( ustrDestURL );
601
602 /* convert source url to system path */
603 eRet = FileURLToPath( srcPath, PATH_MAX, ustrFileURL );
604 if( eRet != osl_File_E_None )
605 return eRet;
606
607 /* convert destination url to system path */
608 eRet = FileURLToPath( destPath, PATH_MAX, ustrDestURL );
609 if( eRet != osl_File_E_None )
610 return eRet;
611
612#ifdef MACOSX
613 if ( macxp_resolveAlias( srcPath, PATH_MAX ) != 0 || macxp_resolveAlias( destPath, PATH_MAX ) != 0 )
614 return oslTranslateFileError( OSL_FET_ERROR, errno );
615#endif/* MACOSX */
616
617 return osl_psz_copyFile( srcPath, destPath );
618}
619
620oslFileError SAL_CALL osl_removeFile( rtl_uString* ustrFileURL )
621{
622 char path[PATH_MAX];
623 oslFileError eRet;
624
625 OSL_ASSERT( ustrFileURL );
626
627 /* convert file url to system path */
628 eRet = FileURLToPath( path, PATH_MAX, ustrFileURL );
629 if( eRet != osl_File_E_None )
630 return eRet;
631
632#ifdef MACOSX
633 if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
634 return oslTranslateFileError( OSL_FET_ERROR, errno );
635#endif/* MACOSX */
636
637 return osl_psz_removeFile( path );
638}
639
640static oslFileError oslDoMoveFile( const sal_Char* pszPath, const sal_Char* pszDestPath)
641{
642 oslFileError tErr = osl_psz_moveFile(pszPath,pszDestPath);
643 if ( tErr == osl_File_E_None )
644 {
645 return tErr;
646 }
647
648 if ( tErr != osl_File_E_XDEV )
649 {
650 return tErr;
651 }
652
653 tErr=osl_psz_copyFile(pszPath,pszDestPath);
654
655 if ( tErr != osl_File_E_None )
656 {
657 osl_psz_removeFile(pszDestPath);
658 return tErr;
659 }
660
661 tErr=osl_psz_removeFile(pszPath);
662
663 return tErr;
664}
665
666static oslFileError osl_psz_removeFile( const sal_Char* pszPath )
667{
668 int nRet=0;
669 struct stat aStat;
670
671 nRet = lstat_c(pszPath,&aStat);
672 if ( nRet < 0 )
673 {
674 nRet=errno;
675 return oslTranslateFileError(OSL_FET_ERROR, nRet);
676 }
677
678 if ( S_ISDIR(aStat.st_mode) )
679 {
680 return osl_File_E_ISDIR;
681 }
682
683 nRet = unlink(pszPath);
684 if ( nRet < 0 )
685 {
686 nRet=errno;
687 return oslTranslateFileError(OSL_FET_ERROR, nRet);
688 }
689
690 return osl_File_E_None;
691}
692
693static oslFileError osl_psz_moveFile(const sal_Char* pszPath, const sal_Char* pszDestPath)
694{
695 int nRet = 0;
696
697 nRet = rename(pszPath,pszDestPath);
698
699 if ( nRet < 0 )
700 {
701 nRet=errno;
702 return oslTranslateFileError(OSL_FET_ERROR, nRet);
703 }
704
705 return osl_File_E_None;
706}
707
708static oslFileError osl_psz_copyFile( const sal_Char* pszPath, const sal_Char* pszDestPath )
709{
710 time_t nAcTime=0;
711 time_t nModTime=0;
712 uid_t nUID=0;
713 gid_t nGID=0;
714 int nRet=0;
715 mode_t nMode=0;
716 struct stat aFileStat;
717 oslFileError tErr=osl_File_E_invalidError;
718 size_t nSourceSize=0;
719 int DestFileExists=1;
720
721 /* mfe: does the source file really exists? */
722 nRet = lstat_c(pszPath,&aFileStat);
723
724 if ( nRet < 0 )
725 {
726 nRet=errno;
727 return oslTranslateFileError(OSL_FET_ERROR, nRet);
728 }
729
730 /* mfe: we do only copy files here! */
731 if ( S_ISDIR(aFileStat.st_mode) )
732 {
733 return osl_File_E_ISDIR;
734 }
735
736 nSourceSize=(size_t)aFileStat.st_size;
737 nMode=aFileStat.st_mode;
738 nAcTime=aFileStat.st_atime;
739 nModTime=aFileStat.st_mtime;
740 nUID=aFileStat.st_uid;
741 nGID=aFileStat.st_gid;
742
743 nRet = stat_c(pszDestPath,&aFileStat);
744 if ( nRet < 0 )
745 {
746 nRet=errno;
747
748 if ( nRet == ENOENT )
749 {
750 DestFileExists=0;
751 }
752 }
753
754 /* mfe: the destination file must not be a directory! */
755 if ( nRet == 0 && S_ISDIR(aFileStat.st_mode) )
756 {
757 return osl_File_E_ISDIR;
758 }
759 else
760 {
761 /* mfe: file does not exists or is no dir */
762 }
763
764 tErr = oslDoCopy(pszPath,pszDestPath,nMode,nSourceSize,DestFileExists);
765
766 if ( tErr != osl_File_E_None )
767 {
768 return tErr;
769 }
770
771 /*
772 * mfe: ignore return code
773 * since only the success of the copy is
774 * important
775 */
776 oslChangeFileModes(pszDestPath,nMode,nAcTime,nModTime,nUID,nGID);
777
778 return tErr;
779}
780
781#define TMP_DEST_FILE_EXTENSION ".osl-tmp"
782
783static oslFileError oslDoCopy(const sal_Char* pszSourceFileName, const sal_Char* pszDestFileName, mode_t nMode, size_t nSourceSize, int DestFileExists)
784{
785 int nRet=0;
786 sal_Char pszTmpDestFile[PATH_MAX];
787 size_t size_tmp_dest_buff = sizeof(pszTmpDestFile);
788
789 /* Quick fix for #106048, the whole copy file function seems
790 to be erroneous anyway and needs to be rewritten.
791 */
792 memset(pszTmpDestFile, 0, size_tmp_dest_buff);
793
794 if ( DestFileExists )
795 {
796 strncpy(pszTmpDestFile, pszDestFileName, size_tmp_dest_buff - 1);
797
798 if ((strlen(pszTmpDestFile) + strlen(TMP_DEST_FILE_EXTENSION)) >= size_tmp_dest_buff)
799 return osl_File_E_NAMETOOLONG;
800
801 strncat(pszTmpDestFile, TMP_DEST_FILE_EXTENSION, strlen(TMP_DEST_FILE_EXTENSION));
802
803 /* FIXME: what if pszTmpDestFile already exists? */
804 /* with getcanonical??? */
805 nRet=rename(pszDestFileName,pszTmpDestFile);
806 }
807
808 /* mfe: should be S_ISREG */
809 if ( !S_ISLNK(nMode) )
810 {
811 /* copy SourceFile to DestFile */
812 nRet = oslDoCopyFile(pszSourceFileName,pszDestFileName,nSourceSize, nMode);
813 }
814 /* mfe: OK redundant at the moment */
815 else if ( S_ISLNK(nMode) )
816 {
817 nRet = oslDoCopyLink(pszSourceFileName,pszDestFileName);
818 }
819 else
820 {
821 /* mfe: what to do here? */
822 nRet=ENOSYS;
823 }
824
825 if ( nRet > 0 && DestFileExists == 1 )
826 {
827 unlink(pszDestFileName);
828 rename(pszTmpDestFile,pszDestFileName);
829 }
830
831 if ( nRet > 0 )
832 {
833 return oslTranslateFileError(OSL_FET_ERROR, nRet);
834 }
835
836 if ( DestFileExists == 1 )
837 {
838 unlink(pszTmpDestFile);
839 }
840
841 return osl_File_E_None;
842}
843
844static oslFileError oslChangeFileModes( const sal_Char* pszFileName, mode_t nMode, time_t nAcTime, time_t nModTime, uid_t nUID, gid_t nGID)
845{
846 int nRet=0;
847 struct utimbuf aTimeBuffer;
848
849 nRet = chmod(pszFileName,nMode);
850 if ( nRet < 0 )
851 {
852 nRet=errno;
853 return oslTranslateFileError(OSL_FET_ERROR, nRet);
854 }
855
856 aTimeBuffer.actime=nAcTime;
857 aTimeBuffer.modtime=nModTime;
858 nRet=utime(pszFileName,&aTimeBuffer);
859 if ( nRet < 0 )
860 {
861 nRet=errno;
862 return oslTranslateFileError(OSL_FET_ERROR, nRet);
863 }
864
865 if ( nUID != getuid() )
866 {
867 nUID=getuid();
868 }
869
870 nRet=chown(pszFileName,nUID,nGID);
871 if ( nRet < 0 )
872 {
873 nRet=errno;
874
875 /* mfe: do not return an error here! */
876 /* return oslTranslateFileError(nRet);*/
877 }
878
879 return osl_File_E_None;
880}
881
882static int oslDoCopyLink(const sal_Char* pszSourceFileName, const sal_Char* pszDestFileName)
883{
884 int nRet=0;
885
886 /* mfe: if dest file is symbolic link remove the link and place the file instead (hro says so) */
887 /* mfe: if source is a link copy the link and not the file it points to (hro says so) */
888 sal_Char pszLinkContent[PATH_MAX+1];
889
890 pszLinkContent[0] = '\0';
891
892 nRet = readlink(pszSourceFileName,pszLinkContent,PATH_MAX);
893
894 if ( nRet < 0 )
895 {
896 nRet=errno;
897 return nRet;
898 }
899 else
900 pszLinkContent[ nRet ] = 0;
901
902 nRet = symlink(pszLinkContent,pszDestFileName);
903
904 if ( nRet < 0 )
905 {
906 nRet=errno;
907 return nRet;
908 }
909
910 return 0;
911}
912
913static int oslDoCopyFile(const sal_Char* pszSourceFileName, const sal_Char* pszDestFileName, size_t nSourceSize, mode_t mode)
914{
915 oslFileHandle SourceFileFH=0;
916 int DestFileFD=0;
917 int nRet=0;
918
919 if (osl_openFilePath(pszSourceFileName,
920 &SourceFileFH,
921 osl_File_OpenFlag_Read|osl_File_OpenFlag_NoLock|osl_File_OpenFlag_NoExcl) != osl_File_E_None)
922 {
923 // Let's hope errno is still set relevantly after osl_openFilePath...
924 nRet=errno;
925 return nRet;
926 }
927
928 DestFileFD=open(pszDestFileName, O_WRONLY | O_CREAT, mode);
929
930 if ( DestFileFD < 0 )
931 {
932 nRet=errno;
933 osl_closeFile(SourceFileFH);
934 return nRet;
935 }
936
937 size_t nRemains = nSourceSize;
938
939 if ( nRemains )
940 {
941 /* mmap has problems, try the direct streaming */
942 char pBuffer[0x7FFF];
943
944 do
945 {
946 size_t nToRead = std::min( sizeof(pBuffer), nRemains );
947 sal_uInt64 nRead;
948 sal_Bool succeeded;
949 if ( osl_readFile( SourceFileFH, pBuffer, nToRead, &nRead ) != osl_File_E_None || nRead > nToRead || nRead == 0 )
950 break;
951
952 succeeded = safeWrite( DestFileFD, pBuffer, nRead );
953 if ( !succeeded )
954 break;
955
956 // We know nRead <= nToRead, so it must fit in a size_t
957 nRemains -= (size_t) nRead;
958 }
959 while( nRemains );
960 }
961
962 if ( nRemains )
963 {
964 if ( errno )
965 nRet = errno;
966 else
967 nRet = ENOSPC;
968 }
969
970 osl_closeFile( SourceFileFH );
971 if ( close( DestFileFD ) == -1 && nRet == 0 )
972 nRet = errno;
973
974 return nRet;
975}
976
977/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
978