~ubuntu-branches/ubuntu/maverick/libtorrent-rasterbar/maverick

« back to all changes in this revision

Viewing changes to src/file.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Christophe Sauthier
  • Date: 2010-08-10 12:59:37 UTC
  • mfrom: (1.3.7 upstream)
  • Revision ID: james.westby@ubuntu.com-20100810125937-jbcmmf17y8yo9hgz
Tags: 0.15.0-0ubuntu1
* New upstream version.
* debian/patches/100_fix_html_docs.patch: refreshed.
* debian/control: bump up standards-version to 3.9.1 (no changes).

Show diffs side-by-side

added added

removed removed

Lines of Context:
30
30
 
31
31
*/
32
32
 
 
33
/*
 
34
        Physical file offset patch by Morten Husveit
 
35
*/
 
36
 
33
37
#include "libtorrent/pch.hpp"
34
38
#include "libtorrent/config.hpp"
 
39
#include "libtorrent/alloca.hpp"
 
40
#include "libtorrent/allocator.hpp" // page_size
35
41
 
36
42
#include <boost/scoped_ptr.hpp>
37
43
#ifdef TORRENT_WINDOWS
48
54
// posix part
49
55
#define _FILE_OFFSET_BITS 64
50
56
#include <unistd.h>
51
 
#include <fcntl.h>
 
57
#include <fcntl.h> // for F_LOG2PHYS
52
58
#include <sys/stat.h>
53
59
#include <sys/types.h>
 
60
#include <sys/statvfs.h>
54
61
#include <errno.h>
 
62
#ifdef TORRENT_LINUX
 
63
#include <sys/ioctl.h>
 
64
#ifdef HAVE_LINUX_FIEMAP_H
 
65
#include <linux/fiemap.h>
 
66
#endif
 
67
 
 
68
#include <asm/unistd.h> // For __NR_fallocate
 
69
 
 
70
// circumvent the lack of support in glibc
 
71
static int my_fallocate(int fd, int mode, loff_t offset, loff_t len)
 
72
{
 
73
#ifdef __NR_fallocate
 
74
        // the man page on fallocate differes between versions of linux.
 
75
        // it appears that fallocate in fact sets errno and returns -1
 
76
        // on failure.
 
77
        return syscall(__NR_fallocate, fd, mode, offset, len);
 
78
#else
 
79
        // pretend that the system call doesn't exist
 
80
        errno = ENOSYS;
 
81
        return -1;
 
82
#endif
 
83
}
 
84
 
 
85
#endif // TORRENT_LINUX
55
86
 
56
87
#include <boost/static_assert.hpp>
57
88
// make sure the _FILE_OFFSET_BITS define worked
58
 
// on this platform
 
89
// on this platform. It's supposed to make file
 
90
// related functions support 64-bit offsets.
 
91
// this test makes sure lseek() returns a type
 
92
// at least 64 bits wide
59
93
BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8);
60
94
 
61
95
#endif
62
96
 
63
97
#include <boost/filesystem/operations.hpp>
64
98
#include "libtorrent/file.hpp"
65
 
#include <sstream>
66
99
#include <cstring>
67
100
#include <vector>
68
101
 
69
 
#ifndef O_BINARY
70
 
#define O_BINARY 0
71
 
#endif
72
 
 
73
 
#ifndef O_RANDOM
74
 
#define O_RANDOM 0
75
 
#endif
76
 
 
77
 
#if TORRENT_USE_WPATH
78
 
// for safe_convert
79
 
#include "libtorrent/storage.hpp"
80
 
#endif
 
102
// for convert_to_wstring and convert_to_native
 
103
#include "libtorrent/escape_string.hpp"
81
104
 
82
105
#include "libtorrent/assert.hpp"
83
106
 
84
 
namespace
85
 
{
86
 
#ifdef TORRENT_WINDOWS
87
 
        std::string utf8_native(std::string const& s)
88
 
        {
89
 
                try
90
 
                {
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 == std::size_t(-1)) return s;
99
 
                        ret.resize(size);
100
 
                        return ret;
101
 
                }
102
 
                catch(std::exception)
103
 
                {
104
 
                        return s;
105
 
                }
106
 
        }
107
 
#else
108
 
 
109
 
        enum { mode_in = 1, mode_out = 2 };
110
 
 
111
 
        mode_t map_open_mode(int m)
112
 
        {
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;
118
 
        }
 
107
#ifdef TORRENT_DEBUG
 
108
BOOST_STATIC_ASSERT((libtorrent::file::rw_mask & libtorrent::file::no_buffer) == 0);
 
109
BOOST_STATIC_ASSERT((libtorrent::file::rw_mask & libtorrent::file::attribute_mask) == 0);
 
110
BOOST_STATIC_ASSERT((libtorrent::file::no_buffer & libtorrent::file::attribute_mask) == 0);
119
111
#endif
120
112
 
121
 
}
122
 
 
123
113
namespace libtorrent
124
114
{
125
115
        namespace fs = boost::filesystem;
126
116
 
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
138
 
 
139
117
        file::file()
140
118
#ifdef TORRENT_WINDOWS
141
119
                : m_file_handle(INVALID_HANDLE_VALUE)
142
120
#else
143
121
                : m_fd(-1)
144
122
#endif
145
 
#ifdef TORRENT_DEBUG
146
123
                , m_open_mode(0)
 
124
#if defined TORRENT_WINDOWS || defined TORRENT_LINUX
 
125
                , m_sector_size(0)
147
126
#endif
148
127
        {}
149
128
 
150
 
        file::file(fs::path const& path, open_mode mode, error_code& ec)
 
129
        file::file(fs::path const& path, int mode, error_code& ec)
151
130
#ifdef TORRENT_WINDOWS
152
131
                : m_file_handle(INVALID_HANDLE_VALUE)
153
132
#else
154
133
                : m_fd(-1)
155
134
#endif
156
 
#ifdef TORRENT_DEBUG
157
135
                , m_open_mode(0)
158
 
#endif
159
136
        {
160
137
                open(path, mode, ec);
161
138
        }
165
142
                close();
166
143
        }
167
144
 
168
 
        bool file::open(fs::path const& path, open_mode mode, error_code& ec)
 
145
        bool file::open(fs::path const& path, int mode, error_code& ec)
169
146
        {
170
147
                close();
171
148
#ifdef TORRENT_WINDOWS
172
149
 
 
150
                struct open_mode_t
 
151
                {
 
152
                        DWORD rw_mode;
 
153
                        DWORD share_mode;
 
154
                        DWORD create_mode;
 
155
                        DWORD flags;
 
156
                };
 
157
 
 
158
                const static open_mode_t mode_array[] =
 
159
                {
 
160
                        // read_only
 
161
                        {GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS},
 
162
                        // write_only
 
163
                        {GENERIC_WRITE, FILE_SHARE_READ, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS},
 
164
                        // read_write
 
165
                        {GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS},
 
166
                        // invalid option
 
167
                        {0,0,0,0},
 
168
                        // read_only no_buffer
 
169
                        {GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING },
 
170
                        // write_only no_buffer
 
171
                        {GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING },
 
172
                        // read_write no_buffer
 
173
                        {GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING },
 
174
                        // invalid option
 
175
                        {0,0,0,0}
 
176
                };
 
177
 
 
178
                const static DWORD attrib_array[] =
 
179
                {
 
180
                        FILE_ATTRIBUTE_NORMAL, // no attrib
 
181
                        FILE_ATTRIBUTE_HIDDEN, // hidden
 
182
                        FILE_ATTRIBUTE_NORMAL, // executable
 
183
                        FILE_ATTRIBUTE_HIDDEN, // hidden + executable
 
184
                };
 
185
 
173
186
#if TORRENT_USE_WPATH
174
187
#define CreateFile_ CreateFileW
175
 
                std::wstring file_path(safe_convert(path.external_file_string()));
 
188
                m_path = convert_to_wstring(path.external_file_string());
176
189
#else
177
190
#define CreateFile_ CreateFileA
178
 
                std::string file_path = utf8_native(path.external_file_string());
 
191
                m_path = convert_to_native(path.external_file_string());
179
192
#endif
180
193
 
181
 
                m_file_handle = CreateFile_(
182
 
                        file_path.c_str()
183
 
                        , mode.m_mask
184
 
                        , FILE_SHARE_READ
185
 
                        , 0
186
 
                        , (mode & out)?OPEN_ALWAYS:OPEN_EXISTING
187
 
                        , FILE_ATTRIBUTE_NORMAL
188
 
                        , 0);
 
194
                TORRENT_ASSERT((mode & mode_mask) < sizeof(mode_array)/sizeof(mode_array[0]));
 
195
                open_mode_t const& m = mode_array[mode & mode_mask];
 
196
                DWORD a = attrib_array[(mode & attribute_mask) >> 12];
 
197
 
 
198
                m_file_handle = CreateFile_(m_path.c_str(), m.rw_mode, m.share_mode, 0
 
199
                        , m.create_mode, m.flags | (a ? a : FILE_ATTRIBUTE_NORMAL), 0);
189
200
 
190
201
                if (m_file_handle == INVALID_HANDLE_VALUE)
191
202
                {
194
205
                }
195
206
 
196
207
                // try to make the file sparse if supported
197
 
                if (mode & out)
 
208
                if (mode & file::sparse)
198
209
                {
199
210
                        DWORD temp;
200
211
                        ::DeviceIoControl(m_file_handle, FSCTL_SET_SPARSE, 0, 0
207
218
                        | S_IRGRP | S_IWGRP
208
219
                        | S_IROTH | S_IWOTH;
209
220
 
210
 
                m_fd = ::open(path.external_file_string().c_str()
211
 
                        , map_open_mode(mode.m_mask), permissions);
212
 
 
 
221
                if (mode & attribute_executable)
 
222
                        permissions |= S_IXGRP | S_IXOTH | S_IXUSR;
 
223
 
 
224
                static const int mode_array[] = {O_RDONLY, O_WRONLY | O_CREAT, O_RDWR | O_CREAT};
 
225
#ifdef O_DIRECT
 
226
                static const int no_buffer_flag[] = {0, O_DIRECT};
 
227
#else
 
228
                static const int no_buffer_flag[] = {0, 0};
 
229
#endif
 
230
 
 
231
                m_fd = ::open(convert_to_native(path.external_file_string()).c_str()
 
232
                        , mode_array[mode & rw_mask] | no_buffer_flag[(mode & no_buffer) >> 2], permissions);
 
233
 
 
234
#ifdef TORRENT_LINUX
 
235
                // workaround for linux bug
 
236
                // https://bugs.launchpad.net/ubuntu/+source/linux/+bug/269946
 
237
                if (m_fd == -1 && (mode & no_buffer) && errno == EINVAL)
 
238
                {
 
239
                        mode &= ~no_buffer;
 
240
                        m_fd = ::open(path.external_file_string().c_str()
 
241
                                , mode & (rw_mask | no_buffer), permissions);
 
242
                }
 
243
 
 
244
#endif
213
245
                if (m_fd == -1)
214
246
                {
215
247
                        ec = error_code(errno, get_posix_category());
 
248
                        TORRENT_ASSERT(ec);
216
249
                        return false;
217
250
                }
218
 
#endif
219
 
#ifdef TORRENT_DEBUG
 
251
 
 
252
#ifdef F_NOCACHE
 
253
                if (mode & no_buffer)
 
254
                {
 
255
                        int yes = 1;
 
256
                        fcntl(m_fd, F_NOCACHE, &yes);
 
257
                }
 
258
#endif
 
259
 
 
260
#ifdef POSIX_FADV_RANDOM
 
261
                // disable read-ahead
 
262
                posix_fadvise(m_fd, 0, 0, POSIX_FADV_RANDOM);
 
263
#endif
 
264
 
 
265
#endif
220
266
                m_open_mode = mode;
221
 
#endif
 
267
 
222
268
                TORRENT_ASSERT(is_open());
223
269
                return true;
224
270
        }
232
278
#endif
233
279
        }
234
280
 
 
281
        int file::pos_alignment() const
 
282
        {
 
283
                // on linux and windows, file offsets needs
 
284
                // to be aligned to the disk sector size
 
285
#if defined TORRENT_LINUX
 
286
                if (m_sector_size == 0)
 
287
                {
 
288
                        struct statvfs fs;
 
289
                        if (fstatvfs(m_fd, &fs) == 0)
 
290
                                m_sector_size = fs.f_bsize;
 
291
                        else
 
292
                                m_sector_size = 4096;
 
293
                }       
 
294
                return m_sector_size;
 
295
#elif defined TORRENT_WINDOWS
 
296
                if (m_sector_size == 0)
 
297
                {
 
298
                        DWORD sectors_per_cluster;
 
299
                        DWORD bytes_per_sector;
 
300
                        DWORD free_clusters;
 
301
                        DWORD total_clusters;
 
302
#if TORRENT_USE_WPATH
 
303
#define GetDiskFreeSpace_ GetDiskFreeSpaceW
 
304
                        wchar_t backslash = L'\\';
 
305
#else
 
306
#define GetDiskFreeSpace_ GetDiskFreeSpaceA
 
307
                        char backslash = '\\';
 
308
#endif
 
309
                        if (GetDiskFreeSpace_(m_path.substr(0, m_path.find_first_of(backslash)+1).c_str()
 
310
                                , &sectors_per_cluster, &bytes_per_sector
 
311
                                , &free_clusters, &total_clusters))
 
312
                        {
 
313
                                m_sector_size = bytes_per_sector;
 
314
                                m_cluster_size = sectors_per_cluster * bytes_per_sector;
 
315
                        }
 
316
                        else
 
317
                        {
 
318
                                // make a conservative guess
 
319
                                m_sector_size = 512;
 
320
                                m_cluster_size = 4096;
 
321
                        }
 
322
                }
 
323
                return m_sector_size;
 
324
#else
 
325
                return 1;
 
326
#endif
 
327
        }
 
328
 
 
329
        int file::buf_alignment() const
 
330
        {
 
331
#if defined TORRENT_WINDOWS
 
332
                init_file();
 
333
                return m_page_size;
 
334
#else
 
335
                return pos_alignment();
 
336
#endif
 
337
        }
 
338
 
 
339
        int file::size_alignment() const
 
340
        {
 
341
#if defined TORRENT_WINDOWS
 
342
                init_file();
 
343
                return m_page_size;
 
344
#else
 
345
                return pos_alignment();
 
346
#endif
 
347
        }
 
348
 
235
349
        void file::close()
236
350
        {
 
351
#if defined TORRENT_WINDOWS || defined TORRENT_LINUX
 
352
                m_sector_size = 0;
 
353
#endif
 
354
 
237
355
#ifdef TORRENT_WINDOWS
238
356
                if (m_file_handle == INVALID_HANDLE_VALUE) return;
239
357
                CloseHandle(m_file_handle);
240
358
                m_file_handle = INVALID_HANDLE_VALUE;
 
359
                m_path.clear();
241
360
#else
242
361
                if (m_fd == -1) return;
243
362
                ::close(m_fd);
244
363
                m_fd = -1;
245
364
#endif
246
 
#ifdef TORRENT_DEBUG
247
365
                m_open_mode = 0;
248
 
#endif
249
 
        }
250
 
 
251
 
        size_type file::read(char* buf, size_type num_bytes, error_code& ec)
252
 
        {
253
 
                TORRENT_ASSERT((m_open_mode & in) == in);
254
 
                TORRENT_ASSERT(buf);
255
 
                TORRENT_ASSERT(num_bytes >= 0);
256
 
                TORRENT_ASSERT(is_open());
257
 
 
258
 
#ifdef TORRENT_WINDOWS
259
 
 
260
 
                TORRENT_ASSERT(DWORD(num_bytes) == num_bytes);
261
 
                DWORD ret = 0;
262
 
                if (num_bytes != 0)
263
 
                {
264
 
                        if (ReadFile(m_file_handle, buf, (DWORD)num_bytes, &ret, 0) == FALSE)
265
 
                        {
266
 
                                ec = error_code(GetLastError(), get_system_category());
267
 
                                return -1;
268
 
                        }
269
 
                }
270
 
#else
271
 
                size_type ret = ::read(m_fd, buf, num_bytes);
272
 
                if (ret == -1) ec = error_code(errno, get_posix_category());
273
 
#endif
274
 
                return ret;
275
 
        }
276
 
 
277
 
        size_type file::write(const char* buf, size_type num_bytes, error_code& ec)
278
 
        {
279
 
                TORRENT_ASSERT((m_open_mode & out) == out);
280
 
                TORRENT_ASSERT(buf);
281
 
                TORRENT_ASSERT(num_bytes >= 0);
282
 
                TORRENT_ASSERT(is_open());
283
 
 
284
 
#ifdef TORRENT_WINDOWS
285
 
 
286
 
                DWORD ret = 0;
287
 
                if (num_bytes != 0)
288
 
                {
289
 
                        if (WriteFile(m_file_handle, buf, (DWORD)num_bytes, &ret, 0) == FALSE)
290
 
                        {
291
 
                                ec = error_code(GetLastError(), get_system_category());
292
 
                                return -1;
293
 
                        }
294
 
                }
295
 
#else
296
 
                size_type ret = ::write(m_fd, buf, num_bytes);
297
 
                if (ret == -1) ec = error_code(errno, get_posix_category());
298
 
#endif
299
 
                return ret;
 
366
        }
 
367
 
 
368
        // defined in storage.cpp
 
369
        int bufs_size(file::iovec_t const* bufs, int num_bufs);
 
370
        
 
371
#if defined TORRENT_WINDOWS || defined TORRENT_LINUX || defined TORRENT_DEBUG
 
372
 
 
373
        int file::m_page_size = 0;
 
374
 
 
375
        void file::init_file()
 
376
        {
 
377
                if (m_page_size != 0) return;
 
378
 
 
379
                m_page_size = page_size();
 
380
        }
 
381
 
 
382
#endif
 
383
 
 
384
        size_type file::readv(size_type file_offset, iovec_t const* bufs, int num_bufs, error_code& ec)
 
385
        {
 
386
                TORRENT_ASSERT((m_open_mode & rw_mask) == read_only || (m_open_mode & rw_mask) == read_write);
 
387
                TORRENT_ASSERT(bufs);
 
388
                TORRENT_ASSERT(num_bufs > 0);
 
389
                TORRENT_ASSERT(is_open());
 
390
 
 
391
#if defined TORRENT_WINDOWS || defined TORRENT_LINUX || defined TORRENT_DEBUG
 
392
                // make sure m_page_size is initialized
 
393
                init_file();
 
394
#endif
 
395
 
 
396
#ifdef TORRENT_DEBUG
 
397
                if (m_open_mode & no_buffer)
 
398
                {
 
399
                        bool eof = false;
 
400
                        int size = 0;
 
401
                        // when opened in no_buffer mode, the file_offset must
 
402
                        // be aligned to pos_alignment()
 
403
                        TORRENT_ASSERT((file_offset & (pos_alignment()-1)) == 0);
 
404
                        for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
 
405
                        {
 
406
                                TORRENT_ASSERT((uintptr_t(i->iov_base) & (buf_alignment()-1)) == 0);
 
407
                                // every buffer must be a multiple of the page size
 
408
                                // except for the last one
 
409
                                TORRENT_ASSERT((i->iov_len & (size_alignment()-1)) == 0 || i == end-1);
 
410
                                if ((i->iov_len & (size_alignment()-1)) != 0) eof = true;
 
411
                                size += i->iov_len;
 
412
                        }
 
413
                        error_code code;
 
414
                        if (eof) TORRENT_ASSERT(file_offset + size >= get_size(code));
 
415
                }
 
416
#endif
 
417
 
 
418
#ifdef TORRENT_WINDOWS
 
419
 
 
420
                DWORD ret = 0;
 
421
 
 
422
                // since the ReadFileScatter requires the file to be opened
 
423
                // with no buffering, and no buffering requires page aligned
 
424
                // buffers, open the file in non-buffered mode in case the
 
425
                // buffer is not aligned. Most of the times the buffer should
 
426
                // be aligned though
 
427
 
 
428
                if ((m_open_mode & no_buffer) == 0)
 
429
                {
 
430
                        // this means the buffer base or the buffer size is not aligned
 
431
                        // to the page size. Use a regular file for this operation.
 
432
 
 
433
                        LARGE_INTEGER offs;
 
434
                        offs.QuadPart = file_offset;
 
435
                        if (SetFilePointerEx(m_file_handle, offs, &offs, FILE_BEGIN) == FALSE)
 
436
                        {
 
437
                                ec = error_code(GetLastError(), get_system_category());
 
438
                                return -1;
 
439
                        }
 
440
 
 
441
                        for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
 
442
                        {
 
443
                                DWORD intermediate = 0;
 
444
                                if (ReadFile(m_file_handle, (char*)i->iov_base
 
445
                                        , (DWORD)i->iov_len, &intermediate, 0) == FALSE)
 
446
                                {
 
447
                                        ec = error_code(GetLastError(), get_system_category());
 
448
                                        return -1;
 
449
                                }
 
450
                                ret += intermediate;
 
451
                        }
 
452
                        return ret;
 
453
                }
 
454
 
 
455
                int size = bufs_size(bufs, num_bufs);
 
456
                // number of pages for the read. round up
 
457
                int num_pages = (size + m_page_size - 1) / m_page_size;
 
458
                // allocate array of FILE_SEGMENT_ELEMENT for ReadFileScatter
 
459
                FILE_SEGMENT_ELEMENT* segment_array = TORRENT_ALLOCA(FILE_SEGMENT_ELEMENT, num_pages + 1);
 
460
                FILE_SEGMENT_ELEMENT* cur_seg = segment_array;
 
461
 
 
462
                for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
 
463
                {
 
464
                        for (int k = 0; k < i->iov_len; k += m_page_size)
 
465
                        {
 
466
                                cur_seg->Buffer = ((char*)i->iov_base) + k;
 
467
                                ++cur_seg;
 
468
                        }
 
469
                }
 
470
                // terminate the array
 
471
                cur_seg->Buffer = 0;
 
472
 
 
473
                OVERLAPPED ol;
 
474
                ol.Internal = 0;
 
475
                ol.InternalHigh = 0;
 
476
                ol.OffsetHigh = file_offset >> 32;
 
477
                ol.Offset = file_offset & 0xffffffff;
 
478
                ol.hEvent = CreateEvent(0, true, false, 0);
 
479
 
 
480
                ret += size;
 
481
                size = num_pages * m_page_size;
 
482
                if (ReadFileScatter(m_file_handle, segment_array, size, 0, &ol) == 0)
 
483
                {
 
484
                        DWORD last_error = GetLastError();
 
485
                        if (last_error != ERROR_IO_PENDING)
 
486
                        {
 
487
                                ec = error_code(GetLastError(), get_system_category());
 
488
                                CloseHandle(ol.hEvent);
 
489
                                return -1;
 
490
                        }
 
491
                        if (GetOverlappedResult(m_file_handle, &ol, &ret, true) == 0)
 
492
                        {
 
493
                                ec = error_code(GetLastError(), get_system_category());
 
494
                                CloseHandle(ol.hEvent);
 
495
                                return -1;
 
496
                        }
 
497
                }
 
498
                CloseHandle(ol.hEvent);
 
499
                return ret;
 
500
 
 
501
#else // TORRENT_WINDOWS
 
502
 
 
503
                size_type ret = lseek(m_fd, file_offset, SEEK_SET);
 
504
                if (ret < 0)
 
505
                {
 
506
                        ec = error_code(errno, get_posix_category());
 
507
                        return -1;
 
508
                }
 
509
#if TORRENT_USE_READV
 
510
 
 
511
#ifdef TORRENT_LINUX
 
512
                bool aligned = false;
 
513
                int size = 0;
 
514
                // if we're not opened in no-buffer mode, we don't need alignment
 
515
                if ((m_open_mode & no_buffer) == 0) aligned = true;
 
516
                if (!aligned)
 
517
                {
 
518
                        size = bufs_size(bufs, num_bufs);
 
519
                        if ((size & (size_alignment()-1)) == 0) aligned = true;
 
520
                }
 
521
                if (aligned)
 
522
#endif // TORRENT_LINUX
 
523
                {
 
524
                        ret = ::readv(m_fd, bufs, num_bufs);
 
525
                        if (ret < 0)
 
526
                        {
 
527
                                ec = error_code(errno, get_posix_category());
 
528
                                return -1;
 
529
                        }
 
530
                        return ret;
 
531
                }
 
532
#ifdef TORRENT_LINUX
 
533
                file::iovec_t* temp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs);
 
534
                memcpy(temp_bufs, bufs, sizeof(file::iovec_t) * num_bufs);
 
535
                iovec_t& last = temp_bufs[num_bufs-1];
 
536
                last.iov_len = (last.iov_len & ~(size_alignment()-1)) + m_page_size;
 
537
                ret = ::readv(m_fd, temp_bufs, num_bufs);
 
538
                if (ret < 0)
 
539
                {
 
540
                        ec = error_code(errno, get_posix_category());
 
541
                        return -1;
 
542
                }
 
543
                return (std::min)(ret, size_type(size));
 
544
#endif // TORRENT_LINUX
 
545
 
 
546
#else // TORRENT_USE_READV
 
547
 
 
548
                ret = 0;
 
549
                for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
 
550
                {
 
551
                        int tmp = read(m_fd, i->iov_base, i->iov_len);
 
552
                        if (tmp < 0)
 
553
                        {
 
554
                                ec = error_code(errno, get_posix_category());
 
555
                                return -1;
 
556
                        }
 
557
                        ret += tmp;
 
558
                        if (tmp < i->iov_len) break;
 
559
                }
 
560
                return ret;
 
561
 
 
562
#endif // TORRENT_USE_READV
 
563
 
 
564
#endif // TORRENT_WINDOWS
 
565
        }
 
