2 // This file is part of the aMule Project.
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 )
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
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
33 #include "config.h" // Needed for HAVE_SYS_PARAM_H
37 #ifdef HAVE_SYS_PARAM_H
38 #include <sys/param.h>
42 #if defined(__WINDOWS__) && !defined(__GNUWIN32__) && !defined(__WXWINE__) && !defined(__WXMICROWIN__)
45 # define WIN32_LEAN_AND_MEAN
50 # define NOGDICAPMASKS
68 #elif (defined(__UNIX__) || defined(__GNUWIN32__))
72 #elif (defined(__WXPM__))
74 #elif (defined(__WXSTUBS__))
75 // Have to ifdef this for different environments
77 #elif (defined(__WXMAC__))
79 int access( const char *path
, int mode
) { return 0 ; }
81 int _access( const char *path
, int mode
) { return 0 ; }
83 char* mktemp( char * path
) { return path
;}
86 # error "Please specify the header with file functions declarations."
89 // there is no distinction between text and binary files under Unix, so define
90 // O_BINARY as 0 if the system headers don't do it already
91 #if defined(__UNIX__) && !defined(O_BINARY)
95 #if defined(__WINDOWS__) && !wxCHECK_VERSION(3, 1, 0)
96 #include <wx/msw/mslu.h>
100 // The following defines handle different names across platforms,
101 // and ensures that we use 64b IO on windows (only 32b by default).
103 #define FLUSH_FD(x) _commit(x)
104 #define SEEK_FD(x, y, z) _lseeki64(x, y, z)
105 #define TELL_FD(x) _telli64(x)
107 #if (__MSVCRT_VERSION__ < 0x0601)
108 //#warning MSCVRT-Version smaller than 6.01
109 #define STAT_FD(x, y) _fstati64(x, y)
110 #define STAT_STRUCT struct _stati64
112 #define STAT_FD(x, y) _fstat64(x, y)
113 #define STAT_STRUCT struct __stat64
117 // We don't need to sync all meta-data, just the contents,
118 // so use fdatasync when possible (see man fdatasync).
119 #if defined(_POSIX_SYNCHRONIZED_IO) && (_POSIX_SYNCHRONIZED_IO > 0)
120 #define FLUSH_FD(x) fdatasync(x)
122 #define FLUSH_FD(x) fsync(x)
125 #define SEEK_FD(x, y, z) lseek(x, y, z)
126 #define TELL_FD(x) wxTell(x)
127 #define STAT_FD(x, y) fstat(x, y)
128 #define STAT_STRUCT struct stat
132 // This function is used to check if a syscall failed, in that case
133 // log an appropriate message containing the errno string.
134 inline void syscall_check(
136 const CPath
& filePath
,
137 const wxString
& what
)
140 AddDebugLogLineC(logCFile
,
141 CFormat(wxT("Error when %s (%s): %s"))
142 % what
% filePath
% wxSysErrorMsg());
147 CSeekFailureException::CSeekFailureException(const wxString
& desc
)
148 : CIOFailureException(wxT("SeekFailure"), desc
)
153 : m_fd(fd_invalid
), m_safeWrite(false)
157 CFile::CFile(const CPath
& fileName
, OpenMode mode
)
160 Open(fileName
, mode
);
164 CFile::CFile(const wxString
& fileName
, OpenMode mode
)
167 Open(fileName
, mode
);
174 // If the writing gets aborted, dtor is still called.
175 // In this case do NOT replace the original file with the
176 // probably broken new one!
183 int CFile::fd() const
189 bool CFile::IsOpened() const
191 return m_fd
!= fd_invalid
;
195 const CPath
& CFile::GetFilePath() const
201 bool CFile::Create(const CPath
& path
, bool overwrite
, int accessMode
)
203 if (!overwrite
&& path
.FileExists()) {
207 return Open(path
, write
, accessMode
);
210 bool CFile::Create(const wxString
& path
, bool overwrite
, int accessMode
)
212 return Create(CPath(path
), overwrite
, accessMode
);
216 bool CFile::Open(const wxString
& fileName
, OpenMode mode
, int accessMode
)
218 MULE_VALIDATE_PARAMS(fileName
.Length(), wxT("CFile: Cannot open, empty path."));
220 return Open(CPath(fileName
), mode
, accessMode
);
224 bool CFile::Open(const CPath
& fileName
, OpenMode mode
, int accessMode
)
226 MULE_VALIDATE_PARAMS(fileName
.IsOk(), wxT("CFile: Cannot open, empty path."));
233 m_filePath
= fileName
;
236 int flags
= O_BINARY
| O_LARGEFILE
;
238 int flags
= O_BINARY
;
246 if (fileName
.FileExists())
248 flags
|= O_WRONLY
| O_APPEND
;
251 //else: fall through as write_append is the same as write if the
252 // file doesn't exist
256 flags
|= O_WRONLY
| O_CREAT
| O_TRUNC
;
260 flags
|= O_WRONLY
| O_CREAT
| O_TRUNC
;
261 m_filePath
= m_filePath
.AppendExt(wxT(".new"));
266 flags
|= O_WRONLY
| O_CREAT
| O_EXCL
;
274 // Windows needs wide character file names
276 m_fd
= _wopen(m_filePath
.GetRaw().c_str(), flags
, accessMode
);
278 Unicode2CharBuf tmpFileName
= filename2char(m_filePath
.GetRaw());
279 wxASSERT_MSG(tmpFileName
, wxT("Convertion failed in CFile::Open"));
280 m_fd
= open(tmpFileName
, flags
, accessMode
);
282 syscall_check(m_fd
!= fd_invalid
, m_filePath
, wxT("opening file"));
288 void CFile::Reopen(OpenMode mode
)
290 if (!Open(m_filePath
, mode
)) {
291 throw CIOFailureException(wxString(wxT("Error reopening file")));
298 MULE_VALIDATE_STATE(IsOpened(), wxT("CFile: Cannot close closed file."));
300 bool closed
= (close(m_fd
) != -1);
301 syscall_check(closed
, m_filePath
, wxT("closing file"));
306 CPath
filePathTemp(m_filePath
);
307 m_filePath
= m_filePath
.RemoveExt(); // restore m_filePath for Reopen()
309 closed
= CPath::RenameFile(filePathTemp
, m_filePath
, true);
319 MULE_VALIDATE_STATE(IsOpened(), wxT("CFile: Cannot flush closed file."));
321 bool flushed
= (FLUSH_FD(m_fd
) != -1);
322 syscall_check(flushed
, m_filePath
, wxT("flushing file"));
328 sint64
CFile::doRead(void* buffer
, size_t count
) const
330 MULE_VALIDATE_PARAMS(buffer
, wxT("CFile: Invalid buffer in read operation."));
331 MULE_VALIDATE_STATE(IsOpened(), wxT("CFile: Cannot read from closed file."));
333 size_t totalRead
= 0;
334 while (totalRead
< count
) {
335 int current
= ::read(m_fd
, (char*)buffer
+ totalRead
, count
- totalRead
);
338 // Read error, nothing we can do other than abort.
339 throw CIOFailureException(wxString(wxT("Error reading from file: ")) + wxSysErrorMsg());
340 } else if ((totalRead
+ current
< count
) && Eof()) {
341 // We may fail to read the specified count in a couple
342 // of situations: EOF and interrupts. The check for EOF
343 // is needed to avoid inf. loops.
347 totalRead
+= current
;
354 sint64
CFile::doWrite(const void* buffer
, size_t nCount
)
356 MULE_VALIDATE_PARAMS(buffer
, wxT("CFile: Invalid buffer in write operation."));
357 MULE_VALIDATE_STATE(IsOpened(), wxT("CFile: Cannot write to closed file."));
359 sint64 result
= ::write(m_fd
, buffer
, nCount
);
361 if (result
!= (sint64
)nCount
) {
362 throw CIOFailureException(wxString(wxT("Error writing to file: ")) + wxSysErrorMsg());
369 sint64
CFile::doSeek(sint64 offset
) const
371 MULE_VALIDATE_STATE(IsOpened(), wxT("Cannot seek on closed file."));
372 MULE_VALIDATE_PARAMS(offset
>= 0, wxT("Invalid position, must be positive."));
374 sint64 result
= SEEK_FD(m_fd
, offset
, SEEK_SET
);
376 if (result
== offset
) {
378 } else if (result
== wxInvalidOffset
) {
379 throw CSeekFailureException(wxString(wxT("Seeking failed: ")) + wxSysErrorMsg());
381 throw CSeekFailureException(wxT("Seeking returned incorrect position"));
386 uint64
CFile::GetPosition() const
388 MULE_VALIDATE_STATE(IsOpened(), wxT("Cannot get position in closed file."));
390 sint64 pos
= TELL_FD(m_fd
);
391 if (pos
== wxInvalidOffset
) {
392 throw CSeekFailureException(wxString(wxT("Failed to retrieve position in file: ")) + wxSysErrorMsg());
399 uint64
CFile::GetLength() const
401 MULE_VALIDATE_STATE(IsOpened(), wxT("CFile: Cannot get length of closed file."));
404 if (STAT_FD(m_fd
, &buf
) == -1) {
405 throw CIOFailureException(wxString(wxT("Failed to retrieve length of file: ")) + wxSysErrorMsg());
412 uint64
CFile::GetAvailable() const
414 const uint64 length
= GetLength();
415 const uint64 position
= GetPosition();
417 // Safely handle seeking past EOF
418 if (position
< length
) {
419 return length
- position
;
422 // File is at or after EOF
427 bool CFile::SetLength(uint64 new_len
)
429 MULE_VALIDATE_STATE(IsOpened(), wxT("CFile: Cannot set length when no file is open."));
433 // MSVC has a 64bit version
434 bool result
= _chsize_s(m_fd
, new_len
) == 0;
436 // MingW has an old runtime without it
437 bool result
= chsize(m_fd
, new_len
) == 0;
440 bool result
= ftruncate(m_fd
, new_len
) != -1;
443 syscall_check(result
, m_filePath
, wxT("truncating file"));
447 // File_checked_for_headers