Upstream tarball 9614
[amule.git] / src / FileArea.cpp
blobf421b41065d6119ff11c36e4a31a657c2a9870e6
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 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
41 #include "Logger.h" // Needed for AddDebugLogLineM
43 #ifdef HAVE_SYS_MMAN_H
44 #include <sys/mman.h>
45 #endif
47 #if !defined(HAVE_SIGACTION) || !defined(SA_SIGINFO) || !defined(HAVE_MMAP)
49 class CFileAreaSigHandler
51 public:
52 static void Init() {};
53 static void Add(CFileArea& area) {};
54 static void Remove(CFileArea& area) {};
55 private:
56 CFileAreaSigHandler() {};
59 #else
61 class CFileAreaSigHandler
63 public:
64 static void Init();
65 static void Add(CFileArea& area);
66 static void Remove(CFileArea& area);
67 private:
68 CFileAreaSigHandler() {};
69 static wxMutex mutex;
70 static CFileArea *first;
71 static bool initialized;
72 static struct sigaction old_segv, old_bus;
73 static void Handler(int sig, siginfo_t *info, void *ctx);
76 wxMutex CFileAreaSigHandler::mutex;
77 CFileArea * CFileAreaSigHandler::first;
78 bool CFileAreaSigHandler::initialized = false;
79 struct sigaction CFileAreaSigHandler::old_segv;
80 struct sigaction CFileAreaSigHandler::old_bus;
82 #define PAGE_SIZE 8192u
84 /* define MAP_ANONYMOUS for Mac OS X */
85 #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
86 #define MAP_ANONYMOUS MAP_ANON
87 #endif
89 // Handle signals.
90 // The idea is to replace faulted memory with zeroes and mark
91 // the error in proper CFileArea
92 void CFileAreaSigHandler::Handler(int sig, siginfo_t *info, void *ctx)
94 CFileArea *cur;
95 // find the mapped section where violation occurred (if any)
97 wxMutexLocker lock(mutex);
98 cur = first;
99 while (cur) {
100 if (cur->m_mmap_buffer && info->si_addr >= cur->m_mmap_buffer && info->si_addr < cur->m_mmap_buffer + cur->m_length)
101 break;
102 cur = cur->m_next;
106 // mark error if found
107 if (cur) {
108 cur->m_error = true;
109 char *start_addr = ((char *) info->si_addr) - (((unsigned long) info->si_addr) % PAGE_SIZE);
110 if (mmap(start_addr, PAGE_SIZE, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) != MAP_FAILED)
111 return;
114 // call old handler
115 struct sigaction* sa = (sig == SIGSEGV) ? &old_segv : &old_bus;
116 if (sa->sa_flags & SA_SIGINFO)
117 sa->sa_sigaction(sig, info, ctx);
118 else if (sa->sa_handler == SIG_DFL || sa->sa_handler == SIG_IGN)
119 abort();
120 else
121 sa->sa_handler(sig);
124 void CFileAreaSigHandler::Init()
126 // init error handler if needed
127 wxMutexLocker lock(mutex);
128 if (initialized)
129 return;
131 // Set our new signal handler.
132 // Note that we safe old handlers (propably wx ones) in order
133 // to be able to call them if signal not handled as desired.
134 // These handler will be removed by wx code when wx will restore
135 // old ones
136 struct sigaction sa;
137 memset(&sa, 0, sizeof(sa));
138 sigemptyset(&sa.sa_mask);
139 sa.sa_sigaction = Handler;
140 sa.sa_flags = SA_NODEFER|SA_SIGINFO;
141 if (sigaction(SIGSEGV, &sa, &old_segv))
142 return;
143 if (sigaction(SIGBUS, &sa, &old_bus)) {
144 sigaction(SIGSEGV, &old_segv, NULL);
145 return;
147 initialized = true;
150 void CFileAreaSigHandler::Add(CFileArea& area)
152 wxMutexLocker lock(mutex);
153 area.m_next = first;
154 first = &area;
157 void CFileAreaSigHandler::Remove(CFileArea& area)
159 wxMutexLocker lock(mutex);
160 CFileArea **cur = &first;
161 while (*cur) {
162 if (*cur == &area) {
163 *cur = area.m_next;
164 area.m_next = NULL;
165 break;
167 cur = &(*cur)->m_next;
170 #endif
172 CFileArea::CFileArea()
173 : m_buffer(NULL), m_mmap_buffer(NULL), m_length(0), m_next(NULL), m_file(NULL), m_error(false)
175 CFileAreaSigHandler::Init();
179 CFileArea::~CFileArea()
181 Close();
182 CheckError();
185 bool CFileArea::Close()
187 if (m_buffer != NULL && m_mmap_buffer == NULL)
189 delete[] m_buffer;
190 m_buffer = NULL;
192 #ifdef HAVE_MMAP
193 if (m_mmap_buffer)
195 munmap(m_mmap_buffer, m_length);
196 // remove from list
197 CFileAreaSigHandler::Remove(*this);
198 m_mmap_buffer = NULL;
199 if (m_file) {
200 m_file->Unlock();
201 m_file = NULL;
204 #endif
205 return true;
209 void CFileArea::ReadAt(CFileAutoClose& file, uint64 offset, size_t count)
211 Close();
213 #ifdef HAVE_MMAP
214 const uint64 pageSize = 8192u;
215 uint64 offStart = offset & (~(pageSize-1));
216 uint64 offEnd = offset + count;
217 m_length = offEnd - offStart;
218 void *p = mmap(NULL, m_length, PROT_READ, MAP_SHARED, file.fd(), offStart);
219 if (p == MAP_FAILED) {
220 file.Unlock();
221 } else {
222 m_file = &file;
223 m_mmap_buffer = (byte*) p;
224 m_buffer = m_mmap_buffer + (offset - offStart);
226 // add to list to catch errors correctly
227 CFileAreaSigHandler::Add(*this);
228 return;
230 #endif
231 m_buffer = new byte[count];
232 file.ReadAt(m_buffer, offset, count);
235 bool CFileArea::Flush()
237 /* currently we don't support write */
238 return true;
241 void CFileArea::CheckError()
243 bool err = m_error;
244 m_error = false;
245 if (err)
246 throw CIOFailureException(wxT("Read error, failed to read from file."));