566
 
 
567
        size_type file::writev(size_type file_offset, iovec_t const* bufs, int num_bufs, error_code& ec)
 
568
        {
 
569
                TORRENT_ASSERT((m_open_mode & rw_mask) == write_only || (m_open_mode & rw_mask) == read_write);
 
570
                TORRENT_ASSERT(bufs);
 
571
                TORRENT_ASSERT(num_bufs > 0);
 
572
                TORRENT_ASSERT(is_open());
 
573
 
 
574
#if defined TORRENT_WINDOWS || defined TORRENT_LINUX || defined TORRENT_DEBUG
 
575
                // make sure m_page_size is initialized
 
576
                init_file();
 
577
#endif
 
578
 
 
579
#ifdef TORRENT_DEBUG
 
580
                if (m_open_mode & no_buffer)
 
581
                {
 
582
                        bool eof = false;
 
583
                        int size = 0;
 
584
                        // when opened in no_buffer mode, the file_offset must
 
585
                        // be aligned to pos_alignment()
 
586
                        TORRENT_ASSERT((file_offset & (pos_alignment()-1)) == 0);
 
587
                        for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
 
588
                        {
 
589
                                TORRENT_ASSERT((uintptr_t(i->iov_base) & (buf_alignment()-1)) == 0);
 
590
                                // every buffer must be a multiple of the page size
 
591
                                // except for the last one
 
592
                                TORRENT_ASSERT((i->iov_len & (size_alignment()-1)) == 0 || i == end-1);
 
593
                                if ((i->iov_len & (size_alignment()-1)) != 0) eof = true;
 
594
                                size += i->iov_len;
 
595
                        }
 
596
                        error_code code;
 
597
                        if (eof) TORRENT_ASSERT(file_offset + size >= get_size(code));
 
598
                }
 
599
#endif
 
600
 
 
601
#ifdef TORRENT_WINDOWS
 
602
 
 
603
                DWORD ret = 0;
 
604
 
 
605
                // since the ReadFileScatter requires the file to be opened
 
606
                // with no buffering, and no buffering requires page aligned
 
607
                // buffers, open the file in non-buffered mode in case the
 
608
                // buffer is not aligned. Most of the times the buffer should
 
609
                // be aligned though
 
610
 
 
611
                if ((m_open_mode & no_buffer) == 0)
 
612
                {
 
613
                        // this means the buffer base or the buffer size is not aligned
 
614
                        // to the page size. Use a regular file for this operation.
 
615
 
 
616
                        LARGE_INTEGER offs;
 
617
                        offs.QuadPart = file_offset;
 
618
                        if (SetFilePointerEx(m_file_handle, offs, &offs, FILE_BEGIN) == FALSE)
 
619
                        {
 
620
                                ec = error_code(GetLastError(), get_system_category());
 
621
                                return -1;
 
622
                        }
 
623
 
 
624
                        for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
 
625
                        {
 
626
                                DWORD intermediate = 0;
 
627
                                if (WriteFile(m_file_handle, (char const*)i->iov_base
 
628
                                        , (DWORD)i->iov_len, &intermediate, 0) == FALSE)
 
629
                                {
 
630
                                        ec = error_code(GetLastError(), get_system_category());
 
631
                                        return -1;
 
632
                                }
 
633
                                ret += intermediate;
 
634
                        }
 
635
                        return ret;
 
636
                }
 
637
 
 
638
                int size = bufs_size(bufs, num_bufs);
 
639
                // number of pages for the write. round up
 
640
                int num_pages = (size + m_page_size - 1) / m_page_size;
 
641
                // allocate array of FILE_SEGMENT_ELEMENT for WriteFileGather
 
642
                FILE_SEGMENT_ELEMENT* segment_array = TORRENT_ALLOCA(FILE_SEGMENT_ELEMENT, num_pages + 1);
 
643
                FILE_SEGMENT_ELEMENT* cur_seg = segment_array;
 
644
 
 
645
                for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
 
646
                {
 
647
                        for (int k = 0; k < i->iov_len; k += m_page_size)
 
648
                        {
 
649
                                cur_seg->Buffer = ((char*)i->iov_base) + k;
 
650
                                ++cur_seg;
 
651
                        }
 
652
                }
 
653
                // terminate the array
 
654
                cur_seg->Buffer = 0;
 
655
 
 
656
                OVERLAPPED ol;
 
657
                ol.Internal = 0;
 
658
                ol.InternalHigh = 0;
 
659
                ol.OffsetHigh = file_offset >> 32;
 
660
                ol.Offset = file_offset & 0xffffffff;
 
661
                ol.hEvent = CreateEvent(0, true, false, 0);
 
662
 
 
663
                ret += size;
 
664
                // if file_size is > 0, the file will be opened in unbuffered
 
665
                // mode after the write completes, and truncate the file to
 
666
                // file_size.
 
667
                size_type file_size = 0;
 
668
        
 
669
                if ((size & (m_page_size-1)) != 0)
 
670
                {
 
671
                        // if size is not an even multiple, this must be the tail
 
672
                        // of the file. Write the whole page and then open a new
 
673
                        // file without FILE_FLAG_NO_BUFFERING and set the
 
674
                        // file size to file_offset + size
 
675
 
 
676
                        file_size = file_offset + size;
 
677
                        size = num_pages * m_page_size;
 
678
                }
 
679
 
 
680
                if (WriteFileGather(m_file_handle, segment_array, size, 0, &ol) == 0)
 
681
                {
 
682
                        if (GetLastError() != ERROR_IO_PENDING)
 
683
                        {
 
684
                                ec = error_code(GetLastError(), get_system_category());
 
685
                                CloseHandle(ol.hEvent);
 
686
                                return -1;
 
687
                        }
 
688
                        DWORD tmp;
 
689
                        if (GetOverlappedResult(m_file_handle, &ol, &tmp, true) == 0)
 
690
                        {
 
691
                                ec = error_code(GetLastError(), get_system_category());
 
692
                                CloseHandle(ol.hEvent);
 
693
                                return -1;
 
694
                        }
 
695
                        if (tmp < ret) ret = tmp;
 
696
                }
 
697
                CloseHandle(ol.hEvent);
 
698
 
 
699
                if (file_size > 0)
 
700
                {
 
701
#if TORRENT_USE_WPATH
 
702
#define CreateFile_ CreateFileW
 
703
#else
 
704
#define CreateFile_ CreateFileA
 
705
#endif
 
706
                        HANDLE f = CreateFile_(m_path.c_str(), GENERIC_WRITE
 
707
                        , FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING
 
708
                        , FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, 0);
 
709
 
 
710
                        if (f == INVALID_HANDLE_VALUE)
 
711
                        {
 
712
                                ec = error_code(GetLastError(), get_system_category());
 
713
                                return -1;
 
714
                        }
 
715
 
 
716
                        LARGE_INTEGER offs;
 
717
                        offs.QuadPart = file_size;
 
718
                        if (SetFilePointerEx(f, offs, &offs, FILE_BEGIN) == FALSE)
 
719
                        {
 
720
                                CloseHandle(f);
 
721
                                ec = error_code(GetLastError(), get_system_category());
 
722
                                return -1;
 
723
                        }
 
724
                        if (::SetEndOfFile(f) == FALSE)
 
725
                        {
 
726
                                ec = error_code(GetLastError(), get_system_category());
 
727
                                CloseHandle(f);
 
728
                                return -1;
 
729
                        }
 
730
                        CloseHandle(f);
 
731
                }
 
732
 
 
733
                return ret;
 
734
#else
 
735
                size_type ret = lseek(m_fd, file_offset, SEEK_SET);
 
736
                if (ret < 0)
 
737
                {
 
738
                        ec = error_code(errno, get_posix_category());
 
739
                        return -1;
 
740
                }
 
741
 
 
742
#if TORRENT_USE_WRITEV
 
743
 
 
744
#ifdef TORRENT_LINUX
 
745
                bool aligned = false;
 
746
                int size = 0;
 
747
                // if we're not opened in no-buffer mode, we don't need alignment
 
748
                if ((m_open_mode & no_buffer) == 0) aligned = true;
 
749
                if (!aligned)
 
750
                {
 
751
                        size = bufs_size(bufs, num_bufs);
 
752
                        if ((size & (size_alignment()-1)) == 0) aligned = true;
 
753
                }
 
754
                if (aligned)
 
755
#endif
 
756
                {
 
757
                        ret = ::writev(m_fd, bufs, num_bufs);
 
758
                        if (ret < 0)
 
759
                        {
 
760
                                ec = error_code(errno, get_posix_category());
 
761
                                return -1;
 
762
                        }
 
763
                        return ret;
 
764
                }
 
765
#ifdef TORRENT_LINUX
 
766
                file::iovec_t* temp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs);
 
