piececache.c
来自「elinks下lynx是最重要的二个文本浏览器, 在linux下非常实用, el」· C语言 代码 · 共 1,360 行 · 第 1/3 页
C
1,360 行
} if (piece == BITTORRENT_PIECE_UNDEF) return NULL; request = add_piece_to_bittorrent_free_list(cache, bittorrent, piece); /* Check for end game once again to update it sooner than later. */ if (cache->remaining_pieces == 0 && list_empty(cache->free_list)) bittorrent->mode = BITTORRENT_MODE_END_GAME; return request;}/* Find all dublicate request and clear the cloned flag if only one is found. */static voidclear_cloned_bittorrent_peer_request(struct bittorrent_connection *bittorrent, struct bittorrent_peer_request *request){ struct bittorrent_peer_request *single_clone = NULL; struct bittorrent_peer_connection *peer; foreach (peer, bittorrent->peers) { struct bittorrent_peer_request *clone; clone = find_local_bittorrent_peer_request(peer, request); if (!clone) continue; /* More than one has the request pending so no cloned flags * needs to be cleared. */ if (single_clone) return; single_clone = clone; } if (single_clone) single_clone->cloned = 0;}static voidadd_request_to_bittorrent_piece_cache(struct bittorrent_connection *bittorrent, struct bittorrent_peer_request *request){ del_from_list(request); if (!request->cloned) { /* fixme: ensure that the free list is sorted by piece the * number of remaining request so that downloading of complete * pieces are prioritized. */ request->requested = 0; add_to_list(bittorrent->cache->free_list, request); return; } clear_cloned_bittorrent_peer_request(bittorrent, request); mem_free(request);}voidadd_requests_to_bittorrent_piece_cache(struct bittorrent_peer_connection *peer, struct bittorrent_peer_status *status){ struct bittorrent_peer_request *request, *next; foreachsafe (request, next, status->requests) { add_request_to_bittorrent_piece_cache(peer->bittorrent, request); }}/* ************************************************************************** *//* Piece cache rarity index management: *//* ************************************************************************** */voidupdate_bittorrent_piece_cache(struct bittorrent_peer_connection *peer, uint32_t piece){ struct bittorrent_piece_cache *cache = peer->bittorrent->cache; assert(piece <= peer->bittorrent->meta.pieces); if (!cache->entries[piece].completed && !cache->entries[piece].rarity) cache->unavailable_pieces--; cache->entries[piece].rarity++; assertm(cache->entries[piece].rarity <= list_size(&peer->bittorrent->peers), "Piece rarity overflow");}voidupdate_bittorrent_piece_cache_from_bitfield(struct bittorrent_peer_connection *peer){ uint32_t piece; assert(peer->bitfield->bitsize == peer->bittorrent->meta.pieces); foreach_bitfield_set (piece, peer->bitfield) { update_bittorrent_piece_cache(peer, piece); }}voidremove_bittorrent_peer_from_piece_cache(struct bittorrent_peer_connection *peer){ struct bittorrent_piece_cache *cache = peer->bittorrent->cache; unsigned int piece; assert(peer->bitfield); foreach_bitfield_set (piece, peer->bitfield) { cache->entries[piece].rarity--; assertm(cache->entries[piece].rarity <= list_size(&peer->bittorrent->peers), "Piece rarity underflow"); if (!cache->entries[piece].completed && !cache->entries[piece].rarity) cache->unavailable_pieces++; }}/* ************************************************************************** *//* Piece cache disk management: *//* ************************************************************************** *//* Recursively create directories in a path. The last element in the path is * taken to be a filename, and simply ignored */static enum bittorrent_statecreate_bittorrent_path(unsigned char *path){ int pos; if (!*path) return BITTORRENT_STATE_ERROR; for (pos = 1; path[pos]; pos++) { unsigned char separator = path[pos]; int ret; if (!dir_sep(separator)) continue; path[pos] = 0; ret = mkdir(path, S_IREAD | S_IWRITE | S_IEXEC); path[pos] = separator; if (ret < 0 && errno != EEXIST) return BITTORRENT_STATE_ERROR; } return BITTORRENT_STATE_OK;}/* Complementary to the above rmdir()s each directory in the path. */static voidremove_bittorrent_path(struct bittorrent_meta *meta, unsigned char *path){ unsigned char *root = strstr(path, meta->name); int pos; assert(meta->type == BITTORRENT_MULTI_FILE); if (!root || root == path) return; for (pos = strlen(root); pos >= 0; pos--) { unsigned char separator = root[pos]; int ret; if (!dir_sep(separator)) continue; root[pos] = 0; ret = rmdir(path); path[pos] = separator; if (ret < 0) return; }}static unsigned char *get_bittorrent_file_name(struct bittorrent_meta *meta, struct bittorrent_file *file){ unsigned char *name; name = expand_tilde(get_opt_str("document.download.directory")); if (!name) return NULL; add_to_strn(&name, "/"); add_to_strn(&name, meta->name); if (meta->type == BITTORRENT_MULTI_FILE) { add_to_strn(&name, "/"); add_to_strn(&name, file->name); } return name;}enum bittorrent_translation { BITTORRENT_READ, BITTORRENT_WRITE, BITTORRENT_SEEK,};static intopen_bittorrent_file(struct bittorrent_meta *meta, struct bittorrent_file *file, enum bittorrent_translation trans, off_t offset){ unsigned char *name = get_bittorrent_file_name(meta, file); off_t seek_result; int fd; int flags = (trans == BITTORRENT_WRITE ? O_WRONLY : O_RDONLY); assert(trans != BITTORRENT_SEEK); if (!name) return -1; fd = open(name, flags, S_IRUSR | S_IWUSR); if (fd == -1) { /* 99% of the time the file will already exist so special case * the directory and file creation. */ if (errno == ENOENT && trans == BITTORRENT_WRITE && create_bittorrent_path(name) == BITTORRENT_STATE_OK) fd = open(name, flags | O_CREAT, S_IRUSR | S_IWUSR); } mem_free(name); if (fd == -1) return -1; if (trans == BITTORRENT_READ) { struct stat stat; if (fstat(fd, &stat) < -1 || stat.st_size < offset) { close(fd); return -1; } } seek_result = lseek(fd, offset, SEEK_SET); if (seek_result == (off_t) -1 || seek_result != offset) { close(fd); return -1; } return fd;}static enum bittorrent_statebittorrent_file_piece_translation(struct bittorrent_meta *meta, struct bittorrent_piece_cache *cache, struct bittorrent_piece_cache_entry *entry, uint32_t piece, enum bittorrent_translation trans){ struct bittorrent_file *file; uint32_t piece_length = get_bittorrent_piece_length(meta, piece); uint32_t piece_offset = 0; off_t torrent_offset = (off_t) meta->piece_length * piece; off_t current_offset = 0; enum bittorrent_state state = BITTORRENT_STATE_OK; int prev_selected = -1; if (trans == BITTORRENT_READ) { assert(!entry->data); entry->data = mem_mmap_alloc(piece_length); if (!entry->data) return BITTORRENT_STATE_OUT_OF_MEM; } foreach (file, meta->files) { unsigned char *data; off_t file_offset, file_length; uint32_t data_length; ssize_t length; int fd; if (piece_offset >= piece_length) break; if (current_offset + file->length < torrent_offset) { current_offset += file->length; continue; } assert(current_offset <= torrent_offset + piece_length); /* Figure out where in the file we need access. */ file_offset = torrent_offset - current_offset; if (file_offset < 0) file_offset = 0; assertm(file_offset <= file->length, "%ld %ld", file_offset, file->length); /* How much needs to be read or written. */ data_length = piece_length - piece_offset; file_length = file->length - file_offset; if (data_length > file_length) data_length = file_length; if (trans == BITTORRENT_SEEK) { /* Mark all pieces which need to be downloaded. */ if (!entry->selected) entry->selected = (file->selected == 1); /* Lock entries that span both selected and unselected * files in memory. */ if (prev_selected != -1 && prev_selected != file->selected) entry->locked = 1; prev_selected = file->selected; /* Prepare the next iteration. */ piece_offset += data_length; current_offset += file->length; continue; } /* Jump on unselected files. */ if (!file->selected) { if (trans != BITTORRENT_READ || !entry->completed) { /* Prepare the next iteration. */ piece_offset += data_length; current_offset += file->length; continue; } /* Allow reading of pieces which is resumable. */ assert(entry->completed && trans == BITTORRENT_READ); } /* Open at the desired offset and possibly create the file and * parent directories. */ fd = open_bittorrent_file(meta, file, trans, file_offset); if (fd == -1) { /* Try to gracefully handle bogus paths; empty file * names and directory names. */ if (!file->name[0] || dir_sep(file->name[strlen(file->name) - 1])) { current_offset += file->length; continue; } if (trans == BITTORRENT_WRITE) { state = BITTORRENT_STATE_ERROR; break; } state = BITTORRENT_STATE_FILE_MISSING; break; } assert(entry->data); data = &entry->data[piece_offset]; /* Do it! ;-) */ if (trans == BITTORRENT_READ) { length = safe_read(fd, data, data_length); } else { length = safe_write(fd, data, data_length); } /* Cleanup. */ close(fd); /* Check if the operation failed. */ if (length != data_length) return BITTORRENT_STATE_ERROR; /* Prepare the next iteration. */ piece_offset += data_length; current_offset += file->length; } if (state != BITTORRENT_STATE_OK || piece_offset != piece_length) { if (piece_offset != piece_length) state = BITTORRENT_STATE_ERROR; assert(trans != BITTORRENT_SEEK); mem_mmap_free(entry->data, piece_length); entry->data = NULL; return state; } if (trans != BITTORRENT_SEEK) { /* Whether we just read or wrote the piece, it is now * disposable. */ add_to_list(cache->queue, entry); } return BITTORRENT_STATE_OK;}/* ************************************************************************** *//* Piece cache block data management: *//* ************************************************************************** */static voidcancel_cloned_bittorrent_peer_requests(struct bittorrent_connection *bittorrent, struct bittorrent_peer_request *request){ struct bittorrent_peer_connection *peer; foreach (peer, bittorrent->peers) { struct bittorrent_peer_request *clone; clone = find_local_bittorrent_peer_request(peer, request); if (!clone) continue; /* Only cancel it if it was actually requested. */ if (clone->requested) cancel_bittorrent_peer_request(peer, clone); del_from_list(clone); mem_free(clone); }}enum bittorrent_stateadd_to_bittorrent_piece_cache(struct bittorrent_peer_connection *peer, uint32_t piece, uint32_t offset, unsigned char *data, uint32_t datalen, int *write_errno){ struct bittorrent_connection *bittorrent = peer->bittorrent; struct bittorrent_meta *meta = &bittorrent->meta; struct bittorrent_piece_cache *cache = bittorrent->cache; struct bittorrent_piece_cache_entry *entry; struct bittorrent_peer_request *request; enum bittorrent_state state; uint32_t piece_length; assert(piece < bittorrent->meta.pieces); assert(offset + datalen <= get_bittorrent_piece_length(meta, piece)); assert(datalen); entry = &cache->entries[piece];
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?