# bfuse.cc

Go to the documentation of this file.

    00001 ///
    00002 /// \file       bfuse.cc
    00003 ///             FUSE filesystem for Blackberry databases, using Barry.
    00004 ///
    00005
    00006 /*
    00007     Copyright (C) 2008-2011, Net Direct Inc. (http://www.netdirect.ca/)
    00008
    00009     This program is free software; you can redistribute it and/or modify
    00010     it under the terms of the GNU General Public License as published by
    00011     the Free Software Foundation; either version 2 of the License, or
    00012     (at your option) any later version.
    00013
    00014     This program is distributed in the hope that it will be useful,
    00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
    00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00017
    00018     See the GNU General Public License in the COPYING file at the
    00019     root directory of this project for more details.
    00020 */
    00021
    00022 #define FUSE_USE_VERSION 25
    00023 #include <fuse.h>
    00024 #include <fuse_opt.h>
    00025
    00026 #include <barry/barry.h>
    00027 #include <sstream>
    00028 #include <getopt.h>
    00029 #include <vector>
    00030 #include <list>
    00031 #include <string>
    00032 #include <stdexcept>
    00033 #include <memory>
    00034 #include <tr1/memory>
    00035 #include <errno.h>
    00036 #include <sys/types.h>
    00037 #include <fcntl.h>
    00038 #include <string.h>
    00039 #include "i18n.h"
    00040
    00041 using namespace std;
    00042 using namespace std::tr1;
    00043 using namespace Barry;
    00044
    00045 // Global filenames
    00046 const char *error_log_filename = "error.log";
    00047
    00048 // Global command line args
    00049 string cmdline_pin;
    00050 string cmdline_password;
    00051
    00052 //
    00053 // Data from the command line
    00054 //
    00055
    00056 /////////////////////////////////////////////////////////////////////////////
    00057 // Command line option handling, through fuse
    00058
    00059 //struct opt {
    00060 //      char
    00061 //};
    00062
    00063 void Blurb()
    00064 {
    00065    int major, minor;
    00066    const char *Version = Barry::Version(major, minor);
    00067
    00068    cerr
    00069    << "bfuse - FUSE filesystem for Blackberry databases\n"
    00070    << "        Copyright 2008-2011, Net Direct Inc. (http://www.netdirect.ca/)\n"
    00071    << "        Using: " << Version << "\n"
    00072    << endl;
    00073 }
    00074
    00075 void Usage()
    00076 {
    00077    cerr
    00078    << "\n"
    00079    << "Barry specific options:\n"
    00080    << "   -p pin    PIN of device to talk with\n"
    00081    << "             If only one device is plugged in, this flag is optional\n"
    00082    << "   -P pass   Simplistic method to specify device password\n"
    00083    << endl;
    00084 /*
    00085    << "   -d db     Specify which database to mount.  If no -d options exist\n"
    00086    << "             then all databases will be mounted.\n"
    00087    << "             Can be used multiple times to mount more than one DB\n"
    00088    << "   -h        This help\n"
    00089    << "   -n        Use null parser on all databases.\n"
    00090 */
    00091 }
    00092
    00093 /////////////////////////////////////////////////////////////////////////////
    00094 // FUSE specific exception
    00095
    00096 class fuse_error : public std::runtime_error
    00097 {
    00098         int m_errno;
    00099 public:
    00100         fuse_error(int errno_, const std::string &msg)
    00101                 : std::runtime_error(msg), m_errno(errno_)
    00102         {}
    00103
    00104         int get_errno() const { return m_errno; }
    00105 };
    00106
    00107
    00108 /////////////////////////////////////////////////////////////////////////////
    00109 // Barry record parsers
    00110
    00111 class DataDumpParser : public Barry::Parser
    00112 {
    00113         uint32_t m_id;
    00114         std::ostream &m_os;
    00115
    00116 public:
    00117         explicit DataDumpParser(std::ostream &os)
    00118                 : m_os(os)
    00119         {
    00120         }
    00121
    00122         virtual void ParseRecord(const Barry::DBData &data,
    00123                                  const Barry::IConverter *ic)
    00124         {
    00125                 m_id = data.GetUniqueId();
    00126                 m_os << "Raw record dump for record: "
    00127                         << std::hex << m_id << std::endl;
    00128                 m_os << data.GetData() << std::endl;
    00129         }
    00130 };
    00131
    00132 template <class Record>
    00133 struct Store
    00134 {
    00135         std::ostream &m_os;
    00136
    00137         explicit Store(std::ostream &os)
    00138                 : m_os(os)
    00139         {
    00140         }
    00141
    00142         // storage operator
    00143         void operator()(const Record &rec)
    00144         {
    00145                 m_os << rec;
    00146         }
    00147 };
    00148
    00149 typedef std::auto_ptr<Barry::Parser>            ParserPtr;
    00150
    00151 ParserPtr GetParser(const string &name, std::ostream &os, bool null_parser)
    00152 {
    00153         if( null_parser ) {
    00154                 // use null parser
    00155                 return ParserPtr( new DataDumpParser(os) );
    00156         }
    00157         // check for recognized database names
    00158         else if( name == Contact::GetDBName() ) {
    00159                 return ParserPtr(
    00160                         new RecordParser<Contact, Store<Contact> > (
    00161                                 new Store<Contact>(os)));
    00162         }
    00163         else if( name == Message::GetDBName() ) {
    00164                 return ParserPtr(
    00165                         new RecordParser<Message, Store<Message> > (
    00166                                 new Store<Message>(os)));
    00167         }
    00168         else if( name == Calendar::GetDBName() ) {
    00169                 return ParserPtr(
    00170                         new RecordParser<Calendar, Store<Calendar> > (
    00171                                 new Store<Calendar>(os)));
    00172         }
    00173         else if( name == CalendarAll::GetDBName() ) {
    00174                 return ParserPtr(
    00175                         new RecordParser<CalendarAll, Store<CalendarAll> > (
    00176                                 new Store<CalendarAll>(os)));
    00177         }
    00178         else if( name == ServiceBook::GetDBName() ) {
    00179                 return ParserPtr(
    00180                         new RecordParser<ServiceBook, Store<ServiceBook> > (
    00181                                 new Store<ServiceBook>(os)));
    00182         }
    00183
    00184         else if( name == Memo::GetDBName() ) {
    00185                 return ParserPtr(
    00186                         new RecordParser<Memo, Store<Memo> > (
    00187                                 new Store<Memo>(os)));
    00188         }
    00189         else if( name == Task::GetDBName() ) {
    00190                 return ParserPtr(
    00191                         new RecordParser<Task, Store<Task> > (
    00192                                 new Store<Task>(os)));
    00193         }
    00194         else if( name == PINMessage::GetDBName() ) {
    00195                 return ParserPtr(
    00196                         new RecordParser<PINMessage, Store<PINMessage> > (
    00197                                 new Store<PINMessage>(os)));
    00198         }
    00199         else if( name == SavedMessage::GetDBName() ) {
    00200                 return ParserPtr(
    00201                         new RecordParser<SavedMessage, Store<SavedMessage> > (
    00202                                 new Store<SavedMessage>(os)));
    00203         }
    00204         else if( name == Folder::GetDBName() ) {
    00205                 return ParserPtr(
    00206                         new RecordParser<Folder, Store<Folder> > (
    00207                                 new Store<Folder>(os)));
    00208         }
    00209         else if( name == Timezone::GetDBName() ) {
    00210                 return ParserPtr(
    00211                         new RecordParser<Timezone, Store<Timezone> > (
    00212                                 new Store<Timezone>(os)));
    00213         }
    00214         else {
    00215                 // unknown database, use null parser
    00216                 return ParserPtr( new DataDumpParser(os) );
    00217         }
    00218 }
    00219
    00220 /////////////////////////////////////////////////////////////////////////////
    00221 // PathSplit class
    00222
    00223 class PathSplit
    00224 {
    00225         std::string m_pin, m_db, m_record, m_field, m_remainder;
    00226
    00227         int m_level;            // the number of slashes, minus the first
    00228                                 // i.e. first level is 0
    00229         bool m_is_root;
    00230
    00231 public:
    00232         explicit PathSplit(const char *path)
    00233                 : m_level(-1)
    00234                 , m_is_root(false)
    00235         {
    00236                 if( *path != '/' )
    00237                         return;         // return in a failed state
    00238
    00239                 if( *(path+1) == 0 ) {
    00240                         m_is_root = true;
    00241                         return;
    00242                 }
    00243
    00244                 const char *s = path, *e = path;
    00245                 while( *e ) {
    00246                         while( *e && *e != '/' )
    00247                                 e++;
    00248
    00249                         m_level++;
    00250
    00251                         if( s != e && (s+1) != e ) {
    00252                                 string token(s+1, e);
    00253
    00254                                 switch( m_level )
    00255                                 {
    00256                                 case 0: // root level, should not have token here
    00257                                         m_level = -1;
    00258                                         return; // failed state
    00259
    00260                                 case 1: // have pin
    00261                                         m_pin = token;
    00262                                         break;
    00263
    00264                                 case 2: // have db
    00265                                         m_db = token;
    00266                                         break;
    00267
    00268                                 case 3: // have record
    00269                                         m_record = token;
    00270                                         break;
    00271
    00272                                 case 4: // have field
    00273                                         m_field = token;
    00274                                         break;
    00275
    00276                                 default:        // too many, store remainder and done
    00277                                         m_remainder = s;        // keeps slash
    00278                                         return;
    00279                                 }
    00280
    00281                                 // next
    00282                                 s = e;
    00283                                 if( *e )
    00284                                         e++;
    00285                         }
    00286                         else if( *e ) {
    00287                                 // next
    00288                                 e++;
    00289                         }
    00290                 }
    00291         }
    00292
    00293         bool IsRoot() const { return m_is_root; }
    00294         const std::string& Pin() const { return m_pin; }
    00295         const std::string& DB() const { return m_db; }
    00296         const std::string& Record() const { return m_record; }
    00297         const std::string& Field() const { return m_field; }
    00298         const std::string& Remainder() const { return m_remainder; }
    00299         int Level() const { return m_level; }
    00300 };
    00301
    00302
    00303 /////////////////////////////////////////////////////////////////////////////
    00304 // API classes
    00305
    00306 class Entry
    00307 {
    00308 public:
    00309         virtual ~Entry() {}
    00310 };
    00311
    00312 class Directory : public Entry
    00313 {
    00314 public:
    00315         virtual int ReadDir(void *buf, fuse_fill_dir_t filler) = 0;
    00316         virtual void FillDirStat(struct stat *st)
    00317         {
    00318                 st->st_mode = S_IFDIR | 0555;
    00319                 st->st_nlink = 2;
    00320         }
    00321 };
    00322
    00323 class File : public Entry
    00324 {
    00325 public:
    00326         virtual void FillFileStat(const char *path, struct stat *st) = 0;
    00327         virtual bool AccessOk(int flags)
    00328         {
    00329                 // default to readonly files
    00330                 return (flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDONLY;
    00331         }
    00332         virtual int ReadFile(const char *path, char *buf, size_t size, off_t offset) = 0;
    00333 };
    00334
    00335 typedef Directory*                              DirectoryPtr;
    00336 typedef File*                                   FilePtr;
    00337 typedef std::string                             NameT;
    00338 typedef std::map<NameT, DirectoryPtr>           DirMap;
    00339 typedef std::map<NameT, FilePtr>                FileMap;
    00340
    00341 static DirMap g_dirmap;
    00342 static FileMap g_filemap;
    00343
    00344 static Directory* FindDir(const NameT &name)
    00345 {
    00346         DirMap::iterator di = g_dirmap.find(name);
    00347         return di == g_dirmap.end() ? 0 : di->second;
    00348 }
    00349
    00350 static File* FindFile(const NameT &name)
    00351 {
    00352         FileMap::iterator fi = g_filemap.find(name);
    00353         return fi == g_filemap.end() ? 0 : fi->second;
    00354 }
    00355
    00356 /////////////////////////////////////////////////////////////////////////////
    00357 // Context classes
    00358
    00359 class Database : public Directory, public File
    00360 {
    00361 public:
    00362         Barry::Mode::Desktop &m_desk;
    00363         std::string m_name;
    00364         const Barry::DatabaseItem *m_pdb;
    00365
    00366 public:
    00367         Database(Barry::Mode::Desktop &desktop,
    00368                 const std::string &pin, const Barry::DatabaseItem *pdb)
    00369                 : m_desk(desktop)
    00370                 , m_pdb(pdb)
    00371         {
    00372                 m_name = string("/") + pin + "/" + m_pdb->Name;
    00373
    00374                 // add to directory list
    00375                 g_dirmap[ m_name ] = this;
    00376         }
    00377
    00378         ~Database()
    00379         {
    00380                 // remove any entries that point to us
    00381                 FileMap::iterator b = g_filemap.begin(), e = g_filemap.end();
    00382                 for( ; b != e; ++b ) {
    00383                         if( b->second == this ) {
    00384                                 g_filemap.erase(b);
    00385                         }
    00386                 }
    00387
    00388                 // erase ourselves from the directory list
    00389                 g_dirmap.erase( m_name );
    00390         }
    00391
    00392         void AddFile(const std::string &recordId)
    00393         {
    00394                 // FIXME - this is a hack to redirect all record files
    00395                 // to this database class... next step is to possibly
    00396                 // split out records into field files if we have a
    00397                 // parser, or just dump the hex if we don't
    00398                 string name = m_name + "/" + recordId;
    00399                 g_filemap[ name ] = this;
    00400         }
    00401
    00402         virtual int ReadDir(void *buf, fuse_fill_dir_t filler)
    00403         {
    00404                 filler(buf, ".", NULL, 0);
    00405                 filler(buf, "..", NULL, 0);
    00406
    00407                 // list all records in database, by recordId
    00408                 Barry::RecordStateTable rst;
    00409                 m_desk.GetRecordStateTable(m_pdb->Number, rst);
    00410
    00411                 Barry::RecordStateTable::StateMapType::iterator
    00412                         b = rst.StateMap.begin(),
    00413                         e = rst.StateMap.end();
    00414                 for( ; b != e; ++ b ) {
    00415                         ostringstream oss;
    00416                         oss << hex << b->second.RecordId;
    00417                         filler(buf, oss.str().c_str(), NULL, 0);
    00418
    00419                         AddFile(oss.str());
    00420                 }
    00421                 return 0;
    00422         }
    00423
    00424         virtual void FillFileStat(const char *path, struct stat *st)
    00425         {
    00426                 // use the path to find the proper record
    00427                 PathSplit ps(path);
    00428
    00429                 string constructed = string("/") + ps.Pin() + "/" + ps.DB();
    00430                 if( constructed != m_name ) {
    00431                         // FIXME - this is shoddy error handling
    00432                         throw std::logic_error("Constructed != name");
    00433                 }
    00434
    00435                 string data = GetRecordData(ps.Record());
    00436
    00437                 st->st_mode = S_IFREG | 0444;
    00438                 st->st_nlink = 1;
    00439                 st->st_size = data.size();
    00440         }
    00441
    00442         virtual int ReadFile(const char *path, char *buf, size_t size, off_t offset)
    00443         {
    00444                 // use the path to find the proper record
    00445                 PathSplit ps(path);
    00446
    00447                 string constructed = string("/") + ps.Pin() + "/" + ps.DB();
    00448                 if( constructed != m_name ) {
    00449                         // FIXME - this is shoddy error handling
    00450                         throw std::logic_error("Constructed != name");
    00451                 }
    00452
    00453                 string data = GetRecordData(ps.Record());
    00454
    00455                 size_t len = data.size();
    00456                 if( offset >= 0 && offset < (off_t)len ) {
    00457                         if( (offset + size) > len )
    00458                                 size = len - offset;
    00459                         memcpy(buf, data.data() + offset, size);
    00460                 }
    00461                 else {
    00462                         size = 0;
    00463                 }
    00464                 return size;
    00465         }
    00466
    00467         const std::string& GetDBName() const { return m_pdb->Name; }
    00468
    00469         std::string GetRecordData(const std::string &recordId)
    00470         {
    00471                 string data;
    00472
    00473                 Barry::RecordStateTable rst;
    00474                 m_desk.GetRecordStateTable(m_pdb->Number, rst);
    00475
    00476                 uint32_t recid = strtoul(recordId.c_str(), NULL, 16);
    00477                 RecordStateTable::IndexType index;
    00478                 if( rst.GetIndex(recid, &index) ) {
    00479                         ostringstream oss;
    00480                         ParserPtr parser = GetParser(m_pdb->Name, oss, false);
    00481                         m_desk.GetRecord(m_pdb->Number, index, *parser);
    00482                         data = oss.str();
    00483                 }
    00484
    00485                 return data;
    00486         }
    00487 };
    00488
    00489 class DesktopCon : public Directory
    00490 {
    00491 public:
    00492         typedef std::tr1::shared_ptr<Database>                  DatabasePtr;
    00493         typedef std::list<DatabasePtr>                          DBList;
    00494 public:
    00495         Barry::Controller m_con;
    00496         Barry::Mode::Desktop m_desk;
    00497         std::string m_pin;
    00498         DBList m_dblist;
    00499
    00500         DesktopCon(const Barry::ProbeResult &result, const std::string &pin)
    00501                 : m_con(result)
    00502                 , m_desk(m_con)
    00503                 , m_pin(pin)
    00504         {
    00505                 // add to directory list
    00506                 g_dirmap[ string("/") + pin ] = this;
    00507         }
    00508
    00509         ~DesktopCon()
    00510         {
    00511                 // remove from directory list
    00512                 g_dirmap.erase( string("/") + m_pin );
    00513         }
    00514
    00515         virtual int ReadDir(void *buf, fuse_fill_dir_t filler)
    00516         {
    00517                 filler(buf, ".", NULL, 0);
    00518                 filler(buf, "..", NULL, 0);
    00519
    00520                 // list all databases in list
    00521                 DBList::const_iterator b = m_dblist.begin(), e = m_dblist.end();
    00522                 for( ; b != e; ++ b ) {
    00523                         filler(buf, (*b)->GetDBName().c_str(), NULL, 0);
    00524                 }
    00525                 return 0;
    00526         }
    00527
    00528         void Open(const char *password = 0)
    00529         {
    00530                 // open our device
    00531                 m_desk.Open(password);
    00532
    00533                 // add all databases as directories
    00534                 DatabaseDatabase::DatabaseArrayType::const_iterator
    00535                         dbi = m_desk.GetDBDB().Databases.begin(),
    00536                         dbe = m_desk.GetDBDB().Databases.end();
    00537                 for( ; dbi != dbe; ++dbi ) {
    00538                         DatabasePtr db = DatabasePtr(
    00539                                 new Database(m_desk, m_pin, &(*dbi)) );
    00540                         m_dblist.push_back(db);
    00541                 }
    00542         }
    00543 };
    00544
    00545 class Context : public Directory, public File
    00546 {
    00547 public:
    00548         typedef std::auto_ptr<Barry::Probe>                     ProbePtr;
    00549         typedef std::tr1::shared_ptr<DesktopCon>                DesktopConPtr;
    00550         typedef std::string                                     PinT;
    00551         typedef std::map<PinT, DesktopConPtr>                   PinMap;
    00552
    00553         ProbePtr m_probe;
    00554         PinMap m_pinmap;
    00555
    00556         string m_error_log;
    00557
    00558         string m_limit_pin;             // only mount device with this pin
    00559         string m_password;              // use this password when connecting
    00560
    00561 public:
    00562         Context(const string &limit_pin = "", const string &password = "")
    00563                 : m_limit_pin(limit_pin)
    00564                 , m_password(password)
    00565         {
    00566                 g_dirmap["/"] = this;
    00567                 g_filemap[string("/") + error_log_filename] = this;
    00568
    00569                 m_error_log = "Hello FUSE world.  This is Barry.  Pleased to meet you.\n";
    00570         }
    00571
    00572         ~Context()
    00573         {
    00574                 g_dirmap.erase("/");
    00575                 g_filemap.erase(string("/") + error_log_filename);
    00576         }
    00577
    00578         virtual int ReadDir(void *buf, fuse_fill_dir_t filler)
    00579         {
    00580                 filler(buf, ".", NULL, 0);
    00581                 filler(buf, "..", NULL, 0);
    00582                 filler(buf, error_log_filename, NULL, 0);
    00583
    00584                 // list all pins in map
    00585                 PinMap::const_iterator b = m_pinmap.begin(), e = m_pinmap.end();
    00586                 for( ; b != e; ++ b ) {
    00587                         filler(buf, b->first.c_str(), NULL, 0);
    00588                 }
    00589                 return 0;
    00590         }
    00591
    00592         virtual void FillFileStat(const char *path, struct stat *st)
    00593         {
    00594                 st->st_mode = S_IFREG | 0444;
    00595                 st->st_nlink = 1;
    00596                 st->st_size = m_error_log.size();
    00597         }
    00598
    00599         virtual int ReadFile(const char *path, char *buf, size_t size, off_t offset)
    00600         {
    00601                 size_t len = m_error_log.size();
    00602                 if( offset >= 0 && offset < (off_t)len ) {
    00603                         if( (offset + size) > len )
    00604                                 size = len - offset;
    00605                         memcpy(buf, m_error_log.data() + offset, size);
    00606                 }
    00607                 else {
    00608                         size = 0;
    00609                 }
    00610                 return size;
    00611         }
    00612
    00613         void Log(const std::string &msg)
    00614         {
    00615                 m_error_log += msg;
    00616                 m_error_log += "\n";
    00617         }
    00618
    00619         const std::string& GetLog() const { return m_error_log; }
    00620
    00621         void ProbeAll()
    00622         {
    00623                 // probe the USB bus for Blackberry devices
    00624                 m_probe.reset( new Probe );
    00625
    00626                 // connect to all PINs found, and add them to our map
    00627                 for( int i = 0; i < m_probe->GetCount(); i++ ) {
    00628                         string curpin = m_probe->Get(i).m_pin.Str();
    00629
    00630                         // don't add a blank or pre-existing pin
    00631                         if( !curpin.size() || m_pinmap.find(curpin) != m_pinmap.end() ) {
    00632                                 continue;
    00633                         }
    00634
    00635                         // don't add non-PIN device if pin specified
    00636                         if( m_limit_pin.size() && curpin != m_limit_pin ) {
    00637                                 continue;
    00638                         }
    00639
    00640                         DesktopConPtr dev = DesktopConPtr (
    00641                                 new DesktopCon(m_probe->Get(i), curpin) );
    00642                         dev->Open(m_password.c_str());
    00643                         m_pinmap[ curpin ] = dev;
    00644                 }
    00645         }
    00646
    00647         DesktopCon* FindPin(PinT pin)
    00648         {
    00649                 PinMap::iterator pi = m_pinmap.find(pin);
    00650                 return pi == m_pinmap.end() ? 0 : pi->second.get();
    00651         }
    00652 };
    00653
    00654
    00655 /////////////////////////////////////////////////////////////////////////////
    00656 // FUSE API hooks
    00657
    00658 static void* bfuse_init()
    00659 {
    00660         // Initialize the barry library.  Must be called before
    00661         // anything else.
    00662         Barry::Init(false);
    00663
    00664         Context *ctx = 0;
    00665
    00666         try {
    00667                 ctx = new Context(cmdline_pin, cmdline_password);
    00668                 ctx->ProbeAll();
    00669         }
    00670         catch( std::exception &e ) {
    00671                 if( ctx ) {
    00672                         ctx->Log(e.what());
    00673                 }
    00674         }
    00675
    00676         return ctx;
    00677 }
    00678
    00679 static void bfuse_destroy(void *data)
    00680 {
    00681         if( data ) {
    00682                 Context *ctx = (Context*) data;
    00683                 delete ctx;
    00684         }
    00685 }
    00686
    00687 static int bfuse_getattr(const char *path, struct stat *st)
    00688 {
    00689         memset(st, 0, sizeof(*st));
    00690
    00691         if( Directory *dir = FindDir(path) ) {
    00692                 dir->FillDirStat(st);
    00693                 return 0;
    00694         }
    00695         else if( File *file = FindFile(path) ) {
    00696                 file->FillFileStat(path, st);
    00697                 return 0;
    00698         }
    00699         else
    00700                 return -ENOENT;
    00701 }
    00702
    00703 static int bfuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
    00704                          off_t /*offset*/, struct fuse_file_info * /*fi*/)
    00705 {
    00706         Directory *dir = FindDir(path);
    00707         if( !dir )
    00708                 return -ENOENT;
    00709         return dir->ReadDir(buf, filler);
    00710 }
    00711
    00712 static int bfuse_open(const char *path, struct fuse_file_info *fi)
    00713 {
    00714         File *file = FindFile(path);
    00715         if( !file )
    00716                 return -ENOENT;
    00717
    00718         if( !file->AccessOk(fi->flags) )
    00719                 return -EACCES;
    00720
    00721         return 0;
    00722 }
    00723
    00724 static int bfuse_read(const char *path, char *buf, size_t size, off_t offset,
    00725                       struct fuse_file_info *fi)
    00726 {
    00727         File *file = FindFile(path);
    00728         if( !file )
    00729                 return -ENOENT;
    00730
    00731         return file->ReadFile(path, buf, size, offset);
    00732 }
    00733
    00734 // static struct here automatically zeros data
    00735 static struct fuse_operations bfuse_oper;
    00736
    00737
    00738 /////////////////////////////////////////////////////////////////////////////
    00739 // main
    00740
    00741 int main(int argc, char *argv[])
    00742 {
    00743         INIT_I18N(PACKAGE);
    00744
    00745         cout.sync_with_stdio(true);     // leave this on, since libusb uses
    00746                                         // stdio for debug messages
    00747
    00748         Blurb();
    00749
    00750         // initialize the operation hooks
    00751         bfuse_oper.init         = bfuse_init;
    00752         bfuse_oper.destroy      = bfuse_destroy;
    00753         bfuse_oper.getattr      = bfuse_getattr;
    00754         bfuse_oper.readdir      = bfuse_readdir;
    00755         bfuse_oper.open         = bfuse_open;
    00756         bfuse_oper.read         = bfuse_read;
    00757
    00758         // process command line options before FUSE does
    00759         // FUSE does its own command line processing, and
    00760         // doesn't seem to have a way to plug into it,
    00761         // so do our own first
    00762         int fuse_argc = 0;
    00763         char **fuse_argv = new char*[argc];
    00764
    00765         for( int i = 0; i < argc; i++ ) {
    00766                 if( argv[i][0] == '-' ) {
    00767
    00768                         switch( argv[i][1] )
    00769                         {
    00770 //                      case 'd':       // mount dbname
    00771 //                              dbNames.push_back(string(optarg));
    00772 //                              break;
    00773
    00774 //                      case 'n':       // use null parser
    00775 //                              null_parser = true;
    00776 //                              break;
    00777
    00778                         case 'p':       // Blackberry PIN
    00779                                 if( i+1 < argc ) {
    00780                                         cmdline_pin = argv[++i];
    00781                                 }
    00782                                 continue;
    00783
    00784                         case 'P':       // Device password
    00785                                 if( i+1 < argc ) {
    00786                                         cmdline_password = argv[++i];
    00787                                 }
    00788                                 continue;
    00789
    00790                         case 'h':       // help
    00791                                 Usage();
    00792                                 break;
    00793                         }
    00794                 }
    00795
    00796                 // if we get here, add this option to FUSE's
    00797                 fuse_argv[fuse_argc] = argv[i];
    00798                 fuse_argc++;
    00799         }
    00800
    00801         int ret = fuse_main(fuse_argc, fuse_argv, &bfuse_oper);
    00802         delete [] fuse_argv;
    00803         return ret;
    00804 }
    00805