767
                memcpy(temp_bufs, bufs, sizeof(file::iovec_t) * num_bufs);
 
768
                iovec_t& last = temp_bufs[num_bufs-1];
 
769
                last.iov_len = (last.iov_len & ~(size_alignment()-1)) + size_alignment();
 
770
                ret = ::writev(m_fd, temp_bufs, num_bufs);
 
771
                if (ret < 0)
 
772
                {
 
773
                        ec = error_code(errno, get_posix_category());
 
774
                        return -1;
 
775
                }
 
776
                if (ftruncate(m_fd, file_offset + size) < 0)
 
777
                {
 
778
                        ec = error_code(errno, get_posix_category());
 
779
                        return -1;
 
780
                }
 
781
                return (std::min)(ret, size_type(size));
 
782
#endif // TORRENT_LINUX
 
783
 
 
784
#else // TORRENT_USE_WRITEV
 
785
 
 
786
                ret = 0;
 
787
                for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
 
788
                {
 
789
                        int tmp = write(m_fd, i->iov_base, i->iov_len);
 
790
                        if (tmp < 0)
 
791
                        {
 
792
                                ec = error_code(errno, get_posix_category());
 
793
                                return -1;
 
794
                        }
 
795
                        ret += tmp;
 
796
                        if (tmp < i->iov_len) break;
 
797
                }
 
