1 /******************************* -*- C++ -*- ******************************* 2 * * 3 * file : fusepod.cpp * 4 * date started : 13 Feb 2006 * 5 * author : Keegan Carruthers-Smith * 6 * email : keegan.csmith@gmail.com * 7 * * 8 * This program is free software; you can redistribute it and/or modify * 9 * it under the terms of the GNU General Public License as published by * 10 * the Free Software Foundation; either version 2 of the License, or * 11 * (at your option) any later version. * 12 * * 13 ***************************************************************************/ 14 15 extern "C" { 16 #ifdef linux 17 /* For pread()/pwrite() */ 18 #define _XOPEN_SOURCE 500 19 #endif 20 21 #include <fuse.h> 22 #include <dirent.h> 23 #include <errno.h> 24 #include <unistd.h> 25 #include <sys/stat.h> 26 #include <sys/types.h> 27 #include <fcntl.h> 28 #include <time.h> 29 } 30 31 #include <iostream> 32 #include <fstream> 33 #include <sstream> 34 #include <vector> 35 #include <cstdlib> 36 #include <cassert> 37 #include <cstdio> 38 #include <cstdlib> 39 #include <algorithm> 40 41 #include "fusepod_ipod.h" 42 #include "fusepod_constants.h" 43 #include "fusepod_util.h" 44 45 using namespace std; 46 47 /* I know globals are regarded as bad, but I'm lazy */ 48 static FUSEPod * fusepod; 49 static string fuse_mount_point; 50 static string ipod_mount_point; 51 static string sync_script; 52 static string add_files_script; 53 static char * add_songs; 54 static bool syncing; 55 static string syncing_file; 56 static string currently_syncing; 57 58 59 /** Returns true if the transfer directory is a prefix of path */ 60 inline static bool transfer_in_dir (const char * path) { 61 return fusepod_starts_with (&(path[1]), dir_transfer.c_str ()); 62 } 63 64 static void transfer_remove (const char * path) { 65 Node * node = fusepod->get_node (path); 66 67 if (!node) 68 return; 69 70 assert (S_ISREG (node->value.mode)); 71 72 node->remove_from_parent (); 73 74 string realpath = fusepod->get_transfer_path (path); 75 76 free ((void*)node->value.text); 77 delete node; 78 79 unlink (realpath.c_str()); 80 } 81 82 static void transfer_add_songs (Node * node, const string & path) { 83 cout <<"Adding files in directory" << path << endl; 84 for (Node::iterator it = node->begin(); it != node->end(); ++it) { 85 Node * n = *it; 86 string new_path = path + "/" + n->value.text; 87 88 if (S_ISDIR(n->value.mode)) { 89 transfer_add_songs (n, new_path); 90 } else if (!(n->value.mode & S_IWUSR)) { 91 //Files that have finished copying are marked not writable 92 currently_syncing = fusepod->get_transfer_path (new_path.c_str()); 93 cout << "Adding track " << currently_syncing << "... "; 94 if (fusepod->upload_song (currently_syncing, false)) 95 cout << "Successful" << endl; 96 else 97 cout << "Failed" << endl; 98 99 transfer_remove (new_path.c_str()); 100 } 101 } 102 } 103 104 static void transfer_remove_empty_dirs (Node * node, const string & path, bool can_delete=true) { 105 for (Node::iterator it = node->begin(); it != node->end(); ++it) 106 if (S_ISDIR((*it)->value.mode)) 107 transfer_remove_empty_dirs (*it, path + "/" + (*it)->value.text); 108 109 if (node->begin() != node->end() || !can_delete || 110 rmdir(fusepod->get_transfer_path (path.c_str()).c_str())) // not empty || cannot delete || can't delete real dir 111 return; 112 113 node->remove_from_parent (); 114 115 free ((void*)node->value.text); 116 delete node; 117 } 118 119 120 /** Returns fusepod->get_statistics with syncing info */ 121 static string fusepod_get_stats () { 122 if (syncing) /* Add syncing stats */ 123 return fusepod->get_statistics () + 124 "Currently Syncing: " + currently_syncing + "\n"; 125 else 126 return fusepod->get_statistics (); 127 } 128 129 static int fusepod_getattr (const char *path, struct stat *stbuf) { 130 Node * tn = fusepod->get_node (path); 131 if (tn == 0) 132 return -ENOENT; 133 134 if (filename_add == &(path[1])) { 135 if (stat (add_songs, stbuf) != 0) 136 return -errno; 137 return 0; 138 } 139 140 /* Update size for statistics file */ 141 if (filename_stats == &(path[1])) 142 tn->value.size = fusepod_get_stats ().length (); 143 144 /* Update size for files in the ipod's transfer dir */ 145 if (transfer_in_dir (path)) { 146 struct stat st; 147 stat (fusepod->get_transfer_path (path).c_str (), &st); 148 tn->value.size = st.st_size; 149 } 150 151 memset (stbuf, 0, sizeof (struct stat)); 152 stbuf->st_uid = getuid (); 153 stbuf->st_gid = getgid (); 154 stbuf->st_mode = tn->value.mode; 155 stbuf->st_size = 4096; 156 stbuf->st_nlink = 1; 157 158 if (tn->value.mode == MODE_DIR) 159 stbuf->st_nlink = tn->value.size + 2; /* +2 for .. and . */ 160 else 161 stbuf->st_size = tn->value.size; 162 163 return 0; 164 } 165 166 static int fusepod_access (const char *path, int mask) { 167 int res; 168 169 struct stat st; 170 res = fusepod_getattr (path, &st); 171 if (res != 0) 172 return res; 173 174 if ((st.st_mode | mask) != st.st_mode) 175 return -EROFS; 176 177 return 0; 178 } 179 180 static int fusepod_readdir (const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { 181 (void) offset; 182 (void) fi; 183 184 Node * tn = fusepod->get_node (path); 185 if (tn == 0 || (tn->value.mode & S_IFREG)) 186 return -ENOENT; 187 188 for (Node::iterator i = tn->begin (); i != tn->end (); ++i) 189 filler (buf, (*i)->value.text, 0, 0); 190 191 return 0; 192 } 193 194 static int fusepod_open(const char *path, struct fuse_file_info *fi) { 195 Node * tn = fusepod->get_node (path); 196 if (tn == 0) 197 return -ENOENT; 198 199 string realpath; 200 201 if (filename_add == &(path[1])) //The special file containing songs to sync 202 realpath = add_songs; 203 else if (transfer_in_dir (path)) //File in transfer directory 204 realpath = fusepod->get_transfer_path (path); 205 else if (tn->value.track) //A song 206 realpath = fusepod->get_real_path (tn->value); 207 else 208 return 0; 209 210 int res = open(realpath.c_str(), fi->flags); 211 if (res == -1) 212 return -errno; 213 214 fi->fh = res; 215 close (res); 216 217 return 0; 218 } 219 220 static int fusepod_truncate (const char * path, off_t offset) { 221 string realpath; 222 223 if (filename_add == &(path[1])) 224 realpath = add_songs; 225 else if (transfer_in_dir (path)) 226 realpath = fusepod->get_transfer_path (path); 227 else 228 return -EACCES; 229 230 if (truncate (realpath.c_str(), offset) != 0) 231 return -errno; 232 233 return 0; 234 } 235 236 static int fusepod_write (const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { 237 string real_path; 238 239 if (filename_add == &(path[1])) 240 real_path = add_songs; 241 else if (transfer_in_dir (path)) 242 real_path = fusepod->get_transfer_path (path); 243 else 244 return -EACCES; 245 246 int fd; 247 int res; 248 249 (void) fi; 250 251 fd = open (real_path.c_str (), O_WRONLY); 252 if (fd == -1) 253 return -errno; 254 255 res = pwrite(fd, buf, size, offset); 256 if (res == -1) 257 res = -errno; 258 259 close(fd); 260 return res; 261 } 262 263 static int fusepod_read_string (const string & str, char *buf, size_t size, off_t offset) { 264 if (offset >= str.length ()) 265 return -EINVAL; 266 267 int bytes_read = min ((int) (str.length () - offset), (int) size); 268 memcpy (buf, &(str.c_str ()[offset]), bytes_read); 269 return bytes_read; 270 } 271 272 static int fusepod_read (const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { 273 int fd; 274 int res; 275 276 Node * tn = fusepod->get_node (path); 277 if (tn == 0) 278 return -ENOENT; 279 else if (!(tn->value.mode & S_IFREG)) 280 return -EACCES; 281 282 283 /* Checking if reading in memory files */ 284 if (filename_sync == &(path[1])) 285 return fusepod_read_string (sync_script, buf, size, offset); 286 287 else if (filename_add_files == &(path[1])) 288 return fusepod_read_string (add_files_script, buf, size, offset); 289 290 else if (filename_stats == &(path[1])) 291 return fusepod_read_string (fusepod_get_stats (), buf, size, offset); 292 293 294 /* Otherwise check if file in really another file on the filesystem */ 295 string realpath; 296 if (filename_add == &(path[1])) 297 realpath = add_songs; 298 else if (transfer_in_dir (path)) 299 realpath = fusepod->get_transfer_path (path); 300 else 301 realpath = fusepod->get_real_path (tn->value).c_str (); 302 303 304 (void) fi; 305 fd = open(realpath.c_str(), O_RDONLY); 306 if (fd == -1) 307 return -errno; 308 309 res = pread(fd, buf, size, offset); 310 if (res == -1) 311 res = -errno; 312 313 close(fd); 314 return res; 315 } 316 317 static int fusepod_mknod (const char * path, mode_t mode, dev_t dev) { 318 if (filename_sync_do == &(path [1]) && !syncing) { 319 /* Synchronize iPod */ 320 syncing = true; 321 ifstream in (add_songs); 322 string path; 323 324 /* Read songs from add_songs */ 325 while (!in.eof ()) { 326 getline (in, path); 327 fusepod_strip_string (path); 328 329 struct stat st; 330 stat (path.c_str (), &st); 331 332 if (S_ISREG(st.st_mode)) { 333 cout << "Adding track " << path << "... "; 334 currently_syncing = path; 335 if (fusepod->upload_song (path)) 336 cout << "Successful" << endl; 337 else 338 cout << "Failed" << endl; 339 } 340 } 341 342 in.close (); 343 344 /* Empty add_songs file */ 345 truncate (add_songs, 0); 346 347 /* Recursively add songs in transfer */ 348 /*string transfer_path = "/" + dir_transfer; 349 Node * transfer_node = fusepod->get_node ( ("/" + dir_transfer).c_str() ); 350 351 transfer_add_songs (transfer_node, transfer_path); 352 transfer_remove_empty_dirs (transfer_node, transfer_path, false);*/ 353 354 currently_syncing = "iTunesDB"; 355 cout << "Syncing database... "; 356 fusepod->flush (); 357 cout << "Successful" << endl; 358 359 syncing = false; 360 currently_syncing = ""; 361 return 0; 362 } 363 364 if (!transfer_in_dir (path)) 365 return -EACCES; 366 367 /* Making a file in the transfer directory */ 368 369 if (!S_ISREG (mode)) 370 return -EPERM; 371 372 string ipod_path = fusepod->get_transfer_path (path); 373 374 if (mknod (ipod_path.c_str (), S_IFREG | 0666, 0)) 375 return -errno; 376 377 struct stat st; 378 stat (ipod_path.c_str (), &st); 379 380 Node * parent = fusepod->root; 381 382 char * split_path = strdup (path); 383 vector<char*> components = fusepod_split_path (split_path, '/'); 384 385 for (size_t i = 0; i < components.size ()-1; i++) 386 parent = parent->find (components [i]); 387 388 //NOTE: filename string will have to be freed when syncing 389 NodeValue nv (strdup (components[components.size () -1]), st.st_mode, 0, st.st_size); 390 391 free (split_path); 392 393 parent->addChild (nv); 394 395 return 0; 396 } 397 398 static int fusepod_mkdir (const char * path, mode_t mode) { 399 //TODO: Add support for adding playlists 400 Node * node = fusepod->get_node (path); 401 402 if (node != 0) 403 return -EEXIST; 404 405 if (!transfer_in_dir (path)) 406 return -EACCES; 407 408 string realpath = fusepod->get_transfer_path (path); 409 410 if (mkdir (realpath.c_str(), S_IFDIR | 0777)) 411 return -errno; 412 413 string parent (path, 0, string(path).rfind ('/')); 414 string filename (path, string(path).rfind('/')+1, strlen(path)-1); 415 416 node = fusepod->get_node (parent.c_str()); 417 node->addChild (NodeValue (strdup(filename.c_str()), S_IFDIR | 0777)); 418 419 return 0; 420 } 421 422 static int fusepod_rmdir (const char * path) { 423 Node * node = fusepod->get_node (path); 424 425 if (node == 0) 426 return -ENOENT; 427 428 //User can only remove directories in playlist or transfer directories 429 if (node->parent && dir_playlists == node->parent->value.text) { 430 431 fusepod->remove_playlist (string (node->value.text)); 432 433 } 434 else if (transfer_in_dir (path)) { 435 436 if (!S_ISDIR(node->value.mode)) 437 return -ENOTDIR; 438 439 if (node->begin() != node->end()) 440 return -ENOTEMPTY; 441 442 if (rmdir (fusepod->get_transfer_path (path).c_str())) 443 return -errno; 444 445 node->remove_from_parent (); 446 447 free ((void*)node->value.text); 448 delete node; 449 450 } 451 else 452 return -EACCES; 453 454 return 0; 455 } 456 457 #define XATTRS gint32 _playcount = (gint32) track->playcount; \ 458 gint32 _rating = track->rating / 20; \ 459 const void * xattrs [14][2] = { \ 460 {"tag.title", track->title}, \ 461 {"tag.artist", track->artist}, \ 462 {"tag.album", track->album}, \ 463 {"tag.genre", track->genre}, \ 464 {"tag.comment", track->comment}, \ 465 {"tag.composer", track->composer}, \ 466 {"tag.description", track->description}, \ 467 {"tag.podcasturl", track->podcasturl}, \ 468 {"tag.podcastrss", track->podcastrss}, \ 469 \ 470 {"tag.track", &track->track_nr}, \ 471 {"tag.length", &track->tracklen}, \ 472 {"tag.year", &track->year}, \ 473 {"tag.playcount", &_playcount}, \ 474 {"tag.rating", &_rating} \ 475 }; 476 477 #define XATTRS_LEN 14 478 #define XATTRS_NUM 9 479 480 481 static int fusepod_listxattr (const char * path, char * attrs, size_t size) { 482 Node * node = fusepod->get_node (path); 483 if (!node) 484 return -ENOENT; 485 486 if (!node->value.track) 487 return 0; 488 489 size_t count = 0, pos = 0; 490 Track * track = node->value.track; 491 492 XATTRS 493 494 for (int i = 0; i < XATTRS_LEN; i++) { 495 if (i < XATTRS_NUM && !xattrs [i][1]) 496 continue; 497 498 size_t len = strlen ((char*)xattrs [i][0]) + 1; 499 500 if (size == 0) { 501 count += len; 502 continue; 503 } 504 505 if (len > size + pos) 506 return -ERANGE; 507 508 memcpy (attrs + pos, xattrs [i][0], len); 509 pos += len; 510 } 511 512 return size == 0 ? count : pos; 513 } 514 515 static int fusepod_getxattr (const char * path, const char * attr, char * buf, size_t size) { 516 Node * node = fusepod->get_node (path); 517 if (!node) 518 return -ENOENT; 519 520 if (!node->value.track) 521 return 0; 522 523 Track * track = node->value.track; 524 525 XATTRS 526 527 for (int i = 0; i < XATTRS_LEN; i++) { 528 if (strcmp ((char*)xattrs [i][0], attr) != 0) 529 continue; 530 531 if (!xattrs [i][1]) 532 return -EACCES; //-ENOATTR; 533 534 char * val; 535 536 if (i < XATTRS_NUM) { 537 val = (char*)xattrs[i][1]; 538 } 539 else { 540 val = new char [11]; //Max length of gint32 string is 10 541 snprintf (val, 11, "%d", *(gint32*)(xattrs[i][1])); 542 val [10] = 0; 543 } 544 545 size_t len = strlen (val) + 1; 546 547 if (size == 0) { 548 if (i >= XATTRS_NUM) delete val; 549 return len; 550 } 551 552 if (len > size) { 553 if (i >= XATTRS_NUM) delete val; 554 return -ERANGE; 555 } 556 557 strcpy (buf, val); 558 559 if (i >= XATTRS_NUM) 560 delete val; 561 562 return len; 563 } 564 565 return -EACCES; //-ENOATTR; 566 } 567 568 static int fusepod_statfs (const char * path, struct statvfs * vfs) { 569 int ret = statvfs (fusepod->mount_point.c_str (), vfs); 570 571 if (ret != 0) 572 return -ret; 573 574 return ret; 575 } 576 577 static int fusepod_unlink (const char * path) { 578 Node * node = fusepod->get_node (path); 579 if (node == 0) 580 return -ENOENT; 581 582 if (S_ISDIR (node->value.mode)) 583 return -EISDIR; 584 585 if (transfer_in_dir (path)) { 586 transfer_remove (path); 587 return 0; 588 } 589 590 if (!node->value.track) 591 return -EACCES; 592 593 if (!fusepod->remove_song (path)) 594 return -EACCES; 595 596 return 0; 597 } 598 599 static int fusepod_release (const char * path, struct fuse_file_info * info) { 600 if (!transfer_in_dir (path)) 601 return 0; 602 603 /* Makes files in the Transfer directory read-only */ 604 Node * node = fusepod->get_node (path); 605 606 if (node) 607 node->value.mode = S_IFREG | 0444; 608 609 //TODO: Add the songs here 610 currently_syncing = fusepod->get_transfer_path (path); 611 cout << "Adding track " << currently_syncing << "... "; 612 Track * track = fusepod->upload_song (currently_syncing, false); 613 if (track) 614 cout << "Successful" << endl; 615 else 616 cout << "Failed" << endl; 617 618 transfer_remove (path); 619 620 if (track) 621 fusepod->add_track (track); 622 623 return 0; 624 } 625 626 static void write_default_config () { 627 cout << "FUSEPod configuration file not found. Writing it now" << endl; 628 629 if (!getenv ("HOME")) 630 return; 631 632 string home = getenv ("HOME"); 633 home += "/.fusepod"; 634 635 ofstream config (home.c_str ()); 636 637 if (!config) 638 return; 639 640 config << default_config_file; 641 642 config.close (); 643 } 644 645 static vector<string> get_string_desc () { 646 istream * config = 0; 647 648 if (getenv ("HOME")) { 649 string home = getenv ("HOME"); 650 home += "/.fusepod"; 651 652 struct stat st; 653 stat (home.c_str (), &st); 654 655 if (!S_ISREG(st.st_mode)) 656 write_default_config (); 657 658 config = new ifstream (home.c_str ()); 659 } 660 661 if (config) 662 cout << "Reading configuration file " << endl; 663 else 664 config = new istringstream (default_config_file); 665 666 vector<string> paths; 667 string line; 668 669 while (!config->eof ()) { 670 getline (*config, line); 671 line = fusepod_strip_string (line); 672 if (line.length () > 0 && line [0] == '/') 673 paths.push_back (line); 674 } 675 676 delete config; 677 678 return paths; 679 } 680 681 void * fusepod_init () { 682 syncing = false; 683 syncing_file = ""; 684 685 srand (time (0)); //Random is used in FUSEPod::generate_filename() 686 687 vector<string> pd = get_string_desc (); 688 689 cout << "Reading iPod at " << ipod_mount_point << endl; 690 fusepod = new FUSEPod (ipod_mount_point, pd); 691 cout << "Finished reading iPod" << endl; 692 693 /* These are special extensions to the filesystem for adding songs to the iPod*/ 694 add_songs = strdup ("/tmp/fusepodXXXXXX"); 695 mkstemp (add_songs); 696 697 /* Create an empty temp file for add_songs */ 698 mknod (add_songs, S_IFREG | 0666, 0); 699 700 NodeValue add_song (fusepod_get_string (filename_add.c_str ()), S_IFREG | 0666); 701 fusepod->root->addChild (add_song); 702 703 NodeValue sync_ipod (fusepod_get_string (filename_sync.c_str ()), S_IFREG | 0555); 704 705 /* Make sync script */ 706 sync_script = "#!/bin/sh\n"; 707 sync_script += "#FUSEPod sync script\n"; 708 sync_script += "echo Syncing iPod...\n"; 709 sync_script += "if [ \"$1\" = '-watch' ]; then\n"; 710 sync_script += " stats='" + fuse_mount_point + "/" + filename_stats + "'\n"; 711 sync_script += " initial=$(grep 'Track Count' \"$stats\" | cut -b 14-)\n"; 712 sync_script += " count=$(grep -c '^.*$' '" + fuse_mount_point + "/" + filename_add + "')\n"; 713 sync_script += " touch " + fuse_mount_point + "/" + filename_sync_do + " >& /dev/null &\n"; 714 sync_script += " sleep 0.2\n"; 715 sync_script += " while [ 1 ]; do\n"; 716 sync_script += " current=$(grep 'Track Count' \"$stats\" | cut -b 14-)\n"; 717 sync_script += " file=$(grep 'Currently Syncing' \"$stats\")\n"; 718 sync_script += " if [ $? != 0 ]; then break; fi\n"; 719 sync_script += " clear && echo $file && echo Track $[$current-$initial] of \"$count\" && sleep 0.2\n"; 720 sync_script += " done\n"; 721 sync_script += "elif [ $# = 0 ]; then touch " + fuse_mount_point + "/" + filename_sync_do + " >& /dev/null\n"; 722 sync_script += "else echo USAGE: $0 '[ -watch ]'\n"; 723 sync_script += "fi\n"; 724 sync_script += "echo Finished syncing iPod\n"; 725 726 sync_ipod.size = sync_script.length (); 727 fusepod->root->addChild (sync_ipod); 728 729 NodeValue add_files (fusepod_get_string (filename_add_files.c_str ()), S_IFREG | 0555); 730 731 /* Make Recursive Directory Add script */ 732 add_files_script = "#!/bin/sh\n"; 733 add_files_script += "#FUSEPod Recursive Directory Add\n"; 734 add_files_script += "if [ $# = 0 ]; then echo \"USAGE: $0 [ file or directory ] ...\"; exit 1; fi\n"; 735 add_files_script += "for file in \"$@\"; do\n"; 736 add_files_script += " echo $file | grep ^/ &> /dev/null\n"; 737 add_files_script += " if [ $? != 0 ]; then file=$PWD/$file; fi\n"; 738 add_files_script += " find \"$file\" | egrep -i '(wav|mp3|m4a)$' >> '" + fuse_mount_point + "/" + filename_add + "'\n"; 739 add_files_script += "done\n"; 740 741 add_files.size = add_files_script.length (); 742 fusepod->root->addChild (add_files); 743 744 /* Add statistics file */ 745 NodeValue stats (fusepod_get_string (filename_stats.c_str ()), S_IFREG | 0444); 746 fusepod->root->addChild (stats); 747 748 /* Remove existing transfer directory the lazy way :) */ 749 string transfer_dir = fusepod->mount_point + "/" + dir_transfer_ipod; 750 system (("rm -rf '" + transfer_dir + "'").c_str ()); 751 752 /* Add transfer directory */ 753 if (!mkdir (transfer_dir.c_str (), S_IFDIR | 0777)) { 754 NodeValue transfer (fusepod_get_string (dir_transfer.c_str ()), S_IFDIR | 0777); 755 fusepod->root->addChild (transfer); 756 } 757 758 cout << "Starting FUSE layer" << endl; 759 760 return 0; 761 } 762 763 static void fusepod_destroy (void * v) { 764 cout << "Cleaning up" << endl; 765 766 if (add_songs) 767 remove (add_songs); 768 769 string mount_point = fusepod->mount_point; 770 771 if (fusepod) 772 delete fusepod; 773 774 string transfer_dir = mount_point + "/" + dir_transfer_ipod; 775 system (("rm -rf '" + transfer_dir + "'").c_str ()); 776 } 777 778 static struct fuse_operations fusepod_oper; 779 780 int main (int argc, char **argv) { 781 //TODO Apparantly error messages aren't clear enough 782 if (argc > 1) { 783 /* This finds out were the mount point is so that any app can use it. 784 Note that it may contain ../ and ./ in it */ 785 fuse_mount_point = argv [argc-1]; 786 if (fuse_mount_point [0] != '/') { 787 if (!getenv ("PWD")) { 788 cerr << "ERROR: Please supply the PWD environment variable or an absolute mount point" << endl; 789 exit (1); 790 } 791 fuse_mount_point = string (getenv ("PWD")) + "/" + fuse_mount_point; 792 } 793 794 /* Fixes subtle bug of when FUSE freezes when it doesn't find the iPod directory. 795 Check for help option. If not found and ipod cannot be found, exit.*/ 796 bool found = false; 797 for (int i = 1; i < argc && !found; i++) 798 if (strcmp (argv [i], "--help") == 0 || strcmp (argv [i], "-h") == 0) 799 found = true; 800 801 ipod_mount_point = FUSEPod::discover_ipod (); 802 803 if (!found && ipod_mount_point == "") { 804 cerr << "ERROR: Cannot find the iPod mount point.\n"; 805 cerr << "This may happen because the iPod is not mounted or you have not created an itunes database yet\n"; 806 cerr << "Please specifiy the mount point through the enviroment variable IPOD_DIR\n"; 807 cerr << "Eg: IPOD_DIR=\"/media/ipod\" fusepod /home/keegan/myipod\n"; 808 cerr << flush; 809 exit (1); 810 } 811 812 char * ipod_dir = getenv ("IPOD_DIR"); 813 if (!ipod_dir) ipod_dir = getenv ("IPOD_MOUNTPOINT"); 814 815 if (!found && ipod_dir && ipod_mount_point == ipod_dir && 816 access ( (string (ipod_dir) + ITUNESDB_PATH).c_str (), F_OK ) != 0) { // Checks for itunesdb 817 cerr << "ERROR: Cannot find the iTunesDB in the directory specified by the IPOD_DIR or IPOD_MOUNTPOINT environment variable.\n"; 818 cerr << "Do you want to create the iTunesDB in the specified directory? (y/n): "; 819 820 char ans; 821 cin >> ans; 822 if (ans != 'y') { 823 cerr << "\nCannot run FUSEPod without iTunesDB.\nExiting...\n"; 824 exit (1); 825 } 826 827 if (!FUSEPod::create_itunes_dirs (string (ipod_dir))) { 828 cerr << "\nERROR: Cannot create iTunesDB directory structure.\n"; 829 cerr << "Exiting...\n"; 830 exit (1); 831 } 832 } 833 } 834 835 fusepod_oper.getattr = fusepod_getattr; 836 fusepod_oper.listxattr = fusepod_listxattr; 837 fusepod_oper.getxattr = fusepod_getxattr; 838 fusepod_oper.access = fusepod_access; 839 fusepod_oper.readdir = fusepod_readdir; 840 fusepod_oper.open = fusepod_open; 841 fusepod_oper.truncate = fusepod_truncate; 842 fusepod_oper.write = fusepod_write; 843 fusepod_oper.read = fusepod_read; 844 fusepod_oper.unlink = fusepod_unlink; 845 fusepod_oper.statfs = fusepod_statfs; 846 fusepod_oper.mknod = fusepod_mknod; 847 fusepod_oper.mkdir = fusepod_mkdir; 848 fusepod_oper.rmdir = fusepod_rmdir; 849 fusepod_oper.release = fusepod_release; 850 fusepod_oper.init = fusepod_init; 851 fusepod_oper.destroy = fusepod_destroy; 852 853 return fuse_main(argc, argv, &fusepod_oper); 854 } 855