debian: fix build-deps for focal
[amule.git] / src / CFile.cpp
blob6d4f88c6a9107991e20937bfe7b2aad97b9639f0
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 1998-2011 Vadim Zeitlin ( zeitlin@dptmaths.ens-cachan.fr )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "CFile.h" // Interface declarations.
28 #include "Logger.h" // Needed for AddDebugLogLineC
29 #include <common/Path.h> // Needed for CPath
30 #include "config.h" // Needed for HAVE_SYS_PARAM_H
32 #ifdef HAVE_SYS_PARAM_H
33 #include <sys/param.h>
34 #endif
36 // standard
37 #if defined(__WINDOWS__) && !defined(__GNUWIN32__) && !defined(__WXWINE__) && !defined(__WXMICROWIN__)
38 # include <io.h>
39 # ifndef __SALFORDC__
40 # define WIN32_LEAN_AND_MEAN
41 # define NOSERVICE
42 # define NOIME
43 # define NOATOM
44 # define NOGDI
45 # define NOGDICAPMASKS
46 # define NOMETAFILE
47 #ifndef NOMINMAX
48 #define NOMINMAX
49 #endif
50 # define NOMSG
51 # define NOOPENFILE
52 # define NORASTEROPS
53 # define NOSCROLL
54 # define NOSOUND
55 # define NOSYSMETRICS
56 # define NOTEXTMETRIC
57 # define NOWH
58 # define NOCOMM
59 # define NOKANJI
60 # define NOCRYPT
61 # define NOMCX
62 # endif
63 #elif (defined(__UNIX__) || defined(__GNUWIN32__))
64 # ifdef __GNUWIN32__
65 # include <windows.h>
66 # endif
67 #elif (defined(__WXPM__))
68 # include <io.h>
69 #elif (defined(__WXSTUBS__))
70 // Have to ifdef this for different environments
71 # include <io.h>
72 #elif (defined(__WXMAC__))
73 #if __MSL__ < 0x6000
74 int access( const char *path, int mode ) { return 0 ; }
75 #else
76 int _access( const char *path, int mode ) { return 0 ; }
77 #endif
78 char* mktemp( char * path ) { return path ;}
79 # include <stat.h>
80 #else
81 # error "Please specify the header with file functions declarations."
82 #endif //Win/UNIX
84 // there is no distinction between text and binary files under Unix, so define
85 // O_BINARY as 0 if the system headers don't do it already
86 #if defined(__UNIX__) && !defined(O_BINARY)
87 # define O_BINARY (0)
88 #endif //__UNIX__
90 #if defined(__WINDOWS__) && !wxCHECK_VERSION(3, 1, 0)
91 #include <wx/msw/mslu.h>
92 #endif
95 // The following defines handle different names across platforms,
96 // and ensures that we use 64b IO on windows (only 32b by default).
97 #ifdef __WINDOWS__
98 #define FLUSH_FD(x) _commit(x)
99 #define SEEK_FD(x, y, z) _lseeki64(x, y, z)
100 #define TELL_FD(x) _telli64(x)
102 #if (__MSVCRT_VERSION__ < 0x0601)
103 //#warning MSCVRT-Version smaller than 6.01
104 #define STAT_FD(x, y) _fstati64(x, y)
105 #define STAT_STRUCT struct _stati64
106 #else
107 #define STAT_FD(x, y) _fstat64(x, y)
108 #define STAT_STRUCT struct __stat64
109 #endif
110 #else
112 // We don't need to sync all meta-data, just the contents,
113 // so use fdatasync when possible (see man fdatasync).
114 #if defined(_POSIX_SYNCHRONIZED_IO) && (_POSIX_SYNCHRONIZED_IO > 0)
115 #define FLUSH_FD(x) fdatasync(x)
116 #else
117 #define FLUSH_FD(x) fsync(x)
118 #endif
120 #define SEEK_FD(x, y, z) lseek(x, y, z)
121 #define TELL_FD(x) wxTell(x)
122 #define STAT_FD(x, y) fstat(x, y)
123 #define STAT_STRUCT struct stat
124 #endif
127 // This function is used to check if a syscall failed, in that case
128 // log an appropriate message containing the errno string.
129 inline void syscall_check(
130 bool check,
131 const CPath& filePath,
132 const wxString& what)
134 if (!check) {
135 AddDebugLogLineC(logCFile,
136 CFormat(wxT("Error when %s (%s): %s"))
137 % what % filePath % wxSysErrorMsg());
142 CSeekFailureException::CSeekFailureException(const wxString& desc)
143 : CIOFailureException(wxT("SeekFailure"), desc)
147 CFile::CFile()
148 : m_fd(fd_invalid), m_safeWrite(false)
152 CFile::CFile(const CPath& fileName, OpenMode mode)
153 : m_fd(fd_invalid)
155 Open(fileName, mode);
159 CFile::CFile(const wxString& fileName, OpenMode mode)
160 : m_fd(fd_invalid)
162 Open(fileName, mode);
166 CFile::~CFile()
168 if (IsOpened()) {
169 // If the writing gets aborted, dtor is still called.
170 // In this case do NOT replace the original file with the
171 // probably broken new one!
172 m_safeWrite = false;
173 Close();
178 int CFile::fd() const
180 return m_fd;
184 bool CFile::IsOpened() const
186 return m_fd != fd_invalid;
190 const CPath& CFile::GetFilePath() const
192 return m_filePath;
196 bool CFile::Create(const CPath& path, bool overwrite, int accessMode)
198 if (!overwrite && path.FileExists()) {
199 return false;
202 return Open(path, write, accessMode);
205 bool CFile::Create(const wxString& path, bool overwrite, int accessMode)
207 return Create(CPath(path), overwrite, accessMode);
211 bool CFile::Open(const wxString& fileName, OpenMode mode, int accessMode)
213 MULE_VALIDATE_PARAMS(fileName.Length(), wxT("CFile: Cannot open, empty path."));
215 return Open(CPath(fileName), mode, accessMode);
219 bool CFile::Open(const CPath& fileName, OpenMode mode, int accessMode)
221 MULE_VALIDATE_PARAMS(fileName.IsOk(), wxT("CFile: Cannot open, empty path."));
223 if (IsOpened()) {
224 Close();
227 m_safeWrite = false;
228 m_filePath = fileName;
230 #ifdef __linux__
231 int flags = O_BINARY | O_LARGEFILE;
232 #else
233 int flags = O_BINARY;
234 #endif
235 switch ( mode ) {
236 case read:
237 flags |= O_RDONLY;
238 break;
240 case write_append:
241 if (fileName.FileExists())
243 flags |= O_WRONLY | O_APPEND;
244 break;
246 //else: fall through as write_append is the same as write if the
247 // file doesn't exist
249 /* fall through */
250 case write:
251 flags |= O_WRONLY | O_CREAT | O_TRUNC;
252 break;
254 case write_safe:
255 flags |= O_WRONLY | O_CREAT | O_TRUNC;
256 m_filePath = m_filePath.AppendExt(wxT(".new"));
257 m_safeWrite = true;
258 break;
260 case write_excl:
261 flags |= O_WRONLY | O_CREAT | O_EXCL;
262 break;
264 case read_write:
265 flags |= O_RDWR;
266 break;
269 // Windows needs wide character file names
270 #ifdef __WINDOWS__
271 m_fd = _wopen(m_filePath.GetRaw().c_str(), flags, accessMode);
272 #else
273 Unicode2CharBuf tmpFileName = filename2char(m_filePath.GetRaw());
274 wxASSERT_MSG(tmpFileName, wxT("Convertion failed in CFile::Open"));
275 m_fd = open(tmpFileName, flags, accessMode);
276 #endif
277 syscall_check(m_fd != fd_invalid, m_filePath, wxT("opening file"));
279 return IsOpened();
283 void CFile::Reopen(OpenMode mode)
285 if (!Open(m_filePath, mode)) {
286 throw CIOFailureException(wxString(wxT("Error reopening file")));
291 bool CFile::Close()
293 MULE_VALIDATE_STATE(IsOpened(), wxT("CFile: Cannot close closed file."));
295 bool closed = (close(m_fd) != -1);
296 syscall_check(closed, m_filePath, wxT("closing file"));
298 m_fd = fd_invalid;
300 if (m_safeWrite) {
301 CPath filePathTemp(m_filePath);
302 m_filePath = m_filePath.RemoveExt(); // restore m_filePath for Reopen()
303 if (closed) {
304 closed = CPath::RenameFile(filePathTemp, m_filePath, true);
308 return closed;
312 bool CFile::Flush()
314 MULE_VALIDATE_STATE(IsOpened(), wxT("CFile: Cannot flush closed file."));
316 bool flushed = (FLUSH_FD(m_fd) != -1);
317 syscall_check(flushed, m_filePath, wxT("flushing file"));
319 return flushed;
323 sint64 CFile::doRead(void* buffer, size_t count) const
325 MULE_VALIDATE_PARAMS(buffer, wxT("CFile: Invalid buffer in read operation."));
326 MULE_VALIDATE_STATE(IsOpened(), wxT("CFile: Cannot read from closed file."));
328 size_t totalRead = 0;
329 while (totalRead < count) {
330 int current = ::read(m_fd, (char*)buffer + totalRead, count - totalRead);
332 if (current == -1) {
333 // Read error, nothing we can do other than abort.
334 throw CIOFailureException(wxString(wxT("Error reading from file: ")) + wxSysErrorMsg());
335 } else if ((totalRead + current < count) && Eof()) {
336 // We may fail to read the specified count in a couple
337 // of situations: EOF and interrupts. The check for EOF
338 // is needed to avoid inf. loops.
339 break;
342 totalRead += current;
345 return totalRead;
349 sint64 CFile::doWrite(const void* buffer, size_t nCount)
351 MULE_VALIDATE_PARAMS(buffer, wxT("CFile: Invalid buffer in write operation."));
352 MULE_VALIDATE_STATE(IsOpened(), wxT("CFile: Cannot write to closed file."));
354 sint64 result = ::write(m_fd, buffer, nCount);
356 if (result != (sint64)nCount) {
357 throw CIOFailureException(wxString(wxT("Error writing to file: ")) + wxSysErrorMsg());
360 return result;
364 sint64 CFile::doSeek(sint64 offset) const
366 MULE_VALIDATE_STATE(IsOpened(), wxT("Cannot seek on closed file."));
367 MULE_VALIDATE_PARAMS(offset >= 0, wxT("Invalid position, must be positive."));
369 sint64 result = SEEK_FD(m_fd, offset, SEEK_SET);
371 if (result == offset) {
372 return result;
373 } else if (result == wxInvalidOffset) {
374 throw CSeekFailureException(wxString(wxT("Seeking failed: ")) + wxSysErrorMsg());
375 } else {
376 throw CSeekFailureException(wxT("Seeking returned incorrect position"));
381 uint64 CFile::GetPosition() const
383 MULE_VALIDATE_STATE(IsOpened(), wxT("Cannot get position in closed file."));
385 sint64 pos = TELL_FD(m_fd);
386 if (pos == wxInvalidOffset) {
387 throw CSeekFailureException(wxString(wxT("Failed to retrieve position in file: ")) + wxSysErrorMsg());
390 return pos;
394 uint64 CFile::GetLength() const
396 MULE_VALIDATE_STATE(IsOpened(), wxT("CFile: Cannot get length of closed file."));
398 STAT_STRUCT buf;
399 if (STAT_FD(m_fd, &buf) == -1) {
400 throw CIOFailureException(wxString(wxT("Failed to retrieve length of file: ")) + wxSysErrorMsg());
403 return buf.st_size;
407 uint64 CFile::GetAvailable() const
409 const uint64 length = GetLength();
410 const uint64 position = GetPosition();
412 // Safely handle seeking past EOF
413 if (position < length) {
414 return length - position;
417 // File is at or after EOF
418 return 0;
422 bool CFile::SetLength(uint64 new_len)
424 MULE_VALIDATE_STATE(IsOpened(), wxT("CFile: Cannot set length when no file is open."));
426 #ifdef __WINDOWS__
427 #ifdef _MSC_VER
428 // MSVC has a 64bit version
429 bool result = _chsize_s(m_fd, new_len) == 0;
430 #else
431 // MingW has an old runtime without it
432 bool result = chsize(m_fd, new_len) == 0;
433 #endif
434 #else
435 bool result = ftruncate(m_fd, new_len) != -1;
436 #endif
438 syscall_check(result, m_filePath, wxT("truncating file"));
440 return result;
442 // File_checked_for_headers