gnunet-fuse

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

gnunet-fuse.c (14352B)


      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 /**
     22  * @file fuse/gnunet-fuse.c
     23  * @brief fuse tool
     24  * @author Christian Grothoff
     25  * @author Mauricio Günther
     26  */
     27 #include "gnunet-fuse.h"
     28 #include "gfs_download.h"
     29 
     30 /**
     31  * Anonymity level to use.
     32  */
     33 unsigned int anonymity_level;
     34 
     35 /**
     36  * Configuration to use.
     37  */
     38 const struct GNUNET_CONFIGURATION_Handle *cfg;
     39 
     40 /**
     41  * Return code from 'main' (0 on success).
     42  */
     43 static int ret;
     44 
     45 /**
     46  * Flag to determine if we should run in single-threaded mode.
     47  */
     48 static int single_threaded;
     49 
     50 /**
     51  * Mounted URI (as string).
     52  */
     53 static char *source;
     54 
     55 /**
     56  * Mount point.
     57  */
     58 static char *directory;
     59 
     60 /**
     61  * Root of the file tree.
     62  */
     63 static struct GNUNET_FUSE_PathInfo *root;
     64 
     65 
     66 /**
     67  * Function used to process entries in a directory; adds the
     68  * respective entry to the parent directory.
     69  *
     70  * @param cls closure with the 'struct GNUNET_FUSE_PathInfo' of the parent
     71  * @param filename name of the file in the directory
     72  * @param uri URI of the file
     73  * @param metadata metadata for the file; metadata for
     74  *        the directory if everything else is NULL/zero
     75  * @param length length of the available data for the file
     76  *           (of type size_t since data must certainly fit
     77  *            into memory; if files are larger than size_t
     78  *            permits, then they will certainly not be
     79  *            embedded with the directory itself).
     80  * @param data data available for the file (length bytes)
     81  */
     82 static void
     83 process_directory_entry (void *cls,
     84                          const char *filename,
     85                          const struct GNUNET_FS_Uri *
     86                          uri,
     87                          const struct
     88                          GNUNET_FS_MetaData *
     89                          meta, size_t length,
     90                          const void *data)
     91 {
     92   struct GNUNET_FUSE_PathInfo *parent = cls;
     93   struct GNUNET_FUSE_PathInfo *pi;
     94   int is_directory;
     95 
     96   if (NULL == filename)
     97     return; /* info about the directory itself */
     98   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     99               "Adding file `%s' to directory `%s'\n",
    100               filename,
    101               parent->filename);
    102   is_directory = GNUNET_FS_meta_data_test_for_directory (meta);
    103   if (GNUNET_SYSERR == is_directory)
    104     is_directory = GNUNET_NO; /* if in doubt, say no */
    105   pi = GNUNET_FUSE_path_info_create (parent, filename, uri, is_directory);
    106   GNUNET_FUSE_path_info_done (pi);
    107 }
    108 
    109 
    110 /**
    111  * Load and parse a directory.
    112  *
    113  * @param pi path to the directory
    114  * @param eno where to store 'errno' on errors
    115  * @return GNUNET_OK on success
    116  */
    117 int
    118 GNUNET_FUSE_load_directory (struct GNUNET_FUSE_PathInfo *pi,
    119                             int *eno)
    120 {
    121   size_t size;
    122   void *data;
    123   struct GNUNET_DISK_MapHandle *mh;
    124   struct GNUNET_DISK_FileHandle *fh;
    125 
    126   /* Need to download directory; store to temporary file */
    127   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    128               "Downloading directory `%s'\n",
    129               pi->filename);
    130   pi->tmpfile = GNUNET_DISK_mktemp ("gnunet-fuse-tempfile");
    131   if (GNUNET_OK != GNUNET_FUSE_download_file (pi,
    132                                               0,
    133                                               GNUNET_FS_uri_chk_get_file_size (
    134                                                 pi->uri)))
    135   {
    136     unlink (pi->tmpfile);
    137     GNUNET_free (pi->tmpfile);
    138     pi->tmpfile = NULL;
    139     *eno = EIO; /* low level IO error */
    140     return GNUNET_SYSERR;
    141   }
    142 
    143   size = (size_t) GNUNET_FS_uri_chk_get_file_size (pi->uri);
    144   fh = GNUNET_DISK_file_open (pi->tmpfile,
    145                               GNUNET_DISK_OPEN_READ,
    146                               GNUNET_DISK_PERM_NONE);
    147   if (NULL == fh)
    148   {
    149     *eno = EIO;
    150     return GNUNET_SYSERR;
    151   }
    152   data = GNUNET_DISK_file_map (fh,
    153                                &mh,
    154                                GNUNET_DISK_MAP_TYPE_READ,
    155                                size);
    156   if (NULL == data)
    157   {
    158     GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
    159     return -ENOMEM;
    160   }
    161   *eno = 0;
    162   if (GNUNET_OK !=
    163       GNUNET_FS_directory_list_contents (size,
    164                                          data, 0LL,
    165                                          &process_directory_entry,
    166                                          pi))
    167     *eno = ENOTDIR;
    168   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_unmap (mh));
    169   GNUNET_DISK_file_close (fh);
    170   if (0 != *eno)
    171     return GNUNET_SYSERR;
    172   return GNUNET_OK;
    173 }
    174 
    175 
    176 /**
    177  * Obtain an existing path info entry from the global map.
    178  *
    179  * @param path path the entry represents
    180  * @param eno where to store 'errno' on errors
    181  * @return NULL if no such path entry exists
    182  */
    183 struct GNUNET_FUSE_PathInfo *
    184 GNUNET_FUSE_path_info_get (const char *path,
    185                            int *eno)
    186 {
    187   size_t slen = strlen (path) + 1;
    188   char buf[slen];
    189   struct GNUNET_FUSE_PathInfo *pi;
    190   struct GNUNET_FUSE_PathInfo *pos;
    191   char *tok;
    192 
    193   memcpy (buf, path, slen);
    194   pi = root;
    195   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    196               "Looking up path `%s'\n",
    197               path);
    198   GNUNET_mutex_lock (pi->lock);
    199   for (tok = strtok (buf, "/"); NULL != tok; tok = strtok (NULL, "/"))
    200   {
    201     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    202                 "Searching for token `%s'\n",
    203                 tok);
    204     if (NULL == pi->tmpfile)
    205     {
    206       if (GNUNET_OK != GNUNET_FUSE_load_directory (pi, eno))
    207       {
    208         GNUNET_mutex_unlock (pi->lock);
    209         return NULL;
    210       }
    211     }
    212 
    213     pos = pi->child_head;
    214     while ( (NULL != pos) &&
    215             (0 != strcmp (tok,
    216                           pos->filename)) )
    217       pos = pos->next;
    218     if (NULL == pos)
    219     {
    220       GNUNET_mutex_unlock (pi->lock);
    221       *eno = ENOENT;
    222       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    223                   "No file with name `%s' in directory `%s'\n",
    224                   tok,
    225                   pi->filename);
    226       return NULL;
    227     }
    228     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    229                 "Descending into directory `%s'\n",
    230                 tok);
    231     GNUNET_mutex_lock (pos->lock);
    232     GNUNET_mutex_unlock (pi->lock);
    233     pi = pos;
    234   }
    235   ++pi->rc;
    236   GNUNET_mutex_unlock (pi->lock);
    237   return pi;
    238 }
    239 
    240 
    241 /**
    242  * Create a new path info entry in the global map.
    243  *
    244  * @param parent parent directory (can be NULL)
    245  * @param filename name of the file to create
    246  * @param uri URI to use for the path
    247  * @param is_directory GNUNET_YES if this entry is for a directory
    248  * @return existing path entry if one already exists, otherwise
    249  *         new path entry with the desired URI; in both cases
    250  *         the reference counter has been incremented by 1
    251  */
    252 struct GNUNET_FUSE_PathInfo *
    253 GNUNET_FUSE_path_info_create (struct GNUNET_FUSE_PathInfo *parent,
    254                               const char *filename,
    255                               const struct GNUNET_FS_Uri *uri,
    256                               int is_directory)
    257 {
    258   struct GNUNET_FUSE_PathInfo *pi;
    259   size_t len;
    260 
    261   if (NULL != parent)
    262   {
    263     GNUNET_mutex_lock (parent->lock);
    264   }
    265 
    266   pi = GNUNET_new (struct GNUNET_FUSE_PathInfo);
    267   pi->parent = parent;
    268   pi->filename = GNUNET_strdup (filename);
    269   len = strlen (pi->filename);
    270   if ('/' == pi->filename[len - 1])
    271     pi->filename[len - 1] = '\0';
    272   pi->uri = GNUNET_FS_uri_dup (uri);
    273   pi->lock = GNUNET_mutex_create (GNUNET_YES);
    274   pi->rc = 1;
    275   pi->stbuf.st_mode = (S_IRUSR | S_IRGRP | S_IROTH); /* read-only */
    276   if (GNUNET_YES == is_directory)
    277   {
    278     pi->stbuf.st_mode |= S_IFDIR | (S_IXUSR | S_IXGRP | S_IXOTH); /* allow traversal */
    279   }
    280   else
    281   {
    282     pi->stbuf.st_mode |= S_IFREG; /* regular file */
    283     pi->stbuf.st_size = (off_t) GNUNET_FS_uri_chk_get_file_size (uri);
    284   }
    285 
    286   if (NULL != parent)
    287   {
    288     GNUNET_CONTAINER_DLL_insert_tail (parent->child_head,
    289                                       parent->child_tail,
    290                                       pi);
    291     GNUNET_mutex_unlock (parent->lock);
    292   }
    293   return pi;
    294 }
    295 
    296 
    297 /**
    298  * Reduce the reference counter of a path info entry.
    299  *
    300  * @param pi entry to decrement the RC of
    301  */
    302 void
    303 GNUNET_FUSE_path_info_done (struct GNUNET_FUSE_PathInfo *pi)
    304 {
    305   if (GNUNET_YES == pi->delete_later)
    306   {
    307     (void) GNUNET_FUSE_path_info_delete (pi);
    308     return;
    309   }
    310   GNUNET_mutex_lock (pi->lock);
    311   --pi->rc;
    312   GNUNET_mutex_unlock (pi->lock);
    313 }
    314 
    315 
    316 /**
    317  * Delete a path info entry from the tree (does not actually
    318  * remove anything from the file system).  Also decrements the RC.
    319  *
    320  * @param pi entry to remove
    321  * @return - ENOENT if the file was already deleted, 0 on success
    322  */
    323 int
    324 GNUNET_FUSE_path_info_delete (struct GNUNET_FUSE_PathInfo *pi)
    325 {
    326   struct GNUNET_FUSE_PathInfo *parent = pi->parent;
    327   int rc;
    328   int ret;
    329 
    330   if (NULL != parent)
    331   {
    332     ret = 0;
    333     GNUNET_mutex_lock (parent->lock);
    334     GNUNET_mutex_lock (pi->lock);
    335     GNUNET_CONTAINER_DLL_remove (parent->child_head,
    336                                  parent->child_tail,
    337                                  pi);
    338     pi->parent = NULL;
    339     GNUNET_mutex_unlock (parent->lock);
    340   }
    341   else
    342   {
    343     ret = -ENOENT;
    344     GNUNET_mutex_lock (pi->lock);
    345   }
    346   rc = --pi->rc;
    347   if (0 != rc)
    348   {
    349     pi->delete_later = GNUNET_YES;
    350     GNUNET_mutex_unlock (pi->lock);
    351   }
    352   else
    353   {
    354     if (NULL != pi->tmpfile)
    355     {
    356       GNUNET_break (0 == unlink (pi->tmpfile));
    357       GNUNET_free (pi->tmpfile);
    358     }
    359     GNUNET_free (pi->filename);
    360     GNUNET_FS_uri_destroy (pi->uri);
    361     GNUNET_mutex_unlock (pi->lock);
    362     GNUNET_mutex_destroy (pi->lock);
    363     GNUNET_free (pi);
    364   }
    365   return ret;
    366 }
    367 
    368 
    369 /**
    370  * Called on each node in the path info tree to clean it up.
    371  *
    372  * @param pi path info to clean up
    373  */
    374 static void
    375 cleanup_path_info (struct GNUNET_FUSE_PathInfo *pi)
    376 {
    377   struct GNUNET_FUSE_PathInfo *pos;
    378 
    379   while (NULL != (pos = pi->child_head))
    380     cleanup_path_info (pos);
    381   ++pi->rc;
    382   (void) GNUNET_FUSE_path_info_delete (pi);
    383 }
    384 
    385 
    386 /**
    387  * Main function that will be run (without the scheduler!)
    388  *
    389  * @param cls closure
    390  * @param args remaining command-line arguments
    391  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    392  * @param c configuration
    393  */
    394 static void
    395 run (void *cls,
    396      char *const *args,
    397      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *c)
    398 {
    399   static struct fuse_operations fops = {
    400     //  .mkdir = gn_mkdir,
    401     //  .mknod = gn_mknod,
    402     //  .release = gn_release,
    403     //  .rename = gn_rename,
    404     //  .rmdir = gn_rmdir,
    405     //  .truncate = gn_truncate,
    406     //  .unlink = gn_unlink,
    407     //  .utimens = gn_utimens,
    408     //  .write = gn_write,
    409     .getattr = gn_getattr,
    410     .readdir = gn_readdir,
    411     .open = gn_open,
    412     .read = gn_read
    413   };
    414 
    415   int argc;
    416   struct GNUNET_FS_Uri *uri;
    417   char *emsg;
    418   int eno;
    419 
    420   cfg = c;
    421   ret = 0;
    422   if (NULL == source)
    423   {
    424     fprintf (stderr, _ ("`%s' option for URI missing\n"), "-s");
    425     ret = 1;
    426     return;
    427   }
    428   if (NULL == directory)
    429   {
    430     fprintf (stderr, _ ("`%s' option for mountpoint missing\n"), "-d");
    431     ret = 2;
    432     return;
    433   }
    434 
    435   /* parse source string to uri */
    436   if (NULL == (uri = GNUNET_FS_uri_parse (source, &emsg)))
    437   {
    438     fprintf (stderr, "%s\n", emsg);
    439     GNUNET_free (emsg);
    440     ret = 3;
    441     return;
    442   }
    443   if ( (GNUNET_YES != GNUNET_FS_uri_test_chk (uri)) &&
    444        (GNUNET_YES != GNUNET_FS_uri_test_loc (uri)) )
    445   {
    446     fprintf (stderr,
    447              _ (
    448                "The given URI is not for a directory and can thus not be mounted\n"));
    449     ret = 4;
    450     GNUNET_FS_uri_destroy (uri);
    451     return;
    452   }
    453 
    454   root = GNUNET_FUSE_path_info_create (NULL, "/", uri, GNUNET_YES);
    455   if (GNUNET_OK !=
    456       GNUNET_FUSE_load_directory (root, &eno))
    457   {
    458     fprintf (stderr,
    459              _ ("Failed to mount `%s': %s\n"),
    460              source,
    461              strerror (eno));
    462     ret = 5;
    463     cleanup_path_info (root);
    464     GNUNET_FS_uri_destroy (uri);
    465     return;
    466   }
    467 
    468   if (GNUNET_YES == single_threaded)
    469     argc = 5;
    470   else
    471     argc = 2;
    472 
    473   {
    474     char *a[argc + 1];
    475     a[0] = "gnunet-fuse";
    476     a[1] = directory;
    477     if (GNUNET_YES == single_threaded)
    478     {
    479       a[2] = "-s";
    480       a[3] = "-f";
    481       a[4] = "-d";
    482     }
    483     a[argc] = NULL;
    484     fuse_main (argc, a, &fops, NULL);
    485   }
    486   cleanup_path_info (root);
    487   GNUNET_FS_uri_destroy (uri);
    488 }
    489 
    490 
    491 /**
    492  * The main function for gnunet-fuse.
    493  *
    494  * @param argc number of arguments from the command line
    495  * @param argv command line arguments
    496  * @return 0 ok, 1 on error
    497  */
    498 int
    499 main (int argc, char *const *argv)
    500 {
    501   struct GNUNET_GETOPT_CommandLineOption options[] = {
    502     GNUNET_GETOPT_option_string ('s',
    503                                  "source",
    504                                  "URI",
    505                                  gettext_noop ("Source you get the URI from"),
    506                                  &source),
    507     GNUNET_GETOPT_option_string ('d',
    508                                  "directory",
    509                                  "PATH",
    510                                  gettext_noop ("path to your mountpoint"),
    511                                  &directory),
    512     GNUNET_GETOPT_option_flag ('t',
    513                                "single-threaded",
    514                                gettext_noop ("run in single-threaded mode"),
    515                                &single_threaded),
    516     GNUNET_GETOPT_OPTION_END
    517   };
    518 
    519   GNUNET_log_setup ("gnunet-fuse",
    520                     "DEBUG",
    521                     NULL);
    522   return (GNUNET_OK ==
    523           GNUNET_PROGRAM_run2 (GNUNET_OS_project_data_gnunet (),
    524                                argc,
    525                                argv,
    526                                "gnunet-fuse -s URI [-- FUSE-OPTIONS] DIRECTORYNAME",
    527                                gettext_noop
    528                                  ("fuse"),
    529                                options,
    530                                &run,
    531                                NULL,
    532                                GNUNET_YES)) ? ret : 1;
    533 }
    534 
    535 
    536 /* end of gnunet-fuse.c */