delete_files bug fix
[libtorrent.git] / src / file.cpp
blob936e13ce41a9f1933c852bdf1ec658d7925964c2
1 /*
3 Copyright (c) 2003, 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/pch.hpp"
34 #include "libtorrent/config.hpp"
36 #include <boost/scoped_ptr.hpp>
37 #ifdef TORRENT_WINDOWS
38 // windows part
39 #include "libtorrent/utf8.hpp"
41 #include <windows.h>
42 #include <winioctl.h>
44 #ifdef UNICODE
45 #include "libtorrent/storage.hpp"
46 #endif
48 #else
49 // posix part
50 #define _FILE_OFFSET_BITS 64
51 #include <unistd.h>
52 #include <fcntl.h>
53 #include <sys/stat.h>
54 #include <sys/types.h>
55 #include <errno.h>
57 #include <boost/static_assert.hpp>
58 // make sure the _FILE_OFFSET_BITS define worked
59 // on this platform
60 BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8);
62 #endif
64 #include <boost/filesystem/operations.hpp>
65 #include "libtorrent/file.hpp"
66 #include <sstream>
67 #include <cstring>
68 #include <vector>
70 #ifndef O_BINARY
71 #define O_BINARY 0
72 #endif
74 #ifndef O_RANDOM
75 #define O_RANDOM 0
76 #endif
78 #ifdef UNICODE
79 #include "libtorrent/storage.hpp"
80 #endif
82 #include "libtorrent/assert.hpp"
84 namespace
86 #ifdef TORRENT_WINDOWS
87 std::string utf8_native(std::string const& s)
89 try
91 std::wstring ws;
92 libtorrent::utf8_wchar(s, ws);
93 std::size_t size = wcstombs(0, ws.c_str(), 0);
94 if (size == std::size_t(-1)) return s;
95 std::string ret;
96 ret.resize(size);
97 size = wcstombs(&ret[0], ws.c_str(), size + 1);
98 if (size == wchar_t(-1)) return s;
99 ret.resize(size);
100 return ret;
102 catch(std::exception)
104 return s;
107 #else
109 enum { mode_in = 1, mode_out = 2 };
111 mode_t map_open_mode(int m)
113 if (m == (mode_in | mode_out)) return O_RDWR | O_CREAT | O_BINARY | O_RANDOM;
114 if (m == mode_out) return O_WRONLY | O_CREAT | O_BINARY | O_RANDOM;
115 if (m == mode_in) return O_RDONLY | O_BINARY | O_RANDOM;
116 TORRENT_ASSERT(false);
117 return 0;
119 #endif
123 namespace libtorrent
125 namespace fs = boost::filesystem;
127 #ifdef TORRENT_WINDOWS
128 const file::open_mode file::in(GENERIC_READ);
129 const file::open_mode file::out(GENERIC_WRITE);
130 const file::seek_mode file::begin(FILE_BEGIN);
131 const file::seek_mode file::end(FILE_END);
132 #else
133 const file::open_mode file::in(mode_in);
134 const file::open_mode file::out(mode_out);
135 const file::seek_mode file::begin(SEEK_SET);
136 const file::seek_mode file::end(SEEK_END);
137 #endif
139 file::file()
140 #ifdef TORRENT_WINDOWS
141 : m_file_handle(INVALID_HANDLE_VALUE)
142 #else
143 : m_fd(-1)
144 #endif
145 #ifndef NDEBUG
146 , m_open_mode(0)
147 #endif
150 file::file(fs::path const& path, open_mode mode, error_code& ec)
151 #ifdef TORRENT_WINDOWS
152 : m_file_handle(INVALID_HANDLE_VALUE)
153 #else
154 : m_fd(-1)
155 #endif
156 #ifndef NDEBUG
157 , m_open_mode(0)
158 #endif
160 open(path, mode, ec);
163 file::~file()
165 close();
168 bool file::open(fs::path const& path, open_mode mode, error_code& ec)
170 close();
171 #ifdef TORRENT_WINDOWS
173 #ifdef UNICODE
174 std::wstring file_path(safe_convert(path.native_file_string()));
175 #else
176 std::string file_path = utf8_native(path.native_file_string());
177 #endif
179 m_file_handle = CreateFile(
180 file_path.c_str()
181 , mode.m_mask
182 , FILE_SHARE_READ
184 , (mode & out)?OPEN_ALWAYS:OPEN_EXISTING
185 , FILE_ATTRIBUTE_NORMAL
186 , 0);
188 if (m_file_handle == INVALID_HANDLE_VALUE)
190 ec = error_code(GetLastError(), get_system_category());
191 return false;
194 // try to make the file sparse if supported
195 if (mode & out)
197 DWORD temp;
198 ::DeviceIoControl(m_file_handle, FSCTL_SET_SPARSE, 0, 0
199 , 0, 0, &temp, 0);
201 #else
202 // rely on default umask to filter x and w permissions
203 // for group and others
204 m_fd = ::open(path.native_file_string().c_str()
205 , map_open_mode(mode.m_mask), S_IRWXU | S_IRWXG | S_IRWXO);
207 if (m_fd == -1)
209 ec = error_code(errno, get_posix_category());
210 return false;
212 #endif
213 #ifndef NDEBUG
214 m_open_mode = mode;
215 #endif
216 TORRENT_ASSERT(is_open());
217 return true;
220 bool file::is_open() const
222 #ifdef TORRENT_WINDOWS
223 return m_file_handle != INVALID_HANDLE_VALUE;
224 #else
225 return m_fd != -1;
226 #endif
229 void file::close()
231 #ifdef TORRENT_WINDOWS
232 if (m_file_handle == INVALID_HANDLE_VALUE) return;
233 CloseHandle(m_file_handle);
234 m_file_handle = INVALID_HANDLE_VALUE;
235 #else
236 if (m_fd == -1) return;
237 ::close(m_fd);
238 m_fd = -1;
239 #endif
240 #ifndef NDEBUG
241 m_open_mode = 0;
242 #endif
245 size_type file::read(char* buf, size_type num_bytes, error_code& ec)
247 TORRENT_ASSERT((m_open_mode & in) == in);
248 TORRENT_ASSERT(buf);
249 TORRENT_ASSERT(num_bytes >= 0);
250 TORRENT_ASSERT(is_open());
252 #ifdef TORRENT_WINDOWS
254 TORRENT_ASSERT(DWORD(num_bytes) == num_bytes);
255 DWORD ret = 0;
256 if (num_bytes != 0)
258 if (ReadFile(m_file_handle, buf, (DWORD)num_bytes, &ret, 0) == FALSE)
260 ec = error_code(GetLastError(), get_system_category());
261 return -1;
264 #else
265 size_type ret = ::read(m_fd, buf, num_bytes);
266 if (ret == -1) ec = error_code(errno, get_posix_category());
267 #endif
268 return ret;
271 size_type file::write(const char* buf, size_type num_bytes, error_code& ec)
273 TORRENT_ASSERT((m_open_mode & out) == out);
274 TORRENT_ASSERT(buf);
275 TORRENT_ASSERT(num_bytes >= 0);
276 TORRENT_ASSERT(is_open());
278 #ifdef TORRENT_WINDOWS
280 DWORD ret = 0;
281 if (num_bytes != 0)
283 if (WriteFile(m_file_handle, buf, (DWORD)num_bytes, &ret, 0) == FALSE)
285 ec = error_code(GetLastError(), get_system_category());
286 return -1;
289 #else
290 size_type ret = ::write(m_fd, buf, num_bytes);
291 if (ret == -1) ec = error_code(errno, get_posix_category());
292 #endif
293 return ret;
296 bool file::set_size(size_type s, error_code& ec)
298 TORRENT_ASSERT(is_open());
299 TORRENT_ASSERT(s >= 0);
301 #ifdef TORRENT_WINDOWS
302 size_type pos = tell(ec);
303 if (ec) return false;
304 seek(s, begin, ec);
305 if (ec) return false;
306 if (::SetEndOfFile(m_file_handle) == FALSE)
308 ec = error_code(GetLastError(), get_system_category());
309 return false;
311 #else
312 if (ftruncate(m_fd, s) < 0)
314 ec = error_code(errno, get_posix_category());
315 return false;
317 #endif
318 return true;
321 size_type file::seek(size_type offset, seek_mode m, error_code& ec)
323 TORRENT_ASSERT(is_open());
325 #ifdef TORRENT_WINDOWS
326 LARGE_INTEGER offs;
327 offs.QuadPart = offset;
328 if (SetFilePointerEx(m_file_handle, offs, &offs, m.m_val) == FALSE)
330 ec = error_code(GetLastError(), get_system_category());
331 return -1;
333 return offs.QuadPart;
334 #else
335 size_type ret = lseek(m_fd, offset, m.m_val);
336 if (ret < 0) ec = error_code(errno, get_posix_category());
337 return ret;
338 #endif
341 size_type file::tell(error_code& ec)
343 TORRENT_ASSERT(is_open());
345 #ifdef TORRENT_WINDOWS
346 LARGE_INTEGER offs;
347 offs.QuadPart = 0;
349 // is there any other way to get offset?
350 if (SetFilePointerEx(m_file_handle, offs, &offs
351 , FILE_CURRENT) == FALSE)
353 ec = error_code(GetLastError(), get_system_category());
354 return -1;
357 return offs.QuadPart;
358 #else
359 size_type ret;
360 ret = lseek(m_fd, 0, SEEK_CUR);
361 if (ret < 0) ec = error_code(errno, get_posix_category());
362 return ret;
363 #endif