Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:seilerphilipp:opentracker
mktorrent
update_to_de7d011b.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File update_to_de7d011b.patch of Package mktorrent
diff --git a/BSDmakefile b/BSDmakefile index 6299527..9dec566 100644 --- a/BSDmakefile +++ b/BSDmakefile @@ -18,7 +18,7 @@ .include "Makefile" CC ?= cc -CFLAGS ?= -O2 -Wall +CFLAGS ?= -O2 -Wall -Wextra -Wpedantic INSTALL ?= install PREFIX ?= /usr/local @@ -60,8 +60,8 @@ all: $(program) .SUFFIXES: .o .c .c.o: - $(CC) $(CFLAGS) $(DEFINES) -DPRIoff="\"`./prefix`d\"" -DVERSION="\"$(version)\"" -c $(.IMPSRC) + $(CC) $(CFLAGS) $(DEFINES) -DVERSION="\"$(version)\"" -c $(.IMPSRC) -$(OBJS): $(HEADERS) prefix +$(OBJS): $(HEADERS) .include "rules.mk" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c7505f9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,75 @@ +# Change Log +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/) +and this project adheres to [Semantic Versioning](https://semver.org/). + +## [Unreleased] +### Added +- `-e`/`--exclude` option to exclude files/directories based on `glob(7)` patterns. ([#56](https://github.com/pobrn/mktorrent/pull/56)) +- Automatic piece length calculation. ([#55](https://github.com/pobrn/mktorrent/pull/55)) +- `CHANGELOG.md` + +## [1.1] - 2017-01-11 +### Added +- Autodetect the number of CPUs available <esmil@mailme.dk> +- Option for source string added to torrent info (`-s`), included in infohash. + Often used by private trackers to create a unique infohash to prevent + peer-leak and the possibility to track the trackers that do use leaked + torrents. Having this option in mktorrent make it possible to create a + infohash accurate torrent to the tracker you want to upload it to +### Changed +- Make `-a` (announce list) optional +- Optional announce URL even for private torrents. No need to require announce + for private torrents, they are added by most private trackers anyway and they + modify the infohash so you'd have to redownload their modified torrent with + injected unique announce URL anyway. (@mathieui, fix from @jrwren) +### Fixed +- DHT makes trackers optional, so remove the warning + +## [1.0] - 2009-08-25 +### Added +- Add an exception to the license to allow distributions to link mktorrent to the OpenSSL library + +## [0.7] - 2009-05-31 +### Fixed +- Proper support for large files on certain 32bit OS's. + Finally mktorrent properly handles files larger than 2Gb on 32bit Linux. + Fixes for use on Windows under MinGW/Cygwin. + +## [0.6] - 2009-05-13 +### Added +- Support for multiple web seeds +### Changed +- Raised allowable piece size +- Only add the announce-list entry if there are more than one tracker. + Thanks to Vladimir Vučićević for reporting. +- Include public domain SHA1 implementation to make the OpenSSL dependency optional. +- Lots of portability improvements. Now compiles on Linux, MinGW, OpenBSD, OSX, + SunOS and probably many more. A big thanks to Edwin Amsler for reporting and + testing. +- Rewrote multi threaded hashing to optimise CPU usage on multi processor machines + +## [0.5] - 2009-03-01 +### Added +- Support for multiple trackers +- Support for web seed (thanks to Justin Camerer) +### Changed +- Better error messages + +## [0.4] - 2009-02-22 +### Added +- Support for the private flag + +## [0.3] - 2009-02-22 +### Fixed +- Bug concerning files longer than 2^32 bytes +- Command line parameters + +## [0.2] - 2009-02-22 +### Added +- Support for single file torrents +- Support for long options can now be disabled at compile time + +## [0.1] - 2009-02-22 +Initial release diff --git a/GNUmakefile b/GNUmakefile index 29244ab..68c78ee 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -18,7 +18,7 @@ include Makefile CC ?= cc -CFLAGS ?= -O2 -Wall +CFLAGS ?= -O2 -Wall -Wextra -Wpedantic INSTALL ?= install PREFIX ?= /usr/local @@ -54,13 +54,11 @@ ifdef DEBUG DEFINES += -DDEBUG endif -OFFPRFX = $(shell ./prefix) - OBJS = $(SRCS:.c=.o) all: $(program) -%.o: %.c $(HEADERS) prefix - $(CC) $(CFLAGS) $(DEFINES) -DPRIoff="\"$(OFFPRFX)d\"" -DVERSION="\"$(version)\"" -c $< +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) $(DEFINES) -DVERSION="\"$(version)\"" -c $< include rules.mk diff --git a/Makefile b/Makefile index e4134fe..998db3f 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ # Default settings shown #CC = cc -#CFLAGS = -O2 -Wall +#CFLAGS = -O2 -Wall -Wextra -Wpedantic #LDFLAGS = #INSTALL = install #PREFIX = /usr/local @@ -65,5 +65,5 @@ program = mktorrent version = 1.1 -HEADERS = mktorrent.h -SRCS = ftw.c init.c sha1.c hash.c output.c main.c +HEADERS = mktorrent.h ll.h +SRCS = ftw.c init.c sha1.c hash.c output.c main.c msg.c ll.c diff --git a/README b/README index 845faf3..482964c 100644 --- a/README +++ b/README @@ -5,4 +5,4 @@ For more options look in the Makefile. If you use an old version of BSD's make, you might need make -f BSDmakefile -See http://mktorrent.sourceforge.net/ for more info. +See https://github.com/pobrn/mktorrent/wiki for more info. diff --git a/export.h b/export.h new file mode 100644 index 0000000..47901d8 --- /dev/null +++ b/export.h @@ -0,0 +1,10 @@ +#ifndef MKTORRENT_EXPORT_H +#define MKTORRENT_EXPORT_H + +#ifdef ALLINONE +#define EXPORT static +#else +#define EXPORT +#endif /* ALLINONE */ + +#endif /* MKTORRENT_EXPORT_H */ diff --git a/ftw.c b/ftw.c index a7f013d..722ebb3 100644 --- a/ftw.c +++ b/ftw.c @@ -16,7 +16,8 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#ifndef ALLINONE + + #include <stdlib.h> #include <errno.h> #include <string.h> @@ -25,18 +26,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include <sys/stat.h> #include <unistd.h> #include <dirent.h> +#include <stdbool.h> +#include <fnmatch.h> -#ifdef _WIN32 -#define DIRSEP_CHAR '\\' -#else -#define DIRSEP_CHAR '/' -#endif /* _WIN32 */ - -#define EXPORT -#endif /* ALLINONE */ - +#include "export.h" +#include "mktorrent.h" /* DIRSEP_CHAR */ #include "ftw.h" + struct dir_state { struct dir_state *next; struct dir_state *prev; @@ -51,7 +48,7 @@ static struct dir_state *dir_state_new(struct dir_state *prev, struct dir_state *ds = malloc(sizeof(struct dir_state)); if (ds == NULL) { - fprintf(stderr, "Out of memory.\n"); + fprintf(stderr, "fatal error: out of memory\n"); return NULL; } @@ -66,7 +63,7 @@ static unsigned int dir_state_open(struct dir_state *ds, const char *name, { ds->dir = opendir(name); if (ds->dir == NULL) { - fprintf(stderr, "Error opening '%s': %s\n", + fprintf(stderr, "fatal error: cannot open '%s': %s\n", name, strerror(errno)); return 1; } @@ -81,7 +78,7 @@ static unsigned int dir_state_reopen(struct dir_state *ds, char *name) name[ds->length] = '\0'; ds->dir = opendir(name); if (ds->dir == NULL) { - fprintf(stderr, "Error opening '%s': %s\n", + fprintf(stderr, "fatal error: cannot open '%s': %s\n", name, strerror(errno)); return 1; } @@ -97,13 +94,13 @@ static unsigned int dir_state_close(struct dir_state *ds) { ds->offset = telldir(ds->dir); if (ds->offset < 0) { - fprintf(stderr, "Error getting dir offset: %s\n", + fprintf(stderr, "fatal error: cannot obtain dir offset: %s\n", strerror(errno)); return 1; } if (closedir(ds->dir)) { - fprintf(stderr, "Error closing directory: %s\n", + fprintf(stderr, "fatal error: cannot close directory: %s\n", strerror(errno)); return 1; } @@ -140,13 +137,14 @@ EXPORT int file_tree_walk(const char *dirname, unsigned int nfds, struct dir_state *ds = dir_state_new(NULL, NULL); struct dir_state *first_open; unsigned int nopen; + struct metafile *m = data; if (ds == NULL) return -1; path = malloc(path_size); if (path == NULL) { - fprintf(stderr, "Out of memory.\n"); + fprintf(stderr, "fatal error: out of memory\n"); return cleanup(ds, NULL, -1); } path_max = path + path_size; @@ -161,7 +159,7 @@ EXPORT int file_tree_walk(const char *dirname, unsigned int nfds, new_path = realloc(path, 2*path_size); if (new_path == NULL) { - fprintf(stderr, "Out of memory.\n"); + fprintf(stderr, "fatal error: out of memory\n"); return cleanup(ds, path, -1); } end = new_path + path_size; @@ -200,6 +198,21 @@ EXPORT int file_tree_walk(const char *dirname, unsigned int nfds, continue; } + bool should_skip = false; + LL_FOR(exclude_node, m->exclude_list) { + const char *exclude_pattern = LL_DATA(exclude_node); + if (fnmatch(exclude_pattern, de->d_name, 0) != FNM_NOMATCH) { + should_skip = true; + break; + } + } + + if (should_skip) { + if (m->verbose) + printf("skipping %s\n", de->d_name); + continue; + } + end = path + ds->length + 1; p = de->d_name; while ((*end = *p)) { @@ -210,7 +223,7 @@ EXPORT int file_tree_walk(const char *dirname, unsigned int nfds, new_path = realloc(path, 2*path_size); if (new_path == NULL) { - fprintf(stderr, "Out of memory.\n"); + fprintf(stderr, "fatal error: out of memory\n"); return cleanup(ds, path, -1); } end = new_path + path_size; @@ -221,7 +234,7 @@ EXPORT int file_tree_walk(const char *dirname, unsigned int nfds, } if (stat(path, &sbuf)) { - fprintf(stderr, "Error stat'ing '%s': %s\n", + fprintf(stderr, "fatal error: cannot stat '%s': %s\n", path, strerror(errno)); return cleanup(ds, path, -1); } @@ -254,7 +267,7 @@ EXPORT int file_tree_walk(const char *dirname, unsigned int nfds, } else { if (closedir(ds->dir)) { path[ds->length] = '\0'; - fprintf(stderr, "Error closing '%s': %s\n", + fprintf(stderr, "fatal error: cannot close '%s': %s\n", path, strerror(errno)); return cleanup(ds, path, -1); } diff --git a/ftw.h b/ftw.h index 66860f9..4386230 100644 --- a/ftw.h +++ b/ftw.h @@ -1,13 +1,13 @@ -#ifndef _FTW_H -#define _FTW_H +#ifndef MKTORRENT_FTW_H +#define MKTORRENT_FTW_H + +#include "export.h" typedef int (*file_tree_walk_cb)(const char *name, const struct stat *sbuf, void *data); -#ifndef ALLINONE -int file_tree_walk(const char *dirname, unsigned int nfds, +EXPORT int file_tree_walk(const char *dirname, unsigned int nfds, file_tree_walk_cb callback, void *data); -#endif /* ALLINONE */ -#endif /* _FTW_H */ +#endif /* MKTORRENT_FTW_H */ diff --git a/hash.c b/hash.c index 6ac1ddf..0cb7aa1 100644 --- a/hash.c +++ b/hash.c @@ -16,26 +16,27 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#ifndef ALLINONE + + #include <stdlib.h> /* exit() */ -#include <sys/types.h> /* off_t */ #include <errno.h> /* errno */ #include <string.h> /* strerror() */ #include <stdio.h> /* printf() etc. */ #include <fcntl.h> /* open() */ #include <unistd.h> /* read(), close() */ +#include <inttypes.h> /* PRId64 etc. */ #ifdef USE_OPENSSL #include <openssl/sha.h> /* SHA1() */ #else -#include <inttypes.h> #include "sha1.h" #endif +#include "export.h" #include "mktorrent.h" - -#define EXPORT -#endif /* ALLINONE */ +#include "hash.h" +#include "msg.h" +#include "ll.h" #ifndef O_BINARY #define O_BINARY 0 @@ -47,24 +48,24 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #define OPENFLAGS (O_RDONLY | O_BINARY) #endif + /* * go through the files in file_list, split their contents into pieces * of size piece_length and create the hash string, which is the * concatenation of the (20 byte) SHA1 hash of every piece * last piece may be shorter */ -EXPORT unsigned char *make_hash(metafile_t *m) +EXPORT unsigned char *make_hash(struct metafile *m) { - flist_t *f; /* pointer to a place in the file list */ unsigned char *hash_string; /* the hash string */ unsigned char *pos; /* position in the hash string */ unsigned char *read_buf; /* read buffer */ int fd; /* file descriptor */ - ssize_t r; /* number of bytes read from file(s) into + size_t r; /* number of bytes read from file(s) into the read buffer */ SHA_CTX c; /* SHA1 hashing context */ #ifndef NO_HASH_CHECK - off_t counter = 0; /* number of bytes hashed + uintmax_t counter = 0; /* number of bytes hashed should match size when done */ #endif @@ -75,25 +76,20 @@ EXPORT unsigned char *make_hash(metafile_t *m) read_buf = malloc(m->piece_length); /* check if we've run out of memory */ - if (hash_string == NULL || read_buf == NULL) { - fprintf(stderr, "Out of memory.\n"); - exit(EXIT_FAILURE); - } + FATAL_IF0(hash_string == NULL || read_buf == NULL, "out of memory\n"); /* initiate pos to point to the beginning of hash_string */ pos = hash_string; /* and initiate r to 0 since we haven't read anything yet */ r = 0; /* go through all the files in the file list */ - for (f = m->file_list; f; f = f->next) { + LL_FOR(file_node, m->file_list) { + struct file_data *f = LL_DATA_AS(file_node, struct file_data*); /* open the current file for reading */ - if ((fd = open(f->path, OPENFLAGS)) == -1) { - fprintf(stderr, "Error opening '%s' for reading: %s\n", - f->path, strerror(errno)); - exit(EXIT_FAILURE); - } - printf("Hashing %s.\n", f->path); + FATAL_IF((fd = open(f->path, OPENFLAGS)) == -1, + "cannot open '%s' for reading: %s\n", f->path, strerror(errno)); + printf("hashing %s\n", f->path); fflush(stdout); /* fill the read buffer with the contents of the file and append @@ -102,12 +98,8 @@ EXPORT unsigned char *make_hash(metafile_t *m) to the end of the file */ while (1) { ssize_t d = read(fd, read_buf + r, m->piece_length - r); - - if (d < 0) { - fprintf(stderr, "Error reading from '%s': %s\n", - f->path, strerror(errno)); - exit(EXIT_FAILURE); - } + FATAL_IF(d < 0, "cannot read from '%s': %s\n", + f->path, strerror(errno)); if (d == 0) /* end of file */ break; @@ -127,11 +119,8 @@ EXPORT unsigned char *make_hash(metafile_t *m) } /* now close the file */ - if (close(fd)) { - fprintf(stderr, "Error closing '%s': %s\n", - f->path, strerror(errno)); - exit(EXIT_FAILURE); - } + FATAL_IF(close(fd), "cannot close '%s': %s\n", + f->path, strerror(errno)); } /* finally append the hash of the last irregular piece to the hash string */ @@ -143,12 +132,10 @@ EXPORT unsigned char *make_hash(metafile_t *m) #ifndef NO_HASH_CHECK counter += r; - if (counter != m->size) { - fprintf(stderr, "Counted %" PRIoff " bytes, " - "but hashed %" PRIoff " bytes. " - "Something is wrong...\n", m->size, counter); - exit(EXIT_FAILURE); - } + FATAL_IF(counter != m->size, + "counted %" PRIuMAX " bytes, but hashed %" PRIuMAX " bytes; " + "something is wrong...\n", + m->size, counter); #endif /* free the read buffer before we return */ diff --git a/hash.h b/hash.h new file mode 100644 index 0000000..02ff4b9 --- /dev/null +++ b/hash.h @@ -0,0 +1,9 @@ +#ifndef MKTORRENT_HASH_H +#define MKTORRENT_HASH_H + +#include "export.h" /* EXPORT */ +#include "mktorrent.h" /* struct metafile */ + +EXPORT unsigned char *make_hash(struct metafile *m); + +#endif /* MKTORRENT_HASH_H */ diff --git a/hash_pthreads.c b/hash_pthreads.c index 84143b6..ec1a686 100644 --- a/hash_pthreads.c +++ b/hash_pthreads.c @@ -16,27 +16,29 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#ifndef ALLINONE -#include <stdlib.h> /* exit(), malloc() */ -#include <sys/types.h> /* off_t */ -#include <errno.h> /* errno */ -#include <string.h> /* strerror() */ -#include <stdio.h> /* printf() etc. */ -#include <fcntl.h> /* open() */ -#include <unistd.h> /* access(), read(), close() */ + + +#include <stdlib.h> /* exit(), malloc() */ +#include <sys/types.h> /* off_t */ +#include <errno.h> /* errno */ +#include <string.h> /* strerror() */ +#include <stdio.h> /* printf() etc. */ +#include <fcntl.h> /* open() */ +#include <unistd.h> /* read(), close() */ +#include <inttypes.h> /* PRId64 etc. */ +#include <pthread.h> +#include <time.h> /* nanosleep() */ + #ifdef USE_OPENSSL -#include <openssl/sha.h> /* SHA1() */ +#include <openssl/sha.h> /* SHA1() */ #else -#include <inttypes.h> #include "sha1.h" #endif -#include <pthread.h> /* pthread functions and data structures */ +#include "export.h" #include "mktorrent.h" - -#define EXPORT -#endif /* ALLINONE */ - +#include "hash.h" +#include "msg.h" #ifndef PROGRESS_PERIOD #define PROGRESS_PERIOD 200000 @@ -52,20 +54,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #define OPENFLAGS (O_RDONLY | O_BINARY) #endif -struct piece_s; -typedef struct piece_s piece_t; -struct piece_s { - piece_t *next; + +struct piece { + struct piece *next; unsigned char *dest; unsigned long len; unsigned char data[1]; }; -struct queue_s; -typedef struct queue_s queue_t; -struct queue_s { - piece_t *free; - piece_t *full; +struct queue { + struct piece *free; + struct piece *full; unsigned int buffers_max; unsigned int buffers; pthread_mutex_t mutex_free; @@ -77,20 +76,18 @@ struct queue_s { unsigned int pieces_hashed; }; -static piece_t *get_free(queue_t *q, size_t piece_length) +static struct piece *get_free(struct queue *q, size_t piece_length) { - piece_t *r; + struct piece *r; pthread_mutex_lock(&q->mutex_free); if (q->free) { r = q->free; q->free = r->next; } else if (q->buffers < q->buffers_max) { - r = malloc(sizeof(piece_t) - 1 + piece_length); - if (r == NULL) { - fprintf(stderr, "Out of memory.\n"); - exit(EXIT_FAILURE); - } + r = malloc(sizeof(struct piece) - 1 + piece_length); + FATAL_IF0(r == NULL, "out of memory\n"); + q->buffers++; } else { @@ -106,9 +103,9 @@ static piece_t *get_free(queue_t *q, size_t piece_length) return r; } -static piece_t *get_full(queue_t *q) +static struct piece *get_full(struct queue *q) { - piece_t *r; + struct piece *r; pthread_mutex_lock(&q->mutex_full); again: @@ -126,7 +123,7 @@ again: return r; } -static void put_free(queue_t *q, piece_t *p, unsigned int hashed) +static void put_free(struct queue *q, struct piece *p, unsigned int hashed) { pthread_mutex_lock(&q->mutex_free); p->next = q->free; @@ -136,7 +133,7 @@ static void put_free(queue_t *q, piece_t *p, unsigned int hashed) pthread_cond_signal(&q->cond_full); } -static void put_full(queue_t *q, piece_t *p) +static void put_full(struct queue *q, struct piece *p) { pthread_mutex_lock(&q->mutex_full); p->next = q->full; @@ -145,7 +142,7 @@ static void put_full(queue_t *q, piece_t *p) pthread_cond_signal(&q->cond_empty); } -static void set_done(queue_t *q) +static void set_done(struct queue *q) { pthread_mutex_lock(&q->mutex_full); q->done = 1; @@ -153,12 +150,12 @@ static void set_done(queue_t *q) pthread_cond_broadcast(&q->cond_empty); } -static void free_buffers(queue_t *q) +static void free_buffers(struct queue *q) { - piece_t *first = q->free; + struct piece *first = q->free; while (first) { - piece_t *p = first; + struct piece *p = first; first = p->next; free(p); } @@ -171,22 +168,22 @@ static void free_buffers(queue_t *q) */ static void *print_progress(void *data) { - queue_t *q = data; + struct queue *q = data; int err; + struct timespec t; + + t.tv_sec = PROGRESS_PERIOD / 1000000; + t.tv_nsec = PROGRESS_PERIOD % 1000000 * 1000; err = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - if (err) { - fprintf(stderr, "Error setting thread cancel type: %s\n", - strerror(err)); - exit(EXIT_FAILURE); - } + FATAL_IF(err, "cannot set thread cancel type: %s\n", strerror(err)); while (1) { /* print progress and flush the buffer immediately */ printf("\rHashed %u of %u pieces.", q->pieces_hashed, q->pieces); fflush(stdout); /* now sleep for PROGRESS_PERIOD microseconds */ - usleep(PROGRESS_PERIOD); + nanosleep(&t, NULL); } return NULL; @@ -194,8 +191,8 @@ static void *print_progress(void *data) static void *worker(void *data) { - queue_t *q = data; - piece_t *p; + struct queue *q = data; + struct piece *p; SHA_CTX c; while ((p = get_full(q))) { @@ -208,36 +205,30 @@ static void *worker(void *data) return NULL; } -static void read_files(metafile_t *m, queue_t *q, unsigned char *pos) +static void read_files(struct metafile *m, struct queue *q, unsigned char *pos) { - int fd; /* file descriptor */ - flist_t *f; /* pointer to a place in the file list */ - ssize_t r = 0; /* number of bytes read from file(s) - into the read buffer */ + int fd; /* file descriptor */ + size_t r = 0; /* number of bytes read from file(s) + into the read buffer */ #ifndef NO_HASH_CHECK - off_t counter = 0; /* number of bytes hashed - should match size when done */ + uintmax_t counter = 0; /* number of bytes hashed + should match size when done */ #endif - piece_t *p = get_free(q, m->piece_length); + struct piece *p = get_free(q, m->piece_length); /* go through all the files in the file list */ - for (f = m->file_list; f; f = f->next) { + LL_FOR(file_node, m->file_list) { + struct file_data *f = LL_DATA_AS(file_node, struct file_data*); /* open the current file for reading */ - if ((fd = open(f->path, OPENFLAGS)) == -1) { - fprintf(stderr, "Error opening '%s' for reading: %s\n", - f->path, strerror(errno)); - exit(EXIT_FAILURE); - } + FATAL_IF((fd = open(f->path, OPENFLAGS)) == -1, + "cannot open '%s' for reading: %s\n", f->path, strerror(errno)); while (1) { ssize_t d = read(fd, p->data + r, m->piece_length - r); - if (d < 0) { - fprintf(stderr, "Error reading from '%s': %s\n", - f->path, strerror(errno)); - exit(EXIT_FAILURE); - } + FATAL_IF(d < 0, "cannot read from '%s': %s\n", + f->path, strerror(errno)); if (d == 0) /* end of file */ break; @@ -258,11 +249,8 @@ static void read_files(metafile_t *m, queue_t *q, unsigned char *pos) } /* now close the file */ - if (close(fd)) { - fprintf(stderr, "Error closing '%s': %s\n", - f->path, strerror(errno)); - exit(EXIT_FAILURE); - } + FATAL_IF(close(fd), "cannot close '%s': %s\n", + f->path, strerror(errno)); } /* finally append the hash of the last irregular piece to the hash string */ @@ -275,18 +263,16 @@ static void read_files(metafile_t *m, queue_t *q, unsigned char *pos) #ifndef NO_HASH_CHECK counter += r; - if (counter != m->size) { - fprintf(stderr, "Counted %" PRIoff " bytes, " - "but hashed %" PRIoff " bytes. " - "Something is wrong...\n", m->size, counter); - exit(EXIT_FAILURE); - } + FATAL_IF(counter != m->size, + "counted %" PRIuMAX " bytes, but hashed %" PRIuMAX " bytes; " + "something is wrong...\n", + m->size, counter); #endif } -EXPORT unsigned char *make_hash(metafile_t *m) +EXPORT unsigned char *make_hash(struct metafile *m) { - queue_t q = { + struct queue q = { NULL, NULL, 0, 0, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, @@ -297,15 +283,12 @@ EXPORT unsigned char *make_hash(metafile_t *m) pthread_t print_progress_thread; /* progress printer thread */ pthread_t *workers; unsigned char *hash_string; /* the hash string */ - unsigned int i; + int i; int err; workers = malloc(m->threads * sizeof(pthread_t)); hash_string = malloc(m->pieces * SHA_DIGEST_LENGTH); - if (workers == NULL || hash_string == NULL) { - fprintf(stderr, "Out of memory.\n"); - exit(EXIT_FAILURE); - } + FATAL_IF0(workers == NULL || hash_string == NULL, "out of memory\n"); q.pieces = m->pieces; q.buffers_max = 3*m->threads; @@ -313,31 +296,19 @@ EXPORT unsigned char *make_hash(metafile_t *m) /* create worker threads */ for (i = 0; i < m->threads; i++) { err = pthread_create(&workers[i], NULL, worker, &q); - if (err) { - fprintf(stderr, "Error creating thread: %s\n", - strerror(err)); - exit(EXIT_FAILURE); - } + FATAL_IF(err, "cannot create thread: %s\n", strerror(err)); } /* now set off the progress printer */ err = pthread_create(&print_progress_thread, NULL, print_progress, &q); - if (err) { - fprintf(stderr, "Error creating thread: %s\n", - strerror(err)); - exit(EXIT_FAILURE); - } + FATAL_IF(err, "cannot create thread: %s\n", strerror(err)); /* read files and feed pieces to the workers */ read_files(m, &q, hash_string); /* we're done so stop printing our progress. */ err = pthread_cancel(print_progress_thread); - if (err) { - fprintf(stderr, "Error cancelling thread: %s\n", - strerror(err)); - exit(EXIT_FAILURE); - } + FATAL_IF(err, "cannot cancel thread: %s\n", strerror(err)); /* inform workers we're done */ set_done(&q); @@ -345,22 +316,14 @@ EXPORT unsigned char *make_hash(metafile_t *m) /* wait for workers to finish */ for (i = 0; i < m->threads; i++) { err = pthread_join(workers[i], NULL); - if (err) { - fprintf(stderr, "Error joining thread: %s\n", - strerror(err)); - exit(EXIT_FAILURE); - } + FATAL_IF(err, "cannot join thread: %s\n", strerror(err)); } free(workers); /* the progress printer should be done by now too */ err = pthread_join(print_progress_thread, NULL); - if (err) { - fprintf(stderr, "Error joining thread: %s\n", - strerror(err)); - exit(EXIT_FAILURE); - } + FATAL_IF(err, "cannot join thread: %s\n", strerror(err)); /* destroy mutexes and condition variables */ pthread_mutex_destroy(&q.mutex_full); @@ -372,7 +335,7 @@ EXPORT unsigned char *make_hash(metafile_t *m) free_buffers(&q); /* ok, let the user know we're done too */ - printf("\rHashed %u of %u pieces.\n", q.pieces_hashed, q.pieces); + printf("\rhashed %u of %u pieces\n", q.pieces_hashed, q.pieces); return hash_string; } diff --git a/init.c b/init.c index c07714c..4ba566e 100644 --- a/init.c +++ b/init.c @@ -16,30 +16,34 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#ifndef ALLINONE + + #include <stdlib.h> /* exit() */ #include <sys/types.h> /* off_t */ #include <errno.h> /* errno */ #include <string.h> /* strerror() */ -#include <stdio.h> /* printf() etc. */ +#include <stdio.h> /* perror(), printf() etc. */ #include <sys/stat.h> /* the stat structure */ #include <unistd.h> /* getopt(), getcwd(), sysconf() */ #include <string.h> /* strcmp(), strlen(), strncpy() */ +#include <strings.h> /* strcasecmp() */ +#include <inttypes.h> /* PRId64 etc. */ + #ifdef USE_LONG_OPTIONS #include <getopt.h> /* getopt_long() */ #endif +#include "export.h" #include "mktorrent.h" #include "ftw.h" - -#define EXPORT -#endif /* ALLINONE */ +#include "msg.h" #ifndef MAX_OPENFD #define MAX_OPENFD 100 /* Maximum number of file descriptors file_tree_walk() will open */ #endif + static void strip_ending_dirseps(char *s) { char *end = s; @@ -65,24 +69,29 @@ static const char *basename(const char *s) return r; } -static void set_absolute_file_path(metafile_t *m) +static void set_absolute_file_path(struct metafile *m) { char *string; /* string to return */ size_t length = 32; /* length of the string */ /* if the file_path is already an absolute path just return that */ - if (m->metainfo_file_path && *m->metainfo_file_path == DIRSEP_CHAR) + if (m->metainfo_file_path && *m->metainfo_file_path == DIRSEP_CHAR) { + /* we need to reallocate the string, because we want to be able to + * free() it in cleanup_metafile(), and that would not be possible + * if m->metainfo_file_path pointed to a string from argv[] + */ + m->metainfo_file_path = strdup(m->metainfo_file_path); + FATAL_IF0(m->metainfo_file_path == NULL, "out of memory\n"); return; + } /* first get the current working directory using getcwd is a bit of a PITA */ /* allocate initial string */ string = malloc(length); - if (string == NULL) { - fprintf(stderr, "Out of memory.\n"); - exit(EXIT_FAILURE); - } + FATAL_IF0(string == NULL, "out of memory\n"); + /* while our allocated memory for the working dir isn't big enough */ while (getcwd(string, length) == NULL) { /* double the buffer size */ @@ -91,10 +100,7 @@ static void set_absolute_file_path(metafile_t *m) free(string); /* and allocate a new one twice as big muahaha */ string = malloc(length); - if (string == NULL) { - fprintf(stderr, "Out of memory.\n"); - exit(EXIT_FAILURE); - } + FATAL_IF0(string == NULL, "out of memory\n"); } /* now set length to the proper length of the working dir */ @@ -104,20 +110,14 @@ static void set_absolute_file_path(metafile_t *m) /* append <torrent name>.torrent to the working dir */ string = realloc(string, length + strlen(m->torrent_name) + 10); - if (string == NULL) { - fprintf(stderr, "Out of memory.\n"); - exit(EXIT_FAILURE); - } + FATAL_IF0(string == NULL, "out of memory\n"); sprintf(string + length, DIRSEP "%s.torrent", m->torrent_name); } else { /* otherwise append the torrent path to the working dir */ string = realloc(string, length + strlen(m->metainfo_file_path) + 2); - if (string == NULL) { - fprintf(stderr, "Out of memory.\n"); - exit(EXIT_FAILURE); - } + FATAL_IF0(string == NULL, "out of memory\n"); sprintf(string + length, DIRSEP "%s", m->metainfo_file_path); } @@ -128,40 +128,28 @@ static void set_absolute_file_path(metafile_t *m) * parse a comma separated list of strings <str>[,<str>]* and * return a string list containing the substrings */ -static slist_t *get_slist(char *s) +static struct ll *get_slist(char *s) { - slist_t *list, *last; char *e; - /* allocate memory for the first node in the list */ - list = last = malloc(sizeof(slist_t)); - if (list == NULL) { - fprintf(stderr, "Out of memory.\n"); - exit(EXIT_FAILURE); - } + /* allocate a new list */ + struct ll *list = ll_new(); + FATAL_IF0(list == NULL, "out of memory\n"); /* add URLs to the list while there are commas in the string */ while ((e = strchr(s, ','))) { /* set the commas to \0 so the URLs appear as * separate strings */ *e = '\0'; - last->s = s; + + FATAL_IF0(ll_append(list, s, 0) == NULL, "out of memory\n"); /* move s to point to the next URL */ s = e + 1; - - /* append another node to the list */ - last->next = malloc(sizeof(slist_t)); - last = last->next; - if (last == NULL) { - fprintf(stderr, "Out of memory.\n"); - exit(EXIT_FAILURE); - } } /* set the last string in the list */ - last->s = s; - last->next = NULL; + FATAL_IF0(ll_append(list, s, 0) == NULL, "out of memory\n"); /* return the list */ return list; @@ -171,41 +159,38 @@ static slist_t *get_slist(char *s) * checks if target is a directory * sets the file_list and size if it isn't */ -static int is_dir(metafile_t *m, char *target) +static int is_dir(struct metafile *m, char *target) { struct stat s; /* stat structure for stat() to fill */ /* stat the target */ - if (stat(target, &s)) { - fprintf(stderr, "Error stat'ing '%s': %s\n", - target, strerror(errno)); - exit(EXIT_FAILURE); - } + FATAL_IF(stat(target, &s), "cannot stat '%s': %s\n", + target, strerror(errno)); /* if it is a directory, just return 1 */ if (S_ISDIR(s.st_mode)) return 1; /* if it isn't a regular file either, something is wrong.. */ - if (!S_ISREG(s.st_mode)) { - fprintf(stderr, - "'%s' is neither a directory nor regular file.\n", - target); - exit(EXIT_FAILURE); - } + FATAL_IF(!S_ISREG(s.st_mode), + "'%s' is neither a directory nor regular file\n", target); + + /* if it has negative size, something it wrong */ + FATAL_IF(s.st_size < 0, "'%s' has negative size\n", target); /* since we know the torrent is just a single file and we've already stat'ed it, we might as well set the file list */ - m->file_list = malloc(sizeof(flist_t)); - if (m->file_list == NULL) { - fprintf(stderr, "Out of memory.\n"); - exit(EXIT_FAILURE); - } - m->file_list->path = target; - m->file_list->size = s.st_size; - m->file_list->next = NULL; + struct file_data fd = { + strdup(target), + (uintmax_t) s.st_size + }; + + FATAL_IF0( + fd.path == NULL || ll_append(m->file_list, &fd, sizeof(fd)) == NULL, + "out of memory\n"); + /* ..and size variable */ - m->size = s.st_size; + m->size = (uintmax_t) s.st_size; /* now return 0 since it isn't a directory */ return 0; @@ -218,9 +203,7 @@ static int is_dir(metafile_t *m, char *target) */ static int process_node(const char *path, const struct stat *sb, void *data) { - flist_t **p; /* pointer to a node in the file list */ - flist_t *new_node; /* place to store a newly created node */ - metafile_t *m = data; + struct metafile *m = data; /* skip non-regular files */ if (!S_ISREG(sb->st_mode)) @@ -232,38 +215,32 @@ static int process_node(const char *path, const struct stat *sb, void *data) /* now path should be readable otherwise * display a warning and skip it */ if (access(path, R_OK)) { - fprintf(stderr, "Warning: Cannot read '%s', skipping.\n", path); + fprintf(stderr, "warning: cannot read '%s', skipping\n", path); + return 0; + } + + if (sb->st_size < 0) { + fprintf(stderr, "warning: '%s' has negative size, skipping\n", path); return 0; } if (m->verbose) - printf("Adding %s\n", path); + printf("adding %s\n", path); /* count the total size of the files */ - m->size += sb->st_size; - - /* find where to insert the new node so that the file list - remains ordered by the path */ - p = &m->file_list; - while (*p && strcmp(path, (*p)->path) > 0) - p = &((*p)->next); + m->size += (uintmax_t) sb->st_size; /* create a new file list node for the file */ - new_node = malloc(sizeof(flist_t)); - if (new_node == NULL || - (new_node->path = strdup(path)) == NULL) { - fprintf(stderr, "Out of memory.\n"); + struct file_data fd = { + strdup(path), + (uintmax_t) sb->st_size + }; + + if (fd.path == NULL || ll_append(m->file_list, &fd, sizeof(fd)) == NULL) { + fprintf(stderr, "fatal error: out of memory\n"); return -1; } - new_node->size = sb->st_size; - - /* now insert the node there */ - new_node->next = *p; - *p = new_node; - /* insertion sort is a really stupid way of sorting a list, - but usually a torrent doesn't contain too many files, - so we'll probably be alright ;) */ return 0; } @@ -277,13 +254,15 @@ static void print_help() "Options:\n" #ifdef USE_LONG_OPTIONS "-a, --announce=<url>[,<url>]* : specify the full announce URLs\n" - " at least one is required\n" " additional -a adds backup trackers\n" "-c, --comment=<comment> : add a comment to the metainfo\n" "-d, --no-date : don't write the creation date\n" + "-e, --exclude=<pat>[,<pat>]* : exclude files whose name matches the pattern <pat>\n" + " see the man page glob(7)\n" + "-f, --force : overwrite output file if it exists\n" "-h, --help : show this help screen\n" "-l, --piece-length=<n> : set the piece length to 2^n bytes,\n" - " default is 18, that is 2^18 = 256kb\n" + " default is calculated from the total size\n" "-n, --name=<name> : set the name of the torrent\n" " default is the basename of the target\n" "-o, --output=<filename> : set the path and filename of the created file\n" @@ -297,15 +276,18 @@ static void print_help() "-v, --verbose : be verbose\n" "-w, --web-seed=<url>[,<url>]* : add web seed URLs\n" " additional -w adds more URLs\n" + "-x, --cross-seed : ensure info hash is unique for easier cross-seeding\n" #else "-a <url>[,<url>]* : specify the full announce URLs\n" - " at least one is required\n" " additional -a adds backup trackers\n" "-c <comment> : add a comment to the metainfo\n" "-d : don't write the creation date\n" + "-e <pat>[,<pat>]* : exclude files whose name matches the pattern <pat>\n" + " see the man page glob(7)\n" + "-f : overwrite output file if it exists\n" "-h : show this help screen\n" "-l <n> : set the piece length to 2^n bytes,\n" - " default is 18, that is 2^18 = 256kb\n" + " default is calculated from the total size\n" "-n <name> : set the name of the torrent,\n" " default is the basename of the target\n" "-o <filename> : set the path and filename of the created file\n" @@ -319,6 +301,7 @@ static void print_help() "-v : be verbose\n" "-w <url>[,<url>]* : add web seed URLs\n" " additional -w adds more URLs\n" + "-x : ensure info hash is unique for easier cross-seeding\n" #endif "\nPlease send bug reports, patches, feature requests, praise and\n" "general gossip about the program to: mktorrent@rudde.org\n"); @@ -327,40 +310,47 @@ static void print_help() /* * print the full announce list */ -static void print_announce_list(llist_t *list) +static void print_announce_list(struct ll *list) { - unsigned int n; + unsigned int tier = 1; + + LL_FOR(node, list) { + + struct ll *inner_list = LL_DATA(node); - for (n = 1; list; list = list->next, n++) { - slist_t *l = list->l; + printf(" %u : %s\n", + tier, LL_DATA_AS(LL_HEAD(inner_list), const char*)); - printf(" %u : %s\n", n, l->s); - for (l = l->next; l; l = l->next) - printf(" %s\n", l->s); + LL_FOR_FROM(inner_node, LL_NEXT(LL_HEAD(inner_list))) { + printf(" %s\n", LL_DATA_AS(inner_node, const char*)); + } + + tier += 1; } } /* * print the list of web seed URLs */ -static void print_web_seed_list(slist_t *list) +static void print_web_seed_list(struct ll *list) { printf(" Web Seed URL: "); - if (list == NULL) { + if (LL_IS_EMPTY(list)) { printf("none\n"); return; } - printf("%s\n", list->s); - for (list = list->next; list; list = list->next) - printf(" %s\n", list->s); + printf("%s\n", LL_DATA_AS(LL_HEAD(list), const char*)); + LL_FOR_FROM(node, LL_NEXT(LL_HEAD(list))) { + printf(" %s\n", LL_DATA_AS(node, const char*)); + } } /* * print out all the options */ -static void dump_options(metafile_t *m) +static void dump_options(struct metafile *m) { printf("Options:\n" " Announce URLs:\n"); @@ -371,7 +361,7 @@ static void dump_options(metafile_t *m) " Metafile: %s\n" " Piece length: %u\n" #ifdef USE_PTHREADS - " Threads: %u\n" + " Threads: %ld\n" #endif " Be verbose: yes\n", m->torrent_name, m->metainfo_file_path, m->piece_length @@ -399,22 +389,52 @@ static void dump_options(metafile_t *m) printf("\"%s\"\n\n", m->comment); } +static int file_data_cmp_by_name(const void *a, const void *b) +{ + const struct file_data *x = a, *y = b; + return strcmp(x->path, y->path); +} + +static void file_data_clear(void *data) +{ + struct file_data *fd = data; + free(fd->path); +} + +static void free_inner_list(void *data) +{ + struct ll *list = data; + ll_free(list, NULL); +} + /* * parse and check the command line options given * and fill out the appropriate fields of the * metafile structure */ -EXPORT void init(metafile_t *m, int argc, char *argv[]) +EXPORT void init(struct metafile *m, int argc, char *argv[]) { int c; /* return value of getopt() */ - llist_t *announce_last = NULL; - slist_t *web_seed_last = NULL; + const uintmax_t piece_len_maxes[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + (uintmax_t) BIT15MAX * ONEMEG, (uintmax_t) BIT16MAX * ONEMEG, + (uintmax_t) BIT17MAX * ONEMEG, (uintmax_t) BIT18MAX * ONEMEG, + (uintmax_t) BIT19MAX * ONEMEG, (uintmax_t) BIT20MAX * ONEMEG, + (uintmax_t) BIT21MAX * ONEMEG, (uintmax_t) BIT22MAX * ONEMEG, + (uintmax_t) BIT23MAX * ONEMEG + }; + + const int num_piece_len_maxes = sizeof(piece_len_maxes) / + sizeof(piece_len_maxes[0]); + #ifdef USE_LONG_OPTIONS /* the option structure to pass to getopt_long() */ static struct option long_options[] = { {"announce", 1, NULL, 'a'}, {"comment", 1, NULL, 'c'}, {"no-date", 0, NULL, 'd'}, + {"exclude", 1, NULL, 'e'}, + {"force", 0, NULL, 'f'}, {"help", 0, NULL, 'h'}, {"piece-length", 1, NULL, 'l'}, {"name", 1, NULL, 'n'}, @@ -426,15 +446,28 @@ EXPORT void init(metafile_t *m, int argc, char *argv[]) #endif {"verbose", 0, NULL, 'v'}, {"web-seed", 1, NULL, 'w'}, + {"cross-seed", 0, NULL, 'x'}, {NULL, 0, NULL, 0} }; #endif + m->announce_list = ll_new(); + FATAL_IF0(m->announce_list == NULL, "out of memory\n"); + + m->web_seed_list = ll_new(); + FATAL_IF0(m->web_seed_list == NULL, "out of memory\n"); + + m->file_list = ll_new(); + FATAL_IF0(m->file_list == NULL, "out of memory\n"); + + m->exclude_list = ll_new(); + FATAL_IF0(m->exclude_list == NULL, "out of memory\n"); + /* now parse the command line options given */ #ifdef USE_PTHREADS -#define OPT_STRING "a:c:dhl:n:o:ps:t:vw:" +#define OPT_STRING "a:c:e:dfhl:n:o:ps:t:vw:x" #else -#define OPT_STRING "a:c:dhl:n:o:ps:vw:" +#define OPT_STRING "a:c:e:dfhl:n:o:ps:vw:x" #endif #ifdef USE_LONG_OPTIONS while ((c = getopt_long(argc, argv, OPT_STRING, @@ -445,20 +478,9 @@ EXPORT void init(metafile_t *m, int argc, char *argv[]) #undef OPT_STRING switch (c) { case 'a': - if (announce_last == NULL) { - m->announce_list = announce_last = - malloc(sizeof(llist_t)); - } else { - announce_last->next = - malloc(sizeof(llist_t)); - announce_last = announce_last->next; - - } - if (announce_last == NULL) { - fprintf(stderr, "Out of memory.\n"); - exit(EXIT_FAILURE); - } - announce_last->l = get_slist(optarg); + FATAL_IF0( + ll_append(m->announce_list, get_slist(optarg), 0) == NULL, + "out of memory\n"); break; case 'c': m->comment = optarg; @@ -466,6 +488,12 @@ EXPORT void init(metafile_t *m, int argc, char *argv[]) case 'd': m->no_creation_date = 1; break; + case 'e': + ll_extend(m->exclude_list, get_slist(optarg)); + break; + case 'f': + m->force_overwrite = 1; + break; case 'h': print_help(); exit(EXIT_SUCCESS); @@ -493,55 +521,29 @@ EXPORT void init(metafile_t *m, int argc, char *argv[]) m->verbose = 1; break; case 'w': - if (web_seed_last == NULL) { - m->web_seed_list = web_seed_last = - get_slist(optarg); - } else { - web_seed_last->next = - get_slist(optarg); - web_seed_last = web_seed_last->next; - } - while (web_seed_last->next) - web_seed_last = web_seed_last->next; + ll_extend(m->web_seed_list, get_slist(optarg)); + break; + case 'x': + m->cross_seed = 1; break; case '?': - fprintf(stderr, "Use -h for help.\n"); - exit(EXIT_FAILURE); + fatal("use -h for help.\n"); } } - /* set the correct piece length. - default is 2^18 = 256kb. */ - if (m->piece_length < 15 || m->piece_length > 28) { - fprintf(stderr, - "The piece length must be a number between " - "15 and 28.\n"); - exit(EXIT_FAILURE); - } - m->piece_length = 1 << m->piece_length; - - if (announce_last != NULL) - announce_last->next = NULL; - - /* ..and a file or directory from which to create the torrent */ - if (optind >= argc) { - fprintf(stderr, "Must specify the contents, " - "use -h for help\n"); - exit(EXIT_FAILURE); - } + /* check that the user provided a file or directory from which to create the torrent */ + FATAL_IF0(optind >= argc, + "must specify the contents, use -h for help\n"); #ifdef USE_PTHREADS /* check the number of threads */ if (m->threads) { - if (m->threads > 20) { - fprintf(stderr, "The number of threads is limited to " - "at most 20\n"); - exit(EXIT_FAILURE); - } + FATAL_IF0(m->threads > 20, + "the number of threads is limited to at most 20\n"); } else { #ifdef _SC_NPROCESSORS_ONLN m->threads = sysconf(_SC_NPROCESSORS_ONLN); - if (m->threads == -1) + if (m->threads <= 0) #endif m->threads = 2; /* some sane default */ } @@ -566,23 +568,54 @@ EXPORT void init(metafile_t *m, int argc, char *argv[]) m->target_is_directory = is_dir(m, argv[optind]); if (m->target_is_directory) { /* change to the specified directory */ - if (chdir(argv[optind])) { - fprintf(stderr, "Error changing directory to '%s': %s\n", - argv[optind], strerror(errno)); - exit(EXIT_FAILURE); - } + FATAL_IF(chdir(argv[optind]), "cannot change directory to '%s': %s\n", + argv[optind], strerror(errno)); if (file_tree_walk("." DIRSEP, MAX_OPENFD, process_node, m)) exit(EXIT_FAILURE); } + ll_sort(m->file_list, file_data_cmp_by_name); + + /* determine the piece length based on the torrent size if + it was not user specified. */ + if (m->piece_length == 0) { + int i; + for (i = 15; i < num_piece_len_maxes && + m->piece_length == 0; i++) + if (m->size <= piece_len_maxes[i]) + m->piece_length = i; + if (m->piece_length == 0) + m->piece_length = num_piece_len_maxes; + } else { + /* if user did specify a piece length, verify its validity */ + FATAL_IF0(m->piece_length < 15 || m->piece_length > 28, + "the piece length must be a number between 15 and 28.\n"); + } + + /* convert the piece length from power of 2 to an integer. */ + m->piece_length = 1 << m->piece_length; + /* calculate the number of pieces pieces = ceil( size / piece_length ) */ m->pieces = (m->size + m->piece_length - 1) / m->piece_length; /* now print the size and piece count if we should be verbose */ if (m->verbose) - printf("\n%" PRIoff " bytes in all.\n" - "That's %u pieces of %u bytes each.\n\n", + printf("\n%" PRIuMAX " bytes in all\n" + "that's %u pieces of %u bytes each\n\n", m->size, m->pieces, m->piece_length); } + +EXPORT void cleanup_metafile(struct metafile *m) +{ + ll_free(m->announce_list, free_inner_list); + + ll_free(m->file_list, file_data_clear); + + ll_free(m->web_seed_list, NULL); + + ll_free(m->exclude_list, NULL); + + free(m->metainfo_file_path); +} diff --git a/init.h b/init.h new file mode 100644 index 0000000..7cade00 --- /dev/null +++ b/init.h @@ -0,0 +1,10 @@ +#ifndef MKTORRENT_INIT_H +#define MKTORRENT_INIT_H + +#include "export.h" /* EXPORT */ +#include "mktorrent.h" /* struct metafile */ + +EXPORT void init(struct metafile *m, int argc, char *argv[]); +EXPORT void cleanup_metafile(struct metafile *m); + +#endif /* MKTORRENT_INIT_H */ diff --git a/ll.c b/ll.c new file mode 100644 index 0000000..a9302ef --- /dev/null +++ b/ll.c @@ -0,0 +1,219 @@ +/* +This file is part of mktorrent + +mktorrent is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +mktorrent is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + + +#include <stdlib.h> +#include <string.h> + +#include "export.h" +#include "ll.h" + + +EXPORT struct ll *ll_new(void) +{ + struct ll *list = calloc(1, sizeof(*list)); + + if (list) + LL_TAIL(list) = &list->head; + + return list; +} + +EXPORT void ll_free(struct ll *list, ll_node_data_destructor destructor) +{ + if (list) { + + struct ll_node *head = LL_HEAD(list), *next; + + while (head) { + + if (destructor) + destructor(LL_DATA(head)); + + if (head->data_size) + free(LL_DATA(head)); + + next = LL_NEXT(head); + + free(head); + + head = next; + } + + free(list); + } +} + +static struct ll_node *ll_node_new( + void *data, + const size_t data_size, + struct ll_node *prev, + struct ll_node *next) +{ + struct ll_node *node = calloc(1, sizeof(*node)); + + if (!node) + return NULL; + + if (data_size) { + LL_DATA(node) = calloc(1, data_size); + + if (!LL_DATA(node)) + goto oom_node_data; + + memcpy(LL_DATA(node), data, data_size); + } + else + LL_DATA(node) = data; + + LL_DATASIZE(node) = data_size; + LL_PREV(node) = prev; + LL_NEXT(node) = next; + + return node; + +oom_node_data: + free(node); + + return NULL; +} + +/* appends a new node with 'data' to the end of 'list' */ +EXPORT struct ll_node *ll_append(struct ll *list, void *data, const size_t data_size) +{ + if (!list) + return NULL; + + struct ll_node *node = ll_node_new(data, data_size, LL_TAIL(list), NULL); + + if (node) { + LL_NEXT(LL_TAIL(list)) = node; + LL_TAIL(list) = node; + } + + return node; +} + +/* concatenates two lists while destroying the second one */ +EXPORT struct ll *ll_extend(struct ll *list, struct ll *other) +{ + if (!list) + return NULL; + + if (!other) + return list; + + if (!LL_IS_EMPTY(other)) { + LL_NEXT(LL_TAIL(list)) = LL_HEAD(other); + LL_PREV(LL_HEAD(other)) = LL_TAIL(list); + LL_TAIL(list) = LL_TAIL(other); + } + + + free(other); + + return list; +} + +/* sort the given range using recursive merge sort in a stable way; + * sets 'first' to the new head, 'last' to the new tail; + * the new head will have ->prev = NULL, + * and the new tail will have ->next = NULL; + */ +static void ll_sort_node_range( + struct ll_node **first, + struct ll_node **last, + ll_node_data_cmp cmp) +{ +#define APPEND_AND_STEP(t, x) do { \ + LL_NEXT(t) = (x); \ + LL_PREV(x) = (t); \ + \ + LL_STEP(x); \ + LL_STEP(t); \ +} while(0) + + if (first == NULL || *first == NULL || last == NULL || *last == NULL) + return; + + /* sorting a one element range is trivial */ + if (*first == *last) { + LL_CLEAR_LINKS(*first); + return; + } + + struct ll_node *middle = *first, *middle2 = *last; + + while (middle != middle2 && LL_NEXT(middle) != middle2) { + middle = LL_NEXT(middle); + middle2 = LL_PREV(middle2); + } + + /* middle is now the midpoint of the list */ + + /* 'tail' is the tail of the new, sorted list */ + struct ll_node dummy, *tail = &dummy; + + struct ll_node *a = *first; + struct ll_node *b = LL_NEXT(middle); + + /* the values of middle and *last are not used anymore in this function, + * so they can be safely overwritten by the recursive calls + */ + ll_sort_node_range(&a, &middle, cmp); + ll_sort_node_range(&b, last, cmp); + + while (a && b) { + int r = cmp(LL_DATA(a), LL_DATA(b)); + + if (r <= 0) APPEND_AND_STEP(tail, a); /* if a.val <= b.val, append a */ + else APPEND_AND_STEP(tail, b); /* otherwise, append b */ + } + + /* at this point only one of a or b might be non-NULL, + * so only one of the next two loops will run + */ + + /* append remaining nodes from the first half */ + while (a) APPEND_AND_STEP(tail, a); + + /* append remaining nodes from the second half */ + while (b) APPEND_AND_STEP(tail, b); + + /* the prev ptr of the first "real" node points to dummy, clear that */ + LL_PREV(LL_NEXT(&dummy)) = NULL; + + /* set the new head and tail */ + *first = LL_NEXT(&dummy); + *last = tail; + +#undef APPEND_AND_STEP +} + +EXPORT struct ll *ll_sort(struct ll *list, ll_node_data_cmp cmp) +{ + if (list == NULL || cmp == NULL) + return NULL; + + ll_sort_node_range(&LL_HEAD(list), &LL_TAIL(list), cmp); + + if (LL_HEAD(list)) + LL_PREV(LL_HEAD(list)) = &list->head; + + return list; +} diff --git a/ll.h b/ll.h new file mode 100644 index 0000000..76550bb --- /dev/null +++ b/ll.h @@ -0,0 +1,69 @@ +#ifndef MKTORRENT_LL_H +#define MKTORRENT_LL_H + +struct ll_node { + struct ll_node *prev, *next; + + size_t data_size; + void *data; +}; + +struct ll { + struct ll_node head, *tail; +}; + +typedef void (*ll_node_data_destructor)(void *); +typedef int (*ll_node_data_cmp)(const void *, const void *); + + +#define LL_DATA(node) ((node)->data) +#define LL_DATA_AS(node, type) ((type) LL_DATA(node)) +#define LL_DATASIZE(node) ((node)->data_size) +#define LL_PREV(node) ((node)->prev) +#define LL_NEXT(node) ((node)->next) +#define LL_STEP(node) ((node) = LL_NEXT(node)) +#define LL_STEP_PREV(node) ((node) = LL_PREV(node)) +#define LL_CLEAR_LINKS(node) do { LL_NEXT(node) = NULL; LL_PREV(node) = NULL; } while(0) + +#define LL_HEAD(list) ((list)->head.next) +#define LL_TAIL(list) ((list)->tail) +#define LL_IS_EMPTY(list) (LL_HEAD(list) == NULL) +#define LL_IS_SINGLETON(list) (!LL_IS_EMPTY(list) && LL_NEXT(LL_HEAD(list)) == NULL) +#define LL_FOR_FROM_TO_STEP(node, from, to, step) for (struct ll_node *(node) = from; node != to; step(node)) +#define LL_FOR(node, list) LL_FOR_FROM_TO_STEP(node, LL_HEAD(list), NULL, LL_STEP) +#define LL_FOR_FROM(node, from) LL_FOR_FROM_TO_STEP(node, from, NULL, LL_STEP) + + +/* creates a new linked list instance */ +EXPORT struct ll *ll_new(void); + + +/* frees the given list, calls a "destructor" function + * on the data pointers if provided + */ +EXPORT void ll_free(struct ll *, ll_node_data_destructor); + + +/* appends a new node with data to the end of the given list, + * if the provided size is zero, then the data pointer is set to + * the pointer provided in the arguments, otherwise size number of + * "bytes" is allocated, and that amount of bytes is copied into + * this newly allocated node from the given pointer + */ +EXPORT struct ll_node *ll_append(struct ll *, void *, const size_t); + + +/* concatenates the second list to the first one + * while destroying the second one, + * returns the first one + */ +EXPORT struct ll *ll_extend(struct ll *, struct ll *); + + +/* sorts the given list using recursive merge sort based on + * the provieded comparator function, fails if either of the arguments is + * NULL, returns the given list + */ +EXPORT struct ll *ll_sort(struct ll *, ll_node_data_cmp); + +#endif /* MKTORRENT_LL_H */ diff --git a/main.c b/main.c index 511d036..79dd5d9 100644 --- a/main.c +++ b/main.c @@ -17,46 +17,27 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include <stdlib.h> /* exit() */ -#include <sys/types.h> /* off_t */ + +#include <stdlib.h> /* exit(), srandom() */ #include <errno.h> /* errno */ #include <string.h> /* strerror() */ #include <stdio.h> /* printf() etc. */ #include <sys/stat.h> /* S_IRUSR, S_IWUSR, S_IRGRP, S_IROTH */ #include <fcntl.h> /* open() */ +#include <time.h> /* clock_gettime() */ -#ifdef ALLINONE -#include <sys/stat.h> -#include <unistd.h> /* access(), read(), close(), getcwd(), sysconf() */ -#ifdef USE_LONG_OPTIONS -#include <getopt.h> /* getopt_long() */ -#endif -#include <time.h> /* time() */ -#include <dirent.h> /* opendir(), closedir(), readdir() etc. */ -#ifdef USE_OPENSSL -#include <openssl/sha.h> /* SHA1(), SHA_DIGEST_LENGTH */ -#else -#include <inttypes.h> -#endif -#ifdef USE_PTHREADS -#include <pthread.h> /* pthread functions and data structures */ -#endif - -#define EXPORT static -#else /* ALLINONE */ - -#define EXPORT -#endif /* ALLINONE */ - +#include "export.h" #include "mktorrent.h" +#include "init.h" +#include "hash.h" +#include "output.h" +#include "msg.h" +#include "ll.h" #ifdef ALLINONE -#include "ftw.c" -#include "init.c" +/* include all .c files in alphabetical order */ -#ifndef USE_OPENSSL -#include "sha1.c" -#endif +#include "ftw.c" #ifdef USE_PTHREADS #include "hash_pthreads.c" @@ -64,14 +45,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include "hash.c" #endif +#include "init.c" +#include "ll.c" +#include "msg.c" #include "output.c" -#else /* ALLINONE */ -/* init.c */ -extern void init(metafile_t *m, int argc, char *argv[]); -/* hash.c */ -extern unsigned char *make_hash(metafile_t *m); -/* output.c */ -extern void write_metainfo(FILE *f, metafile_t *m, unsigned char *hash_string); + +#ifndef USE_OPENSSL +#include "sha1.c" +#endif + #endif /* ALLINONE */ #ifndef O_BINARY @@ -81,35 +63,34 @@ extern void write_metainfo(FILE *f, metafile_t *m, unsigned char *hash_string); #ifndef S_IRGRP #define S_IRGRP 0 #endif + #ifndef S_IROTH #define S_IROTH 0 #endif + /* * create and open the metainfo file for writing and create a stream for it * we don't want to overwrite anything, so abort if the file is already there + * and force is false */ -static FILE *open_file(const char *path) +static FILE *open_file(const char *path, int force) { int fd; /* file descriptor */ FILE *f; /* file stream */ + int flags = O_WRONLY | O_BINARY | O_CREAT; + if (!force) + flags |= O_EXCL; + /* open and create the file if it doesn't exist already */ - fd = open(path, O_WRONLY | O_BINARY | O_CREAT | O_EXCL, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (fd < 0) { - fprintf(stderr, "Error creating '%s': %s\n", - path, strerror(errno)); - exit(EXIT_FAILURE); - } + fd = open(path, flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + FATAL_IF(fd < 0, "cannot create '%s': %s\n", path, strerror(errno)); /* create the stream from this filedescriptor */ f = fdopen(fd, "wb"); - if (f == NULL) { - fprintf(stderr, "Error creating stream for '%s': %s\n", - path, strerror(errno)); - exit(EXIT_FAILURE); - } + FATAL_IF(f == NULL, "cannot create stream for '%s': %s\n", + path, strerror(errno)); return f; } @@ -120,11 +101,7 @@ static FILE *open_file(const char *path) static void close_file(FILE *f) { /* close the metainfo file */ - if (fclose(f)) { - fprintf(stderr, "Error closing stream: %s\n", - strerror(errno)); - exit(EXIT_FAILURE); - } + FATAL_IF(fclose(f), "cannot close stream: %s\n", strerror(errno)); } /* @@ -133,9 +110,9 @@ static void close_file(FILE *f) int main(int argc, char *argv[]) { FILE *file; /* stream for writing to the metainfo file */ - metafile_t m = { + struct metafile m = { /* options */ - 18, /* piece_length, 2^18 = 256kb by default */ + 0, /* piece_length, 0 by default indicates length should be calculated automatically */ NULL, /* announce_list */ NULL, /* torrent_name */ NULL, /* metainfo_file_path */ @@ -145,7 +122,10 @@ int main(int argc, char *argv[]) 0, /* no_creation_date */ 0, /* private */ NULL, /* source string */ + 0, /* cross_seed */ 0, /* verbose */ + 0, /* force_overwrite */ + NULL, /* exclude_list */ #ifdef USE_PTHREADS 0, /* threads, initialised by init() */ #endif @@ -159,19 +139,32 @@ int main(int argc, char *argv[]) /* print who we are */ printf("mktorrent " VERSION " (c) 2007, 2009 Emil Renner Berthing\n\n"); + /* seed PRNG with current time */ + struct timespec ts; + FATAL_IF(clock_gettime(CLOCK_REALTIME, &ts) == -1, + "failed to get time: %s\n", strerror(errno)); + srandom(ts.tv_nsec ^ ts.tv_sec); + /* process options */ init(&m, argc, argv); /* open the file stream now, so we don't have to abort _after_ we did all the hashing in case we fail */ - file = open_file(m.metainfo_file_path); + file = open_file(m.metainfo_file_path, m.force_overwrite); + + /* calculate hash string... */ + unsigned char *hash = make_hash(&m); - /* calculate hash string and write the metainfo to file */ - write_metainfo(file, &m, make_hash(&m)); + /* and write the metainfo to file */ + write_metainfo(file, &m, hash); /* close the file stream */ close_file(file); + /* free allocated memory */ + cleanup_metafile(&m); + free(hash); + /* yeih! everything seemed to go as planned */ return EXIT_SUCCESS; } diff --git a/mktorrent.h b/mktorrent.h index 5f07a66..2ce3393 100644 --- a/mktorrent.h +++ b/mktorrent.h @@ -1,5 +1,5 @@ -#ifndef _MKTORRENT_H -#define _MKTORRENT_H +#ifndef MKTORRENT_MKTORRENT_H +#define MKTORRENT_MKTORRENT_H #ifdef _WIN32 #define DIRSEP "\\" @@ -9,52 +9,54 @@ #define DIRSEP_CHAR '/' #endif -/* string list */ -struct slist_s; -typedef struct slist_s slist_t; -struct slist_s { - char *s; - slist_t *next; -}; +/* number of bytes in one MB */ +#define ONEMEG 1048576 -/* list of string lists */ -struct llist_s; -typedef struct llist_s llist_t; -struct llist_s { - slist_t *l; - llist_t *next; -}; +/* max torrent size in MB for a given piece length in bits */ +/* where an X bit piece length equals a 2^X byte piece size */ +#define BIT23MAX 12800 +#define BIT22MAX 6400 +#define BIT21MAX 3200 +#define BIT20MAX 1600 +#define BIT19MAX 800 +#define BIT18MAX 400 +#define BIT17MAX 200 +#define BIT16MAX 100 +#define BIT15MAX 50 + +#include <stdint.h> -/* file list */ -struct flist_s; -typedef struct flist_s flist_t; -struct flist_s { +#include "ll.h" + +struct file_data { char *path; - off_t size; - flist_t *next; + uintmax_t size; }; -typedef struct { +struct metafile { /* options */ unsigned int piece_length; /* piece length */ - llist_t *announce_list; /* announce URLs */ + struct ll *announce_list; /* announce URLs */ char *comment; /* optional comment */ const char *torrent_name; /* name of torrent (name of directory) */ char *metainfo_file_path; /* absolute path to the metainfo file */ - slist_t *web_seed_list; /* web seed URLs */ + struct ll *web_seed_list; /* web seed URLs */ int target_is_directory; /* target is a directory */ int no_creation_date; /* don't write the creation date */ int private; /* set the private flag */ char *source; /* set source for private trackers */ + int cross_seed; /* ensure info hash is unique for easier cross-seeding */ int verbose; /* be verbose */ + int force_overwrite; /* overwrite existing output file */ + struct ll *exclude_list; /* exclude list */ #ifdef USE_PTHREADS - unsigned int threads; /* number of threads used for hashing */ + long threads; /* number of threads used for hashing */ #endif /* information calculated by read_dir() */ - off_t size; /* combined size of all files */ - flist_t *file_list; /* list of files and their sizes */ + uintmax_t size; /* combined size of all files */ + struct ll *file_list; /* list of files and their sizes */ unsigned int pieces; /* number of pieces */ -} metafile_t; +}; -#endif /* _MKTORRENT_H */ +#endif /* MKTORRENT_MKTORRENT_H */ diff --git a/prefix.c b/msg.c similarity index 51% rename from prefix.c rename to msg.c index 447772e..e1596e3 100644 --- a/prefix.c +++ b/msg.c @@ -1,6 +1,5 @@ /* This file is part of mktorrent -Copyright (C) 2009 Emil Renner Berthing mktorrent is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,46 +16,25 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include <stdlib.h> -#include <sys/types.h> -#include <stdio.h> - -#ifndef TYPE -#define TYPE off_t -#endif - -int main(int argc, char *argv[]) -{ - unsigned int i; - char *prefix[9]; - for (i = 0; i < 9; i++) - prefix[i] = NULL; - - prefix[1] = "hh"; - prefix[2] = "h"; +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> - prefix[8] = "ll"; - prefix[sizeof(int)] = ""; - prefix[sizeof(long int)] = "l"; +#include "export.h" +#include "msg.h" -#ifdef DEBUG -#define xstr(s) str(s) -#define str(s) #s -#ifdef _LARGEFILE_SOURCE - printf("_LARGEFILE_SOURCE is set\n"); -#endif +EXPORT void fatal(const char *format, ...) +{ -#ifdef _FILE_OFFSET_BITS - printf("_FILE_OFFSET_BITS = %lu\n", _FILE_OFFSET_BITS); -#endif + va_list args; + va_start(args, format); - printf("sizeof(" xstr(TYPE) ") = %lu, %lu bits\n", - sizeof(TYPE), 8*sizeof(TYPE)); -#endif /* DEBUG */ + fputs("fatal error: ", stderr); + vfprintf(stderr, format, args); - printf("%s\n", prefix[sizeof(TYPE)]); + va_end(args); - return EXIT_SUCCESS; + exit(EXIT_FAILURE); } diff --git a/msg.h b/msg.h new file mode 100644 index 0000000..c444a8d --- /dev/null +++ b/msg.h @@ -0,0 +1,12 @@ +#ifndef MKTORRENT_MSG_H +#define MKTORRENT_MSG_H + +#include "export.h" + +EXPORT void fatal(const char *format, ...); + + +#define FATAL_IF(cond, format, ...) do { if (cond) fatal(format, __VA_ARGS__); } while(0) +#define FATAL_IF0(cond, format) do { if (cond) fatal(format); } while(0) + +#endif /* MKTORRENT_MSG_H */ diff --git a/output.c b/output.c index ef5f060..d0b5761 100644 --- a/output.c +++ b/output.c @@ -16,39 +16,48 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#ifndef ALLINONE + + #include <sys/types.h> /* off_t */ -#include <stdio.h> /* printf() etc. */ +#include <stdio.h> /* fprintf() etc. */ #include <string.h> /* strlen() etc. */ #include <time.h> /* time() */ +#include <inttypes.h> /* PRIuMAX */ +#include <stdlib.h> /* random() */ + #ifdef USE_OPENSSL #include <openssl/sha.h> /* SHA_DIGEST_LENGTH */ #else -#include <inttypes.h> #include "sha1.h" #endif -#include "mktorrent.h" +#include "export.h" /* EXPORT */ +#include "mktorrent.h" /* struct metafile */ +#include "output.h" -#define EXPORT -#endif /* ALLINONE */ /* * write announce list */ -static void write_announce_list(FILE *f, llist_t *list) +static void write_announce_list(FILE *f, struct ll *list) { /* the announce list is a list of lists of urls */ fprintf(f, "13:announce-listl"); /* go through them all.. */ - for (; list; list = list->next) { - slist_t *l; + LL_FOR(tier_node, list) { /* .. and print the lists */ fprintf(f, "l"); - for (l = list->l; l; l = l->next) + + LL_FOR(announce_url_node, LL_DATA_AS(tier_node, struct ll*)) { + + const char *announce_url = + LL_DATA_AS(announce_url_node, const char*); + fprintf(f, "%lu:%s", - (unsigned long)strlen(l->s), l->s); + (unsigned long) strlen(announce_url), announce_url); + } + fprintf(f, "e"); } fprintf(f, "e"); @@ -57,29 +66,31 @@ static void write_announce_list(FILE *f, llist_t *list) /* * write file list */ -static void write_file_list(FILE *f, flist_t *list) +static void write_file_list(FILE *f, struct ll *list) { char *a, *b; fprintf(f, "5:filesl"); /* go through all the files */ - for (; list; list = list->next) { + LL_FOR(file_node, list) { + struct file_data *fd = LL_DATA_AS(file_node, struct file_data*); + /* the file list contains a dictionary for every file with entries for the length and path write the length first */ - fprintf(f, "d6:lengthi%" PRIoff "e4:pathl", list->size); + fprintf(f, "d6:lengthi%" PRIuMAX "e4:pathl", fd->size); /* the file path is written as a list of subdirectories and the last entry is the filename sorry this code is even uglier than the rest */ - a = list->path; + a = fd->path; /* while there are subdirectories before the filename.. */ while ((b = strchr(a, DIRSEP_CHAR)) != NULL) { /* set the next DIRSEP_CHAR to '\0' so fprintf will only write the first subdirectory name */ *b = '\0'; /* print it bencoded */ - fprintf(f, "%lu:%s", (unsigned long)strlen(a), a); + fprintf(f, "%lu:%s", b - a, a); /* undo our alteration to the string */ *b = DIRSEP_CHAR; /* and move a to the beginning of the next @@ -98,13 +109,16 @@ static void write_file_list(FILE *f, flist_t *list) /* * write web seed list */ -static void write_web_seed_list(FILE *f, slist_t *list) +static void write_web_seed_list(FILE *f, struct ll *list) { /* print the entry and start the list */ fprintf(f, "8:url-listl"); /* go through the list and write each URL */ - for (; list; list = list->next) - fprintf(f, "%lu:%s", (unsigned long)strlen(list->s), list->s); + LL_FOR(node, list) { + const char *web_seed_url = LL_DATA_AS(node, const char*); + fprintf(f, "%lu:%s", + (unsigned long) strlen(web_seed_url), web_seed_url); + } /* end the list */ fprintf(f, "e"); } @@ -113,23 +127,33 @@ static void write_web_seed_list(FILE *f, slist_t *list) * write metainfo to the file stream using all the information * we've gathered so far and the hash string calculated */ -EXPORT void write_metainfo(FILE *f, metafile_t *m, unsigned char *hash_string) +EXPORT void write_metainfo(FILE *f, struct metafile *m, unsigned char *hash_string) { /* let the user know we've started writing the metainfo file */ - printf("Writing metainfo file... "); + printf("writing metainfo file... "); fflush(stdout); /* every metainfo file is one big dictonary */ fprintf(f, "d"); - if (m->announce_list != NULL) { + if (!LL_IS_EMPTY(m->announce_list)) { + + struct ll *first_tier = + LL_DATA_AS(LL_HEAD(m->announce_list), struct ll*); + /* write the announce URL */ + const char *first_announce_url + = LL_DATA_AS(LL_HEAD(first_tier), const char*); + fprintf(f, "8:announce%lu:%s", - (unsigned long)strlen(m->announce_list->l->s), - m->announce_list->l->s); + (unsigned long) strlen(first_announce_url), first_announce_url); + /* write the announce-list entry if we have - more than one announce URL */ - if (m->announce_list->next || m->announce_list->l->next) + * more than one announce URL, namely + * a) there are at least two tiers, or (first part of OR) + * b) there are at least two URLs in tier 1 (second part of OR) + */ + if (LL_NEXT(LL_HEAD(m->announce_list)) || LL_NEXT(LL_HEAD(first_tier))) write_announce_list(f, m->announce_list); } @@ -151,10 +175,20 @@ EXPORT void write_metainfo(FILE *f, metafile_t *m, unsigned char *hash_string) /* first entry is either 'length', which specifies the length of a single file torrent, or a list of files and their respective sizes */ if (!m->target_is_directory) - fprintf(f, "6:lengthi%" PRIoff "e", m->file_list->size); + fprintf(f, "6:lengthi%" PRIuMAX "e", + LL_DATA_AS(LL_HEAD(m->file_list), struct file_data*)->size); else write_file_list(f, m->file_list); + if (m->cross_seed) { + fprintf(f, "12:x_cross_seed%u:mktorrent-", CROSS_SEED_RAND_LENGTH * 2 + 10); + for (int i = 0; i < CROSS_SEED_RAND_LENGTH; i++) { + unsigned char rand_byte = random(); + fputc("0123456789ABCDEF"[rand_byte >> 4], f); + fputc("0123456789ABCDEF"[rand_byte & 0x0F], f); + } + } + /* the info section also contains the name of the torrent, the piece length and the hash string */ fprintf(f, "4:name%lu:%s12:piece lengthi%ue6:pieces%u:", @@ -167,18 +201,21 @@ EXPORT void write_metainfo(FILE *f, metafile_t *m, unsigned char *hash_string) fprintf(f, "7:privatei1e"); if (m->source) - fprintf(f, "6:source%lu:%s", (unsigned long)strlen(m->source), m->source); + fprintf(f, "6:source%lu:%s", + (unsigned long) strlen(m->source), m->source); /* end the info section */ fprintf(f, "e"); /* add url-list if one is specified */ - if (m->web_seed_list != NULL) { - if (m->web_seed_list->next == NULL) + if (!LL_IS_EMPTY(m->web_seed_list)) { + if (LL_IS_SINGLETON(m->web_seed_list)) { + const char *first_web_seed = + LL_DATA_AS(LL_HEAD(m->web_seed_list), const char*); + fprintf(f, "8:url-list%lu:%s", - (unsigned long)strlen(m->web_seed_list->s), - m->web_seed_list->s); - else + (unsigned long) strlen(first_web_seed), first_web_seed); + } else write_web_seed_list(f, m->web_seed_list); } @@ -186,6 +223,6 @@ EXPORT void write_metainfo(FILE *f, metafile_t *m, unsigned char *hash_string) fprintf(f, "e"); /* let the user know we're done already */ - printf("done.\n"); + printf("done\n"); fflush(stdout); } diff --git a/output.h b/output.h new file mode 100644 index 0000000..2664ec0 --- /dev/null +++ b/output.h @@ -0,0 +1,14 @@ +#ifndef MKTORRENT_OUTPUT_H +#define MKTORRENT_OUTPUT_H + +#include <stdio.h> /* FILE */ + +#include "export.h" /* EXPORT */ +#include "mktorrent.h" /* struct metafile */ + +#define CROSS_SEED_RAND_LENGTH 16 + +EXPORT void write_metainfo(FILE *f, struct metafile *m, + unsigned char *hash_string); + +#endif /* MKTORRENT_OUTPUT_H */ diff --git a/rules.mk b/rules.mk index 5960822..1b0b42d 100644 --- a/rules.mk +++ b/rules.mk @@ -17,14 +17,11 @@ .PHONY: strip indent clean install uninstall -prefix: prefix.c - $(CC) $(CFLAGS) $(DEFINES) $(LDFLAGS) $< -o $@ - $(program): $(OBJS) $(CC) $(CFLAGS) $(OBJS) -o $(program) $(LDFLAGS) $(LIBS) -allinone: $(SRCS) $(HEADERS) prefix - $(CC) $(CFLAGS) $(DEFINES) -DPRIoff="\"`./prefix`d\"" -DVERSION="\"$(version)\"" -DALLINONE main.c -o $(program) $(LDFLAGS) $(LIBS) +allinone: $(SRCS) $(HEADERS) + $(CC) $(CFLAGS) $(DEFINES) -DVERSION="\"$(version)\"" -DALLINONE main.c -o $(program) $(LDFLAGS) $(LIBS) strip: strip $(program) @@ -33,7 +30,7 @@ indent: indent -kr -i8 *.c *.h clean: - rm -f $(program) prefix *.o *.c~ *.h~ + rm -f $(program) *.o *.c~ *.h~ install: $(program) $(INSTALL) -d $(DESTDIR)$(PREFIX)/bin diff --git a/sha1.c b/sha1.c index 78f7988..f688953 100644 --- a/sha1.c +++ b/sha1.c @@ -16,16 +16,16 @@ /* #define SHA1_WIPE_VARS */ /* #define SHA1_VERBOSE */ -#ifndef ALLINONE + #ifdef SHA1_TEST #include <stdio.h> #endif + #include <string.h> +#include <stdint.h> #include <inttypes.h> -#define EXPORT -#endif /* ALLINONE */ - +#include "export.h" #include "sha1.h" #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) diff --git a/sha1.h b/sha1.h index ef3bf52..567543c 100644 --- a/sha1.h +++ b/sha1.h @@ -1,8 +1,13 @@ /* Public API for Steve Reid's public domain SHA-1 implementation */ /* This file is in the public domain */ -#ifndef __SHA1_H -#define __SHA1_H + +#ifndef MKTORRENT_SHA1_H +#define MKTORRENT_SHA1_H + +#include <stdint.h> /* uintX_t */ + +#include "export.h" /* EXPORT */ typedef struct { uint32_t state[5]; @@ -12,10 +17,9 @@ typedef struct { #define SHA_DIGEST_LENGTH 20 -#ifndef ALLINONE -void SHA1_Init(SHA_CTX *context); -void SHA1_Update(SHA_CTX *context, const uint8_t *data, unsigned long len); -void SHA1_Final(uint8_t *digest, SHA_CTX *context); -#endif -#endif /* __SHA1_H */ +EXPORT void SHA1_Init(SHA_CTX *context); +EXPORT void SHA1_Update(SHA_CTX *context, const uint8_t *data, unsigned long len); +EXPORT void SHA1_Final(uint8_t *digest, SHA_CTX *context); + +#endif /* MKTORRENT_SHA1_H */
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor