]> git.saurik.com Git - wxWidgets.git/blame - src/common/fileback.cpp
fixing overrelease and out-of-bounds write, fixes #13725
[wxWidgets.git] / src / common / fileback.cpp
CommitLineData
f8f6c91a
MW
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/common/fileback.cpp
3// Purpose: Back an input stream with memory or a file
4// Author: Mike Wetherell
5// RCS-ID: $Id$
6// Copyright: (c) 2006 Mike Wetherell
7// Licence: wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
13#ifdef __BORLANDC__
14 #pragma hdrstop
15#endif
16
916af76f 17#if wxUSE_FILESYSTEM
f8f6c91a 18
916af76f 19#include "wx/private/fileback.h"
360c6a85 20
f8f6c91a 21#ifndef WX_PRECOMP
f8f6c91a
MW
22 #include "wx/utils.h"
23 #include "wx/log.h"
24#endif
25
f8f6c91a
MW
26#include "wx/private/filename.h"
27
28// Prefer wxFFile unless wxFile has large file support but wxFFile does not.
29//
82b99cf9 30#if wxUSE_FFILE && (defined wxHAS_LARGE_FFILES || !defined wxHAS_LARGE_FILES)
f8f6c91a
MW
31typedef wxFFile wxBFFile;
32static const bool wxBadSeek = false;
33#else
34typedef wxFile wxBFFile;
35static const wxFileOffset wxBadSeek = wxInvalidOffset;
36#endif
37
38/////////////////////////////////////////////////////////////////////////////
39// Backing file implementation
40
41class wxBackingFileImpl
42{
43public:
44 wxBackingFileImpl(wxInputStream *stream,
45 size_t bufsize,
46 const wxString& prefix);
47 ~wxBackingFileImpl();
48
49 void Release() { if (--m_refcount == 0) delete this; }
50 wxBackingFileImpl *AddRef() { m_refcount++; return this; }
51
52 wxStreamError ReadAt(wxFileOffset pos, void *buffer, size_t *size);
53 wxFileOffset GetLength() const;
54
55private:
56 int m_refcount;
57
58 wxInputStream *m_stream;
59 wxStreamError m_parenterror;
60
61 char *m_buf;
62 size_t m_bufsize;
63 size_t m_buflen;
64
65 wxString m_prefix;
66 wxString m_filename;
67 wxBFFile m_file;
68 wxFileOffset m_filelen;
69};
70
71wxBackingFileImpl::wxBackingFileImpl(wxInputStream *stream,
72 size_t bufsize,
73 const wxString& prefix)
74 : m_refcount(1),
75 m_stream(stream),
76 m_parenterror(wxSTREAM_NO_ERROR),
77 m_buf(NULL),
78 m_bufsize(bufsize),
79 m_buflen(0),
80 m_prefix(prefix),
81 m_filelen(0)
82{
83 wxFileOffset len = m_stream->GetLength();
84
12811c1c 85 if (len >= 0 && len + size_t(1) < m_bufsize)
8aa3cd14 86 m_bufsize = size_t(len + 1);
f8f6c91a
MW
87
88 if (m_bufsize)
89 m_buf = new char[m_bufsize];
90}
91
92wxBackingFileImpl::~wxBackingFileImpl()
93{
94 delete m_stream;
95 delete [] m_buf;
96
97 if (!m_filename.empty())
98 wxRemoveFile(m_filename);
99}
100
101wxStreamError wxBackingFileImpl::ReadAt(wxFileOffset pos,
102 void *buffer,
103 size_t *size)
104{
105 size_t reqestedSize = *size;
106 *size = 0;
107
108 // size1 is the number of bytes it will read directly from the backing
109 // file. size2 is any remaining bytes not yet backed, these are returned
110 // from the buffer or read from the parent stream.
111 size_t size1, size2;
112
113 if (pos + reqestedSize <= m_filelen + size_t(0)) {
114 size1 = reqestedSize;
115 size2 = 0;
116 } else if (pos < m_filelen) {
4ee9daf4 117 size1 = size_t(m_filelen - pos);
f8f6c91a
MW
118 size2 = reqestedSize - size1;
119 } else {
120 size1 = 0;
121 size2 = reqestedSize;
122 }
123
124 if (pos < 0)
125 return wxSTREAM_READ_ERROR;
126
127 // read the backing file
128 if (size1) {
129 if (m_file.Seek(pos) == wxBadSeek)
130 return wxSTREAM_READ_ERROR;
131
132 ssize_t n = m_file.Read(buffer, size1);
133 if (n > 0) {
134 *size = n;
135 pos += n;
136 }
137
138 if (*size < size1)
139 return wxSTREAM_READ_ERROR;
140 }
141
142 // read from the buffer or parent stream
143 if (size2)
144 {
145 while (*size < reqestedSize)
146 {
147 // if pos is further ahead than the parent has been read so far,
148 // then read forward in the parent stream
149 while (pos - m_filelen + size_t(0) >= m_buflen)
150 {
151 // if the parent is small enough, don't use a backing file
152 // just the buffer memory
153 if (!m_stream && m_filelen == 0)
154 return m_parenterror;
155
156 // before refilling the buffer write out the current buffer
157 // to the backing file if there is anything in it
158 if (m_buflen)
159 {
160 if (!m_file.IsOpened())
161 if (!wxCreateTempFile(m_prefix, &m_file, &m_filename))
162 return wxSTREAM_READ_ERROR;
163
164 if (m_file.Seek(m_filelen) == wxBadSeek)
165 return wxSTREAM_READ_ERROR;
166
167 size_t count = m_file.Write(m_buf, m_buflen);
168 m_filelen += count;
169
170 if (count < m_buflen) {
5276b0a5 171 wxDELETE(m_stream);
f8f6c91a 172 if (count > 0) {
5276b0a5 173 wxDELETEA(m_buf);
f8f6c91a
MW
174 m_buflen = 0;
175 }
176 m_parenterror = wxSTREAM_READ_ERROR;
177 return m_parenterror;
178 }
179
180 m_buflen = 0;
181
182 if (!m_stream) {
5276b0a5 183 wxDELETEA(m_buf);
f8f6c91a
MW
184 }
185 }
186
187 if (!m_stream)
188 return m_parenterror;
189
190 // refill buffer
191 m_buflen = m_stream->Read(m_buf, m_bufsize).LastRead();
192
193 if (m_buflen < m_bufsize) {
194 m_parenterror = m_stream->GetLastError();
195 if (m_parenterror == wxSTREAM_NO_ERROR)
196 m_parenterror = wxSTREAM_EOF;
5276b0a5 197 wxDELETE(m_stream);
f8f6c91a
MW
198 }
199 }
200
201 // copy to the user's buffer
4ee9daf4 202 size_t start = size_t(pos - m_filelen);
f8f6c91a
MW
203 size_t len = wxMin(m_buflen - start, reqestedSize - *size);
204
205 memcpy((char*)buffer + *size, m_buf + start, len);
206 *size += len;
207 pos += len;
208 }
209 }
210
211 return wxSTREAM_NO_ERROR;
212}
213
214wxFileOffset wxBackingFileImpl::GetLength() const
215{
216 if (m_parenterror != wxSTREAM_EOF) {
217 wxLogNull nolog;
218 return m_stream->GetLength();
219 }
220 return m_filelen + m_buflen;
221}
222
223
224/////////////////////////////////////////////////////////////////////////////
225// Backing File, the handle part
226
227wxBackingFile::wxBackingFile(wxInputStream *stream,
228 size_t bufsize,
229 const wxString& prefix)
230 : m_impl(new wxBackingFileImpl(stream, bufsize, prefix))
231{
232}
233
234wxBackingFile::wxBackingFile(const wxBackingFile& backer)
235 : m_impl(backer.m_impl ? backer.m_impl->AddRef() : NULL)
236{
237}
238
239wxBackingFile& wxBackingFile::operator=(const wxBackingFile& backer)
240{
241 if (backer.m_impl != m_impl) {
242 if (m_impl)
243 m_impl->Release();
244
245 m_impl = backer.m_impl;
246
247 if (m_impl)
248 m_impl->AddRef();
249 }
250
251 return *this;
252}
253
254wxBackingFile::~wxBackingFile()
255{
256 if (m_impl)
257 m_impl->Release();
258}
259
260
261/////////////////////////////////////////////////////////////////////////////
262// Input stream
263
264wxBackedInputStream::wxBackedInputStream(const wxBackingFile& backer)
265 : m_backer(backer),
266 m_pos(0)
267{
268}
269
1ab48408
MW
270wxFileOffset wxBackedInputStream::GetLength() const
271{
272 return m_backer.m_impl->GetLength();
273}
274
275wxFileOffset wxBackedInputStream::FindLength() const
276{
277 wxFileOffset len = GetLength();
278
279 if (len == wxInvalidOffset && IsOk()) {
280 // read a byte at 7ff...ffe
281 wxFileOffset pos = 1;
282 pos <<= sizeof(pos) * 8 - 1;
283 pos = ~pos - 1;
284 char ch;
285 size_t size = 1;
286 m_backer.m_impl->ReadAt(pos, &ch, &size);
287 len = GetLength();
288 }
289
290 return len;
291}
03647350 292
f8f6c91a
MW
293size_t wxBackedInputStream::OnSysRead(void *buffer, size_t size)
294{
295 if (!IsOk())
296 return 0;
297
298 m_lasterror = m_backer.m_impl->ReadAt(m_pos, buffer, &size);
299 m_pos += size;
300 return size;
301}
302
f8f6c91a
MW
303wxFileOffset wxBackedInputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
304{
305 switch (mode) {
306 case wxFromCurrent:
307 {
308 m_pos += pos;
309 break;
310 }
311 case wxFromEnd:
312 {
313 wxFileOffset len = GetLength();
314 if (len == wxInvalidOffset)
315 return wxInvalidOffset;
316 m_pos = len + pos;
317 break;
318 }
319 default:
320 {
321 m_pos = pos;
322 break;
323 }
324 }
325
326 return m_pos;
327}
328
329wxFileOffset wxBackedInputStream::OnSysTell() const
330{
331 return m_pos;
332}
333
916af76f 334#endif // wxUSE_FILESYSTEM