"Fossies" - the Fresh Open Source Software Archive

Member "fusesmb-0.8.7/fusesmb.c" (7 Sep 2007, 32861 Bytes) of package /linux/privat/old/fusesmb-0.8.7.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "fusesmb.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * SMB for FUSE
    3  *
    4  * Mount complete "Network Neighbourhood"
    5  *
    6  * Copyright (C) 2006 Vincent Wagelaar
    7  *
    8  * This program is free software; you can redistribute it and/or
    9  * modify it under the terms of the GNU General Public License
   10  * as published by the Free Software Foundation; either version 2
   11  * of the License, or (at your option) any later version.
   12  *
   13  * This program is distributed in the hope that it will be useful,
   14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16  * GNU General Public License for more details.
   17  *
   18  * You should have received a copy of the GNU General Public License
   19  * along with this program; if not, write to the Free Software
   20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
   21  */
   22 
   23 #ifdef HAVE_CONFIG_H
   24 # include <config.h>
   25 #endif
   26 
   27 #include <fuse.h>
   28 #include <stdio.h>
   29 #include <string.h>
   30 #include <stdlib.h>
   31 #include <stddef.h>
   32 #include <unistd.h>
   33 #include <errno.h>
   34 #include <fcntl.h>
   35 #include <dirent.h>
   36 #include <sys/param.h>
   37 #include <sys/vfs.h>
   38 #include <pthread.h>
   39 #include <libsmbclient.h>
   40 #include <time.h>
   41 #include "debug.h"
   42 #include "hash.h"
   43 #include "smbctx.h"
   44 
   45 #define MY_MAXPATHLEN (MAXPATHLEN + 256)
   46 
   47 /* Mutex for locking the Samba context */
   48 
   49 /* To prevent deadlock, locking order should be:
   50 
   51 [rwd]ctx_mutex -> cfg_mutex -> opts_mutex
   52 [rwd]ctx_mutex -> opts_mutex
   53 */
   54 
   55 static pthread_mutex_t ctx_mutex = PTHREAD_MUTEX_INITIALIZER;
   56 static pthread_mutex_t rwd_ctx_mutex = PTHREAD_MUTEX_INITIALIZER;
   57 static SMBCCTX *ctx, *rwd_ctx;
   58 pthread_t cleanup_thread;
   59 
   60 /*
   61  * Hash for storing files/directories that were not found, an optimisation
   62  * for programs like konqueror and freevo that do a lot of lookups:
   63  * .directory, share.fxd etc..
   64  */
   65 static hash_t *notfound_cache;
   66 static pthread_mutex_t notfound_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
   67 
   68 
   69 typedef struct {
   70     time_t ctime;  /* Time of creation */
   71     int err;       /* errno variable */
   72 } notfound_node_t;
   73 
   74 struct fusesmb_opt {
   75     int global_showhiddenshares;
   76     int global_interval;
   77     int global_timeout;
   78     char *global_username;
   79     char *global_password;
   80 };
   81 /* Read settings from fusesmb.conf and or set default value */
   82 config_t cfg;
   83 pthread_mutex_t cfg_mutex = PTHREAD_MUTEX_INITIALIZER;
   84 struct fusesmb_opt opts;
   85 pthread_mutex_t opts_mutex = PTHREAD_MUTEX_INITIALIZER;
   86 char fusesmb_cache_bin[MAXPATHLEN];
   87 
   88 static void options_read(config_t *cfg, struct fusesmb_opt *opt)
   89 {
   90     if (-1 == config_read_bool(cfg, "global", "showhiddenshares", &(opt->global_showhiddenshares)))
   91         opt->global_showhiddenshares = 1;
   92     if (-1 == config_read_int(cfg, "global", "timeout", &(opt->global_timeout)))
   93         opt->global_timeout = 10;
   94 
   95     /* Timeout less then 2 seconds is not really useful */
   96     if(opt->global_timeout <= 2)
   97         opt->global_timeout = 2;
   98 
   99     if (-1 == config_read_int(cfg, "global", "interval", &(opt->global_interval)))
  100         opt->global_interval = 15;
  101     if (opt->global_interval <= 0)
  102         opt->global_interval = 0;
  103 
  104     if (-1 == config_read_string(cfg, "global", "username", &(opt->global_username)))
  105         opt->global_username = NULL;
  106     if (-1 == config_read_string(cfg, "global", "password", &(opt->global_password)))
  107         opt->global_password = NULL;
  108 }
  109 
  110 static void options_free(struct fusesmb_opt *opt)
  111 {
  112     if (NULL != opt->global_username)
  113         free(opt->global_password);
  114     if (NULL != opt->global_password)
  115         free(opt->global_username);
  116 }
  117 
  118 
  119 /*
  120  * Thread for cleaning up connections to hosts, current interval of
  121  * 15 seconds looks reasonable
  122  */
  123 static void *smb_purge_thread(void *data)
  124 {
  125     (void)data;
  126     int count = 0;
  127     while (1)
  128     {
  129 
  130         pthread_mutex_lock(&ctx_mutex);
  131         ctx->callbacks.purge_cached_fn(ctx);
  132         pthread_mutex_unlock(&ctx_mutex);
  133 
  134         pthread_mutex_lock(&rwd_ctx_mutex);
  135         rwd_ctx->callbacks.purge_cached_fn(rwd_ctx);
  136         pthread_mutex_unlock(&rwd_ctx_mutex);
  137         /*
  138          * Look every minute in the notfound cache for items that are
  139          * no longer used
  140          */
  141         if (count > (60 / 15)) /* 1 minute */
  142         {
  143             pthread_mutex_lock(&notfound_cache_mutex);
  144             hscan_t sc;
  145             hash_scan_begin(&sc, notfound_cache);
  146             hnode_t *n;
  147             while (NULL != (n = hash_scan_next(&sc)))
  148             {
  149                 notfound_node_t *data = hnode_get(n);
  150                 if (time(NULL) - data->ctime > 15 * 60) /* 15 minutes */
  151                 {
  152                     const void *key = hnode_getkey(n);
  153                     debug("Deleting notfound node: %s", (char *)key);
  154                     hash_scan_delfree(notfound_cache, n);
  155                     free((void *)key);
  156                     free(data);
  157                 }
  158 
  159             }
  160             pthread_mutex_unlock(&notfound_cache_mutex);
  161             count = 0;
  162         }
  163         else
  164         {
  165             count++;
  166         }
  167 
  168         char cachefile[1024];
  169         snprintf(cachefile, 1024, "%s/.smb/fusesmb.cache", getenv("HOME"));
  170         struct stat st;
  171         memset(&st, 0, sizeof(struct stat));
  172 
  173         if(opts.global_interval > 0)
  174         {
  175             if (-1 == stat(cachefile, &st))
  176             {
  177                 if (errno == ENOENT)
  178                 {
  179                     system(fusesmb_cache_bin);
  180                 }
  181             }
  182             else if (time(NULL) - st.st_mtime > opts.global_interval * 60)
  183             {
  184                 system("fusesmb.cache");
  185             }
  186         }
  187 
  188 
  189         /* Look if any changes have been made to the configfile */
  190         int changed;
  191         pthread_mutex_lock(&cfg_mutex);
  192         if (0 == (changed = config_reload_ifneeded(&cfg)))
  193         {
  194             /* Lookout for deadlocks !!!! (order of setting locks within locks) */
  195             pthread_mutex_lock(&opts_mutex);
  196             options_free(&opts);
  197             options_read(&cfg, &opts);
  198             pthread_mutex_unlock(&opts_mutex);
  199         }
  200         pthread_mutex_unlock(&cfg_mutex);
  201 
  202         /* Prevent unnecessary locks within locks */
  203         if (changed == 0)
  204         {
  205             pthread_mutex_lock(&ctx_mutex);
  206             ctx->timeout = opts.global_timeout * 1000;
  207             pthread_mutex_unlock(&ctx_mutex);
  208 
  209             pthread_mutex_lock(&rwd_ctx_mutex);
  210             rwd_ctx->timeout = opts.global_timeout * 1000;
  211             pthread_mutex_unlock(&rwd_ctx_mutex);
  212         }
  213 
  214 
  215         sleep(15);
  216     }
  217     return NULL;
  218 }
  219 
  220 static const char *stripworkgroup(const char *file)
  221 {
  222     unsigned int i = 0, ret = 0, goodpos = 0, file_len = strlen(file);
  223 
  224     for (i = 0; i < file_len; i++)
  225     {
  226         if (ret == 2)
  227         {
  228             goodpos--;
  229             break;
  230         }
  231         if (file[i] == '/')
  232             ret++;
  233         goodpos++;
  234     }
  235     if (ret == 1)
  236         return file;
  237     else
  238         return &file[goodpos];
  239 }
  240 
  241 static unsigned int slashcount(const char *file)
  242 {
  243     unsigned int i = 0, count = 0, file_len = strlen(file);
  244 
  245     for (i = 0; i < file_len; i++)
  246     {
  247         if (file[i] == '/')
  248             count++;
  249     }
  250     return count;
  251 }
  252 
  253 static int fusesmb_getattr(const char *path, struct stat *stbuf)
  254 {
  255     char smb_path[MY_MAXPATHLEN] = "smb:/", buf[MY_MAXPATHLEN], cache_file[1024];
  256     int path_exists = 0;
  257     FILE *fp;
  258     struct stat cache;
  259     memset(stbuf, 0, sizeof(struct stat));
  260 
  261     /* Check the cache for valid workgroup, hosts and shares */
  262     if (slashcount(path) <= 3)
  263     {
  264         snprintf(cache_file, 1024, "%s/.smb/fusesmb.cache", getenv("HOME"));
  265 
  266         if (strlen(path) == 1 && path[0] == '/')
  267             path_exists = 1;
  268         else
  269         {
  270             fp = fopen(cache_file, "r");
  271             if (!fp)
  272                 return -ENOENT;
  273 
  274             while (!feof(fp))
  275             {
  276                 fgets(buf, MY_MAXPATHLEN, fp);
  277                 if (strncmp(buf, path, strlen(path)) == 0 &&
  278                     (buf[strlen(path)] == '/' || buf[strlen(path)] == '\n'))
  279                 {
  280                     path_exists = 1;
  281                     break;
  282                 }
  283             }
  284             fclose(fp);
  285         }
  286         if (path_exists != 1)
  287             return -ENOENT;
  288 
  289         memset(&cache, 0, sizeof(cache));
  290         stat(cache_file, &cache);
  291         memset(stbuf, 0, sizeof(stbuf));
  292         stbuf->st_mode  = S_IFDIR | 0755;
  293         stbuf->st_nlink = 3;
  294         stbuf->st_size  = 4096;
  295         stbuf->st_uid   = cache.st_uid;
  296         stbuf->st_gid   = cache.st_gid;
  297         stbuf->st_ctime = cache.st_ctime;
  298         stbuf->st_mtime = cache.st_mtime;
  299         stbuf->st_atime = cache.st_atime;
  300         return 0;
  301 
  302     }
  303     /* We're within a share here  */
  304     else
  305     {
  306         /* Prevent connecting too often to a share because this is slow */
  307         if (slashcount(path) == 4)
  308         {
  309             pthread_mutex_lock(&notfound_cache_mutex);
  310             hnode_t *node = hash_lookup(notfound_cache, path);
  311             if (node)
  312             {
  313                 debug("NotFoundCache hit for: %s", path);
  314                 notfound_node_t *data = hnode_get(node);
  315                 int err = data->err;
  316                 data->ctime = time(NULL);
  317                 pthread_mutex_unlock(&notfound_cache_mutex);
  318                 return -err;
  319             }
  320             pthread_mutex_unlock(&notfound_cache_mutex);
  321         }
  322 
  323         strcat(smb_path, stripworkgroup(path));
  324         pthread_mutex_lock(&ctx_mutex);
  325         if (ctx->stat(ctx, smb_path, stbuf) < 0)
  326         {
  327             pthread_mutex_unlock(&ctx_mutex);
  328             if (slashcount(path) == 4)
  329             {
  330                 int err = errno;
  331                 pthread_mutex_lock(&notfound_cache_mutex);
  332                 char *key = strdup(path);
  333                 if (key == NULL)
  334                 {
  335                     pthread_mutex_unlock(&notfound_cache_mutex);
  336                     return -errno;
  337                 }
  338                 notfound_node_t *data = (notfound_node_t *)malloc(sizeof(notfound_node_t));
  339                 if (data == NULL)
  340                 {
  341                     pthread_mutex_unlock(&notfound_cache_mutex);
  342                     return -errno;
  343                 }
  344                 data->ctime = time(NULL);
  345                 data->err = err;
  346 
  347                 hash_alloc_insert(notfound_cache, key, data);
  348                 pthread_mutex_unlock(&notfound_cache_mutex);
  349             }
  350             return -errno;
  351 
  352         }
  353         pthread_mutex_unlock(&ctx_mutex);
  354         return 0;
  355 
  356     }
  357 }
  358 
  359 static int fusesmb_opendir(const char *path, struct fuse_file_info *fi)
  360 {
  361     if (slashcount(path) <= 2)
  362         return 0;
  363     SMBCFILE *dir;
  364     char smb_path[MY_MAXPATHLEN] = "smb:/";
  365     strcat(smb_path, stripworkgroup(path));
  366     pthread_mutex_lock(&ctx_mutex);
  367     dir = ctx->opendir(ctx, smb_path);
  368     if (dir == NULL)
  369     {
  370         pthread_mutex_unlock(&ctx_mutex);
  371         return -errno;
  372     }
  373     fi->fh = (unsigned long)dir;
  374     pthread_mutex_unlock(&ctx_mutex);
  375     return 0;
  376 }
  377 
  378 static int fusesmb_readdir(const char *path, void *h, fuse_fill_dir_t filler,
  379                        off_t offset, struct fuse_file_info *fi)
  380 {
  381     (void)offset;
  382     struct smbc_dirent *pdirent;
  383     char buf[MY_MAXPATHLEN],
  384          last_dir_entry[MY_MAXPATHLEN] = "",
  385          cache_file[1024];
  386     FILE *fp;
  387     char *dir_entry;
  388     struct stat st;
  389     memset(&st, 0, sizeof(st));
  390     int dircount = 0;
  391 
  392     /*
  393        Check the cache file for workgroups/hosts and shares that are currently online
  394        Cases handled here are:
  395        / ,
  396        /WORKGROUP and
  397        /WORKGROUP/COMPUTER
  398      */
  399     if (slashcount(path) <= 2)
  400     {
  401         /* Listing Workgroups */
  402         snprintf(cache_file, 1024, "%s/.smb/fusesmb.cache", getenv("HOME"));
  403         fp = fopen(cache_file, "r");
  404         if (!fp)
  405             return -ENOENT;
  406         while (!feof(fp))
  407         {
  408             if (NULL == fgets(buf, sizeof(buf), fp))
  409                 continue;
  410 
  411             if (strncmp(buf, path, strlen(path)) == 0 &&
  412                 (strlen(buf) > strlen(path)))
  413             {
  414                 /* Note: strtok is safe because the static buffer is is not reused */
  415                 if (buf[strlen(path)] == '/' || strlen(path) == 1)
  416                 {
  417                     /* Path is workgroup or server */
  418                     if (strlen(path) > 1)
  419                     {
  420                         dir_entry = strtok(&buf[strlen(path) + 1], "/");
  421                         /* Look if share is a hidden share, dir_entry still contains '\n' */
  422                         if (slashcount(path) == 2)
  423                         {
  424                             if (dir_entry[strlen(dir_entry)-2] == '$')
  425                             {
  426                                 int showhidden = 0;
  427                                 pthread_mutex_lock(&cfg_mutex);
  428                                 if (0 == config_read_bool(&cfg, stripworkgroup(path), "showhiddenshares", &showhidden))
  429                                 {
  430                                     pthread_mutex_unlock(&cfg_mutex);
  431                                     if (showhidden == 1)
  432                                         continue;
  433                                 }
  434                                 pthread_mutex_unlock(&cfg_mutex);
  435 
  436                                 pthread_mutex_lock(&opts_mutex);
  437                                 if (opts.global_showhiddenshares == 0)
  438                                 {
  439                                     pthread_mutex_unlock(&opts_mutex);
  440                                     continue;
  441                                 }
  442                                 pthread_mutex_unlock(&opts_mutex);
  443                             }
  444                         }
  445                     }
  446                     /* Path is root */
  447                     else
  448                     {
  449                         dir_entry = strtok(buf, "/");
  450                     }
  451                     /* Only unique workgroups or servers */
  452                     if (strcmp(last_dir_entry, dir_entry) == 0)
  453                         continue;
  454 
  455                     st.st_mode = DT_DIR << 12;
  456                     filler(h, strtok(dir_entry, "\n"), &st, 0);
  457                     dircount++;
  458                     strncpy(last_dir_entry, dir_entry, 4096);
  459                 }
  460             }
  461         }
  462         fclose(fp);
  463 
  464         if (dircount == 0)
  465             return -ENOENT;
  466 
  467         /* The workgroup / host and share lists don't have . and .. , so putting them in */
  468         st.st_mode = DT_DIR << 12;
  469         filler(h, ".", &st, 0);
  470         filler(h, "..", &st, 0);
  471         return 0;
  472     }
  473     /* Listing contents of a share */
  474     else
  475     {
  476         pthread_mutex_lock(&ctx_mutex);
  477         while (NULL != (pdirent = ctx->readdir(ctx, (SMBCFILE *)fi->fh)))
  478         {
  479             if (pdirent->smbc_type == SMBC_DIR)
  480             {
  481                 st.st_mode = DT_DIR << 12;
  482                 filler(h, pdirent->name, &st, 0);
  483             }
  484             if (pdirent->smbc_type == SMBC_FILE)
  485             {
  486                 st.st_mode = DT_REG << 12;
  487                 filler(h, pdirent->name, &st, 0);
  488             }
  489             if (slashcount(path) == 4 && 
  490                 (pdirent->smbc_type == SMBC_FILE || pdirent->smbc_type == SMBC_DIR))
  491             {
  492                 /* Clear item from notfound_cache */
  493         pthread_mutex_lock(&notfound_cache_mutex);
  494                 char full_entry_path[MY_MAXPATHLEN];
  495                 snprintf(full_entry_path, sizeof(full_entry_path)-1, "%s/%s", path, pdirent->name);
  496                 hnode_t *node = hash_lookup(notfound_cache, full_entry_path);
  497                 if (node != NULL)
  498                 {
  499                     void *data = hnode_get(node);
  500                     const void *key = hnode_getkey(node);
  501                     hash_delete_free(notfound_cache, node);
  502                     free((void *)key);
  503                     free(data);
  504                 }
  505                 pthread_mutex_unlock(&notfound_cache_mutex);
  506             }
  507         }
  508         pthread_mutex_unlock(&ctx_mutex);
  509     }
  510     return 0;
  511 }
  512 
  513 static int fusesmb_releasedir(const char *path, struct fuse_file_info *fi)
  514 {
  515     (void) path;
  516     if (slashcount(path) <= 2)
  517         return 0;
  518 
  519     pthread_mutex_lock(&ctx_mutex);
  520     ctx->closedir(ctx, (SMBCFILE *)fi->fh);
  521     pthread_mutex_unlock(&ctx_mutex);
  522     return 0;
  523 }
  524 
  525 static int fusesmb_open(const char *path, struct fuse_file_info *fi)
  526 {
  527     SMBCFILE *file;
  528     char smb_path[MY_MAXPATHLEN] = "smb:/";
  529 
  530     /* You cannot open directories */
  531     if (slashcount(path) <= 3)
  532         return -EACCES;
  533 
  534     /* Not sure what this code is doing */
  535     //if((flags & 3) != O_RDONLY)
  536     //    return -ENOENT;
  537     strcat(smb_path, stripworkgroup(path));
  538 
  539     pthread_mutex_lock(&rwd_ctx_mutex);
  540     file = rwd_ctx->open(rwd_ctx, smb_path, fi->flags, 0);
  541 
  542     if (file == NULL)
  543     {
  544         pthread_mutex_unlock(&rwd_ctx_mutex);
  545         return -errno;
  546     }
  547 
  548     fi->fh = (unsigned long)file;
  549     pthread_mutex_unlock(&rwd_ctx_mutex);
  550     return 0;
  551 }
  552 
  553 static int fusesmb_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
  554 {
  555     SMBCFILE *file;
  556     char smb_path[MY_MAXPATHLEN] = "smb:/";
  557 
  558     //printf("%i\n", offset);
  559     //fflush(stdout);
  560 
  561     strcat(smb_path, stripworkgroup(path));
  562 
  563     int tries = 0;              //For number of retries before failing
  564     ssize_t ssize;              //Returned by ctx->read
  565 
  566     pthread_mutex_lock(&rwd_ctx_mutex);
  567     /* Ugly goto but it works ;) But IMHO easiest solution for error handling here */
  568     goto seek;
  569   reopen:
  570     if ((file = rwd_ctx->open(rwd_ctx, smb_path, fi->flags, 0)) == NULL)
  571     {
  572         /* Trying to reopen when out of memory */
  573         if (errno == ENOMEM)
  574         {
  575             tries++;
  576             if (tries > 4)
  577             {
  578                 pthread_mutex_unlock(&rwd_ctx_mutex);
  579                 return -errno;
  580             }
  581             goto reopen;
  582         }
  583         /* Other errors from docs cannot be recovered from so returning the error */
  584         else
  585         {
  586             pthread_mutex_unlock(&rwd_ctx_mutex);
  587             return -errno;
  588         }
  589     }
  590     fi->fh = (unsigned long)file;
  591   seek:
  592 
  593     if (rwd_ctx->lseek(rwd_ctx, (SMBCFILE *)fi->fh, offset, SEEK_SET) == (off_t) - 1)
  594     {
  595         /* Bad file descriptor try to reopen */
  596         if (errno == EBADF)
  597         {
  598             goto reopen;
  599         }
  600         else
  601         {
  602             //SMB Init failed
  603             pthread_mutex_unlock(&rwd_ctx_mutex);
  604             return -errno;
  605         }
  606     }
  607     if ((ssize = rwd_ctx->read(rwd_ctx, (SMBCFILE *)fi->fh, buf, size)) < 0)
  608     {
  609         /* Bad file descriptor try to reopen */
  610         if (errno == EBADF)
  611         {
  612             goto reopen;
  613         }
  614         /* Tried opening a directory / or smb_init failed */
  615         else
  616         {
  617             pthread_mutex_unlock(&rwd_ctx_mutex);
  618             return -errno;
  619         }
  620     }
  621     pthread_mutex_unlock(&rwd_ctx_mutex);
  622     return (size_t) ssize;
  623 }
  624 
  625 static int fusesmb_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
  626 {
  627     SMBCFILE *file;
  628     char smb_path[MY_MAXPATHLEN] = "smb:/";
  629 
  630     strcat(smb_path, stripworkgroup(path));
  631 
  632     int tries = 0;              //For number of retries before failing
  633     ssize_t ssize;              //Returned by ctx->read
  634 
  635     pthread_mutex_lock(&rwd_ctx_mutex);
  636     /* Ugly goto but it works ;) But IMHO easiest solution for error handling here */
  637     goto seek;
  638   reopen:
  639     if (NULL == (file = rwd_ctx->open(rwd_ctx, smb_path, fi->flags, 0)))
  640     {
  641         /* Trying to reopen when out of memory */
  642         if (errno == ENOMEM)
  643         {
  644             tries++;
  645             if (tries > 4)
  646             {
  647                 pthread_mutex_unlock(&rwd_ctx_mutex);
  648                 return -errno;
  649             }
  650             goto reopen;
  651         }
  652         /* Other errors from docs cannot be recovered from so returning the error */
  653         pthread_mutex_unlock(&rwd_ctx_mutex);
  654         return -errno;
  655 
  656     }
  657     fi->fh = (unsigned long)file;
  658   seek:
  659 
  660     if (rwd_ctx->lseek(rwd_ctx, (SMBCFILE *)fi->fh, offset, SEEK_SET) == (off_t) - 1)
  661     {
  662         /* Bad file descriptor try to reopen */
  663         if (errno == EBADF)
  664         {
  665             goto reopen;
  666         }
  667         else
  668         {
  669             //SMB Init failed
  670             pthread_mutex_unlock(&rwd_ctx_mutex);
  671             return -errno;
  672         }
  673     }
  674     if ((ssize = rwd_ctx->write(rwd_ctx, (SMBCFILE *)fi->fh, (void *) buf, size)) < 0)
  675     {
  676         /* Bad file descriptor try to reopen */
  677         if (errno == EBADF)
  678         {
  679             goto reopen;
  680         }
  681         /* Tried opening a directory / or smb_init failed */
  682         else
  683         {
  684             pthread_mutex_unlock(&rwd_ctx_mutex);
  685             return -errno;
  686         }
  687     }
  688     pthread_mutex_unlock(&rwd_ctx_mutex);
  689     return (size_t) ssize;
  690 }
  691 
  692 static int fusesmb_release(const char *path, struct fuse_file_info *fi)
  693 {
  694     (void)path;
  695     pthread_mutex_lock(&rwd_ctx_mutex);
  696 #ifdef HAVE_LIBSMBCLIENT_CLOSE_FN
  697     rwd_ctx->close_fn(rwd_ctx, (SMBCFILE *)fi->fh);
  698 #else
  699     rwd_ctx->close(rwd_ctx, (SMBCFILE *)fi->fh);
  700 #endif
  701     pthread_mutex_unlock(&rwd_ctx_mutex);
  702     return 0;
  703 
  704 }
  705 
  706 static int fusesmb_mknod(const char *path, mode_t mode,
  707                      __attribute__ ((unused)) dev_t rdev)
  708 {
  709     char smb_path[MY_MAXPATHLEN] = "smb:/";
  710     SMBCFILE *file;
  711 
  712     /* FIXME:
  713        Check which rdevs are supported, currently only a file
  714        is created
  715      */
  716     //if (rdev != S_IFREG)
  717     //  return -EACCES;
  718     if (slashcount(path) <= 3)
  719         return -EACCES;
  720 
  721     strcat(smb_path, stripworkgroup(path));
  722     pthread_mutex_lock(&ctx_mutex);
  723     if ((file = ctx->creat(ctx, smb_path, mode)) == NULL)
  724     {
  725         pthread_mutex_unlock(&ctx_mutex);
  726         return -errno;
  727     }
  728 #ifdef HAVE_LIBSMBCLIENT_CLOSE_FN
  729     ctx->close_fn(ctx, file);
  730 #else
  731     ctx->close(ctx, file);
  732 #endif
  733 
  734     pthread_mutex_unlock(&ctx_mutex);
  735     /* Clear item from notfound_cache */
  736     if (slashcount(path) == 4)
  737     {
  738         pthread_mutex_lock(&notfound_cache_mutex);
  739         hnode_t *node = hash_lookup(notfound_cache, path);
  740         if (node != NULL)
  741         {
  742             const void *key = hnode_getkey(node);
  743             void *data = hnode_get(node);
  744             hash_delete_free(notfound_cache, node);
  745             free((void *)key);
  746             free(data);
  747         }
  748         pthread_mutex_unlock(&notfound_cache_mutex);
  749     }
  750     return 0;
  751 }
  752 
  753 static int fusesmb_statfs(const char *path, struct statfs *fst)
  754 {
  755     /* Returning stat of local filesystem, call is too expensive */
  756     (void)path;
  757     memset(fst, 0, sizeof(struct statfs));
  758     if (statfs("/", fst) != 0)
  759         return -errno;
  760     return 0;
  761 }
  762 
  763 static int fusesmb_unlink(const char *file)
  764 {
  765     char smb_path[MY_MAXPATHLEN] = "smb:/";
  766 
  767     if (slashcount(file) <= 3)
  768         return -EACCES;
  769 
  770     strcat(smb_path, stripworkgroup(file));
  771     pthread_mutex_lock(&ctx_mutex);
  772     if (ctx->unlink(ctx, smb_path) < 0)
  773     {
  774         pthread_mutex_unlock(&ctx_mutex);
  775         return -errno;
  776     }
  777     pthread_mutex_unlock(&ctx_mutex);
  778     return 0;
  779 }
  780 
  781 static int fusesmb_rmdir(const char *path)
  782 {
  783     char smb_path[MY_MAXPATHLEN] = "smb:/";
  784 
  785     if (slashcount(path) <= 3)
  786         return -EACCES;
  787 
  788     strcat(smb_path, stripworkgroup(path));
  789     pthread_mutex_lock(&ctx_mutex);
  790 
  791     if (ctx->rmdir(ctx, smb_path) < 0)
  792     {
  793         pthread_mutex_unlock(&ctx_mutex);
  794         return -errno;
  795     }
  796     pthread_mutex_unlock(&ctx_mutex);
  797     return 0;
  798 }
  799 
  800 static int fusesmb_mkdir(const char *path, mode_t mode)
  801 {
  802     char smb_path[MY_MAXPATHLEN] = "smb:/";
  803 
  804     if (slashcount(path) <= 3)
  805         return -EACCES;
  806 
  807     strcat(smb_path, stripworkgroup(path));
  808     pthread_mutex_lock(&ctx_mutex);
  809     if (ctx->mkdir(ctx, smb_path, mode) < 0)
  810     {
  811         pthread_mutex_unlock(&ctx_mutex);
  812         return -errno;
  813     }
  814     pthread_mutex_unlock(&ctx_mutex);
  815     
  816     /* Clear item from notfound_cache */
  817     if (slashcount(path) == 4)
  818     {
  819         pthread_mutex_lock(&notfound_cache_mutex);
  820     hnode_t *node = hash_lookup(notfound_cache, path);
  821     if (node != NULL)
  822     {
  823         void *data = hnode_get(node);
  824         const void *key = hnode_getkey(node);
  825         hash_delete_free(notfound_cache, node);
  826         free((void *)key);
  827         free(data);
  828     }
  829     pthread_mutex_unlock(&notfound_cache_mutex);
  830     }
  831     return 0;
  832 }
  833 
  834 static int fusesmb_utime(const char *path, struct utimbuf *buf)
  835 {
  836     struct timeval tbuf[2];
  837     debug("path: %s, atime: %ld, mtime: %ld", path, buf->actime, buf->modtime);
  838 
  839     char smb_path[MY_MAXPATHLEN] = "smb:/";
  840     if (slashcount(path) <= 3)
  841         return -EACCES;
  842 
  843     strcat(smb_path, stripworkgroup(path));
  844     tbuf[0].tv_sec = buf->actime;
  845     tbuf[0].tv_usec = 0;
  846     tbuf[1].tv_sec = buf->modtime;
  847     tbuf[1].tv_usec = 0;
  848 
  849     pthread_mutex_lock(&ctx_mutex);
  850     if (ctx->utimes(ctx, smb_path, tbuf) < 0)
  851     {
  852         pthread_mutex_unlock(&ctx_mutex);
  853         return -errno;
  854     }
  855     pthread_mutex_unlock(&ctx_mutex);
  856 
  857 
  858     return 0;
  859 }
  860 
  861 static int fusesmb_chmod(const char *path, mode_t mode)
  862 {
  863     if (slashcount(path) <= 3)
  864         return -EPERM;
  865 
  866     char smb_path[MY_MAXPATHLEN] = "smb:/";
  867     strcat(smb_path, stripworkgroup(path));
  868 
  869     pthread_mutex_lock(&ctx_mutex);
  870     if (ctx->chmod(ctx, smb_path, mode) < 0)
  871     {
  872         pthread_mutex_unlock(&ctx_mutex);
  873         return -errno;
  874     }
  875     pthread_mutex_unlock(&ctx_mutex);
  876     return 0;
  877 }
  878 static int fusesmb_chown(const char *path, uid_t uid, gid_t gid)
  879 {
  880     (void)path;
  881     (void)uid;
  882     (void)gid;
  883     /* libsmbclient has no equivalent function for this, so
  884        always returning success
  885      */
  886     return 0;
  887 }
  888 
  889 static int fusesmb_truncate(const char *path, off_t size)
  890 {
  891 
  892     debug("path: %s, size: %lld", path, size);
  893     char smb_path[MY_MAXPATHLEN] = "smb:/";
  894     if (slashcount(path) <= 3)
  895         return -EACCES;
  896 
  897     SMBCFILE *file;
  898     strcat(smb_path, stripworkgroup(path));
  899     if (size == 0)
  900     {
  901         pthread_mutex_lock(&ctx_mutex);
  902         if (NULL == (file = ctx->creat(ctx, smb_path, 0666)))
  903         {
  904             pthread_mutex_unlock(&ctx_mutex);
  905             return -errno;
  906         }
  907 #ifdef HAVE_LIBSMBCLIENT_CLOSE_FN
  908         ctx->close_fn(ctx, file);
  909 #else
  910         ctx->close(ctx, file);
  911 #endif
  912         pthread_mutex_unlock(&ctx_mutex);
  913         return 0;
  914     }
  915     else
  916     {
  917          /* If the truncate size is equal to the current file size, the file
  918             is also correctly truncated (fixes an error from OpenOffice)
  919             */
  920          pthread_mutex_lock(&ctx_mutex);
  921          struct stat st;
  922          if (ctx->stat(ctx, smb_path, &st) < 0)
  923          {
  924              pthread_mutex_unlock(&ctx_mutex);
  925              return -errno;
  926          }
  927          pthread_mutex_unlock(&ctx_mutex);
  928          if (size == st.st_size)
  929          {
  930              return 0;
  931          }
  932     }
  933     return -ENOTSUP;
  934 }
  935 
  936 static int fusesmb_rename(const char *path, const char *new_path)
  937 {
  938     char smb_path[MY_MAXPATHLEN]     = "smb:/",
  939          new_smb_path[MY_MAXPATHLEN] = "smb:/";
  940 
  941     if (slashcount(path) <= 3 || slashcount(new_path) <= 3)
  942         return -EACCES;
  943 
  944     strcat(smb_path, stripworkgroup(path));
  945     strcat(new_smb_path, stripworkgroup(new_path));
  946 
  947     pthread_mutex_lock(&ctx_mutex);
  948     if (ctx->rename(ctx, smb_path, ctx, new_smb_path) < 0)
  949     {
  950         pthread_mutex_unlock(&ctx_mutex);
  951         return -errno;
  952     }
  953     pthread_mutex_unlock(&ctx_mutex);
  954     return 0;
  955 }
  956 
  957 static void *fusesmb_init()
  958 {
  959     debug();
  960     if (0 != pthread_create(&cleanup_thread, NULL, smb_purge_thread, NULL))
  961         exit(EXIT_FAILURE);
  962     return NULL;
  963 }
  964 
  965 static void fusesmb_destroy(void *private_data)
  966 {
  967     (void)private_data;
  968     pthread_cancel(cleanup_thread);
  969     pthread_join(cleanup_thread, NULL);
  970 
  971 }
  972 
  973 static struct fuse_operations fusesmb_oper = {
  974     .getattr    = fusesmb_getattr,
  975     .readlink   = NULL, //fusesmb_readlink,
  976     .opendir    = fusesmb_opendir,
  977     .readdir    = fusesmb_readdir,
  978     .releasedir = fusesmb_releasedir,
  979     .mknod      = fusesmb_mknod,
  980     .mkdir      = fusesmb_mkdir,
  981     .symlink    = NULL, //fusesmb_symlink,
  982     .unlink     = fusesmb_unlink,
  983     .rmdir      = fusesmb_rmdir,
  984     .rename     = fusesmb_rename,
  985     .link       = NULL, //fusesmb_link,
  986     .chmod      = fusesmb_chmod,
  987     .chown      = fusesmb_chown,
  988     .truncate   = fusesmb_truncate,
  989     .utime      = fusesmb_utime,
  990     .open       = fusesmb_open,
  991     .read       = fusesmb_read,
  992     .write      = fusesmb_write,
  993     .statfs     = fusesmb_statfs,
  994     .release    = fusesmb_release,
  995     .fsync      = NULL, //fusesmb_fsync,
  996     .init       = fusesmb_init,
  997     .destroy    = fusesmb_destroy,
  998 #ifdef HAVE_SETXATTR
  999     .setxattr   = fusesmb_setxattr,
 1000     .getxattr   = fusesmb_getxattr,
 1001     .listxattr  = fusesmb_listxattr,
 1002     .removexattr= fusesmb_removexattr,
 1003 #endif
 1004 };
 1005 
 1006 
 1007 int main(int argc, char *argv[])
 1008 {
 1009     /* Workaround for bug in libsmbclient:
 1010        Limit reads to 32 kB
 1011      */
 1012     int my_argc = 0, i = 0;
 1013 
 1014     /* Check if the directory for smbcache exists and if not so create it */
 1015     char cache_path[1024];
 1016     snprintf(cache_path, 1024, "%s/.smb/", getenv("HOME"));
 1017     struct stat st;
 1018     if (-1 == stat(cache_path, &st))
 1019     {
 1020         if (errno != ENOENT)
 1021         {
 1022             fprintf(stderr, strerror(errno));
 1023             exit(EXIT_FAILURE);
 1024         }
 1025         if (-1 == mkdir(cache_path, 0777))
 1026         {
 1027             fprintf(stderr, strerror(errno));
 1028             exit(EXIT_FAILURE);
 1029        }
 1030     }
 1031     else if (!S_ISDIR(st.st_mode))
 1032     {
 1033         fprintf(stderr, "%s is not a directory\n", cache_path);
 1034         exit(EXIT_FAILURE);
 1035     }
 1036 
 1037     char configfile[1024];
 1038     snprintf(configfile, 1024, "%s/.smb/fusesmb.conf", getenv("HOME"));
 1039     if (-1 == stat(configfile, &st))
 1040     {
 1041         if (errno != ENOENT)
 1042         {
 1043             fprintf(stderr, strerror(errno));
 1044             exit(EXIT_FAILURE);
 1045         }
 1046         int fd;
 1047         /* Create configfile with read-write permissions for the owner */
 1048         if (-1 == (fd = open(configfile, O_WRONLY | O_CREAT, 00600)))
 1049         {
 1050             fprintf(stderr, strerror(errno));
 1051             exit(EXIT_FAILURE);
 1052         }
 1053         close(fd);
 1054     }
 1055     else
 1056     {
 1057         /* Check if configfile is only accessible by the owner */
 1058         if ((st.st_mode & 00777) != 00700 &&
 1059              (st.st_mode & 00777) != 00600 &&
 1060               (st.st_mode & 00777) != 00400)
 1061         {
 1062             fprintf(stderr, "The config file should only be readable by the owner.\n"
 1063                             "You can correct the permissions by executing:\n"
 1064                             " chmod 600 %s\n\n", configfile);
 1065             exit(EXIT_FAILURE);
 1066         }
 1067     }
 1068     /* Check if fusesmb.cache can be found
 1069        we're looking in FUSESMB_CACHE_BINDIR, $PATH or in cwd */
 1070     if (-1 == stat(FUSESMB_CACHE_BINDIR"/fusesmb.cache", &st))
 1071     {
 1072         if (-1 == stat("fusesmb.cache", &st))
 1073         {
 1074             fprintf(stderr, "Could not find the required file fusesmb.cache.\n"
 1075                             "This file should either be in:\n"
 1076                             " - "FUSESMB_CACHE_BINDIR"\n"
 1077                             " - $PATH\n"
 1078                             " - your current working directory\n"
 1079                             "(%s)\n", strerror(errno));
 1080             exit(EXIT_FAILURE);
 1081         }
 1082         else
 1083         {
 1084             strncpy(fusesmb_cache_bin, "fusesmb.cache", MAXPATHLEN-1);
 1085         }
 1086     }
 1087     else
 1088     {
 1089         strncpy(fusesmb_cache_bin, FUSESMB_CACHE_BINDIR"/fusesmb.cache", MAXPATHLEN-1);
 1090     }
 1091 
 1092     if (-1 == config_init(&cfg, configfile))
 1093     {
 1094         fprintf(stderr, "Could not open config file: %s (%s)", configfile, strerror(errno));
 1095         exit(EXIT_FAILURE);
 1096     }
 1097 
 1098     char **my_argv = (char **) malloc((argc + 10) * sizeof(char *));
 1099     if (my_argv == NULL)
 1100         exit(EXIT_FAILURE);
 1101 
 1102     /* libsmbclient doesn't work with reads bigger than 32k */
 1103     char *max_read = "-omax_read=32768";
 1104 
 1105     for (i = 0; i < argc; i++)
 1106     {
 1107         my_argv[i] = argv[i];
 1108         my_argc++;
 1109     }
 1110     my_argv[my_argc++] = max_read;
 1111 
 1112     options_read(&cfg, &opts);
 1113 
 1114     ctx = fusesmb_new_context(&cfg, &cfg_mutex);
 1115     rwd_ctx = fusesmb_new_context(&cfg, &cfg_mutex);
 1116 
 1117     if (ctx == NULL || rwd_ctx == NULL)
 1118         exit(EXIT_FAILURE);
 1119 
 1120     notfound_cache = hash_create(HASHCOUNT_T_MAX, NULL, NULL);
 1121     if (notfound_cache == NULL)
 1122         exit(EXIT_FAILURE);
 1123 
 1124     fuse_main(my_argc, my_argv, &fusesmb_oper);
 1125 
 1126     smbc_free_context(ctx, 1);
 1127     smbc_free_context(rwd_ctx, 1);
 1128 
 1129     options_free(&opts);
 1130     config_free(&cfg);
 1131 
 1132     hscan_t sc;
 1133     hnode_t *n;
 1134     hash_scan_begin(&sc, notfound_cache);
 1135     while (NULL != (n = hash_scan_next(&sc)))
 1136     {
 1137         void *data = hnode_get(n);
 1138         const void *key = hnode_getkey(n);
 1139         hash_scan_delfree(notfound_cache, n);
 1140         free((void *)key);
 1141         free(data);
 1142 
 1143     }
 1144     hash_destroy(notfound_cache);
 1145     exit(EXIT_SUCCESS);
 1146 }