xref: /aoo41x/main/tools/source/fsys/filecopy.cxx (revision cdf0e10c)
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