xref: /trunk/main/tools/source/fsys/filecopy.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_tools.hxx"
30 
31 #if defined WNT
32 #ifndef _SVWIN_H
33 #include <io.h>
34 #include <tools/svwin.h>
35 #endif
36 
37 #elif defined(OS2)
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <share.h>
42 #include <io.h>
43 
44 #elif defined UNX
45 #include <fcntl.h>
46 #include <unistd.h>
47 #include <sys/stat.h>
48 
49 #endif
50 
51 #include <ctype.h>
52 #include <errno.h>
53 #include <stdlib.h>
54 #include <string.h>
55 
56 #include <stdio.h>
57 #include "comdep.hxx"
58 #include <tools/fsys.hxx>
59 #include <tools/stream.hxx>
60 #include <osl/file.hxx>
61 
62 using namespace ::osl;
63 
64 /*************************************************************************
65 |*
66 |*    FileCopier::FileCopier()
67 |*
68 |*    Beschreibung      FSYS.SDW
69 |*    Ersterstellung    MI 13.04.94
70 |*    Letzte Aenderung  MI 13.04.94
71 |*
72 *************************************************************************/
73 
74 FileCopier::FileCopier() :
75 
76     nBytesTotal ( 0 ),
77     nBytesCopied( 0 ),
78     nBlockSize  ( 4096 ),
79     pImp        ( new FileCopier_Impl )
80 
81 {
82 }
83 
84 // -----------------------------------------------------------------------
85 
86 FileCopier::FileCopier( const DirEntry& rSource, const DirEntry& rTarget ) :
87 
88     aSource     ( rSource ),
89     aTarget     ( rTarget ),
90     nBytesTotal ( 0 ),
91     nBytesCopied( 0 ),
92     nBlockSize  ( 4096 ),
93     pImp        ( new FileCopier_Impl )
94 
95 {
96 }
97 
98 // -----------------------------------------------------------------------
99 
100 FileCopier::FileCopier( const FileCopier& rCopier ) :
101 
102     aSource         ( rCopier.aSource ),
103     aTarget         ( rCopier.aTarget ),
104     nBytesTotal     ( 0 ),
105     nBytesCopied    ( 0 ),
106     aProgressLink   ( rCopier.aProgressLink ),
107     nBlockSize      ( 4096 ),
108     pImp            ( new FileCopier_Impl )
109 
110 {
111 }
112 
113 /*************************************************************************
114 |*
115 |*    FileCopier::~FileCopier()
116 |*
117 |*    Beschreibung      FSYS.SDW
118 |*    Ersterstellung    MI 13.04.94
119 |*    Letzte Aenderung  MI 13.04.94
120 |*
121 *************************************************************************/
122 
123 FileCopier::~FileCopier()
124 {
125     delete pImp;
126 }
127 
128 /*************************************************************************
129 |*
130 |*    FileCopier::operator =()
131 |*
132 |*    Beschreibung      FSYS.SDW
133 |*    Ersterstellung    MI 13.04.94
134 |*    Letzte Aenderung  MI 13.04.94
135 |*
136 *************************************************************************/
137 
138 FileCopier& FileCopier::operator = ( const FileCopier &rCopier )
139 {
140     aSource = rCopier.aSource;
141     aTarget = rCopier.aTarget;
142     nBytesTotal = rCopier.nBytesTotal;
143     nBytesCopied = rCopier.nBytesCopied;
144     nBytesCopied = rCopier.nBytesCopied;
145     nBlockSize = rCopier.nBlockSize;
146     aProgressLink = rCopier.aProgressLink;
147     *pImp = *(rCopier.pImp);
148     return *this;
149 }
150 
151 /*************************************************************************
152 |*
153 |*    FileCopier::Progress()
154 |*
155 |*    Beschreibung      FSYS.SDW
156 |*    Ersterstellung    MI 13.04.94
157 |*    Letzte Aenderung  MI 13.04.94
158 |*
159 *************************************************************************/
160 
161 sal_Bool FileCopier::Progress()
162 {
163     if ( !aProgressLink )
164         return sal_True;
165     else
166     {
167         if ( aProgressLink.Call( this ) )
168             return sal_True;
169         return ( 0 == Error( ERRCODE_ABORT, 0, 0 ) );
170     }
171 }
172 
173 //---------------------------------------------------------------------------
174 
175 ErrCode FileCopier::Error( ErrCode eErr, const DirEntry* pSource, const DirEntry* pTarget )
176 {
177     // kein Fehler oder kein ErrorHandler?
178     if ( !eErr || !pImp->aErrorLink )
179         // => Error beibehalten
180         return eErr;
181 
182     // sonst gesetzten ErrorHandler fragen
183     pImp->pErrSource = pSource;
184     pImp->pErrTarget = pTarget;
185     pImp->eErr = eErr;
186     ErrCode eRet = (ErrCode) pImp->aErrorLink.Call( this );
187     pImp->pErrSource = 0;
188     pImp->pErrTarget = 0;
189     return eRet;
190 }
191 
192 //---------------------------------------------------------------------------
193 
194 const DirEntry* FileCopier::GetErrorSource() const
195 {
196     return pImp->pErrSource;
197 }
198 
199 //---------------------------------------------------------------------------
200 
201 const DirEntry* FileCopier::GetErrorTarget() const
202 {
203     return pImp->pErrTarget;
204 }
205 
206 //---------------------------------------------------------------------------
207 
208 ErrCode FileCopier::GetError() const
209 {
210     return pImp->eErr;
211 }
212 
213 //---------------------------------------------------------------------------
214 
215 void FileCopier::SetErrorHdl( const Link &rLink )
216 {
217     pImp->aErrorLink = rLink;
218 }
219 
220 //---------------------------------------------------------------------------
221 
222 const Link& FileCopier::GetErrorHdl() const
223 {
224     return pImp->aErrorLink ;
225 }
226 
227 /*************************************************************************
228 |*
229 |*    FileCopier::Execute()
230 |*
231 |*    Beschreibung      FSYS.SDW
232 |*    Ersterstellung    MI 13.04.94
233 |*    Letzte Aenderung  PB 16.06.00
234 |*
235 *************************************************************************/
236 
237 FSysError FileCopier::DoCopy_Impl(
238     const DirEntry &rSource, const DirEntry &rTarget )
239 {
240     FSysError eRet = FSYS_ERR_OK;
241     ErrCode eWarn = FSYS_ERR_OK;
242 
243     // HPFS->FAT?
244     FSysPathStyle eSourceStyle = DirEntry::GetPathStyle( rSource.ImpGetTopPtr()->GetName() );
245     FSysPathStyle eTargetStyle = DirEntry::GetPathStyle( rTarget.ImpGetTopPtr()->GetName() );
246     sal_Bool bMakeShortNames = ( eSourceStyle == FSYS_STYLE_HPFS && eTargetStyle == FSYS_STYLE_FAT );
247 
248     // Zieldateiname ggf. kuerzen
249     DirEntry aTgt;
250     if ( bMakeShortNames )
251     {
252         aTgt = rTarget.GetPath();
253         aTgt.MakeShortName( rTarget.GetName() );
254     }
255     else
256         aTgt = rTarget;
257 
258     // kein Move wenn Namen gekuerzt werden muessten
259     if ( bMakeShortNames && FSYS_ACTION_MOVE == ( pImp->nActions & FSYS_ACTION_MOVE ) && aTgt != rTarget )
260         return ERRCODE_IO_NAMETOOLONG;
261 
262     // source is directory?
263     FileStat aSourceFileStat( rSource );
264     if ( aSourceFileStat.IsKind( FSYS_KIND_DIR ) )
265     {
266 #ifdef OS2
267         CHAR szSource[CCHMAXPATHCOMP];
268         HOBJECT hSourceObject;
269 
270         strcpy(szSource, ByteString(rSource.GetFull(), osl_getThreadTextEncoding()).GetBuffer());
271         hSourceObject = WinQueryObject(szSource);
272 
273         if ( hSourceObject )
274         {
275             PSZ  pszSourceName;
276             PSZ  pszTargetName;
277             CHAR szTarget[CCHMAXPATHCOMP];
278             HOBJECT hTargetObject;
279             HOBJECT hReturn = NULLHANDLE;
280 
281             strcpy(szTarget, ByteString(rTarget.GetFull(), osl_getThreadTextEncoding()).GetBuffer());
282             pszTargetName = strrchr(szTarget, '\\');
283             pszSourceName = strrchr(szSource, '\\');
284 
285             hTargetObject = WinQueryObject(szTarget);
286 
287             if ( hTargetObject )
288                 WinDestroyObject(hTargetObject);
289 
290             if ( pszTargetName && pszSourceName )
291             {
292                 *pszTargetName = '\0';
293                 pszSourceName++;
294                 pszTargetName++;
295 
296                 if(strcmp(pszSourceName, pszTargetName) == 0)
297                 {
298                     hTargetObject = WinQueryObject(szTarget);
299 
300                     if(pImp->nActions & FSYS_ACTION_MOVE)
301                     {
302                         hReturn = WinMoveObject(hSourceObject, hTargetObject, 0);
303                     }
304                     else
305                     {
306                         hReturn = WinCopyObject(hSourceObject, hTargetObject, 0);
307                     }
308                     if ( bMakeShortNames && aTarget.Exists() )
309                         aTarget.Kill();
310                     return hReturn ? FSYS_ERR_OK : FSYS_ERR_UNKNOWN;
311                 }
312             }
313         }
314 #endif
315         // recursive copy
316         eRet = Error( aTgt.MakeDir() ? FSYS_ERR_OK : FSYS_ERR_UNKNOWN, 0, &aTgt );
317         Dir aSourceDir( rSource, FSYS_KIND_DIR|FSYS_KIND_FILE );
318         for ( sal_uInt16 n = 0; ERRCODE_TOERROR(eRet) == FSYS_ERR_OK && n < aSourceDir.Count(); ++n )
319         {
320             const DirEntry &rSubSource = aSourceDir[n];
321             DirEntryFlag eFlag = rSubSource.GetFlag();
322             if ( eFlag != FSYS_FLAG_CURRENT && eFlag != FSYS_FLAG_PARENT )
323             {
324                 DirEntry aSubTarget( aTgt );
325                 aSubTarget += rSubSource.GetName();
326                 eRet = DoCopy_Impl( rSubSource, aSubTarget );
327                 if ( eRet && !eWarn )
328                 eWarn = eRet;
329             }
330         }
331     }
332     else if ( aSourceFileStat.IsKind(FSYS_KIND_FILE) )
333     {
334         if ( ( FSYS_ACTION_KEEP_EXISTING == ( pImp->nActions & FSYS_ACTION_KEEP_EXISTING ) ) &&
335              aTgt.Exists() )
336         {
337             // Do not overwrite existing file in target folder.
338             return ERRCODE_NONE;
339         }
340 
341         // copy file
342         nBytesCopied = 0;
343         nBytesTotal = FileStat( rSource ).GetSize();
344 
345         ::rtl::OUString aFileName;
346         FileBase::getFileURLFromSystemPath( ::rtl::OUString(rSource.GetFull()), aFileName );
347         SvFileStream aSrc( aFileName, STREAM_READ|STREAM_NOCREATE|STREAM_SHARE_DENYNONE );
348 
349         if ( !aSrc.GetError() )
350         {
351 #ifdef UNX
352             struct stat buf;
353             if ( fstat( aSrc.GetFileHandle(), &buf ) == -1 )
354                 eRet = Error( FSYS_ERR_ACCESSDENIED, 0, &aTgt );
355 #endif
356             ::rtl::OUString aTargetFileName;
357             FileBase::getFileURLFromSystemPath( ::rtl::OUString(aTgt.GetFull()), aTargetFileName );
358 
359             SvFileStream aTargetStream( aTargetFileName, STREAM_WRITE | STREAM_TRUNC | STREAM_SHARE_DENYWRITE );
360             if ( !aTargetStream.GetError() )
361             {
362 #ifdef UNX
363                 if ( fchmod( aTargetStream.GetFileHandle(), buf.st_mode ) == -1 )
364                     eRet = Error( FSYS_ERR_ACCESSDENIED, 0, &aTgt );
365 #endif
366                 size_t nAllocSize = 0, nSize = 0;
367                 char *pBuf = 0;
368                 while ( Progress() && nSize == nAllocSize && eRet == FSYS_ERR_OK )
369                 {
370                     // adjust the block-size
371                     if ( nBlockSize > nAllocSize )
372                     {
373                         delete[] pBuf;
374                         nAllocSize = nBlockSize;
375                         pBuf = new char[nAllocSize];
376                     }
377 
378                     // copy one block
379                     nSize = aSrc.Read( pBuf, nBlockSize );
380                     aTargetStream.Write( pBuf, nSize );
381                     if ( aTargetStream.GetError() )
382                         eRet = Error( aTargetStream.GetError(), 0, &aTgt );
383 
384                     // adjust counters
385                     nBytesCopied += nSize;
386                     if ( nBytesCopied > nBytesTotal )
387                         nBytesTotal = nBytesCopied;
388                 }
389                 delete[] pBuf;
390             }
391             else
392                 eRet = Error( aTargetStream.GetError(), 0, &aTgt );
393 
394             // unvollstaendiges File wieder loeschen
395             aTargetStream.Close();
396 
397             if ( nBytesCopied != nBytesTotal )
398             {
399                 aTgt.Kill();
400             }
401         }
402         else
403             eRet = Error( aSrc.GetError(), &rSource, 0 );
404     }
405     else if ( aSourceFileStat.IsKind(FSYS_KIND_NONE) )
406         eRet = Error( ERRCODE_IO_NOTEXISTS, &rSource, 0 );
407     else
408         eRet = Error( ERRCODE_IO_NOTSUPPORTED, &rSource, 0 );
409 
410 #ifdef WNT
411     // Set LastWriteTime and Attributes of the target identical with the source
412 
413     if ( FSYS_ERR_OK == ERRCODE_TOERROR(eRet) )
414     {
415         WIN32_FIND_DATA fdSource;
416         ByteString aFullSource(aSource.GetFull(), osl_getThreadTextEncoding());
417         ByteString aFullTarget(aTgt.GetFull(), osl_getThreadTextEncoding());
418         HANDLE  hFind = FindFirstFile( aFullSource.GetBuffer() , &fdSource );
419         if ( hFind != INVALID_HANDLE_VALUE )
420         {
421             FindClose( hFind );
422 
423             HANDLE hFile = CreateFile( aFullTarget.GetBuffer(), GENERIC_WRITE,
424                                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
425                                        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
426 
427             if ( hFile != INVALID_HANDLE_VALUE )
428             {
429                 SetFileTime( hFile, NULL, NULL, &fdSource.ftLastWriteTime );
430                 CloseHandle( hFile );
431             }
432 
433             SetFileAttributes( aFullTarget.GetBuffer(), fdSource.dwFileAttributes );
434         }
435     }
436 #endif
437     // bei Move ggf. das File/Dir loeschen
438     if ( FSYS_ERR_OK == ERRCODE_TOERROR(eRet) && ( pImp->nActions  & FSYS_ACTION_MOVE ) )
439     {
440         ErrCode eKillErr = Error( rSource.Kill() | ERRCODE_WARNING_MASK, &rSource, 0 );
441         if ( eKillErr != ERRCODE_WARNING_MASK )
442         {
443             if ( rSource.Exists() )
444                 // loeschen ging nicht => dann die Kopie wieder loeschen
445                 aTgt.Kill( pImp->nActions );
446             if ( !eWarn )
447                 eWarn = eKillErr;
448         }
449     }
450 
451     return !eRet ? eWarn : eRet;
452 }
453 
454 // -----------------------------------------------------------------------
455 
456 FSysError FileCopier::Execute( FSysAction nActions )
457 {
458     return ExecuteExact( nActions );
459 }
460 
461 // -----------------------------------------------------------------------
462 
463 FSysError FileCopier::ExecuteExact( FSysAction nActions, FSysExact eExact )
464 {
465     DirEntry aAbsSource = DirEntry( aSource);
466     DirEntry aAbsTarget = DirEntry( aTarget );
467     pImp->nActions = nActions;
468 
469     // check if both pathes are accessible and source and target are different
470     if ( !aAbsTarget.ToAbs() || !aAbsSource.ToAbs() || aAbsTarget == aAbsSource )
471         return FSYS_ERR_ACCESSDENIED;
472 
473     // check if copy would be endless recursive into itself
474     if ( FSYS_ACTION_RECURSIVE == ( nActions & FSYS_ACTION_RECURSIVE ) &&
475          aAbsSource.Contains( aAbsTarget ) )
476         return ERRCODE_IO_RECURSIVE;
477 
478     // target is directory?
479     if ( eExact == FSYS_NOTEXACT &&
480          FileStat( aAbsTarget ).IsKind(FSYS_KIND_DIR) && FileStat( aAbsSource ).IsKind(FSYS_KIND_FILE) )
481         // append name of source
482         aAbsTarget += aSource.GetName();
483 
484     // recursive copy
485     return DoCopy_Impl( aAbsSource, aAbsTarget );
486 }
487