src/add.c \
src/bencode/decode.c \
src/bencode/encode.c \
+ src/block.c \
src/default.c \
src/file.c \
src/hash.c \
seederd_SOURCES += \
inc/add.h \
inc/bencode.h \
+ inc/block.h \
inc/default.h \
inc/file.h \
inc/hash.h \
--- /dev/null
+#ifndef __BLOCK_H_
+#define __BLOCK_H_
+
+#include<sodium.h>
+
+#define BLOCK_SIZE 16384
+
+struct block {
+ uint8_t *data;
+ unsigned char hash[crypto_hash_sha256_BYTES];
+ struct block *next;
+};
+
+int block_append_blank(struct block*);
+int block_duplicate(struct block**,struct block*);
+void block_free(struct block*);
+int block_init(struct block**);
+size_t block_length(struct block*);
+int block_merkle_root(struct block*);
+
+#endif
#include<sodium.h>
+#define FILE_MESSAGE_FREAD_FAILED "failed to read file: %s\n"
+
struct file {
char *name;
char *path;
unsigned char root[crypto_hash_sha256_BYTES];
+ struct block *blocks;
+ size_t size;
};
void file_free(struct file*);
--- /dev/null
+#include<block.h>
+
+int block_append_block(struct block *p) {
+ if(NULL==p) { return -1; }
+
+ while(p->next!=NULL) {
+ p = p->next;
+ }
+
+ if(block_init(&(p->next))<0) { return -1; }
+ memset(p->next->hash,0,crypto_hash_sha256_BYTES);
+
+ return 1;
+}
+
+int block_duplicate(struct block **p, struct block *to_dup) {
+ struct block *next;
+
+ if(NULL==p) { return -1; }
+ if(NULL==to_dup) { return -1; }
+
+ while(to_dup!=NULL) {
+ if(block_init(p)<0) { return -1; }
+
+ memcpy((*p)->hash,to_dup->hash,crypto_hash_sha256_BYTES);
+
+ (*p) = (*p)->next;
+ to_dup = to_dup->next;
+ }
+
+ return 1;
+}
+
+void block_free(struct block *p) {
+ struct block *next;
+
+ while(p!=NULL) {
+ if(p->data!=NULL) {
+ free(p->data);
+ }
+
+ next = p->next;
+ free(p);
+ p = next;
+ }
+}
+
+int block_init(struct block **p) {
+ if(NULL==p) { return -1; }
+
+ *p = malloc(sizeof(struct block));
+ if(NULL==(*p)) {
+ perror("malloc");
+ return -1;
+ }
+
+ (*p)->data = NULL;
+ (*p)->next = NULL;
+
+ return 1;
+}
+
+size_t block_length(struct block *p) {
+ size_t i = 0;
+ while(p!=NULL) {
+ i++;
+ p = p->next;
+ }
+
+ return i;
+}
+
+int block_merkle_root(struct block *root) {
+ struct block *p, *to_free;
+ crypto_hash_sha256_state state;
+
+ if(NULL==root) { return -1; }
+
+ while(p!=NULL) {
+ if(NULL==p->next) { return -1; }
+ if(hash_init(&state)<0) { return -1; }
+
+ if(hash_update(&state,p->hash,crypto_hash_sha256_BYTES)<0) { return -1; }
+ if(hash_update(&state,p->next->hash,crypto_hash_sha256_BYTES)<0) { return -1; }
+
+ if(hash_final(&state,p->hash,crypto_hash_sha256_BYTES)<0) { return -1; }
+
+ to_free = p->next;
+ p = p->next->next;
+ block_free(to_free);
+
+ if((NULL==p)&&(p!=root)) {
+ p = root;
+ }
+ }
+
+ return 1;
+}
free(p);
}
-int file_hash(struct file *p) {
+int file_hash(struct file *file_p) {
+ uint8_t data[BLOCK_SIZE];
+ struct block *start, *p, *next, *end;
+ FILE *fp;
+ int blocks_per_piece;
+
+ blocks_per_piece = global_opts.piece_length / BLOCK_SIZE;
+
+ fp = fopen(file_p->path,"rb");
+ if(NULL==fp) { return -1; }
+
+ while(1) {
+ if(block_init(&start)<0) { goto clean; }
+ p = start;
+ for(size_t i=0;i<blocks_per_piece;i++) {
+ size_t len = fread(data,sizeof(uint8_t),BLOCK_SIZE,fp);
+ if((len<=BLOCK_SIZE)&&(ferror(fp)!=0)) {
+ perror("fread");
+ log_err(FILE_MESSAGE_FREAD_FAILED,file_p->path);
+ goto clean;
+ }
+
+ if(len==0) { break; }
+
+ file_p->size += len;
+
+ if(block_init(&next)<0) { goto clean; }
+ if(hash(data,len,next->hash,crypto_hash_sha256_BYTES)<0) { goto clean; }
+
+ p->next = next;
+ p = p->next;
+ }
+
+ ssize_t blocks = block_length(start);
+ if(blocks<=0) { goto clean; }
+
+ if(blocks!=blocks_per_piece) {
+ // if the file is smaller than one piece then the block hashes
+ // should be padded to the next power of two instead of the next
+ // piece boundary
+ size_t leaves_required = next_power_2(blocks);
+ while(leaves_required>0) {
+ if(block_append_blank(start)<0) { goto clean; }
+ leaves_required--;
+ }
+ }
+
+ if(block_merkle_root(start)<0) { goto clean; }
+
+ if(NULL==file_p->blocks) {
+ file_p->blocks = start;
+ end = start;
+ }
+
+ end->next = start;
+ end = end->next;
+ }
+
+ fclose(fp);
+
+ if(block_duplicate(&start,file_p->blocks)<0) { return -1; }
+ if(block_merkle_root(start)<0) { return -1; }
+ memcpy(file_p->root,start->hash,crypto_hash_sha256_BYTES);
+ block_free(start);
+
return 1;
+clean:
+ block_free(start);
+ fclose(fp);
}
int file_init(struct file **p, const char *path) {
(*p)->name = strdup(b);
memset((*p)->root,0,crypto_hash_sha256_BYTES);
+ (*p)->blocks = NULL;
+ (*p)->size = 0;
return 1;
}