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 + -
显示快捷键?