798
                return ret;
 
799
 
 
800
#endif // TORRENT_USE_WRITEV
 
801
 
 
802
#endif // TORRENT_WINDOWS
 
803
        }
 
804
 
 
805
        size_type file::phys_offset(size_type offset)
 
806
        {
 
807
#ifdef FS_IOC_FIEMAP
 
808
                // for documentation of this feature
 
809
                // http://lwn.net/Articles/297696/
 
810
                struct
 
811
                {
 
812
                        struct fiemap fiemap;
 
813
                        struct fiemap_extent extent;
 
814
                } fm;
 
815
 
 
816
                memset(&fm, 0, sizeof(fm));
 
817
                fm.fiemap.fm_start = offset;
 
818
                fm.fiemap.fm_length = size_alignment();
 
819
                // this sounds expensive
 
820
                fm.fiemap.fm_flags = FIEMAP_FLAG_SYNC;
 
821
                fm.fiemap.fm_extent_count = 1;
 
822
 
 
823
                if (ioctl(m_fd, FS_IOC_FIEMAP, &fm) == -1)
 
824
                        return 0;
 
825
 
 
826
                if (fm.fiemap.fm_extents[0].fe_flags & FIEMAP_EXTENT_UNKNOWN)
 
827
                        return 0;
 
828
 
 
829
                // the returned extent is not guaranteed to start
 
830
                // at the requested offset, adjust for that in
 
831
                // case they differ
 
832
                TORRENT_ASSERT(offset >= fm.fiemap.fm_extents[0].fe_logical);
 
833
                return fm.fiemap.fm_extents[0].fe_physical + (offset - fm.fiemap.fm_extents[0].fe_logical);
 
834
 
 
835
#elif defined F_LOG2PHYS
 
836
                // for documentation of this feature
 
837
                // http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man2/fcntl.2.html
 
838
 
 
839
                log2phys l;
 
840
                size_type ret = lseek(m_fd, offset, SEEK_SET);
 
841
                if (ret < 0) return 0;
 
842
                if (fcntl(m_fd, F_LOG2PHYS, &l) == -1) return 0;
 
843
                return l.l2p_devoffset;
 
844
#elif defined TORRENT_WINDOWS
 
845
                // for documentation of this feature
 
846
                // http://msdn.microsoft.com/en-us/library/aa364572(VS.85).aspx
 
847
                STARTING_VCN_INPUT_BUFFER in;
 
848
                RETRIEVAL_POINTERS_BUFFER out;
 
849
                DWORD out_bytes;
 
850
 
 
851
                // query cluster size
 
852
                pos_alignment();
 
853
                in.StartingVcn.QuadPart = offset / m_cluster_size;
 
854
                int cluster_offset = in.StartingVcn.QuadPart % m_cluster_size;
 
855
 
 
856
                if (DeviceIoControl(m_file_handle, FSCTL_GET_RETRIEVAL_POINTERS, &in
 
857
                        , sizeof(in), &out, sizeof(out), &out_bytes, 0) == 0)
 
858
                {
 
859
                        DWORD error = GetLastError();
 
860
                        TORRENT_ASSERT(error != ERROR_INVALID_PARAMETER);
 
861
 
 
862
                        // insufficient buffer error is expected, but we're
 
863
                        // only interested in the first extent anyway
 
864
                        if (error != ERROR_MORE_DATA) return 0;
 
865
                }
 
866
                if (out_bytes < sizeof(out)) return 0;
 
867
                if (out.ExtentCount == 0) return 0;
 
868
                if (out.Extents[0].Lcn.QuadPart == (LONGLONG)-1) return 0;
 
869
                TORRENT_ASSERT(in.StartingVcn.QuadPart >= out.StartingVcn.QuadPart);
 
870
                return (out.Extents[0].Lcn.QuadPart
 
871
                        + (in.StartingVcn.QuadPart - out.StartingVcn.QuadPart))
 
872
                        * m_cluster_size + cluster_offset;
 
873
#endif
 
874
                return 0;
300
875
        }
