added missing file error_code.cpp
[libtorrent.git] / src / file_win.cpp
blobfb4f0ce9a912a024c1cd6daa7ccae5bb5acea844
1 /*
3 Copyright (c) 2003, Magnus Jonsson & Arvid Norberg
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15 * Neither the name of the author nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
33 #include "libtorrent/file.hpp"
34 #include "libtorrent/utf8.hpp"
35 #include "libtorrent/assert.hpp"
37 #ifdef UNICODE
38 #include "libtorrent/storage.hpp"
39 #endif
41 #include <sstream>
42 #include <windows.h>
43 #include <winioctl.h>
44 #include <boost/scoped_ptr.hpp>
46 namespace
48 // must be used to not leak memory in case something would throw
49 class auto_localfree
51 public:
52 auto_localfree(HLOCAL memory)
53 : m_memory(memory)
56 ~auto_localfree()
58 if (m_memory)
59 LocalFree(m_memory);
61 private:
62 HLOCAL m_memory;
65 std::string utf8_native(std::string const& s)
67 try
69 std::wstring ws;
70 libtorrent::utf8_wchar(s, ws);
71 std::size_t size = wcstombs(0, ws.c_str(), 0);
72 if (size == std::size_t(-1)) return s;
73 std::string ret;
74 ret.resize(size);
75 size = wcstombs(&ret[0], ws.c_str(), size + 1);
76 if (size == wchar_t(-1)) return s;
77 ret.resize(size);
78 return ret;
80 catch(std::exception)
82 return s;
87 namespace libtorrent
90 struct file::impl : boost::noncopyable
92 enum open_flags
94 read_flag = 1,
95 write_flag = 2
98 enum seek_mode
100 seek_begin = FILE_BEGIN,
101 seek_from_here = FILE_CURRENT,
102 seek_end = FILE_END
105 void set_error(const char* thrower)
107 DWORD err = GetLastError();
109 #ifdef UNICODE
110 wchar_t *wbuffer = 0;
111 FormatMessage(
112 FORMAT_MESSAGE_FROM_SYSTEM
113 |FORMAT_MESSAGE_ALLOCATE_BUFFER
114 , 0, err, 0, (LPWSTR)&wbuffer, 0, 0);
115 auto_localfree auto_free(wbuffer);
116 std::string tmp_utf8;
117 libtorrent::wchar_utf8(wbuffer, tmp_utf8);
118 char const* buffer = tmp_utf8.c_str();
119 #else
120 char* buffer = 0;
121 FormatMessage(
122 FORMAT_MESSAGE_FROM_SYSTEM
123 |FORMAT_MESSAGE_ALLOCATE_BUFFER
124 , 0, err, 0, (LPSTR)&buffer, 0, 0);
125 auto_localfree auto_free(buffer);
126 #endif
128 std::stringstream s;
129 s << (thrower ? thrower : "NULL") << ": " << (buffer ? buffer : "NULL");
131 if (!m_error) m_error.reset(new std::string);
132 *m_error = s.str();
135 impl()
137 m_file_handle = INVALID_HANDLE_VALUE;
140 bool open(const char *file_name, open_flags flags)
142 TORRENT_ASSERT(file_name);
143 TORRENT_ASSERT(flags & (read_flag | write_flag));
145 DWORD access_mask = 0;
146 if (flags & read_flag)
147 access_mask |= GENERIC_READ;
148 if (flags & write_flag)
149 access_mask |= GENERIC_WRITE;
151 TORRENT_ASSERT(access_mask & (GENERIC_READ | GENERIC_WRITE));
153 #ifdef UNICODE
154 std::wstring wfile_name(safe_convert(file_name));
155 HANDLE new_handle = CreateFile(
156 wfile_name.c_str()
157 , access_mask
158 , FILE_SHARE_READ
160 , (flags & write_flag)?OPEN_ALWAYS:OPEN_EXISTING
161 , FILE_ATTRIBUTE_NORMAL
162 , 0);
163 #else
164 HANDLE new_handle = CreateFile(
165 utf8_native(file_name).c_str()
166 , access_mask
167 , FILE_SHARE_READ
169 , (flags & write_flag)?OPEN_ALWAYS:OPEN_EXISTING
170 , FILE_ATTRIBUTE_NORMAL
171 , 0);
172 #endif
174 if (new_handle == INVALID_HANDLE_VALUE)
176 set_error(file_name);
177 return false;
179 // try to make the file sparse if supported
180 if (access_mask & GENERIC_WRITE)
182 DWORD temp;
183 ::DeviceIoControl(new_handle, FSCTL_SET_SPARSE, 0, 0
184 , 0, 0, &temp, 0);
186 // will only close old file if the open succeeded
187 close();
188 m_file_handle = new_handle;
189 return true;
192 void close()
194 if (m_file_handle != INVALID_HANDLE_VALUE)
196 CloseHandle(m_file_handle);
197 m_file_handle = INVALID_HANDLE_VALUE;
201 ~impl()
203 close();
206 size_type write(const char* buffer, size_type num_bytes)
208 TORRENT_ASSERT(buffer);
209 TORRENT_ASSERT((DWORD)num_bytes == num_bytes);
210 DWORD bytes_written = 0;
211 if (num_bytes != 0)
213 if (FALSE == WriteFile(
214 m_file_handle
215 , buffer
216 , (DWORD)num_bytes
217 , &bytes_written
218 , 0))
220 set_error("file::write");
221 return -1;
224 return bytes_written;
227 size_type read(char* buffer, size_type num_bytes)
229 TORRENT_ASSERT(buffer);
230 TORRENT_ASSERT(num_bytes >= 0);
231 TORRENT_ASSERT((DWORD)num_bytes == num_bytes);
233 DWORD bytes_read = 0;
234 if (num_bytes != 0)
236 if (FALSE == ReadFile(
237 m_file_handle
238 , buffer
239 , (DWORD)num_bytes
240 , &bytes_read
241 , 0))
243 set_error("file::set_size");
244 return -1;
247 return bytes_read;
250 bool set_size(size_type s)
252 size_type pos = tell();
253 seek(s, seek_begin);
254 if (FALSE == ::SetEndOfFile(m_file_handle))
256 set_error("file::set_size");
257 return false;
259 return true;
262 size_type seek(size_type pos, seek_mode from_where)
264 TORRENT_ASSERT(pos >= 0 || from_where != seek_begin);
265 TORRENT_ASSERT(pos <= 0 || from_where != seek_end);
266 LARGE_INTEGER offs;
267 offs.QuadPart = pos;
268 if (FALSE == SetFilePointerEx(
269 m_file_handle
270 , offs
271 , &offs
272 , from_where))
274 set_error("file::seek");
275 return -1;
277 return offs.QuadPart;
280 size_type tell()
282 LARGE_INTEGER offs;
283 offs.QuadPart = 0;
285 // is there any other way to get offset?
286 if (FALSE == SetFilePointerEx(
287 m_file_handle
288 , offs
289 , &offs
290 , FILE_CURRENT))
292 set_error("file::tell");
293 return -1;
296 size_type pos = offs.QuadPart;
297 TORRENT_ASSERT(pos >= 0);
298 return pos;
301 size_type size()
303 LARGE_INTEGER s;
304 if (FALSE == GetFileSizeEx(m_file_handle, &s))
306 throw_exception("file::size");
309 size_type size = s.QuadPart;
310 TORRENT_ASSERT(size >= 0);
311 return size;
315 std::string const& error() const
317 if (!m_error) m_error.reset(new std::string);
318 return *m_error;
321 private:
323 HANDLE m_file_handle;
324 mutable boost::scoped_ptr<std::string> m_error;
329 namespace libtorrent
332 const file::seek_mode file::begin(file::impl::seek_begin);
333 const file::seek_mode file::end(file::impl::seek_end);
335 const file::open_mode file::in(file::impl::read_flag);
336 const file::open_mode file::out(file::impl::write_flag);
338 file::file()
339 : m_impl(new libtorrent::file::impl())
342 file::file(boost::filesystem::path const& p, open_mode m)
343 : m_impl(new libtorrent::file::impl())
345 open(p,m);
348 file::~file()
352 bool file::open(boost::filesystem::path const& p, open_mode m)
354 TORRENT_ASSERT(p.is_complete());
355 return m_impl->open(p.native_file_string().c_str(), impl::open_flags(m.m_mask));
358 void file::close()
360 m_impl->close();
363 size_type file::write(const char* buffer, size_type num_bytes)
365 return m_impl->write(buffer, num_bytes);
368 size_type file::read(char* buffer, size_type num_bytes)
370 return m_impl->read(buffer, num_bytes);
373 bool file::set_size(size_type s)
375 return m_impl->set_size(s);
378 size_type file::seek(size_type pos, seek_mode m)
380 return m_impl->seek(pos,impl::seek_mode(m.m_val));
383 size_type file::tell()
385 return m_impl->tell();
388 std::string const& file::error() const
390 return m_impl->error();