Updated Russian documentation by Morse.
[amule.git] / src / FileArea.cpp
blob912ebd4f4fa09281556a3150d6c1b4da997517e3
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 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.
20 //
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 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
31 #ifdef HAVE_ERRNO_H
32 #include <errno.h>
33 #endif
35 #ifdef HAVE_SIGNAL_H
36 #include <signal.h>
37 #endif
39 #include "FileArea.h" // Interface declarations.
40 #include "FileAutoClose.h" // Needed for CFileAutoClose
42 #ifdef HAVE_SYS_MMAN_H
43 #include <sys/mman.h>
44 #endif
46 #ifdef HAVE_MMAP
47 # if defined(HAVE_SYSCONF) && defined(HAVE__SC_PAGESIZE)
48 static const long gs_pageSize = sysconf(_SC_PAGESIZE);
49 # elif defined(HAVE_SYSCONF) && defined(HAVE__SC_PAGE_SIZE)
50 static const long gs_pageSize = sysconf(_SC_PAGE_SIZE);
51 # elif defined(HAVE_GETPAGESIZE)
52 static const int gs_pageSize = getpagesize();
53 # else
54 # error "Should use memory mapped files but don't know how to determine page size!"
55 # endif
56 #endif
58 #if !defined(HAVE_SIGACTION) || !defined(SA_SIGINFO) || !defined(HAVE_MMAP) || defined(__UCLIBC__)
60 class CFileAreaSigHandler
62 public:
63 static void Init() {};
64 static void Add(CFileArea&) {};
65 static void Remove(CFileArea&) {};
66 private:
67 CFileAreaSigHandler() {};
70 #else
72 class CFileAreaSigHandler
74 public:
75 static void Init();
76 static void Add(CFileArea& area);
77 static void Remove(CFileArea& area);
78 private:
79 CFileAreaSigHandler() {};
80 static wxMutex mutex;
81 static CFileArea *first;
82 static bool initialized;
83 static struct sigaction old_segv, old_bus;
84 static void Handler(int sig, siginfo_t *info, void *ctx);
87 wxMutex CFileAreaSigHandler::mutex;
88 CFileArea * CFileAreaSigHandler::first;
89 bool CFileAreaSigHandler::initialized = false;
90 struct sigaction CFileAreaSigHandler::old_segv;
91 struct sigaction CFileAreaSigHandler::old_bus;
93 /* define MAP_ANONYMOUS for Mac OS X */
94 #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
95 #define MAP_ANONYMOUS MAP_ANON
96 #endif
98 // Handle signals.
99 // The idea is to replace faulted memory with zeroes and mark
100 // the error in proper CFileArea
101 void CFileAreaSigHandler::Handler(int sig, siginfo_t *info, void *ctx)
103 CFileArea *cur;
104 // find the mapped section where violation occurred (if any)
106 wxMutexLocker lock(mutex);
107 cur = first;
108 while (cur) {
109 if (cur->m_mmap_buffer && info->si_addr >= cur->m_mmap_buffer && info->si_addr < cur->m_mmap_buffer + cur->m_length)
110 break;
111 cur = cur->m_next;
115 // mark error if found
116 if (cur && gs_pageSize > 0) {
117 cur->m_error = true;
118 char *start_addr = ((char *) info->si_addr) - (((unsigned long) info->si_addr) % gs_pageSize);
119 if (mmap(start_addr, gs_pageSize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) != MAP_FAILED)
120 return;
123 // call old handler
124 struct sigaction* sa = (sig == SIGSEGV) ? &old_segv : &old_bus;
125 if (sa->sa_flags & SA_SIGINFO)
126 sa->sa_sigaction(sig, info, ctx);
127 else if (sa->sa_handler == SIG_DFL || sa->sa_handler == SIG_IGN)
128 abort();
129 else
130 sa->sa_handler(sig);
133 void CFileAreaSigHandler::Init()
135 // init error handler if needed
136 wxMutexLocker lock(mutex);
137 if (initialized)
138 return;
140 // Set our new signal handler.
141 // Note that we safe old handlers (propably wx ones) in order
142 // to be able to call them if signal not handled as desired.
143 // These handler will be removed by wx code when wx will restore
144 // old ones
145 struct sigaction sa;
146 memset(&sa, 0, sizeof(sa));
147 sigemptyset(&sa.sa_mask);
148 sa.sa_sigaction = Handler;
149 sa.sa_flags = SA_NODEFER|SA_SIGINFO;
150 if (sigaction(SIGSEGV, &sa, &old_segv))
151 return;
152 if (sigaction(SIGBUS, &sa, &old_bus)) {
153 sigaction(SIGSEGV, &old_segv, NULL);
154 return;
156 initialized = true;
159 void CFileAreaSigHandler::Add(CFileArea& area)
161 wxMutexLocker lock(mutex);
162 area.m_next = first;
163 first = &area;
166 void CFileAreaSigHandler::Remove(CFileArea& area)
168 wxMutexLocker lock(mutex);
169 CFileArea **cur = &first;
170 while (*cur) {
171 if (*cur == &area) {
172 *cur = area.m_next;
173 area.m_next = NULL;
174 break;
176 cur = &(*cur)->m_next;
179 #endif
181 CFileArea::CFileArea()
182 : m_buffer(NULL), m_mmap_buffer(NULL), m_length(0), m_next(NULL), m_file(NULL), m_error(false)
184 CFileAreaSigHandler::Init();
188 CFileArea::~CFileArea()
190 Close();
191 CheckError();
194 bool CFileArea::Close()
196 if (m_buffer != NULL && m_mmap_buffer == NULL)
198 delete[] m_buffer;
199 m_buffer = NULL;
201 #ifdef HAVE_MMAP
202 if (m_mmap_buffer)
204 munmap(m_mmap_buffer, m_length);
205 // remove from list
206 CFileAreaSigHandler::Remove(*this);
207 m_buffer = NULL;
208 m_mmap_buffer = NULL;
209 if (m_file) {
210 m_file->Unlock();
211 m_file = NULL;
214 #endif
215 return true;
219 void CFileArea::ReadAt(CFileAutoClose& file, uint64 offset, size_t count)
221 Close();
223 #ifdef HAVE_MMAP
224 uint64 offEnd = offset + count;
225 if (gs_pageSize > 0 && offEnd < 0x100000000ull) {
226 uint64 offStart = offset & (~((uint64)gs_pageSize-1));
227 m_length = offEnd - offStart;
228 void *p = mmap(NULL, m_length, PROT_READ, MAP_SHARED, file.fd(), offStart);
229 if (p != MAP_FAILED) {
230 m_file = &file;
231 m_mmap_buffer = (byte*) p;
232 m_buffer = m_mmap_buffer + (offset - offStart);
234 // add to list to catch errors correctly
235 CFileAreaSigHandler::Add(*this);
236 return;
239 file.Unlock();
240 #endif
241 m_buffer = new byte[count];
242 file.ReadAt(m_buffer, offset, count);
245 #ifdef HAVE_MMAP
246 void CFileArea::StartWriteAt(CFileAutoClose& file, uint64 offset, size_t count)
248 Close();
250 uint64 offEnd = offset + count;
251 if (file.GetLength() >= offEnd && gs_pageSize > 0 && offEnd < 0x100000000ull) {
252 uint64 offStart = offset & (~((uint64)gs_pageSize-1));
253 m_length = offEnd - offStart;
254 void *p = mmap(NULL, m_length, PROT_READ|PROT_WRITE, MAP_SHARED, file.fd(), offStart);
255 if (p != MAP_FAILED)
257 m_file = &file;
258 m_mmap_buffer = (byte*) p;
259 m_buffer = m_mmap_buffer + (offset - offStart);
261 // add to list to catch errors correctly
262 CFileAreaSigHandler::Add(*this);
263 return;
265 file.Unlock();
267 m_buffer = new byte[count];
269 #else
270 void CFileArea::StartWriteAt(CFileAutoClose&, uint64, size_t count)
272 Close();
273 m_buffer = new byte[count];
275 #endif
278 bool CFileArea::FlushAt(CFileAutoClose& file, uint64 offset, size_t count)
280 if (!m_buffer)
281 return false;
283 #ifdef HAVE_MMAP
284 if (m_mmap_buffer) {
285 if (msync(m_mmap_buffer, m_length, MS_SYNC))
286 return false;
287 Close();
288 return true;
290 #endif
291 file.WriteAt(m_buffer, offset, count);
292 Close();
293 return true;
296 void CFileArea::CheckError()
298 bool err = m_error;
299 m_error = false;
300 if (err)
301 throw CIOFailureException(wxT("Read error, failed to read from file."));