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 | |
60 | typedef 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 | |
75 | DirectoryItem_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 | } |
84 | DirectoryItem_Impl::~DirectoryItem_Impl() |
85 | { |
86 | if (m_ustrFilePath != 0) |
87 | rtl_uString_release(m_ustrFilePath); |
88 | } |
89 | |
90 | void * DirectoryItem_Impl::operator new(size_t n) |
91 | { |
92 | return rtl_allocateMemory(n); |
93 | } |
94 | void DirectoryItem_Impl::operator delete(void * p) |
95 | { |
96 | rtl_freeMemory(p); |
97 | } |
98 | |
99 | void DirectoryItem_Impl::acquire() |
100 | { |
101 | ++m_RefCount; |
102 | } |
103 | void DirectoryItem_Impl::release() |
104 | { |
105 | if (0 == --m_RefCount) |
106 | delete this; |
107 | } |
108 | |
109 | oslFileType 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 | |
134 | static oslFileError osl_psz_createDirectory(const sal_Char* pszPath); |
135 | static oslFileError osl_psz_removeDirectory(const sal_Char* pszPath); |
136 | |
137 | oslFileError 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 | |
228 | oslFileError 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 | |
266 | static 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 | |
282 | oslFileError 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 | |
349 | oslFileError 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 | |
377 | oslFileError 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 | |
387 | oslFileError 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 | |
397 | oslFileError 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 | |
417 | oslFileError 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 | |
437 | static 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 | |
453 | static 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 | |
468 | static 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 | |
481 | static 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 | |
501 | static 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 | |
535 | oslFileError 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 | |
556 | static oslFileError osl_psz_removeFile(const sal_Char* pszPath); |
557 | static oslFileError osl_psz_copyFile(const sal_Char* pszPath, const sal_Char* pszDestPath); |
558 | static oslFileError osl_psz_moveFile(const sal_Char* pszPath, const sal_Char* pszDestPath); |
559 | |
560 | static oslFileError oslDoCopy(const sal_Char* pszSourceFileName, const sal_Char* pszDestFileName, mode_t nMode, size_t nSourceSize, int DestFileExists); |
561 | static oslFileError oslChangeFileModes(const sal_Char* pszFileName, mode_t nMode, time_t nAcTime, time_t nModTime, uid_t nUID, gid_t nGID); |
562 | static int oslDoCopyLink(const sal_Char* pszSourceFileName, const sal_Char* pszDestFileName); |
563 | static int oslDoCopyFile(const sal_Char* pszSourceFileName, const sal_Char* pszDestFileName, size_t nSourceSize, mode_t mode); |
564 | static oslFileError oslDoMoveFile(const sal_Char* pszPath, const sal_Char* pszDestPath); |
565 | |
566 | oslFileError 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 | |
593 | oslFileError 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 | |
620 | oslFileError 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 | |
640 | static 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 | |
666 | static 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 | |
693 | static 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 | |
708 | static 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 | |
783 | static 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 | |
844 | static 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 | |
882 | static 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 | |
913 | static 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 | |