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 */