301
876
 
302
877
        bool file::set_size(size_type s, error_code& ec)
305
880
                TORRENT_ASSERT(s >= 0);
306
881
 
307
882
#ifdef TORRENT_WINDOWS
308
 
                size_type pos = tell(ec);
309
 
                if (ec) return false;
310
 
                seek(s, begin, ec);
311
 
                if (ec) return false;
 
883
                LARGE_INTEGER offs;
 
884
                LARGE_INTEGER cur_size;
 
885
                if (GetFileSizeEx(m_file_handle, &cur_size) == FALSE)
 
886
                {
 
887
                        ec = error_code(GetLastError(), get_system_category());
 
888
                        return false;
 
889
                }
 
890
                offs.QuadPart = s;
 
891
                // only set the file size if it's not already at
 
892
                // the right size. We don't want to update the
 
893
                // modification time if we don't have to
 
894
                if (cur_size.QuadPart != s)
 
895
                {
 
896
                        if (SetFilePointerEx(m_file_handle, offs, &offs, FILE_BEGIN) == FALSE)
 
897
                        {
 
898
                                ec.assign(GetLastError(), get_system_category());
 
899
                                return false;
 
900
                        }
 
901
                        if (::SetEndOfFile(m_file_handle) == FALSE)
 
902
                        {
 
903
                                ec.assign(GetLastError(), get_system_category());
 
904
                                return false;
 
905
                        }
 
906
                }
 
