PageRenderTime 47ms CodeModel.GetById 12ms app.highlight 29ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/llmessage/llxfer_file.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 439 lines | 296 code | 80 blank | 63 comment | 37 complexity | d055321016fb8582061ecd6139916ade MD5 | raw file
  1/** 
  2 * @file llxfer_file.cpp
  3 * @brief implementation of LLXfer_File class for a single xfer (file)
  4 *
  5 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "linden_common.h"
 28
 29#if !LL_WINDOWS
 30#include <errno.h>
 31#include <unistd.h>
 32#endif
 33
 34#include "llxfer_file.h"
 35#include "lluuid.h"
 36#include "llerror.h"
 37#include "llmath.h"
 38#include "llstring.h"
 39#include "lldir.h"
 40
 41// size of chunks read from/written to disk
 42const U32 LL_MAX_XFER_FILE_BUFFER = 65536;
 43
 44// local function to copy a file
 45S32 copy_file(const std::string& from, const std::string& to);
 46
 47///////////////////////////////////////////////////////////
 48
 49LLXfer_File::LLXfer_File (S32 chunk_size)
 50: LLXfer(chunk_size)
 51{
 52	init(LLStringUtil::null, FALSE, chunk_size);
 53}
 54
 55LLXfer_File::LLXfer_File (const std::string& local_filename, BOOL delete_local_on_completion, S32 chunk_size)
 56: LLXfer(chunk_size)
 57{
 58	init(local_filename, delete_local_on_completion, chunk_size);
 59}
 60
 61///////////////////////////////////////////////////////////
 62
 63LLXfer_File::~LLXfer_File ()
 64{
 65	cleanup();
 66}
 67
 68///////////////////////////////////////////////////////////
 69
 70void LLXfer_File::init (const std::string& local_filename, BOOL delete_local_on_completion, S32 chunk_size)
 71{
 72
 73	mFp = NULL;
 74	mLocalFilename.clear();
 75	mRemoteFilename.clear();
 76	mRemotePath = LL_PATH_NONE;
 77	mTempFilename.clear();
 78	mDeleteLocalOnCompletion = FALSE;
 79	mDeleteRemoteOnCompletion = FALSE;
 80
 81	if (!local_filename.empty())
 82	{
 83		mLocalFilename =  local_filename.substr(0,LL_MAX_PATH-1);
 84
 85		// You can only automatically delete .tmp file as a safeguard against nasty messages.
 86		std::string exten = mLocalFilename.substr(mLocalFilename.length()-4, 4);
 87		mDeleteLocalOnCompletion = (delete_local_on_completion && exten == ".tmp");
 88	}
 89}
 90	
 91///////////////////////////////////////////////////////////
 92
 93void LLXfer_File::cleanup ()
 94{
 95	if (mFp)
 96	{
 97		fclose(mFp);
 98		mFp = NULL;
 99	}
100
101	LLFile::remove(mTempFilename);
102
103	if (mDeleteLocalOnCompletion)
104	{
105		lldebugs << "Removing file: " << mLocalFilename << llendl;
106		LLFile::remove(mLocalFilename);
107	}
108	else
109	{
110		lldebugs << "Keeping local file: " << mLocalFilename << llendl;
111	}
112
113	LLXfer::cleanup();
114}
115
116///////////////////////////////////////////////////////////
117
118S32 LLXfer_File::initializeRequest(U64 xfer_id,
119				   const std::string& local_filename,
120				   const std::string& remote_filename,
121				   ELLPath remote_path,
122				   const LLHost& remote_host,
123				   BOOL delete_remote_on_completion,
124				   void (*callback)(void**,S32,LLExtStat),
125				   void** user_data)
126{
127 	S32 retval = 0;  // presume success
128	
129	mID = xfer_id;
130	mLocalFilename = local_filename;
131	mRemoteFilename = remote_filename;
132	mRemotePath = remote_path;
133	mRemoteHost = remote_host;
134	mDeleteRemoteOnCompletion = delete_remote_on_completion;
135
136	mTempFilename = gDirUtilp->getTempFilename();
137
138	mCallback = callback;
139	mCallbackDataHandle = user_data;
140	mCallbackResult = LL_ERR_NOERR;
141
142	llinfos << "Requesting xfer from " << remote_host << " for file: " << mLocalFilename << llendl;
143
144	if (mBuffer)
145	{
146		delete(mBuffer);
147		mBuffer = NULL;
148	}
149
150	mBuffer = new char[LL_MAX_XFER_FILE_BUFFER];
151	mBufferLength = 0;
152
153	mPacketNum = 0;
154
155 	mStatus = e_LL_XFER_PENDING;
156	return retval;
157}
158
159///////////////////////////////////////////////////////////
160
161S32 LLXfer_File::startDownload()
162{
163 	S32 retval = 0;  // presume success
164	mFp = LLFile::fopen(mTempFilename,"w+b");		/* Flawfinder : ignore */
165	if (mFp)
166	{
167		fclose(mFp);
168		mFp = NULL;
169
170		gMessageSystem->newMessageFast(_PREHASH_RequestXfer);
171		gMessageSystem->nextBlockFast(_PREHASH_XferID);
172		gMessageSystem->addU64Fast(_PREHASH_ID, mID);
173		gMessageSystem->addStringFast(_PREHASH_Filename, mRemoteFilename);
174		gMessageSystem->addU8("FilePath", (U8) mRemotePath);
175		gMessageSystem->addBOOL("DeleteOnCompletion", mDeleteRemoteOnCompletion);
176		gMessageSystem->addBOOL("UseBigPackets", BOOL(mChunkSize == LL_XFER_LARGE_PAYLOAD));
177		gMessageSystem->addUUIDFast(_PREHASH_VFileID, LLUUID::null);
178		gMessageSystem->addS16Fast(_PREHASH_VFileType, -1);
179	
180		gMessageSystem->sendReliable(mRemoteHost);		
181		mStatus = e_LL_XFER_IN_PROGRESS;
182	}
183	else
184	{
185		llwarns << "Couldn't create file to be received!" << llendl;
186		retval = -1;
187	}
188
189	return (retval);
190}
191
192///////////////////////////////////////////////////////////
193
194S32 LLXfer_File::startSend (U64 xfer_id, const LLHost &remote_host)
195{
196	S32 retval = LL_ERR_NOERR;  // presume success
197	
198    mRemoteHost = remote_host;
199	mID = xfer_id;
200   	mPacketNum = -1;
201
202//	cout << "Sending file: " << mLocalFilename << endl;
203
204	delete [] mBuffer;
205	mBuffer = new char[LL_MAX_XFER_FILE_BUFFER];
206
207	mBufferLength = 0;
208	mBufferStartOffset = 0;	
209	
210	mFp = LLFile::fopen(mLocalFilename,"rb");		/* Flawfinder : ignore */
211	if (mFp)
212	{
213		fseek(mFp,0,SEEK_END);
214	
215		S32 file_size = ftell(mFp);
216		if (file_size <= 0)
217		{
218			return LL_ERR_FILE_EMPTY;
219		}
220		setXferSize(file_size);
221
222		fseek(mFp,0,SEEK_SET);
223	}
224	else
225	{
226		llinfos << "Warning: " << mLocalFilename << " not found." << llendl;
227		return (LL_ERR_FILE_NOT_FOUND);
228	}
229
230	mStatus = e_LL_XFER_PENDING;
231
232	return (retval);
233}
234
235///////////////////////////////////////////////////////////
236
237S32 LLXfer_File::getMaxBufferSize ()
238{
239	return(LL_MAX_XFER_FILE_BUFFER);
240}
241
242///////////////////////////////////////////////////////////
243
244S32 LLXfer_File::suck(S32 start_position)
245{
246	S32 retval = 0;
247
248	if (mFp)
249	{
250		// grab a buffer from the right place in the file
251		fseek (mFp,start_position,SEEK_SET);
252		
253		mBufferLength = (U32)fread(mBuffer,1,LL_MAX_XFER_FILE_BUFFER,mFp);
254		mBufferStartOffset = start_position;
255			
256		if (feof(mFp))
257		{
258			mBufferContainsEOF = TRUE;
259		}
260		else
261		{
262			mBufferContainsEOF = FALSE;
263		}		
264	}
265	else
266	{
267		retval = -1;
268	}
269
270	return (retval);
271}
272
273///////////////////////////////////////////////////////////
274
275S32 LLXfer_File::flush()
276{
277	S32 retval = 0;
278	if (mBufferLength)
279	{
280		if (mFp)
281		{
282			llerrs << "Overwriting open file pointer!" << llendl;
283		}
284		mFp = LLFile::fopen(mTempFilename,"a+b");		/* Flawfinder : ignore */
285
286		if (mFp)
287		{
288			if (fwrite(mBuffer,1,mBufferLength,mFp) != mBufferLength)
289			{
290				llwarns << "Short write" << llendl;
291			}
292			
293//			llinfos << "******* wrote " << mBufferLength << " bytes of file xfer" << llendl;
294			fclose(mFp);
295			mFp = NULL;
296			
297			mBufferLength = 0;
298		}
299		else
300		{
301			llwarns << "LLXfer_File::flush() unable to open " << mTempFilename << " for writing!" << llendl;
302			retval = LL_ERR_CANNOT_OPEN_FILE;
303		}
304	}
305	return (retval);
306}
307
308///////////////////////////////////////////////////////////
309
310S32 LLXfer_File::processEOF()
311{
312	S32 retval = 0;
313	mStatus = e_LL_XFER_COMPLETE;
314
315	S32 flushval = flush();
316
317	// If we have no other errors, our error becomes the error generated by
318	// flush.
319	if (!mCallbackResult)
320	{
321		mCallbackResult = flushval;
322	}
323
324	LLFile::remove(mLocalFilename);
325
326	if (!mCallbackResult)
327	{
328		if (LLFile::rename(mTempFilename,mLocalFilename))
329		{
330#if !LL_WINDOWS
331			S32 error_number = errno;
332			llinfos << "Rename failure (" << error_number << ") - "
333					<< mTempFilename << " to " << mLocalFilename << llendl;
334			if(EXDEV == error_number)
335			{
336				if(copy_file(mTempFilename, mLocalFilename) == 0)
337				{
338					llinfos << "Rename across mounts; copying+unlinking the file instead." << llendl;
339					unlink(mTempFilename.c_str());
340				}
341				else
342				{
343					llwarns << "Copy failure - " << mTempFilename << " to "
344							<< mLocalFilename << llendl;
345				}
346			}
347			else
348			{
349				//LLFILE* fp = LLFile::fopen(mTempFilename, "r");
350				//llwarns << "File " << mTempFilename << " does "
351				//		<< (!fp ? "not" : "" ) << " exit." << llendl;
352				//if(fp) fclose(fp);
353				//fp = LLFile::fopen(mLocalFilename, "r");
354				//llwarns << "File " << mLocalFilename << " does "
355				//		<< (!fp ? "not" : "" ) << " exit." << llendl;
356				//if(fp) fclose(fp);
357				llwarns << "Rename fatally failed, can only handle EXDEV ("
358						<< EXDEV << ")" << llendl;
359			}
360#else
361			llwarns << "Rename failure - " << mTempFilename << " to "
362					<< mLocalFilename << llendl;
363#endif
364		}
365	}
366
367	if (mFp)
368	{
369		fclose(mFp);
370		mFp = NULL;
371	}
372
373	retval = LLXfer::processEOF();
374
375	return(retval);
376}
377
378///////////////////////////////////////////////////////////
379
380BOOL LLXfer_File::matchesLocalFilename(const std::string& filename) 
381{
382	return (filename == mLocalFilename);
383}
384
385///////////////////////////////////////////////////////////
386
387BOOL LLXfer_File::matchesRemoteFilename(const std::string& filename, ELLPath remote_path) 
388{
389	return ((filename == mRemoteFilename) && (remote_path == mRemotePath));
390}
391
392
393///////////////////////////////////////////////////////////
394
395std::string LLXfer_File::getFileName() 
396{
397	return mLocalFilename;
398}
399
400///////////////////////////////////////////////////////////
401
402// hacky - doesn't matter what this is
403// as long as it's different from the other classes
404U32 LLXfer_File::getXferTypeTag()
405{
406	return LLXfer::XFER_FILE;
407}
408
409///////////////////////////////////////////////////////////
410
411#if !LL_WINDOWS
412
413// This is really close to, but not quite a general purpose copy
414// function. It does not really spam enough information, but is useful
415// for this cpp file, because this should never be called in a
416// production environment.
417S32 copy_file(const std::string& from, const std::string& to)
418{
419	S32 rv = 0;
420	LLFILE* in = LLFile::fopen(from, "rb");	/*Flawfinder: ignore*/
421	LLFILE* out = LLFile::fopen(to, "wb");	/*Flawfinder: ignore*/
422	if(in && out)
423	{
424		S32 read = 0;
425		const S32 COPY_BUFFER_SIZE = 16384;
426		U8 buffer[COPY_BUFFER_SIZE];
427		while(((read = fread(buffer, 1, sizeof(buffer), in)) > 0)
428			  && (fwrite(buffer, 1, read, out) == (U32)read));		/* Flawfinder : ignore */
429		if(ferror(in) || ferror(out)) rv = -2;
430	}
431	else
432	{
433		rv = -1;
434	}
435	if(in) fclose(in);
436	if(out) fclose(out);
437	return rv;
438}
439#endif