source: box/trunk/lib/common/FileStream.cpp @ 3065

Revision 3065, 10.8 KB checked in by chris, 4 months ago (diff)

Log messages on failure to read, write, stat, seek and close files, with the filename.

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    FileStream.cpp
5//              Purpose: IOStream interface to files
6//              Created: 2003/07/31
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11#include "FileStream.h"
12#include "CommonException.h"
13#include "Logging.h"
14
15#include <errno.h>
16
17#include "MemLeakFindOn.h"
18
19// --------------------------------------------------------------------------
20//
21// Function
22//              Name:    FileStream::FileStream(const char *, int, int)
23//              Purpose: Constructor, opens file
24//              Created: 2003/07/31
25//
26// --------------------------------------------------------------------------
27FileStream::FileStream(const std::string& rFilename, int flags, int mode)
28#ifdef WIN32
29        : mOSFileHandle(::openfile(rFilename.c_str(), flags, mode)),
30#else
31        : mOSFileHandle(::open(rFilename.c_str(), flags, mode)),
32#endif
33          mIsEOF(false),
34          mFileName(rFilename)
35{
36        AfterOpen();
37}
38
39// --------------------------------------------------------------------------
40//
41// Function
42//              Name:    FileStream::FileStream(const char *, int, int)
43//              Purpose: Alternative constructor, takes a const char *,
44//                       avoids const strings being interpreted as handles!
45//              Created: 2003/07/31
46//
47// --------------------------------------------------------------------------
48FileStream::FileStream(const char *pFilename, int flags, int mode)
49#ifdef WIN32
50        : mOSFileHandle(::openfile(pFilename, flags, mode)),
51#else
52        : mOSFileHandle(::open(pFilename, flags, mode)),
53#endif
54          mIsEOF(false),
55          mFileName(pFilename)
56{
57        AfterOpen();
58}
59
60void FileStream::AfterOpen()
61{
62#ifdef WIN32
63        if(mOSFileHandle == INVALID_HANDLE_VALUE)
64#else
65        if(mOSFileHandle < 0)
66#endif
67        {
68                MEMLEAKFINDER_NOT_A_LEAK(this);
69
70                #ifdef WIN32
71                BOX_LOG_WIN_WARNING_NUMBER("Failed to open file: " <<
72                        mFileName, winerrno);
73                #else
74                BOX_LOG_SYS_WARNING("Failed to open file: " <<
75                        mFileName);
76                #endif
77
78                if(errno == EACCES)
79                {
80                        THROW_EXCEPTION(CommonException, AccessDenied)
81                }
82                else
83                {
84                        THROW_EXCEPTION(CommonException, OSFileOpenError)
85                }
86        }
87}
88
89
90// --------------------------------------------------------------------------
91//
92// Function
93//              Name:    FileStream::FileStream(tOSFileHandle)
94//              Purpose: Constructor, using existing file descriptor
95//              Created: 2003/08/28
96//
97// --------------------------------------------------------------------------
98FileStream::FileStream(tOSFileHandle FileDescriptor)
99        : mOSFileHandle(FileDescriptor),
100          mIsEOF(false),
101          mFileName("HANDLE")
102{
103#ifdef WIN32
104        if(mOSFileHandle == INVALID_HANDLE_VALUE)
105#else
106        if(mOSFileHandle < 0)
107#endif
108        {
109                MEMLEAKFINDER_NOT_A_LEAK(this);
110                BOX_ERROR("FileStream: called with invalid file handle");
111                THROW_EXCEPTION(CommonException, OSFileOpenError)
112        }
113}
114
115#if 0
116// --------------------------------------------------------------------------
117//
118// Function
119//              Name:    FileStream::FileStream(const FileStream &)
120//              Purpose: Copy constructor, creates a duplicate of the file handle
121//              Created: 2003/07/31
122//
123// --------------------------------------------------------------------------
124FileStream::FileStream(const FileStream &rToCopy)
125        : mOSFileHandle(::dup(rToCopy.mOSFileHandle)),
126          mIsEOF(rToCopy.mIsEOF)
127{
128#ifdef WIN32
129        if(mOSFileHandle == INVALID_HANDLE_VALUE)
130#else
131        if(mOSFileHandle < 0)
132#endif
133        {
134                MEMLEAKFINDER_NOT_A_LEAK(this);
135                BOX_ERROR("FileStream: copying unopened file");
136                THROW_EXCEPTION(CommonException, OSFileOpenError)
137        }
138}
139#endif // 0
140
141// --------------------------------------------------------------------------
142//
143// Function
144//              Name:    FileStream::~FileStream()
145//              Purpose: Destructor, closes file
146//              Created: 2003/07/31
147//
148// --------------------------------------------------------------------------
149FileStream::~FileStream()
150{
151        if(mOSFileHandle != INVALID_FILE)
152        {
153                Close();
154        }
155}
156
157// --------------------------------------------------------------------------
158//
159// Function
160//              Name:    FileStream::Read(void *, int)
161//              Purpose: Reads bytes from the file
162//              Created: 2003/07/31
163//
164// --------------------------------------------------------------------------
165int FileStream::Read(void *pBuffer, int NBytes, int Timeout)
166{
167        if(mOSFileHandle == INVALID_FILE) 
168        {
169                THROW_EXCEPTION(CommonException, FileClosed)
170        }
171
172#ifdef WIN32
173        int r;
174        DWORD numBytesRead = 0;
175        BOOL valid = ReadFile(
176                this->mOSFileHandle,
177                pBuffer,
178                NBytes,
179                &numBytesRead,
180                NULL
181                );
182
183        if(valid)
184        {
185                r = numBytesRead;
186        }
187        else if(GetLastError() == ERROR_BROKEN_PIPE)
188        {
189                r = 0;
190        }
191        else
192        {
193                THROW_WIN_FILE_ERROR("Failed to read from file", mFileName,
194                        CommonException, OSFileReadError);
195        }
196
197        if(r == -1)
198        {
199                THROW_EXCEPTION(CommonException, OSFileReadError)
200        }
201#else
202        int r = ::read(mOSFileHandle, pBuffer, NBytes);
203        if(r == -1)
204        {
205                THROW_SYS_FILE_ERROR("Failed to read from file", mFileName,
206                        CommonException, OSFileReadError);
207        }
208#endif
209
210        if(r == 0)
211        {
212                mIsEOF = true;
213        }
214       
215        return r;
216}
217
218
219// --------------------------------------------------------------------------
220//
221// Function
222//              Name:    FileStream::BytesLeftToRead()
223//              Purpose: Returns number of bytes to read (may not be most efficient function ever)
224//              Created: 2003/08/28
225//
226// --------------------------------------------------------------------------
227IOStream::pos_type FileStream::BytesLeftToRead()
228{
229        EMU_STRUCT_STAT st;
230        if(EMU_FSTAT(mOSFileHandle, &st) != 0)
231        {
232                BOX_LOG_SYS_ERROR(BOX_FILE_MESSAGE("Failed to stat file", mFileName));
233        }
234       
235        return st.st_size - GetPosition();
236}
237
238
239// --------------------------------------------------------------------------
240//
241// Function
242//              Name:    FileStream::Write(void *, int)
243//              Purpose: Writes bytes to the file
244//              Created: 2003/07/31
245//
246// --------------------------------------------------------------------------
247void FileStream::Write(const void *pBuffer, int NBytes)
248{
249        if(mOSFileHandle == INVALID_FILE) 
250        {
251                THROW_EXCEPTION(CommonException, FileClosed)
252        }
253
254#ifdef WIN32
255        DWORD numBytesWritten = 0;
256        BOOL res = WriteFile(
257                this->mOSFileHandle,
258                pBuffer,
259                NBytes,
260                &numBytesWritten,
261                NULL
262                );
263
264        if ((res == 0) || (numBytesWritten != (DWORD)NBytes))
265        {
266                THROW_WIN_FILE_ERROR("Failed to write to file", mFileName,
267                        CommonException, OSFileWriteError);
268        }
269#else
270        if(::write(mOSFileHandle, pBuffer, NBytes) != NBytes)
271        {
272                THROW_SYS_FILE_ERROR("Failed to write to file", mFileName,
273                        CommonException, OSFileWriteError);
274        }
275#endif
276}
277
278
279// --------------------------------------------------------------------------
280//
281// Function
282//              Name:    FileStream::GetPosition()
283//              Purpose: Get position in stream
284//              Created: 2003/08/21
285//
286// --------------------------------------------------------------------------
287IOStream::pos_type FileStream::GetPosition() const
288{
289        if(mOSFileHandle == INVALID_FILE) 
290        {
291                THROW_EXCEPTION(CommonException, FileClosed)
292        }
293
294#ifdef WIN32
295        LARGE_INTEGER conv;
296        conv.HighPart = 0;
297        conv.LowPart = SetFilePointer(this->mOSFileHandle, 0, &conv.HighPart, FILE_CURRENT);
298
299        if(conv.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
300        {
301                THROW_WIN_FILE_ERROR("Failed to seek in file", mFileName,
302                        CommonException, OSFileError);
303        }
304
305        return (IOStream::pos_type)conv.QuadPart;
306#else // ! WIN32
307        off_t p = ::lseek(mOSFileHandle, 0, SEEK_CUR);
308        if(p == -1)
309        {
310                THROW_SYS_FILE_ERROR("Failed to seek in file", mFileName,
311                        CommonException, OSFileError);
312        }
313       
314        return (IOStream::pos_type)p;
315#endif // WIN32
316}
317
318
319// --------------------------------------------------------------------------
320//
321// Function
322//              Name:    FileStream::Seek(pos_type, int)
323//              Purpose: Seeks within file, as lseek
324//              Created: 2003/07/31
325//
326// --------------------------------------------------------------------------
327void FileStream::Seek(IOStream::pos_type Offset, int SeekType)
328{
329        if(mOSFileHandle == INVALID_FILE) 
330        {
331                THROW_EXCEPTION(CommonException, FileClosed)
332        }
333
334#ifdef WIN32
335        LARGE_INTEGER conv;
336        conv.QuadPart = Offset;
337        DWORD retVal = SetFilePointer(this->mOSFileHandle, conv.LowPart, &conv.HighPart, ConvertSeekTypeToOSWhence(SeekType));
338
339        if(retVal == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
340        {
341                THROW_WIN_FILE_ERROR("Failed to seek in file", mFileName,
342                        CommonException, OSFileError);
343        }
344#else // ! WIN32
345        if(::lseek(mOSFileHandle, Offset, ConvertSeekTypeToOSWhence(SeekType)) == -1)
346        {
347                THROW_SYS_FILE_ERROR("Failed to seek in file", mFileName,
348                        CommonException, OSFileError);
349        }
350#endif // WIN32
351
352        // Not end of file any more!
353        mIsEOF = false;
354}
355
356
357// --------------------------------------------------------------------------
358//
359// Function
360//              Name:    FileStream::Close()
361//              Purpose: Closes the underlying file
362//              Created: 2003/07/31
363//
364// --------------------------------------------------------------------------
365void FileStream::Close()
366{
367        if(mOSFileHandle == INVALID_FILE)
368        {
369                THROW_EXCEPTION(CommonException, FileAlreadyClosed)
370        }
371
372#ifdef WIN32
373        if(::CloseHandle(mOSFileHandle) == 0)
374        {
375                THROW_WIN_FILE_ERROR("Failed to close file", mFileName,
376                        CommonException, OSFileCloseError);
377        }
378#else // ! WIN32
379        if(::close(mOSFileHandle) != 0)
380        {
381                THROW_SYS_FILE_ERROR("Failed to close file", mFileName,
382                        CommonException, OSFileCloseError);
383        }
384#endif // WIN32
385
386        mOSFileHandle = INVALID_FILE;
387        mIsEOF = true;
388}
389
390
391
392// --------------------------------------------------------------------------
393//
394// Function
395//              Name:    FileStream::StreamDataLeft()
396//              Purpose: Any data left to write?
397//              Created: 2003/08/02
398//
399// --------------------------------------------------------------------------
400bool FileStream::StreamDataLeft()
401{
402        return !mIsEOF;
403}
404
405// --------------------------------------------------------------------------
406//
407// Function
408//              Name:    FileStream::StreamClosed()
409//              Purpose: Is the stream closed?
410//              Created: 2003/08/02
411//
412// --------------------------------------------------------------------------
413bool FileStream::StreamClosed()
414{
415        return (mOSFileHandle == INVALID_FILE);
416}
417
418// --------------------------------------------------------------------------
419//
420// Function
421//              Name:    FileStream::CompareWith(IOStream&, int)
422//              Purpose: Compare bytes in this file with other stream's data
423//              Created: 2009/01/03
424//
425// --------------------------------------------------------------------------
426bool FileStream::CompareWith(IOStream& rOther, int Timeout)
427{
428        // Size
429        IOStream::pos_type mySize = BytesLeftToRead();
430        IOStream::pos_type otherSize = 0;
431       
432        // Test the contents
433        char buf1[2048];
434        char buf2[2048];
435        while(StreamDataLeft() && rOther.StreamDataLeft())
436        {
437                int readSize = rOther.Read(buf1, sizeof(buf1), Timeout);
438                otherSize += readSize;
439               
440                if(Read(buf2, readSize) != readSize ||
441                        ::memcmp(buf1, buf2, readSize) != 0)
442                {
443                        return false;
444                }
445        }
446
447        // Check read all the data from the server and file -- can't be
448        // equal if local and remote aren't the same length. Can't use
449        // StreamDataLeft() test on local file, because if it's the same
450        // size, it won't know it's EOF yet.
451       
452        if(rOther.StreamDataLeft() || otherSize != mySize)
453        {
454                return false;
455        }
456
457        return true;
458}
Note: See TracBrowser for help on using the repository browser.