907
#if _WIN32_WINNT >= 0x501               
 
908
                if ((m_open_mode & sparse) == 0)
 
909
                {
 
910
                        // only allocate the space if the file
 
911
                        // is not fully allocated
 
912
                        offs.LowPart = GetCompressedFileSize(m_path.c_str(), &offs.HighPart);
 
913
                        ec.assign(GetLastError(), get_system_category());
 
914
                        if (ec) return false;
 
915
                        if (offs.QuadPart != s)
 
916
                        {
 
917
                                // if the user has permissions, avoid filling
 
918
                                // the file with zeroes, but just fill it with
 
919
                                // garbage instead
 
920
                                SetFileValidData(m_file_handle, offs.QuadPart);
 
921
                        }
 
922
                }
 
923
#endif
312
924
                if (::SetEndOfFile(m_file_handle) == FALSE)
313
925
                {
314
926
                        ec = error_code(GetLastError(), get_system_category());
315
927
                        return false;
316
928
                }
317
929
#else
318
 
                if (ftruncate(m_fd, s) < 0)
 
930
                struct stat st;
 
931
                if (fstat(m_fd, &st) != 0)
 
932
                {
 
933
                        ec.assign(errno, get_posix_category());
 
934
                        return false;
 
935
                }
 
936
 
 
937
                // only truncate the file if it doesn't already
 
938
                // have the right size. We don't want to update
 
939
                if (st.st_size != s && ftruncate(m_fd, s) < 0)
 
940
                {
 
941
                        ec.assign(errno, get_posix_category());
 
942
                        return false;
 
943
                }
 
944
 
 
945
                // if we're not in sparse mode, allocate the storage
 
946
                // but only if the number of allocated blocks for the file
 
947
                // is less than the file size. Otherwise we would just
 
948
                // update the modification time of the file for no good
 
949
                // reason.
 
950
                if ((m_open_mode & sparse) == 0
 
951
                        && st.st_blocks < (s + st.st_blksize - 1) / st.st_blksize)
 
952
                {
 
953
                        // How do we know that the file is already allocated?
 
954
                        // if we always try to allocate the space, we'll update
 
955
                        // the modification time without actually changing the file
 
956
                        // but if we don't do anything if the file size is
 
957
#ifdef F_PREALLOCATE
 
958
                        fstore_t f = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, s, 0};
 
959
                        if (fcntl(m_fd, F_PREALLOCATE, &f) < 0)
 
960
                        {
 
961
                                ec = error_code(errno, get_posix_category());
 
962
                                return false;
 
963
                        }
 
964
#elif defined TORRENT_LINUX
 
965
                        int ret = my_fallocate(m_fd, 0, 0, s);
 
966
                        // if we return 0, everything went fine
 
967
                        // the fallocate call succeeded
 
968
                        if (ret == 0) return true;
 
