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

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

debugging messages for BMW's crash

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