transmission 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +0 -0
- data/LICENSE +23 -0
- data/README +50 -0
- data/Rakefile +41 -0
- data/ext/bencode.c +335 -0
- data/ext/bencode.h +64 -0
- data/ext/choking.c +335 -0
- data/ext/choking.h +30 -0
- data/ext/clients.c +90 -0
- data/ext/clients.h +25 -0
- data/ext/completion.c +266 -0
- data/ext/completion.h +67 -0
- data/ext/extconf.rb +3 -0
- data/ext/fastresume.h +375 -0
- data/ext/fdlimit.c +297 -0
- data/ext/fdlimit.h +36 -0
- data/ext/inout.c +620 -0
- data/ext/inout.h +38 -0
- data/ext/internal.h +202 -0
- data/ext/metainfo.c +339 -0
- data/ext/metainfo.h +44 -0
- data/ext/net.c +405 -0
- data/ext/net.h +54 -0
- data/ext/peer.c +584 -0
- data/ext/peer.h +55 -0
- data/ext/peermessages.h +326 -0
- data/ext/peerparse.h +564 -0
- data/ext/peerutils.h +394 -0
- data/ext/platform.c +196 -0
- data/ext/platform.h +63 -0
- data/ext/r_transmission.c +1135 -0
- data/ext/ratecontrol.c +170 -0
- data/ext/ratecontrol.h +33 -0
- data/ext/sha1.c +235 -0
- data/ext/sha1.h +68 -0
- data/ext/tracker.c +767 -0
- data/ext/tracker.h +53 -0
- data/ext/transmission.c +776 -0
- data/ext/transmission.h +317 -0
- data/ext/utils.c +142 -0
- data/ext/utils.h +164 -0
- data/lib/transmission.rb +75 -0
- metadata +98 -0
data/ext/fdlimit.c
ADDED
@@ -0,0 +1,297 @@
|
|
1
|
+
/******************************************************************************
|
2
|
+
* $Id: fdlimit.c 261 2006-05-29 21:27:31Z titer $
|
3
|
+
*
|
4
|
+
* Copyright (c) 2005-2006 Transmission authors and contributors
|
5
|
+
*
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a
|
7
|
+
* copy of this software and associated documentation files (the "Software"),
|
8
|
+
* to deal in the Software without restriction, including without limitation
|
9
|
+
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
10
|
+
* and/or sell copies of the Software, and to permit persons to whom the
|
11
|
+
* Software is furnished to do so, subject to the following conditions:
|
12
|
+
*
|
13
|
+
* The above copyright notice and this permission notice shall be included in
|
14
|
+
* all copies or substantial portions of the Software.
|
15
|
+
*
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
22
|
+
* DEALINGS IN THE SOFTWARE.
|
23
|
+
*****************************************************************************/
|
24
|
+
|
25
|
+
#include "transmission.h"
|
26
|
+
|
27
|
+
#define TR_MAX_OPEN_FILES 16 /* That is, real files, not sockets */
|
28
|
+
#define TR_RESERVED_FDS 16 /* Number of sockets reserved for
|
29
|
+
connections to trackers */
|
30
|
+
|
31
|
+
typedef struct tr_openFile_s
|
32
|
+
{
|
33
|
+
char path[MAX_PATH_LENGTH];
|
34
|
+
int file;
|
35
|
+
|
36
|
+
#define STATUS_INVALID 1
|
37
|
+
#define STATUS_UNUSED 2
|
38
|
+
#define STATUS_USED 4
|
39
|
+
#define STATUS_CLOSING 8
|
40
|
+
int status;
|
41
|
+
|
42
|
+
uint64_t date;
|
43
|
+
|
44
|
+
} tr_openFile_t;
|
45
|
+
|
46
|
+
struct tr_fd_s
|
47
|
+
{
|
48
|
+
tr_lock_t lock;
|
49
|
+
|
50
|
+
int reserved;
|
51
|
+
|
52
|
+
int normal;
|
53
|
+
int normalMax;
|
54
|
+
|
55
|
+
tr_openFile_t open[TR_MAX_OPEN_FILES];
|
56
|
+
};
|
57
|
+
|
58
|
+
/***********************************************************************
|
59
|
+
* tr_fdInit
|
60
|
+
**********************************************************************/
|
61
|
+
tr_fd_t * tr_fdInit()
|
62
|
+
{
|
63
|
+
tr_fd_t * f;
|
64
|
+
int i, j, s[4096];
|
65
|
+
|
66
|
+
f = calloc( sizeof( tr_fd_t ), 1 );
|
67
|
+
|
68
|
+
/* Init lock */
|
69
|
+
tr_lockInit( &f->lock );
|
70
|
+
|
71
|
+
/* Detect the maximum number of open files or sockets */
|
72
|
+
for( i = 0; i < 4096; i++ )
|
73
|
+
{
|
74
|
+
if( ( s[i] = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
|
75
|
+
{
|
76
|
+
break;
|
77
|
+
}
|
78
|
+
}
|
79
|
+
for( j = 0; j < i; j++ )
|
80
|
+
{
|
81
|
+
tr_netClose( s[j] );
|
82
|
+
}
|
83
|
+
|
84
|
+
tr_dbg( "%d usable file descriptors", i );
|
85
|
+
|
86
|
+
f->reserved = 0;
|
87
|
+
f->normal = 0;
|
88
|
+
|
89
|
+
f->normalMax = i - TR_RESERVED_FDS - 10;
|
90
|
+
/* To be safe, in case the UI needs to write a preferences file
|
91
|
+
or something */
|
92
|
+
|
93
|
+
for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
|
94
|
+
{
|
95
|
+
f->open[i].status = STATUS_INVALID;
|
96
|
+
}
|
97
|
+
|
98
|
+
return f;
|
99
|
+
}
|
100
|
+
|
101
|
+
/***********************************************************************
|
102
|
+
* tr_fdFileOpen
|
103
|
+
**********************************************************************/
|
104
|
+
int tr_fdFileOpen( tr_fd_t * f, char * path )
|
105
|
+
{
|
106
|
+
int i, winner;
|
107
|
+
uint64_t date;
|
108
|
+
|
109
|
+
tr_lockLock( &f->lock );
|
110
|
+
|
111
|
+
/* Is it already open? */
|
112
|
+
for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
|
113
|
+
{
|
114
|
+
if( f->open[i].status > STATUS_INVALID &&
|
115
|
+
!strcmp( path, f->open[i].path ) )
|
116
|
+
{
|
117
|
+
if( f->open[i].status & STATUS_CLOSING )
|
118
|
+
{
|
119
|
+
/* Wait until the file is closed */
|
120
|
+
tr_lockUnlock( &f->lock );
|
121
|
+
tr_wait( 10 );
|
122
|
+
tr_lockLock( &f->lock );
|
123
|
+
i = -1;
|
124
|
+
continue;
|
125
|
+
}
|
126
|
+
winner = i;
|
127
|
+
goto done;
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
/* Can we open one more file? */
|
132
|
+
for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
|
133
|
+
{
|
134
|
+
if( f->open[i].status & STATUS_INVALID )
|
135
|
+
{
|
136
|
+
winner = i;
|
137
|
+
goto open;
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
for( ;; )
|
142
|
+
{
|
143
|
+
/* Close the oldest currently unused file */
|
144
|
+
date = tr_date() + 1;
|
145
|
+
winner = -1;
|
146
|
+
|
147
|
+
for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
|
148
|
+
{
|
149
|
+
if( !( f->open[i].status & STATUS_UNUSED ) )
|
150
|
+
{
|
151
|
+
continue;
|
152
|
+
}
|
153
|
+
if( f->open[i].date < date )
|
154
|
+
{
|
155
|
+
winner = i;
|
156
|
+
date = f->open[i].date;
|
157
|
+
}
|
158
|
+
}
|
159
|
+
|
160
|
+
if( winner >= 0 )
|
161
|
+
{
|
162
|
+
/* Close the file: we mark it as closing then release the
|
163
|
+
lock while doing so, because close may take same time
|
164
|
+
and we don't want to block other threads */
|
165
|
+
tr_dbg( "Closing %s", f->open[winner].path );
|
166
|
+
f->open[winner].status = STATUS_CLOSING;
|
167
|
+
tr_lockUnlock( &f->lock );
|
168
|
+
close( f->open[winner].file );
|
169
|
+
tr_lockLock( &f->lock );
|
170
|
+
goto open;
|
171
|
+
}
|
172
|
+
|
173
|
+
/* All used! Wait a bit and try again */
|
174
|
+
tr_lockUnlock( &f->lock );
|
175
|
+
tr_wait( 10 );
|
176
|
+
tr_lockLock( &f->lock );
|
177
|
+
}
|
178
|
+
|
179
|
+
open:
|
180
|
+
tr_dbg( "Opening %s", path );
|
181
|
+
snprintf( f->open[winner].path, MAX_PATH_LENGTH, "%s", path );
|
182
|
+
f->open[winner].file = open( path, O_RDWR, 0 );
|
183
|
+
|
184
|
+
done:
|
185
|
+
f->open[winner].status = STATUS_USED;
|
186
|
+
f->open[winner].date = tr_date();
|
187
|
+
tr_lockUnlock( &f->lock );
|
188
|
+
|
189
|
+
return f->open[winner].file;
|
190
|
+
}
|
191
|
+
|
192
|
+
/***********************************************************************
|
193
|
+
* tr_fdFileRelease
|
194
|
+
**********************************************************************/
|
195
|
+
void tr_fdFileRelease( tr_fd_t * f, int file )
|
196
|
+
{
|
197
|
+
int i;
|
198
|
+
tr_lockLock( &f->lock );
|
199
|
+
|
200
|
+
for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
|
201
|
+
{
|
202
|
+
if( f->open[i].file == file )
|
203
|
+
{
|
204
|
+
f->open[i].status = STATUS_UNUSED;
|
205
|
+
break;
|
206
|
+
}
|
207
|
+
}
|
208
|
+
|
209
|
+
tr_lockUnlock( &f->lock );
|
210
|
+
}
|
211
|
+
|
212
|
+
/***********************************************************************
|
213
|
+
* tr_fdFileClose
|
214
|
+
**********************************************************************/
|
215
|
+
void tr_fdFileClose( tr_fd_t * f, char * path )
|
216
|
+
{
|
217
|
+
int i;
|
218
|
+
|
219
|
+
tr_lockLock( &f->lock );
|
220
|
+
|
221
|
+
/* Is it already open? */
|
222
|
+
for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
|
223
|
+
{
|
224
|
+
if( f->open[i].status & STATUS_INVALID )
|
225
|
+
{
|
226
|
+
continue;
|
227
|
+
}
|
228
|
+
if( !strcmp( path, f->open[i].path ) )
|
229
|
+
{
|
230
|
+
tr_dbg( "Closing %s", path );
|
231
|
+
close( f->open[i].file );
|
232
|
+
f->open[i].status = STATUS_INVALID;
|
233
|
+
break;
|
234
|
+
}
|
235
|
+
}
|
236
|
+
|
237
|
+
tr_lockUnlock( &f->lock );
|
238
|
+
}
|
239
|
+
|
240
|
+
int tr_fdSocketWillCreate( tr_fd_t * f, int reserved )
|
241
|
+
{
|
242
|
+
int ret;
|
243
|
+
|
244
|
+
tr_lockLock( &f->lock );
|
245
|
+
|
246
|
+
if( reserved )
|
247
|
+
{
|
248
|
+
if( f->reserved < TR_RESERVED_FDS )
|
249
|
+
{
|
250
|
+
ret = 0;
|
251
|
+
(f->reserved)++;
|
252
|
+
}
|
253
|
+
else
|
254
|
+
{
|
255
|
+
ret = 1;
|
256
|
+
}
|
257
|
+
}
|
258
|
+
else
|
259
|
+
{
|
260
|
+
if( f->normal < f->normalMax )
|
261
|
+
{
|
262
|
+
ret = 0;
|
263
|
+
(f->normal)++;
|
264
|
+
}
|
265
|
+
else
|
266
|
+
{
|
267
|
+
ret = 1;
|
268
|
+
}
|
269
|
+
}
|
270
|
+
|
271
|
+
tr_lockUnlock( &f->lock );
|
272
|
+
|
273
|
+
return ret;
|
274
|
+
}
|
275
|
+
|
276
|
+
void tr_fdSocketClosed( tr_fd_t * f, int reserved )
|
277
|
+
{
|
278
|
+
tr_lockLock( &f->lock );
|
279
|
+
|
280
|
+
if( reserved )
|
281
|
+
{
|
282
|
+
(f->reserved)--;
|
283
|
+
}
|
284
|
+
else
|
285
|
+
{
|
286
|
+
(f->normal)--;
|
287
|
+
}
|
288
|
+
|
289
|
+
tr_lockUnlock( &f->lock );
|
290
|
+
}
|
291
|
+
|
292
|
+
void tr_fdClose( tr_fd_t * f )
|
293
|
+
{
|
294
|
+
tr_lockClose( &f->lock );
|
295
|
+
free( f );
|
296
|
+
}
|
297
|
+
|
data/ext/fdlimit.h
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
/******************************************************************************
|
2
|
+
* $Id: fdlimit.h 261 2006-05-29 21:27:31Z titer $
|
3
|
+
*
|
4
|
+
* Copyright (c) 2005-2006 Transmission authors and contributors
|
5
|
+
*
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a
|
7
|
+
* copy of this software and associated documentation files (the "Software"),
|
8
|
+
* to deal in the Software without restriction, including without limitation
|
9
|
+
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
10
|
+
* and/or sell copies of the Software, and to permit persons to whom the
|
11
|
+
* Software is furnished to do so, subject to the following conditions:
|
12
|
+
*
|
13
|
+
* The above copyright notice and this permission notice shall be included in
|
14
|
+
* all copies or substantial portions of the Software.
|
15
|
+
*
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
22
|
+
* DEALINGS IN THE SOFTWARE.
|
23
|
+
*****************************************************************************/
|
24
|
+
|
25
|
+
typedef struct tr_fd_s tr_fd_t;
|
26
|
+
|
27
|
+
tr_fd_t * tr_fdInit();
|
28
|
+
|
29
|
+
int tr_fdFileOpen ( tr_fd_t *, char * );
|
30
|
+
void tr_fdFileRelease ( tr_fd_t *, int );
|
31
|
+
void tr_fdFileClose ( tr_fd_t *, char * );
|
32
|
+
|
33
|
+
int tr_fdSocketWillCreate ( tr_fd_t *, int );
|
34
|
+
void tr_fdSocketClosed ( tr_fd_t *, int );
|
35
|
+
|
36
|
+
void tr_fdClose ( tr_fd_t * );
|
data/ext/inout.c
ADDED
@@ -0,0 +1,620 @@
|
|
1
|
+
/******************************************************************************
|
2
|
+
* $Id: inout.c 346 2006-06-13 00:28:03Z titer $
|
3
|
+
*
|
4
|
+
* Copyright (c) 2005-2006 Transmission authors and contributors
|
5
|
+
*
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a
|
7
|
+
* copy of this software and associated documentation files (the "Software"),
|
8
|
+
* to deal in the Software without restriction, including without limitation
|
9
|
+
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
10
|
+
* and/or sell copies of the Software, and to permit persons to whom the
|
11
|
+
* Software is furnished to do so, subject to the following conditions:
|
12
|
+
*
|
13
|
+
* The above copyright notice and this permission notice shall be included in
|
14
|
+
* all copies or substantial portions of the Software.
|
15
|
+
*
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
22
|
+
* DEALINGS IN THE SOFTWARE.
|
23
|
+
*****************************************************************************/
|
24
|
+
|
25
|
+
#include "transmission.h"
|
26
|
+
|
27
|
+
struct tr_io_s
|
28
|
+
{
|
29
|
+
tr_torrent_t * tor;
|
30
|
+
|
31
|
+
/* Position of pieces
|
32
|
+
-1 = we haven't started to download this piece yet
|
33
|
+
n = we have started or completed the piece in slot n */
|
34
|
+
int * pieceSlot;
|
35
|
+
|
36
|
+
/* Pieces in slot
|
37
|
+
-1 = unused slot
|
38
|
+
n = piece n */
|
39
|
+
int * slotPiece;
|
40
|
+
|
41
|
+
int slotsUsed;
|
42
|
+
};
|
43
|
+
|
44
|
+
#include "fastresume.h"
|
45
|
+
|
46
|
+
/***********************************************************************
|
47
|
+
* Local prototypes
|
48
|
+
**********************************************************************/
|
49
|
+
static int createFiles( tr_io_t * );
|
50
|
+
static int checkFiles( tr_io_t * );
|
51
|
+
static void closeFiles( tr_io_t * );
|
52
|
+
static int readOrWriteBytes( tr_io_t *, uint64_t, int, uint8_t *, int );
|
53
|
+
static int readOrWriteSlot( tr_io_t * io, int slot, uint8_t * buf,
|
54
|
+
int * size, int write );
|
55
|
+
static void findSlotForPiece( tr_io_t *, int );
|
56
|
+
|
57
|
+
#define readBytes(io,o,s,b) readOrWriteBytes(io,o,s,b,0)
|
58
|
+
#define writeBytes(io,o,s,b) readOrWriteBytes(io,o,s,b,1)
|
59
|
+
|
60
|
+
#define readSlot(io,sl,b,s) readOrWriteSlot(io,sl,b,s,0)
|
61
|
+
#define writeSlot(io,sl,b,s) readOrWriteSlot(io,sl,b,s,1)
|
62
|
+
|
63
|
+
/***********************************************************************
|
64
|
+
* tr_ioLoadResume
|
65
|
+
***********************************************************************
|
66
|
+
* Try to load the fast resume file
|
67
|
+
**********************************************************************/
|
68
|
+
void tr_ioLoadResume( tr_torrent_t * tor )
|
69
|
+
{
|
70
|
+
tr_io_t * io;
|
71
|
+
tr_info_t * inf = &tor->info;
|
72
|
+
|
73
|
+
io = malloc( sizeof( tr_io_t ) );
|
74
|
+
io->tor = tor;
|
75
|
+
|
76
|
+
io->pieceSlot = malloc( inf->pieceCount * sizeof( int ) );
|
77
|
+
io->slotPiece = malloc( inf->pieceCount * sizeof( int ) );
|
78
|
+
|
79
|
+
fastResumeLoad( io );
|
80
|
+
|
81
|
+
free( io->pieceSlot );
|
82
|
+
free( io->slotPiece );
|
83
|
+
free( io );
|
84
|
+
}
|
85
|
+
|
86
|
+
/***********************************************************************
|
87
|
+
* tr_ioInit
|
88
|
+
***********************************************************************
|
89
|
+
* Open all files we are going to write to
|
90
|
+
**********************************************************************/
|
91
|
+
tr_io_t * tr_ioInit( tr_torrent_t * tor )
|
92
|
+
{
|
93
|
+
tr_io_t * io;
|
94
|
+
|
95
|
+
io = malloc( sizeof( tr_io_t ) );
|
96
|
+
io->tor = tor;
|
97
|
+
|
98
|
+
if( createFiles( io ) || checkFiles( io ) )
|
99
|
+
{
|
100
|
+
free( io );
|
101
|
+
return NULL;
|
102
|
+
}
|
103
|
+
|
104
|
+
return io;
|
105
|
+
}
|
106
|
+
|
107
|
+
/***********************************************************************
|
108
|
+
* tr_ioRead
|
109
|
+
***********************************************************************
|
110
|
+
*
|
111
|
+
**********************************************************************/
|
112
|
+
int tr_ioRead( tr_io_t * io, int index, int begin, int length,
|
113
|
+
uint8_t * buf )
|
114
|
+
{
|
115
|
+
uint64_t offset;
|
116
|
+
tr_info_t * inf = &io->tor->info;
|
117
|
+
|
118
|
+
offset = (uint64_t) io->pieceSlot[index] *
|
119
|
+
(uint64_t) inf->pieceSize + (uint64_t) begin;
|
120
|
+
|
121
|
+
return readBytes( io, offset, length, buf );
|
122
|
+
}
|
123
|
+
|
124
|
+
/***********************************************************************
|
125
|
+
* tr_ioWrite
|
126
|
+
***********************************************************************
|
127
|
+
*
|
128
|
+
**********************************************************************/
|
129
|
+
int tr_ioWrite( tr_io_t * io, int index, int begin, int length,
|
130
|
+
uint8_t * buf )
|
131
|
+
{
|
132
|
+
tr_torrent_t * tor = io->tor;
|
133
|
+
tr_info_t * inf = &io->tor->info;
|
134
|
+
uint64_t offset;
|
135
|
+
int i, hashFailed;
|
136
|
+
uint8_t hash[SHA_DIGEST_LENGTH];
|
137
|
+
uint8_t * pieceBuf;
|
138
|
+
int pieceSize;
|
139
|
+
int startBlock, endBlock;
|
140
|
+
|
141
|
+
if( io->pieceSlot[index] < 0 )
|
142
|
+
{
|
143
|
+
findSlotForPiece( io, index );
|
144
|
+
tr_inf( "Piece %d: starting in slot %d", index,
|
145
|
+
io->pieceSlot[index] );
|
146
|
+
}
|
147
|
+
|
148
|
+
offset = (uint64_t) io->pieceSlot[index] *
|
149
|
+
(uint64_t) inf->pieceSize + (uint64_t) begin;
|
150
|
+
|
151
|
+
if( writeBytes( io, offset, length, buf ) )
|
152
|
+
{
|
153
|
+
return 1;
|
154
|
+
}
|
155
|
+
|
156
|
+
startBlock = tr_pieceStartBlock( index );
|
157
|
+
endBlock = startBlock + tr_pieceCountBlocks( index );
|
158
|
+
for( i = startBlock; i < endBlock; i++ )
|
159
|
+
{
|
160
|
+
if( !tr_cpBlockIsComplete( tor->completion, i ) )
|
161
|
+
{
|
162
|
+
/* The piece is not complete */
|
163
|
+
return 0;
|
164
|
+
}
|
165
|
+
}
|
166
|
+
|
167
|
+
/* The piece is complete, check the hash */
|
168
|
+
pieceSize = tr_pieceSize( index );
|
169
|
+
pieceBuf = malloc( pieceSize );
|
170
|
+
readBytes( io, (uint64_t) io->pieceSlot[index] *
|
171
|
+
(uint64_t) inf->pieceSize, pieceSize, pieceBuf );
|
172
|
+
SHA1( pieceBuf, pieceSize, hash );
|
173
|
+
free( pieceBuf );
|
174
|
+
|
175
|
+
hashFailed = memcmp( hash, &inf->pieces[20*index], SHA_DIGEST_LENGTH );
|
176
|
+
if( hashFailed )
|
177
|
+
{
|
178
|
+
tr_inf( "Piece %d (slot %d): hash FAILED", index,
|
179
|
+
io->pieceSlot[index] );
|
180
|
+
|
181
|
+
/* We will need to reload the whole piece */
|
182
|
+
for( i = startBlock; i < endBlock; i++ )
|
183
|
+
{
|
184
|
+
tr_cpBlockRem( tor->completion, i );
|
185
|
+
}
|
186
|
+
}
|
187
|
+
else
|
188
|
+
{
|
189
|
+
tr_inf( "Piece %d (slot %d): hash OK", index,
|
190
|
+
io->pieceSlot[index] );
|
191
|
+
tr_cpPieceAdd( tor->completion, index );
|
192
|
+
}
|
193
|
+
|
194
|
+
/* Assign blame or credit to peers */
|
195
|
+
for( i = 0; i < tor->peerCount; i++ )
|
196
|
+
{
|
197
|
+
tr_peerBlame( tor, tor->peers[i], index, !hashFailed );
|
198
|
+
}
|
199
|
+
|
200
|
+
return 0;
|
201
|
+
}
|
202
|
+
|
203
|
+
void tr_ioClose( tr_io_t * io )
|
204
|
+
{
|
205
|
+
closeFiles( io );
|
206
|
+
|
207
|
+
fastResumeSave( io );
|
208
|
+
|
209
|
+
free( io->pieceSlot );
|
210
|
+
free( io->slotPiece );
|
211
|
+
free( io );
|
212
|
+
}
|
213
|
+
|
214
|
+
void tr_ioSaveResume( tr_io_t * io )
|
215
|
+
{
|
216
|
+
fastResumeSave( io );
|
217
|
+
}
|
218
|
+
|
219
|
+
/***********************************************************************
|
220
|
+
* createFiles
|
221
|
+
***********************************************************************
|
222
|
+
* Make sure the existing folders/files have correct types and
|
223
|
+
* permissions, and create missing folders and files
|
224
|
+
**********************************************************************/
|
225
|
+
static int createFiles( tr_io_t * io )
|
226
|
+
{
|
227
|
+
tr_torrent_t * tor = io->tor;
|
228
|
+
tr_info_t * inf = &tor->info;
|
229
|
+
|
230
|
+
int i;
|
231
|
+
char * path, * p;
|
232
|
+
struct stat sb;
|
233
|
+
int file;
|
234
|
+
|
235
|
+
tr_dbg( "Creating files..." );
|
236
|
+
|
237
|
+
for( i = 0; i < inf->fileCount; i++ )
|
238
|
+
{
|
239
|
+
asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
|
240
|
+
|
241
|
+
/* Create folders */
|
242
|
+
if( NULL != ( p = strrchr( path, '/' ) ) )
|
243
|
+
{
|
244
|
+
*p = '\0';
|
245
|
+
if( tr_mkdir( path ) )
|
246
|
+
{
|
247
|
+
free( path );
|
248
|
+
return 1;
|
249
|
+
}
|
250
|
+
*p = '/';
|
251
|
+
}
|
252
|
+
|
253
|
+
if( stat( path, &sb ) )
|
254
|
+
{
|
255
|
+
/* File doesn't exist yet */
|
256
|
+
if( ( file = open( path, O_WRONLY|O_CREAT|O_TRUNC, 0666 ) ) < 0 )
|
257
|
+
{
|
258
|
+
tr_err( "Could not create `%s' (%s)", path,
|
259
|
+
strerror( errno ) );
|
260
|
+
free( path );
|
261
|
+
return 1;
|
262
|
+
}
|
263
|
+
close( file );
|
264
|
+
}
|
265
|
+
else if( ( sb.st_mode & S_IFMT ) != S_IFREG )
|
266
|
+
{
|
267
|
+
/* Node exists but isn't a file */
|
268
|
+
printf( "Remove %s, it's in the way.\n", path );
|
269
|
+
free( path );
|
270
|
+
return 1;
|
271
|
+
}
|
272
|
+
|
273
|
+
free( path );
|
274
|
+
}
|
275
|
+
|
276
|
+
return 0;
|
277
|
+
}
|
278
|
+
|
279
|
+
/***********************************************************************
|
280
|
+
* checkFiles
|
281
|
+
***********************************************************************
|
282
|
+
* Look for pieces
|
283
|
+
**********************************************************************/
|
284
|
+
static int checkFiles( tr_io_t * io )
|
285
|
+
{
|
286
|
+
tr_torrent_t * tor = io->tor;
|
287
|
+
tr_info_t * inf = &tor->info;
|
288
|
+
|
289
|
+
int i;
|
290
|
+
uint8_t * buf;
|
291
|
+
uint8_t hash[SHA_DIGEST_LENGTH];
|
292
|
+
|
293
|
+
io->pieceSlot = malloc( inf->pieceCount * sizeof( int ) );
|
294
|
+
io->slotPiece = malloc( inf->pieceCount * sizeof( int ) );
|
295
|
+
|
296
|
+
if( !fastResumeLoad( io ) )
|
297
|
+
{
|
298
|
+
return 0;
|
299
|
+
}
|
300
|
+
|
301
|
+
tr_dbg( "Checking pieces..." );
|
302
|
+
|
303
|
+
/* Yet we don't have anything */
|
304
|
+
memset( io->pieceSlot, 0xFF, inf->pieceCount * sizeof( int ) );
|
305
|
+
memset( io->slotPiece, 0xFF, inf->pieceCount * sizeof( int ) );
|
306
|
+
|
307
|
+
/* Check pieces */
|
308
|
+
io->slotsUsed = 0;
|
309
|
+
buf = malloc( inf->pieceSize );
|
310
|
+
for( i = 0; i < inf->pieceCount; i++ )
|
311
|
+
{
|
312
|
+
int size, j;
|
313
|
+
|
314
|
+
if( readSlot( io, i, buf, &size ) )
|
315
|
+
{
|
316
|
+
break;
|
317
|
+
}
|
318
|
+
|
319
|
+
io->slotsUsed = i + 1;
|
320
|
+
SHA1( buf, size, hash );
|
321
|
+
|
322
|
+
for( j = i; j < inf->pieceCount - 1; j++ )
|
323
|
+
{
|
324
|
+
if( !memcmp( hash, &inf->pieces[20*j], SHA_DIGEST_LENGTH ) )
|
325
|
+
{
|
326
|
+
io->pieceSlot[j] = i;
|
327
|
+
io->slotPiece[i] = j;
|
328
|
+
|
329
|
+
tr_cpPieceAdd( tor->completion, j );
|
330
|
+
break;
|
331
|
+
}
|
332
|
+
}
|
333
|
+
|
334
|
+
if( io->slotPiece[i] > -1 )
|
335
|
+
{
|
336
|
+
continue;
|
337
|
+
}
|
338
|
+
|
339
|
+
/* Special case for the last piece */
|
340
|
+
SHA1( buf, tr_pieceSize( inf->pieceCount - 1 ), hash );
|
341
|
+
if( !memcmp( hash, &inf->pieces[20 * (inf->pieceCount - 1)],
|
342
|
+
SHA_DIGEST_LENGTH ) )
|
343
|
+
{
|
344
|
+
io->pieceSlot[inf->pieceCount - 1] = i;
|
345
|
+
io->slotPiece[i] = inf->pieceCount - 1;
|
346
|
+
|
347
|
+
tr_cpPieceAdd( tor->completion, inf->pieceCount - 1 );
|
348
|
+
}
|
349
|
+
}
|
350
|
+
free( buf );
|
351
|
+
|
352
|
+
return 0;
|
353
|
+
}
|
354
|
+
|
355
|
+
/***********************************************************************
|
356
|
+
* closeFiles
|
357
|
+
**********************************************************************/
|
358
|
+
static void closeFiles( tr_io_t * io )
|
359
|
+
{
|
360
|
+
tr_torrent_t * tor = io->tor;
|
361
|
+
tr_info_t * inf = &tor->info;
|
362
|
+
|
363
|
+
int i;
|
364
|
+
char * path;
|
365
|
+
|
366
|
+
for( i = 0; i < inf->fileCount; i++ )
|
367
|
+
{
|
368
|
+
asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
|
369
|
+
tr_fdFileClose( tor->fdlimit, path );
|
370
|
+
free( path );
|
371
|
+
}
|
372
|
+
}
|
373
|
+
|
374
|
+
/***********************************************************************
|
375
|
+
* readOrWriteBytes
|
376
|
+
***********************************************************************
|
377
|
+
*
|
378
|
+
**********************************************************************/
|
379
|
+
typedef size_t (* iofunc) ( int, void *, size_t );
|
380
|
+
static int readOrWriteBytes( tr_io_t * io, uint64_t offset, int size,
|
381
|
+
uint8_t * buf, int isWrite )
|
382
|
+
{
|
383
|
+
tr_torrent_t * tor = io->tor;
|
384
|
+
tr_info_t * inf = &tor->info;
|
385
|
+
|
386
|
+
int piece = offset / inf->pieceSize;
|
387
|
+
int begin = offset % inf->pieceSize;
|
388
|
+
int i;
|
389
|
+
size_t cur;
|
390
|
+
char * path;
|
391
|
+
int file;
|
392
|
+
iofunc readOrWrite = isWrite ? (iofunc) write : (iofunc) read;
|
393
|
+
|
394
|
+
/* Release the torrent lock so the UI can still update itself if
|
395
|
+
this blocks for a while */
|
396
|
+
tr_lockUnlock( &tor->lock );
|
397
|
+
|
398
|
+
/* We don't ever read or write more than a piece at a time */
|
399
|
+
if( tr_pieceSize( piece ) < begin + size )
|
400
|
+
{
|
401
|
+
tr_err( "readOrWriteBytes: trying to write more than a piece" );
|
402
|
+
goto fail;
|
403
|
+
}
|
404
|
+
|
405
|
+
/* Find which file we shall start reading/writing in */
|
406
|
+
for( i = 0; i < inf->fileCount; i++ )
|
407
|
+
{
|
408
|
+
if( offset < inf->files[i].length )
|
409
|
+
{
|
410
|
+
/* This is the file */
|
411
|
+
break;
|
412
|
+
}
|
413
|
+
offset -= inf->files[i].length;
|
414
|
+
}
|
415
|
+
if( i >= inf->fileCount )
|
416
|
+
{
|
417
|
+
/* Should not happen */
|
418
|
+
tr_err( "readOrWriteBytes: offset out of range (%lld, %d, %d)",
|
419
|
+
offset, size, write );
|
420
|
+
goto fail;
|
421
|
+
}
|
422
|
+
|
423
|
+
while( size > 0 )
|
424
|
+
{
|
425
|
+
/* How much can we put or take with this file */
|
426
|
+
if( inf->files[i].length < offset + size )
|
427
|
+
{
|
428
|
+
cur = (int) ( inf->files[i].length - offset );
|
429
|
+
}
|
430
|
+
else
|
431
|
+
{
|
432
|
+
cur = size;
|
433
|
+
}
|
434
|
+
|
435
|
+
/* Now let's get a stream on the file... */
|
436
|
+
asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
|
437
|
+
file = tr_fdFileOpen( tor->fdlimit, path );
|
438
|
+
if( file < 0 )
|
439
|
+
{
|
440
|
+
tr_err( "readOrWriteBytes: could not open file '%s'", path );
|
441
|
+
free( path );
|
442
|
+
goto fail;
|
443
|
+
}
|
444
|
+
free( path );
|
445
|
+
|
446
|
+
/* seek to the right offset... */
|
447
|
+
if( lseek( file, offset, SEEK_SET ) < 0 )
|
448
|
+
{
|
449
|
+
goto fail;
|
450
|
+
}
|
451
|
+
|
452
|
+
/* do what we are here to do... */
|
453
|
+
if( readOrWrite( file, buf, cur ) != cur )
|
454
|
+
{
|
455
|
+
goto fail;
|
456
|
+
}
|
457
|
+
|
458
|
+
/* and close the stream. */
|
459
|
+
tr_fdFileRelease( tor->fdlimit, file );
|
460
|
+
|
461
|
+
/* 'cur' bytes done, 'size - cur' bytes to go with the next file */
|
462
|
+
i += 1;
|
463
|
+
offset = 0;
|
464
|
+
size -= cur;
|
465
|
+
buf += cur;
|
466
|
+
}
|
467
|
+
|
468
|
+
tr_lockLock( &tor->lock );
|
469
|
+
return 0;
|
470
|
+
|
471
|
+
fail:
|
472
|
+
tr_lockLock( &tor->lock );
|
473
|
+
return 1;
|
474
|
+
}
|
475
|
+
|
476
|
+
/***********************************************************************
|
477
|
+
* readSlot
|
478
|
+
***********************************************************************
|
479
|
+
*
|
480
|
+
**********************************************************************/
|
481
|
+
static int readOrWriteSlot( tr_io_t * io, int slot, uint8_t * buf,
|
482
|
+
int * size, int write )
|
483
|
+
{
|
484
|
+
tr_torrent_t * tor = io->tor;
|
485
|
+
tr_info_t * inf = &tor->info;
|
486
|
+
|
487
|
+
uint64_t offset = (uint64_t) slot * (uint64_t) inf->pieceSize;
|
488
|
+
|
489
|
+
*size = 0;
|
490
|
+
if( slot == inf->pieceCount - 1 )
|
491
|
+
{
|
492
|
+
*size = inf->totalSize % inf->pieceSize;
|
493
|
+
}
|
494
|
+
if( !*size )
|
495
|
+
{
|
496
|
+
*size = inf->pieceSize;
|
497
|
+
}
|
498
|
+
|
499
|
+
return readOrWriteBytes( io, offset, *size, buf, write );
|
500
|
+
}
|
501
|
+
|
502
|
+
static void invertSlots( tr_io_t * io, int slot1, int slot2 )
|
503
|
+
{
|
504
|
+
tr_torrent_t * tor = io->tor;
|
505
|
+
tr_info_t * inf = &tor->info;
|
506
|
+
|
507
|
+
uint8_t * buf1, * buf2;
|
508
|
+
int piece1, piece2, foo;
|
509
|
+
|
510
|
+
buf1 = calloc( inf->pieceSize, 1 );
|
511
|
+
buf2 = calloc( inf->pieceSize, 1 );
|
512
|
+
|
513
|
+
readSlot( io, slot1, buf1, &foo );
|
514
|
+
readSlot( io, slot2, buf2, &foo );
|
515
|
+
|
516
|
+
writeSlot( io, slot1, buf2, &foo );
|
517
|
+
writeSlot( io, slot2, buf1, &foo );
|
518
|
+
|
519
|
+
free( buf1 );
|
520
|
+
free( buf2 );
|
521
|
+
|
522
|
+
piece1 = io->slotPiece[slot1];
|
523
|
+
piece2 = io->slotPiece[slot2];
|
524
|
+
io->slotPiece[slot1] = piece2;
|
525
|
+
io->slotPiece[slot2] = piece1;
|
526
|
+
if( piece1 >= 0 )
|
527
|
+
{
|
528
|
+
io->pieceSlot[piece1] = slot2;
|
529
|
+
}
|
530
|
+
if( piece2 >= 0 )
|
531
|
+
{
|
532
|
+
io->pieceSlot[piece2] = slot1;
|
533
|
+
}
|
534
|
+
}
|
535
|
+
|
536
|
+
static void reorderPieces( tr_io_t * io )
|
537
|
+
{
|
538
|
+
tr_torrent_t * tor = io->tor;
|
539
|
+
tr_info_t * inf = &tor->info;
|
540
|
+
|
541
|
+
int i, didInvert;
|
542
|
+
|
543
|
+
/* Try to move pieces to their final places */
|
544
|
+
do
|
545
|
+
{
|
546
|
+
didInvert = 0;
|
547
|
+
|
548
|
+
for( i = 0; i < inf->pieceCount; i++ )
|
549
|
+
{
|
550
|
+
if( io->pieceSlot[i] < 0 )
|
551
|
+
{
|
552
|
+
/* We haven't started this piece yet */
|
553
|
+
continue;
|
554
|
+
}
|
555
|
+
if( io->pieceSlot[i] == i )
|
556
|
+
{
|
557
|
+
/* Already in place */
|
558
|
+
continue;
|
559
|
+
}
|
560
|
+
if( i >= io->slotsUsed )
|
561
|
+
{
|
562
|
+
/* File is not big enough yet */
|
563
|
+
continue;
|
564
|
+
}
|
565
|
+
|
566
|
+
/* Move piece i into slot i */
|
567
|
+
tr_inf( "invert %d and %d", io->pieceSlot[i], i );
|
568
|
+
invertSlots( io, i, io->pieceSlot[i] );
|
569
|
+
didInvert = 1;
|
570
|
+
}
|
571
|
+
} while( didInvert );
|
572
|
+
}
|
573
|
+
|
574
|
+
static void findSlotForPiece( tr_io_t * io, int piece )
|
575
|
+
{
|
576
|
+
int i;
|
577
|
+
#if 0
|
578
|
+
tr_torrent_t * tor = io->tor;
|
579
|
+
tr_info_t * inf = &tor->info;
|
580
|
+
|
581
|
+
tr_dbg( "Entering findSlotForPiece" );
|
582
|
+
|
583
|
+
for( i = 0; i < inf->pieceCount; i++ )
|
584
|
+
printf( "%02d ", io->slotPiece[i] );
|
585
|
+
printf( "\n" );
|
586
|
+
for( i = 0; i < inf->pieceCount; i++ )
|
587
|
+
printf( "%02d ", io->pieceSlot[i] );
|
588
|
+
printf( "\n" );
|
589
|
+
#endif
|
590
|
+
|
591
|
+
/* Look for an empty slot somewhere */
|
592
|
+
for( i = 0; i < io->slotsUsed; i++ )
|
593
|
+
{
|
594
|
+
if( io->slotPiece[i] < 0 )
|
595
|
+
{
|
596
|
+
io->pieceSlot[piece] = i;
|
597
|
+
io->slotPiece[i] = piece;
|
598
|
+
goto reorder;
|
599
|
+
}
|
600
|
+
}
|
601
|
+
|
602
|
+
/* No empty slot, extend the file */
|
603
|
+
io->pieceSlot[piece] = io->slotsUsed;
|
604
|
+
io->slotPiece[io->slotsUsed] = piece;
|
605
|
+
(io->slotsUsed)++;
|
606
|
+
|
607
|
+
reorder:
|
608
|
+
reorderPieces( io );
|
609
|
+
|
610
|
+
#if 0
|
611
|
+
for( i = 0; i < inf->pieceCount; i++ )
|
612
|
+
printf( "%02d ", io->slotPiece[i] );
|
613
|
+
printf( "\n" );
|
614
|
+
for( i = 0; i < inf->pieceCount; i++ )
|
615
|
+
printf( "%02d ", io->pieceSlot[i] );
|
616
|
+
printf( "\n" );
|
617
|
+
|
618
|
+
printf( "Leaving findSlotForPiece\n" );
|
619
|
+
#endif
|
620
|
+
}
|