969
                        // otherwise, something went wrong. If the error
 
970
                        // is ENOSYS, just keep going and do it the old-fashioned
 
971
                        // way. If fallocate failed with some other error, it
 
972
                        // probably means the user should know about it, error out
 
973
                        // and report it.
 
974
                        if (errno != ENOSYS)
 
975
                        {
 
976
                                ec.assign(ret, get_posix_category());
 
977
                                return false;
 
978
                        }
 
979
                        // if fallocate failed, we have to use posix_fallocate
 
980
                        // which can be painfully slow
 
981
                        ret = posix_fallocate(m_fd, 0, s);
 
982
                        if (ret != 0)
 
983
                        {
 
984
                                ec = error_code(ret, get_posix_category());
 
985
                                return false;
 
986
                        }
 
987
#elif TORRENT_HAS_FALLOCATE
 
988
                        int ret = posix_fallocate(m_fd, 0, s);
 
989
                        if (ret != 0)
 
990
                        {
 
991
                                ec = error_code(ret, get_posix_category());
 
992
                                return false;
 
993
                        }
 
994
#endif
 
995
                }
 
996
#endif
 
997
                return true;
 
998
        }
 
999
 
 
1000
        size_type file::get_size(error_code& ec) const
 
1001
        {
 
1002
#ifdef TORRENT_WINDOWS
 
1003
                LARGE_INTEGER file_size;
 
1004
                if (!GetFileSizeEx(m_file_handle, &file_size))
 
1005
                {
 
1006
                        ec = error_code(GetLastError(), get_system_category());
 
1007
                        return -1;
 
1008
                }
 
1009
                return file_size.QuadPart;
 
1010
#else
 
1011
                struct stat fs;
 
1012
                if (fstat(m_fd, &fs) != 0)
319
1013
                {
320
1014
                        ec = error_code(errno, get_posix_category());
321
 
                        return false;
322
 
                }
323
 
#endif
324
 
                return true;
325
 
        }
326
 
 
327
 
        size_type file::seek(size_type offset, seek_mode m, error_code& ec)
328
 
        {
329
 
                TORRENT_ASSERT(is_open());
330
 
 
331
 
#ifdef TORRENT_WINDOWS
332
 
                LARGE_INTEGER offs;
333
 
                offs.QuadPart = offset;
334
 
                if (SetFilePointerEx(m_file_handle, offs, &offs, m.m_val) == FALSE)
335
 
                {
336
 
                        ec = error_code(GetLastError(), get_system_category());
337
 
                        return -1;
338
 
                }
339
 
                return offs.QuadPart;
340
 
#else
341
 
                size_type ret = lseek(m_fd, offset, m.m_val);
342
 
                if (ret < 0) ec = error_code(errno, get_posix_category());
343
 
                return ret;
344
 
#endif
345
 
        }
346
 
 
347
 
        size_type file::tell(error_code& ec)
348
 
        {
349
 
                TORRENT_ASSERT(is_open());
350
 
 
351
 
#ifdef TORRENT_WINDOWS
352
 
                LARGE_INTEGER offs;
353
 
                offs.QuadPart = 0;
354
 
 
355
 
                // is there any other way to get offset?
356
 
                if (SetFilePointerEx(m_file_handle, offs, &offs
357
 
                        , FILE_CURRENT) == FALSE)
358
 
                {
359
 
                        ec = error_code(GetLastError(), get_system_category());
360
 
                        return -1;
361
 
                }
362
 
 
363
 
                return offs.QuadPart;
364
 
#else
365
 
                size_type ret;
366
 
                ret = lseek(m_fd, 0, SEEK_CUR);
367
 
                if (ret < 0) ec = error_code(errno, get_posix_category());
368
 
                return ret;
369
 
#endif
370
 
        }
 
1015
                        return -1;
 
1016
                }
 
1017
                return fs.st_size;
 
1018
#endif
 
1019
        }
 
1020
 
 
1021
        size_type file::sparse_end(size_type start) const
 
1022
        {
 
1023
#ifdef TORRENT_WINDOWS
 
1024
#ifdef TORRENT_MINGW
 
1025
typedef struct _FILE_ALLOCATED_RANGE_BUFFER {
 
1026
        LARGE_INTEGER FileOffset;
 
1027
        LARGE_INTEGER Length;
 
1028
} FILE_ALLOCATED_RANGE_BUFFER, *PFILE_ALLOCATED_RANGE_BUFFER;
 
1029
#define FSCTL_QUERY_ALLOCATED_RANGES ((0x9 << 16) | (1 << 14) | (51 << 2) | 3)
 
1030
#endif
 
1031
                FILE_ALLOCATED_RANGE_BUFFER buffer;
 
1032
                DWORD bytes_returned = 0;
 
1033
                FILE_ALLOCATED_RANGE_BUFFER in;
 
1034
                error_code ec;
 
1035
                size_type file_size = get_size(ec);
 
1036
                if (ec) return start;
 
1037
                in.FileOffset.QuadPart = start;
 
1038
                in.Length.QuadPart = file_size - start;
 
1039
                if (!DeviceIoControl(m_file_handle, FSCTL_QUERY_ALLOCATED_RANGES
 
1040
                        , &in, sizeof(FILE_ALLOCATED_RANGE_BUFFER)
 
1041
                        , &buffer, sizeof(FILE_ALLOCATED_RANGE_BUFFER), &bytes_returned, 0))
 
1042
                {
 
1043
                        int err = GetLastError();
 
1044
                        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return start;
 
1045
                }
 
1046
 
 
1047
                // if there are no allocated regions within the rest
 
1048
                // of the file, return the end of the file
 
1049
                if (bytes_returned == 0) return file_size;
 
1050
 
 
1051
                // assume that this range overlaps the start of the
 
1052
                // region we were interested in, and that start actually
 
1053
                // resides in an allocated region.
 
1054
                if (buffer.FileOffset.QuadPart < start) return start;
 
1055
 
 
1056
                // return the offset to the next allocated region
 
1057
                return buffer.FileOffset.QuadPart;
 
1058
                
 
1059
#elif defined SEEK_DATA
 
1060
                // this is supported on solaris
 
1061
                size_type ret = lseek(m_fd, start, SEEK_DATA);
 
1062
                if (ret < 0) return start;
 
1063
#else
 
1064
                return start;
 
1065
#endif
 
1066
        }
 
1067
 
371
1068
}
372
1069