gnunet-fuse

GNUnet file-sharing directory mounting via FUSE
Log | Files | Refs | Submodules | README | LICENSE

gfs_download.c (6224B)


      1 /*
      2   This file is part of gnunet-fuse.
      3   Copyright (C) 2012 GNUnet e.V.
      4 
      5   gnunet-fuse is free software; you can redistribute it and/or modify
      6   it under the terms of the GNU General Public License as published
      7   by the Free Software Foundation; either version 3, or (at your
      8   option) any later version.
      9 
     10   gnunet-fuse is distributed in the hope that it will be useful,
     11   but WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13   GNU General Public License for more details.
     14 
     15   You should have received a copy of the GNU General Public License
     16   along with this program; if not, write to the Free Software
     17   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
     18 
     19 */
     20 /**
     21  * @file fuse/gfs_download.h
     22  * @brief download files using FS
     23  * @author Christian Grothoff
     24  */
     25 #include "gfs_download.h"
     26 
     27 
     28 /**
     29  * Context for a download operation.
     30  */
     31 struct Context
     32 {
     33 
     34   /**
     35    * Information about the file we are downloading.
     36    */
     37   struct GNUNET_FUSE_PathInfo *path_info;
     38 
     39   /**
     40    * Download handle.
     41    */
     42   struct GNUNET_FS_DownloadContext *dc;
     43 
     44   /**
     45    * FS handle.
     46    */
     47   struct GNUNET_FS_Handle *fs;
     48 
     49   /**
     50    * Start offset.
     51    */
     52   off_t start_offset;
     53 
     54   /**
     55    * Number of bytes to download.
     56    */
     57   uint64_t length;
     58 
     59   /**
     60    * Return value for the operation, 0 on success.
     61    */
     62   int ret;
     63 
     64 };
     65 
     66 
     67 /**
     68  * Task run when we shut down.
     69  *
     70  * @param cls our 'struct Context'
     71  */
     72 static void
     73 shutdown_task (void *cls)
     74 {
     75   struct Context *ctx = cls;
     76 
     77   if (NULL != ctx->dc)
     78   {
     79     GNUNET_FS_download_stop (ctx->dc, GNUNET_YES);
     80     ctx->dc = NULL;
     81   }
     82   if (NULL != ctx->fs)
     83   {
     84     GNUNET_FS_stop (ctx->fs);
     85     ctx->fs = NULL;
     86   }
     87 }
     88 
     89 
     90 /**
     91  * Function called from FS with progress information.
     92  *
     93  * @param cls our 'struct Context'
     94  * @param info progress information
     95  * @return NULL
     96  */
     97 static void *
     98 progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info)
     99 {
    100   struct Context *ctx = cls;
    101   char *s;
    102 
    103   switch (info->status)
    104     {
    105     case GNUNET_FS_STATUS_DOWNLOAD_START:
    106       GNUNET_break (info->value.download.dc == ctx->dc);
    107       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    108 		  "Started download `%s'.\n",
    109 		  info->value.download.filename);
    110       break;
    111     case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
    112       GNUNET_break (info->value.download.dc == ctx->dc);
    113       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    114 		  "Downloading `%s' at %llu/%llu\n",
    115 		  info->value.download.filename,
    116 		  (unsigned long long) info->value.download.completed,
    117 		  (unsigned long long) info->value.download.size);
    118       break;
    119     case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
    120       GNUNET_break (info->value.download.dc == ctx->dc);
    121       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    122 		  "Error downloading: %s.\n",
    123 		  info->value.download.specifics.error.message);
    124       GNUNET_SCHEDULER_shutdown ();
    125       break;
    126     case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
    127       GNUNET_break (info->value.download.dc == ctx->dc);
    128       s =
    129 	GNUNET_STRINGS_byte_size_fancy (info->value.download.completed *
    130 					1000000LL /
    131 					(info->value.download.
    132 					 duration.rel_value_us + 1));
    133       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    134 		  "Downloading `%s' done (%s/s).\n",
    135 		  info->value.download.filename, s);
    136       GNUNET_free (s);
    137       ctx->ret = 0;
    138       GNUNET_SCHEDULER_shutdown ();
    139       break;
    140     case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
    141       GNUNET_SCHEDULER_shutdown ();
    142       break;
    143     case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
    144     case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
    145       break;
    146     default:
    147       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    148 		  _("Unexpected status: %d\n"), info->status);
    149       break;
    150     }
    151   return NULL;
    152 }
    153 
    154 
    155 /**
    156  * Main task run by the helper process which downloads the file.
    157  *
    158  * @param cls 'struct Context' with information about the download
    159  */
    160 static void
    161 download_task (void *cls)
    162 {
    163   struct Context *ctx = cls;
    164 
    165   ctx->fs = GNUNET_FS_start (cfg, "gnunet-fuse", &progress_cb, ctx,
    166 			     GNUNET_FS_FLAGS_NONE,
    167 			     GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM, 1,
    168 			     GNUNET_FS_OPTIONS_REQUEST_PARALLELISM, 1,
    169 			     GNUNET_FS_OPTIONS_END);
    170   if (NULL == ctx->fs)
    171   {
    172     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Could not initialize `%s' subsystem.\n"), "FS");
    173     return;
    174   }
    175   ctx->dc = GNUNET_FS_download_start (ctx->fs,
    176 				      ctx->path_info->uri, ctx->path_info->meta,
    177 				      ctx->path_info->tmpfile, NULL,
    178 				      (uint64_t) ctx->start_offset,
    179 				      ctx->length,
    180 				      anonymity_level,
    181 				      GNUNET_FS_DOWNLOAD_OPTION_NONE,
    182 				      NULL, NULL);
    183   if (NULL == ctx->dc)
    184   {
    185     GNUNET_FS_stop (ctx->fs);
    186     ctx->fs = NULL;
    187     return;
    188   }
    189   GNUNET_SCHEDULER_add_shutdown (&shutdown_task, ctx);
    190 }
    191 
    192 
    193 /**
    194  * Download a file.  Blocks until we're done.
    195  *
    196  * @param path_info information about the file to download
    197  * @param start_offset offset of the first byte to download
    198  * @param length number of bytes to download from 'start_offset'
    199  * @return GNUNET_OK on success
    200  */
    201 int
    202 GNUNET_FUSE_download_file (struct GNUNET_FUSE_PathInfo *path_info,
    203 			   off_t start_offset,
    204 			   uint64_t length)
    205 {
    206   struct Context ctx;
    207   pid_t pid;
    208   int status;
    209   int ret;
    210 
    211   /* lock to prevent two processes from downloading / manipulating the
    212      same file at the same time */
    213   GNUNET_mutex_lock (path_info->lock);
    214   pid = fork ();
    215   if (-1 == pid)
    216   {
    217     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
    218     GNUNET_mutex_unlock (path_info->lock);
    219     return GNUNET_SYSERR;
    220   }
    221   if (0 != pid)
    222   {
    223     while ( (-1 == (ret = waitpid (pid, &status, 0))) &&
    224 	    (EINTR == errno) ) ;
    225     if (pid != ret)
    226     {
    227       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "waitpid");
    228       (void) kill (pid, SIGKILL);
    229       (void) waitpid (pid, &status, 0);
    230       GNUNET_mutex_unlock (path_info->lock);
    231       return GNUNET_SYSERR;
    232     }
    233     GNUNET_mutex_unlock (path_info->lock);
    234     if ( (WIFEXITED (status)) &&
    235 	 (0 == WEXITSTATUS (status)) )
    236       return GNUNET_OK;
    237     return GNUNET_SYSERR;
    238   }
    239   memset (&ctx, 0, sizeof (ctx));
    240   ctx.ret = 1;
    241   ctx.path_info = path_info;
    242   ctx.start_offset = start_offset;
    243   ctx.length = length;
    244   GNUNET_SCHEDULER_run (&download_task, &ctx);
    245   _exit (ctx.ret);
    246 }
    247 
    248 /* end of gfs_download.c */