source: branches/file_selection/libtransmission/makemeta.c @ 2097

Last change on this file since 2097 was 2097, checked in by charles, 15 years ago

more rough work on makemeta. not stable yet.

File size: 11.1 KB
Line 
1/******************************************************************************
2 * $Id:$
3 *
4 * Copyright (c) 2005-2007 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 <sys/types.h>
26#include <sys/stat.h>
27#include <unistd.h>
28#include <libgen.h>
29#include <dirent.h>
30#include <stdio.h> /* FILE, snprintf, stderr */
31#include <stdlib.h> /* malloc, calloc */
32
33#include "transmission.h"
34#include "internal.h" /* for tr_torrent_t */
35#include "bencode.h"
36#include "makemeta.h"
37#include "version.h"
38
39/****
40*****
41****/
42
43struct FileList
44{
45    struct FileList * next;
46    char filename[MAX_PATH_LENGTH];
47};
48
49static struct FileList*
50getFiles( const char        * dir,
51          const char        * base,
52          struct FileList   * list )
53{
54    char buf[MAX_PATH_LENGTH];
55    struct stat sb;
56    sb.st_size = 0;
57
58    snprintf( buf, sizeof(buf), "%s"TR_PATH_DELIMITER_STR"%s", dir, base );
59    stat( buf, &sb );
60
61    if ( S_ISDIR( sb.st_mode ) )
62    {
63        DIR * odir = opendir( buf );
64        struct dirent *d;
65        for (d = readdir( odir ); d!=NULL; d=readdir( odir ) )
66            if( strcmp( d->d_name,"." ) && strcmp( d->d_name,".." ) )
67                list = getFiles( buf, d->d_name, list );
68        closedir( odir );
69    }
70    else if( S_ISREG( sb.st_mode ) )
71    {
72        struct FileList * node = malloc( sizeof( struct FileList ) );
73        snprintf( node->filename, sizeof( node->filename ), "%s", buf );
74        node->next = list;
75        list = node;
76    }
77
78    return list;
79}
80
81static void
82freeFileList( struct FileList * list )
83{
84    while( list ) {
85        struct FileList * tmp = list->next;
86        free( list );
87        list = tmp;
88    }
89}
90
91static off_t
92getFileSize ( const char * filename )
93{
94    struct stat sb;
95    sb.st_size = 0;
96    stat( filename, &sb );
97    return sb.st_size;
98}
99
100#define MiB 1048576ul
101#define GiB 1073741824ul
102
103static size_t
104bestPieceSize( size_t totalSize )
105{
106    /* almost always best to have a piee size of 512 or 256 kb.
107       common practice seems to be to bump up to 1MB pieces at
108       at total size of around 8GiB or so */
109
110    if (totalSize >= (8 * GiB) )
111        return MiB;
112
113    if (totalSize >= GiB )
114        return MiB / 2;
115
116    return MiB / 4;
117}
118
119/****
120*****
121****/
122
123static int pstrcmp( const void * va, const void * vb)
124{
125    const char * a = *(const char**) va;
126    const char * b = *(const char**) vb;
127    return strcmp( a, b );
128}
129
130meta_info_builder_t*
131tr_metaInfoBuilderCreate( const char * topFile )
132{
133    size_t i;
134    struct FileList * files;
135    const struct FileList * walk;
136    meta_info_builder_t * ret = calloc( 1, sizeof(meta_info_builder_t) );
137    ret->top = tr_strdup( topFile );
138
139    if (1) {
140        struct stat sb;
141        stat( topFile, &sb );
142        ret->isSingleFile = !S_ISDIR( sb.st_mode );
143    }
144
145    /* build a list of files containing topFile and,
146       if it's a directory, all of its children */
147    if (1) {
148        char *dir, *base;
149        char dirbuf[MAX_PATH_LENGTH];
150        char basebuf[MAX_PATH_LENGTH];
151        strlcpy( dirbuf, topFile, sizeof( dirbuf ) );
152        strlcpy( basebuf, topFile, sizeof( basebuf ) );
153        dir = dirname( dirbuf );
154        base = basename( basebuf );
155        files = getFiles( dir, base, NULL );
156        assert( files != NULL  );
157    }
158
159    for( walk=files; walk!=NULL; walk=walk->next )
160        ++ret->fileCount;
161    ret->files = calloc( ret->fileCount, sizeof(char*) );
162    ret->fileLengths = calloc( ret->fileCount, sizeof(size_t) );
163
164    for( i=0, walk=files; walk!=NULL; walk=walk->next, ++i )
165        ret->files[i] = tr_strdup( walk->filename );
166
167    qsort( ret->files, ret->fileCount, sizeof(char*), pstrcmp );
168    for( i=0; i<ret->fileCount; ++i ) {
169        ret->fileLengths[i] = getFileSize( ret->files[i] );
170        ret->totalSize += ret->fileLengths[i];
171    }
172
173    freeFileList( files );
174   
175    ret->pieceSize = bestPieceSize( ret->totalSize );
176    ret->pieceCount = MAX( 1, ret->totalSize / ret->pieceSize );
177    if( ret->totalSize % ret->pieceSize )
178        ++ret->pieceCount;
179
180    return ret;
181}
182
183void
184tr_metaInfoBuilderFree( meta_info_builder_t * builder )
185{
186    size_t i;
187
188    for( i=0; i<builder->fileCount; ++i )
189        free( builder->files[i] );
190    free( builder->top );
191    free( builder );
192}
193
194/****
195*****
196****/
197
198static uint8_t*
199getHashInfo ( const meta_info_builder_t  * builder,
200              int                        * setmeCount )
201{
202    size_t i;
203    tr_torrent_t t;
204    uint8_t *ret, *walk;
205    const size_t topLen = strlen(builder->top) + 1; /* +1 for '/' */
206
207    /* build a mock tr_torrent_t that we can feed to tr_ioRecalculateHash() */ 
208    memset( &t, 0, sizeof( tr_torrent_t ) );
209    tr_lockInit ( &t.lock );
210    t.destination = (char*) builder->top;
211    t.info.fileCount = builder->fileCount;
212    t.info.files = calloc( t.info.fileCount, sizeof( tr_file_t ) );
213    t.info.totalSize = builder->totalSize;
214    t.info.pieceSize = builder->pieceSize;
215    t.info.pieceCount = builder->pieceCount;
216    for( i=0; i<builder->fileCount; ++i ) {
217        tr_file_t * file = &t.info.files[i];
218        file->length = builder->fileLengths[i];
219        strlcpy( file->name, builder->files[i]+topLen, sizeof(file->name) );
220    }
221    t.info.pieces = calloc( t.info.pieceCount, sizeof( tr_piece_t ) );
222    tr_torrentInitFilePieces( &t );
223    ret = (uint8_t*) malloc ( SHA_DIGEST_LENGTH * t.info.pieceCount );
224    walk = ret;
225    for( i=0; i<(size_t)t.info.pieceCount; ++i ) {
226        tr_ioRecalculateHash( &t, i, walk );
227        walk += SHA_DIGEST_LENGTH;
228    }
229    assert( walk-ret == SHA_DIGEST_LENGTH*t.info.pieceCount );
230
231    *setmeCount = t.info.pieceCount;
232    tr_lockClose ( &t.lock );
233    free( t.info.pieces );
234    free( t.info.files );
235    return ret;
236}
237
238static void
239getFileInfo( const char * topFile,
240             const char * filename,
241             benc_val_t * uninitialized_length,
242             benc_val_t * uninitialized_path )
243{
244    benc_val_t *sub;
245    const char *pch, *prev;
246    const size_t topLen = strlen(topFile) + 1; /* +1 for '/' */
247    int n;
248
249    /* get the file size */
250    tr_bencInitInt( uninitialized_length, getFileSize(filename) );
251
252    /* the path list */
253    n = 1;
254    for( pch=filename+topLen; *pch; ++pch )
255        if (*pch == TR_PATH_DELIMITER)
256            ++n;
257    tr_bencInit( uninitialized_path, TYPE_LIST );
258    tr_bencListReserve( uninitialized_path, n );
259    for( prev=pch=filename+topLen; ; ++pch )
260    {
261        char buf[MAX_PATH_LENGTH];
262
263        if (*pch && *pch!=TR_PATH_DELIMITER )
264            continue;
265
266        memcpy( buf, prev, pch-prev );
267        buf[pch-prev] = '\0';
268        fprintf ( stderr, "adding [%s] to the list of paths\n", buf );
269
270        sub = tr_bencListAdd( uninitialized_path );
271        tr_bencInitStrDup( sub, buf );
272
273        prev = pch + 1;
274        if (!*pch)
275           break;
276    }
277}
278
279static void
280makeFilesList( benc_val_t                 * list,
281               const meta_info_builder_t  * builder )
282{
283    size_t i = 0;
284
285    tr_bencListReserve( list, builder->fileCount );
286
287    for( i=0; i<builder->fileCount; ++i )
288    {
289        benc_val_t * dict = tr_bencListAdd( list );
290        benc_val_t *length, *pathVal;
291
292        tr_bencInit( dict, TYPE_DICT );
293        tr_bencDictReserve( dict, 2 );
294        length = tr_bencDictAdd( dict, "length" );
295        pathVal = tr_bencDictAdd( dict, "path" );
296        getFileInfo( builder->top, builder->files[i], length, pathVal );
297    }
298}
299
300static void
301makeInfoDict ( benc_val_t                 * dict,
302               const meta_info_builder_t  * builder,
303               int                          isPrivate )
304{
305    uint8_t * pch;
306    int pieceCount = 0;
307    benc_val_t * val;
308    char base[MAX_PATH_LENGTH];
309
310    tr_bencDictReserve( dict, 5 );
311
312    val = tr_bencDictAdd( dict, "name" );
313    strlcpy( base, builder->top, sizeof( base ) );
314    tr_bencInitStrDup ( val, basename( base ) );
315
316    val = tr_bencDictAdd( dict, "piece length" );
317    tr_bencInitInt( val, builder->pieceSize );
318
319    pch = getHashInfo( builder, &pieceCount );
320    val = tr_bencDictAdd( dict, "pieces" );
321    tr_bencInitStr( val, pch, SHA_DIGEST_LENGTH * pieceCount, 0 );
322
323    if ( builder->isSingleFile )
324    {
325        val = tr_bencDictAdd( dict, "length" );
326        tr_bencInitInt( val, builder->fileLengths[0] );
327    }
328    else
329    {
330        val = tr_bencDictAdd( dict, "files" );
331        tr_bencInit( val, TYPE_LIST );
332        makeFilesList( val, builder );
333    }
334
335    val = tr_bencDictAdd( dict, "private" );
336    tr_bencInitInt( val, isPrivate ? 1 : 0 );
337}
338
339/* if outputFile is NULL, builder->top + ".torrent" is used */
340int
341tr_makeMetaInfo( const meta_info_builder_t  * builder,
342                 const char                 * outputFile,
343                 const char                 * announce,
344                 const char                 * comment,
345                 int                          isPrivate )
346{
347    int n = 5;
348    benc_val_t top, *val;
349
350    tr_bencInit ( &top, TYPE_DICT );
351    if ( comment && *comment ) ++n;
352    tr_bencDictReserve( &top, n );
353
354        val = tr_bencDictAdd( &top, "announce" );
355        tr_bencInitStrDup( val, announce );
356
357        val = tr_bencDictAdd( &top, "created by" );
358        tr_bencInitStrDup( val, TR_NAME " " VERSION_STRING );
359
360        val = tr_bencDictAdd( &top, "creation date" );
361        tr_bencInitInt( val, time(0) );
362
363        val = tr_bencDictAdd( &top, "encoding" );
364        tr_bencInitStrDup( val, "UTF-8" );
365
366        if( comment && *comment ) {
367            val = tr_bencDictAdd( &top, "comment" );
368            tr_bencInitStrDup( val, comment );
369        }
370
371        val = tr_bencDictAdd( &top, "info" );
372        tr_bencInit( val, TYPE_DICT );
373        tr_bencDictReserve( val, 666 );
374        makeInfoDict( val, builder, isPrivate );
375
376    /* debugging... */
377    tr_bencPrint( &top );
378
379    /* save the file */
380    if (1) {
381        char out[MAX_PATH_LENGTH];
382        FILE * fp;
383        char * pch;
384        if ( !outputFile || !*outputFile ) {
385            snprintf( out, sizeof(out), "%s.torrent", builder->top);
386            outputFile = out;
387        }
388        fp = fopen( outputFile, "wb+" );
389        pch = tr_bencSaveMalloc( &top, &n );
390        fwrite( pch, n, 1, fp );
391        free( pch );
392        fclose( fp );
393    }
394
395    /* cleanup */
396    tr_bencFree( & top );
397    return 0;
398}
Note: See TracBrowser for help on using the repository browser.