📄 download.c
字号:
/* This file is part of GNUnet. (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008 Christian Grothoff (and other contributing authors) GNUnet 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, or (at your option) any later version. GNUnet 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 GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*//** * @file applications/fs/ecrs/download.c * @brief DOWNLOAD helper methods (which do the real work). * @author Christian Grothoff */#include "platform.h"#include "gnunet_protocols.h"#include "gnunet_ecrs_lib.h"#include "gnunet_fs_lib.h"#include "gnunet_identity_lib.h"#include "ecrs_core.h"#include "ecrs.h"#include "fs.h"#include "tree.h"#define DEBUG_DOWNLOAD GNUNET_NO/** * Node-specific data (not shared, keep small!). 152 bytes. * Nodes are kept in a doubly-linked list. */struct Node{ /** * Pointer to shared data between all nodes (request manager, * progress data, etc.). */ struct GNUNET_ECRS_DownloadContext *ctx; /** * Previous entry in DLL. */ struct Node *prev; /** * Next entry in DLL. */ struct Node *next; /** * What is the GNUNET_EC_ContentHashKey for this block? */ GNUNET_EC_ContentHashKey chk; /** * At what offset (on the respective level!) is this * block? */ unsigned long long offset; /** * 0 for dblocks, >0 for iblocks. */ unsigned int level;};/** * @brief structure that keeps track of currently pending requests for * a download * * Handle to the state of a request manager. Here we keep track of * which queries went out with which priorities and which nodes in * the merkle-tree are waiting for the replies. */struct GNUNET_ECRS_DownloadContext{ /** * Total number of bytes in the file. */ unsigned long long total; /** * Number of bytes already obtained */ unsigned long long completed; /** * Starting-offset in file (for partial download) */ unsigned long long offset; /** * Length of the download (starting at offset). */ unsigned long long length; /** * Time download was started. */ GNUNET_CronTime startTime; /** * Doubly linked list of all pending requests (head) */ struct Node *head; /** * Doubly linked list of all pending requests (tail) */ struct Node *tail; /** * FSLIB context for issuing requests. */ struct GNUNET_FS_SearchContext *sctx; /** * Context for error reporting. */ struct GNUNET_GE_Context *ectx; /** * Configuration information. */ struct GNUNET_GC_Configuration *cfg; /** * The file handle. */ int handle; /** * Do we exclusively own this sctx? */ int my_sctx; /** * The base-filename */ char *filename; /** * Main thread running the operation. */ struct GNUNET_ThreadHandle *main; /** * Function to call when we make progress. */ GNUNET_ECRS_DownloadProgressCallback dpcb; /** * Extra argument to dpcb. */ void *dpcbClosure; /** * Identity of the peer having the content, or all-zeros * if we don't know of such a peer. */ GNUNET_PeerIdentity target; /** * Abort? Flag that can be set at any time * to abort the RM as soon as possible. Set * to GNUNET_YES during orderly shutdown, * set to GNUNET_SYSERR on error. */ int abortFlag; /** * Do we have a specific peer from which we download * from? */ int have_target; /** * Desired anonymity level for the download. */ unsigned int anonymityLevel; /** * The depth of the file-tree. */ unsigned int treedepth;};static intcontent_receive_callback (const GNUNET_HashCode * query, const GNUNET_DatastoreValue * reply, void *cls, unsigned long long uid);/** * Close the files and free the associated resources. * * @param self reference to the download context */static voidfree_request_manager (struct GNUNET_ECRS_DownloadContext *rm){ struct Node *pos; if (rm->abortFlag == GNUNET_NO) rm->abortFlag = GNUNET_YES; if (rm->my_sctx == GNUNET_YES) GNUNET_FS_destroy_search_context (rm->sctx); else GNUNET_FS_suspend_search_context (rm->sctx); while (rm->head != NULL) { pos = rm->head; if (rm->my_sctx != GNUNET_YES) GNUNET_FS_stop_search (rm->sctx, &content_receive_callback, pos); rm->head = pos->next; GNUNET_free (pos); } if (rm->my_sctx != GNUNET_YES) GNUNET_FS_resume_search_context (rm->sctx); rm->tail = NULL; if (rm->handle >= 0) CLOSE (rm->handle); if (rm->main != NULL) GNUNET_thread_release_self (rm->main); GNUNET_free_non_null (rm->filename); rm->sctx = NULL; GNUNET_free (rm);}/** * Read method. * * @param self reference to the download context * @param level level in the tree to read/write at * @param pos position where to read or write * @param buf where to read from or write to * @param len how many bytes to read or write * @return number of bytes read, GNUNET_SYSERR on error */static intread_from_files (struct GNUNET_ECRS_DownloadContext *self, unsigned int level, unsigned long long pos, void *buf, unsigned int len){ if ((level > 0) || (self->handle == -1)) return GNUNET_SYSERR; LSEEK (self->handle, pos, SEEK_SET); return READ (self->handle, buf, len);}/** * Write method. * * @param self reference to the download context * @param level level in the tree to write to * @param pos position where to write * @param buf where to write to * @param len how many bytes to write * @return number of bytes written, GNUNET_SYSERR on error */static intwrite_to_files (struct GNUNET_ECRS_DownloadContext *self, unsigned int level, unsigned long long pos, void *buf, unsigned int len){ int ret; if (level > 0) return len; /* lie -- no more temps */ if (self->handle == -1) return len; LSEEK (self->handle, pos, SEEK_SET); ret = WRITE (self->handle, buf, len); if (ret != len) GNUNET_GE_LOG_STRERROR_FILE (self->ectx, GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER, "write", self->filename); return ret;}/** * Queue a request for execution. * * @param rm the request manager struct from createRequestManager * @param node the node to call once a reply is received */static voidadd_request (struct Node *node){ struct GNUNET_ECRS_DownloadContext *rm = node->ctx; node->next = rm->head; if (node->next != NULL) node->next->prev = node; node->prev = NULL; rm->head = node; if (rm->tail == NULL) rm->tail = node; GNUNET_FS_start_search (rm->sctx, rm->have_target == GNUNET_NO ? NULL : &rm->target, GNUNET_ECRS_BLOCKTYPE_DATA, 1, &node->chk.query, rm->anonymityLevel, &content_receive_callback, node);}static voidsignal_abort (struct GNUNET_ECRS_DownloadContext *rm, const char *msg){ rm->abortFlag = GNUNET_SYSERR; if ((rm->head != NULL) && (rm->dpcb != NULL)) rm->dpcb (rm->length + 1, 0, 0, 0, msg, 0, rm->dpcbClosure); GNUNET_thread_stop_sleep (rm->main);}/** * Dequeue a request. * * @param self the request manager struct from createRequestManager * @param node the block for which the request is canceled */static voiddelete_node (struct Node *node){ struct GNUNET_ECRS_DownloadContext *rm = node->ctx; if (node->prev == NULL) rm->head = node->next; else node->prev->next = node->next; if (node->next == NULL) rm->tail = node->prev; else node->next->prev = node->prev; GNUNET_free (node); if (rm->head == NULL) GNUNET_thread_stop_sleep (rm->main);}/** * Compute how many bytes of data are stored in * this node. */static unsigned intget_node_size (const struct Node *node){ unsigned int i; unsigned int ret; unsigned long long rsize; unsigned long long spos; unsigned long long epos; GNUNET_GE_ASSERT (node->ctx->ectx, node->offset < node->ctx->total); if (node->level == 0) { ret = GNUNET_ECRS_DBLOCK_SIZE; if (node->offset + (unsigned long long) ret > node->ctx->total) ret = (unsigned int) (node->ctx->total - node->offset);#if DEBUG_DOWNLOAD GNUNET_GE_LOG (node->ctx->rm->ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, "Node at offset %llu and level %d has size %u\n", node->offset, node->level, ret);#endif return ret; } rsize = GNUNET_ECRS_DBLOCK_SIZE; for (i = 0; i < node->level - 1; i++) rsize *= GNUNET_ECRS_CHK_PER_INODE; spos = rsize * (node->offset / sizeof (GNUNET_EC_ContentHashKey)); epos = spos + rsize * GNUNET_ECRS_CHK_PER_INODE; if (epos > node->ctx->total) epos = node->ctx->total; ret = (epos - spos) / rsize; if (ret * rsize < epos - spos) ret++; /* need to round up! */#if DEBUG_DOWNLOAD GNUNET_GE_LOG (node->ctx->rm->ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, "Node at offset %llu and level %d has size %u\n", node->offset, node->level, ret * sizeof (GNUNET_EC_ContentHashKey));#endif return ret * sizeof (GNUNET_EC_ContentHashKey);}/** * Notify client about progress. */static voidnotify_client_about_progress (const struct Node *node, const char *data, unsigned int size){ struct GNUNET_ECRS_DownloadContext *rm = node->ctx; GNUNET_CronTime eta; if ((rm->abortFlag != GNUNET_NO) || (node->level != 0)) return; rm->completed += size; eta = GNUNET_get_time (); if (rm->completed > 0) eta = (GNUNET_CronTime) (rm->startTime + (((double) (eta - rm->startTime) / (double) rm->completed)) * (double) rm->length); if (rm->dpcb != NULL) rm->dpcb (rm->length, rm->completed, eta, node->offset, data, size, rm->dpcbClosure);}/** * DOWNLOAD children of this GNUNET_EC_IBlock. * * @param node the node for which the children should be downloaded * @param data data for the node * @param size size of data */static void iblock_download_children (const struct Node *node, const char *data, unsigned int size);/** * Check if self block is already present on the drive. If the block * is a dblock and present, the ProgressModel is notified. If the * block is present and it is an iblock, downloading the children is * triggered. * * Also checks if the block is within the range of blocks * that we are supposed to download. If not, the method * returns as if the block is present but does NOT signal * progress. * * @param node that is checked for presence * @return GNUNET_YES if present, GNUNET_NO if not. */static intcheck_node_present (const struct Node *node){ int res; int ret; char *data; unsigned int size; GNUNET_HashCode hc; size = get_node_size (node); /* first check if node is within range. For now, keeping it simple, we only do this for level-0 nodes */ if ((node->level == 0) && ((node->offset + size < node->ctx->offset) || (node->offset >= node->ctx->offset + node->ctx->length))) return GNUNET_YES; data = GNUNET_malloc (size); ret = GNUNET_NO; res = read_from_files (node->ctx, node->level, node->offset, data, size); if (res == size)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -