Line data Source code
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 : #if defined WNT
21 : #include <windows.h>
22 : #include <io.h>
23 : #elif defined UNX
24 : #include <fcntl.h>
25 : #include <unistd.h>
26 : #include <sys/stat.h>
27 : #endif
28 :
29 : #include <ctype.h>
30 : #include <errno.h>
31 : #include <stdlib.h>
32 : #include <string.h>
33 :
34 : #include <stdio.h>
35 : #include "comdep.hxx"
36 : #include <tools/fsys.hxx>
37 : #include <tools/stream.hxx>
38 : #include <osl/file.hxx>
39 :
40 : using namespace ::osl;
41 :
42 630 : FileCopier::FileCopier( const DirEntry& rSource, const DirEntry& rTarget ) :
43 : aSource ( rSource ),
44 : aTarget ( rTarget ),
45 : nBytesTotal ( 0 ),
46 : nBytesCopied( 0 ),
47 : nBlockSize ( 4096 ),
48 630 : pImp ( new FileCopier_Impl )
49 : {
50 630 : }
51 :
52 0 : FileCopier::FileCopier( const FileCopier& rCopier ) :
53 : aSource ( rCopier.aSource ),
54 : aTarget ( rCopier.aTarget ),
55 : nBytesTotal ( 0 ),
56 : nBytesCopied ( 0 ),
57 : aProgressLink ( rCopier.aProgressLink ),
58 : nBlockSize ( 4096 ),
59 0 : pImp ( new FileCopier_Impl )
60 : {
61 0 : }
62 :
63 1260 : FileCopier::~FileCopier()
64 : {
65 630 : delete pImp;
66 630 : }
67 :
68 0 : FileCopier& FileCopier::operator = ( const FileCopier &rCopier )
69 : {
70 0 : aSource = rCopier.aSource;
71 0 : aTarget = rCopier.aTarget;
72 0 : nBytesTotal = rCopier.nBytesTotal;
73 0 : nBytesCopied = rCopier.nBytesCopied;
74 0 : nBytesCopied = rCopier.nBytesCopied;
75 0 : nBlockSize = rCopier.nBlockSize;
76 0 : aProgressLink = rCopier.aProgressLink;
77 0 : *pImp = *(rCopier.pImp);
78 0 : return *this;
79 : }
80 :
81 1787 : sal_Bool FileCopier::Progress()
82 : {
83 1787 : if ( !aProgressLink )
84 1787 : return sal_True;
85 : else
86 : {
87 0 : if ( aProgressLink.Call( this ) )
88 0 : return sal_True;
89 0 : return ( 0 == Error( ERRCODE_ABORT, 0, 0 ) );
90 : }
91 : }
92 :
93 0 : ErrCode FileCopier::Error( ErrCode eErr, const DirEntry* pSource, const DirEntry* pTarget )
94 : {
95 : // No error or no error handler?
96 0 : if ( !eErr || !pImp->aErrorLink )
97 : // => keep error
98 0 : return eErr;
99 :
100 : // otherwise request from ErrorHandler
101 0 : pImp->pErrSource = pSource;
102 0 : pImp->pErrTarget = pTarget;
103 0 : pImp->eErr = eErr;
104 0 : ErrCode eRet = (ErrCode) pImp->aErrorLink.Call( this );
105 0 : pImp->pErrSource = 0;
106 0 : pImp->pErrTarget = 0;
107 0 : return eRet;
108 : }
109 :
110 630 : FSysError FileCopier::DoCopy_Impl( const DirEntry &rSource, const DirEntry &rTarget )
111 : {
112 630 : FSysError eRet = FSYS_ERR_OK;
113 630 : ErrCode eWarn = FSYS_ERR_OK;
114 :
115 : // shorten target name if necessary
116 630 : DirEntry aTgt;
117 630 : aTgt = rTarget;
118 :
119 : // source is directory?
120 630 : FileStat aSourceFileStat( rSource );
121 630 : if ( aSourceFileStat.IsKind( FSYS_KIND_DIR ) )
122 : {
123 : // recursive copy
124 0 : eRet = Error( aTgt.MakeDir() ? FSYS_ERR_OK : FSYS_ERR_UNKNOWN, 0, &aTgt );
125 0 : Dir aSourceDir( rSource, FSYS_KIND_DIR|FSYS_KIND_FILE );
126 0 : for ( sal_uInt16 n = 0; ERRCODE_TOERROR(eRet) == FSYS_ERR_OK && n < aSourceDir.Count(); ++n )
127 : {
128 0 : const DirEntry &rSubSource = aSourceDir[n];
129 0 : DirEntryFlag eFlag = rSubSource.GetFlag();
130 0 : if ( eFlag != FSYS_FLAG_CURRENT && eFlag != FSYS_FLAG_PARENT )
131 : {
132 0 : DirEntry aSubTarget( aTgt );
133 0 : aSubTarget += rSubSource.GetName();
134 0 : eRet = DoCopy_Impl( rSubSource, aSubTarget );
135 0 : if ( eRet && !eWarn )
136 0 : eWarn = eRet;
137 : }
138 0 : }
139 : }
140 630 : else if ( aSourceFileStat.IsKind(FSYS_KIND_FILE) )
141 : {
142 630 : if ( ( FSYS_ACTION_KEEP_EXISTING == ( pImp->nActions & FSYS_ACTION_KEEP_EXISTING ) ) &&
143 0 : aTgt.Exists() )
144 : {
145 : // Do not overwrite existing file in target folder.
146 0 : return ERRCODE_NONE;
147 : }
148 :
149 : // copy file
150 630 : nBytesCopied = 0;
151 630 : nBytesTotal = FileStat( rSource ).GetSize();
152 :
153 630 : ::rtl::OUString aFileName;
154 630 : FileBase::getFileURLFromSystemPath( ::rtl::OUString(rSource.GetFull()), aFileName );
155 630 : SvFileStream aSrc( aFileName, STREAM_READ|STREAM_NOCREATE|STREAM_SHARE_DENYNONE );
156 :
157 630 : if ( !aSrc.GetError() )
158 : {
159 : #ifdef UNX
160 : struct stat buf;
161 630 : if ( fstat( aSrc.GetFileHandle(), &buf ) == -1 )
162 0 : eRet = Error( FSYS_ERR_ACCESSDENIED, 0, &aTgt );
163 : #endif
164 630 : ::rtl::OUString aTargetFileName;
165 630 : FileBase::getFileURLFromSystemPath( ::rtl::OUString(aTgt.GetFull()), aTargetFileName );
166 :
167 630 : SvFileStream aTargetStream( aTargetFileName, STREAM_WRITE | STREAM_TRUNC | STREAM_SHARE_DENYWRITE );
168 630 : if ( !aTargetStream.GetError() )
169 : {
170 : #ifdef UNX
171 630 : if ( fchmod( aTargetStream.GetFileHandle(), buf.st_mode ) == -1 )
172 0 : eRet = Error( FSYS_ERR_ACCESSDENIED, 0, &aTgt );
173 : #endif
174 630 : size_t nAllocSize = 0, nSize = 0;
175 630 : char *pBuf = 0;
176 2417 : while ( Progress() && nSize == nAllocSize && eRet == FSYS_ERR_OK )
177 : {
178 : // adjust the block-size
179 1157 : if ( nBlockSize > nAllocSize )
180 : {
181 630 : delete[] pBuf;
182 630 : nAllocSize = nBlockSize;
183 630 : pBuf = new char[nAllocSize];
184 : }
185 :
186 : // copy one block
187 1157 : nSize = aSrc.Read( pBuf, nBlockSize );
188 1157 : aTargetStream.Write( pBuf, nSize );
189 1157 : if ( aTargetStream.GetError() )
190 0 : eRet = Error( aTargetStream.GetError(), 0, &aTgt );
191 :
192 : // adjust counters
193 1157 : nBytesCopied += nSize;
194 1157 : if ( nBytesCopied > nBytesTotal )
195 0 : nBytesTotal = nBytesCopied;
196 : }
197 630 : delete[] pBuf;
198 : }
199 : else
200 0 : eRet = Error( aTargetStream.GetError(), 0, &aTgt );
201 :
202 630 : aTargetStream.Close();
203 :
204 : // remove incomplete file
205 630 : if ( nBytesCopied != nBytesTotal )
206 : {
207 0 : aTgt.Kill();
208 630 : }
209 : }
210 : else
211 0 : eRet = Error( aSrc.GetError(), &rSource, 0 );
212 : }
213 0 : else if ( aSourceFileStat.IsKind(FSYS_KIND_NONE) )
214 0 : eRet = Error( ERRCODE_IO_NOTEXISTS, &rSource, 0 );
215 : else
216 0 : eRet = Error( ERRCODE_IO_NOTSUPPORTED, &rSource, 0 );
217 :
218 : #ifdef WNT
219 : // Set LastWriteTime and Attributes of the target identical with the source
220 :
221 : if ( FSYS_ERR_OK == ERRCODE_TOERROR(eRet) )
222 : {
223 : WIN32_FIND_DATA fdSource;
224 : rtl::OString aFullSource(rtl::OUStringToOString(aSource.GetFull(), osl_getThreadTextEncoding()));
225 : rtl::OString aFullTarget(rtl::OUStringToOString(aTgt.GetFull(), osl_getThreadTextEncoding()));
226 : HANDLE hFind = FindFirstFile( aFullSource.getStr() , &fdSource );
227 : if ( hFind != INVALID_HANDLE_VALUE )
228 : {
229 : FindClose( hFind );
230 :
231 : HANDLE hFile = CreateFile( aFullTarget.getStr(), GENERIC_WRITE,
232 : FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
233 : OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
234 :
235 : if ( hFile != INVALID_HANDLE_VALUE )
236 : {
237 : SetFileTime( hFile, NULL, NULL, &fdSource.ftLastWriteTime );
238 : CloseHandle( hFile );
239 : }
240 :
241 : SetFileAttributes( aFullTarget.getStr(), fdSource.dwFileAttributes );
242 : }
243 : }
244 : #endif
245 : // Remove File/Dir upon Move if necessary
246 630 : if ( FSYS_ERR_OK == ERRCODE_TOERROR(eRet) && ( pImp->nActions & FSYS_ACTION_MOVE ) )
247 : {
248 0 : ErrCode eKillErr = Error( rSource.Kill() | ERRCODE_WARNING_MASK, &rSource, 0 );
249 0 : if ( eKillErr != ERRCODE_WARNING_MASK )
250 : {
251 0 : if ( rSource.Exists() )
252 : // Removal failed => remove copy
253 0 : aTgt.Kill( pImp->nActions );
254 0 : if ( !eWarn )
255 0 : eWarn = eKillErr;
256 : }
257 : }
258 :
259 630 : return !eRet ? eWarn : eRet;
260 : }
261 :
262 630 : FSysError FileCopier::Execute( FSysAction nActions )
263 : {
264 630 : return ExecuteExact( nActions );
265 : }
266 :
267 630 : FSysError FileCopier::ExecuteExact( FSysAction nActions, FSysExact eExact )
268 : {
269 630 : DirEntry aAbsSource = DirEntry( aSource);
270 630 : DirEntry aAbsTarget = DirEntry( aTarget );
271 630 : pImp->nActions = nActions;
272 :
273 : // check if both paths are accessible and source and target are different
274 630 : if ( !aAbsTarget.ToAbs() || !aAbsSource.ToAbs() || aAbsTarget == aAbsSource )
275 0 : return FSYS_ERR_ACCESSDENIED;
276 :
277 : // check if copy would be endless recursive into itself
278 630 : if ( FSYS_ACTION_RECURSIVE == ( nActions & FSYS_ACTION_RECURSIVE ) &&
279 0 : aAbsSource.Contains( aAbsTarget ) )
280 0 : return ERRCODE_IO_RECURSIVE;
281 :
282 : // target is directory?
283 2520 : if ( eExact == FSYS_NOTEXACT &&
284 1890 : FileStat( aAbsTarget ).IsKind(FSYS_KIND_DIR) && FileStat( aAbsSource ).IsKind(FSYS_KIND_FILE) )
285 : // append name of source
286 0 : aAbsTarget += aSource.GetName();
287 :
288 : // recursive copy
289 630 : return DoCopy_Impl( aAbsSource, aAbsTarget );
290 : }
291 :
292 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|