"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(¬found_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(¬found_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(¬found_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(¬found_cache_mutex);
318 return -err;
319 }
320 pthread_mutex_unlock(¬found_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(¬found_cache_mutex);
332 char *key = strdup(path);
333 if (key == NULL)
334 {
335 pthread_mutex_unlock(¬found_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(¬found_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(¬found_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(¬found_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(¬found_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(¬found_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(¬found_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(¬found_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(¬found_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 }