1 /*
2     FUSE Filesystem for libgphoto2.
3     Copyright (C) 2005  Philip Langdale <philipl@mail.utexas.edu>
4     Copyright (C) 2007  Marcus Meissner <marcus@jet.franken.de>
5 
6     This program can be distributed under the terms of the GNU GPL.
7     See the file COPYING.
8 */
9 
10 #ifdef HAVE_CONFIG_H
11 #include "config.h"
12 #endif
13 
14 #include <fuse.h>
15 
16 #include <gphoto2/gphoto2.h>
17 
18 #include <glib.h>
19 #include <glib/gi18n.h>
20 #include <glib/gprintf.h>
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <sys/time.h>
29 
30 /*
31  * Static variables set by command line arguments.
32  */
33 static gchar *sPort = NULL;
34 static gchar *sModel = NULL;
35 static gchar *sUsbid = NULL;
36 static gint sSpeed = 0;
37 static gboolean sHelp;
38 
39 static struct timeval glob_tv_zero;
40 
41 /*
42  * The OpenFile struct encapsulates a CameraFile and an open count.
43  * This allows us to track multiple overlapping open/release calls
44  * on a file.
45  */
46 
47 struct OpenFile {
48    CameraFile *file;
49    unsigned long count;
50 
51    void *buf;
52    unsigned long size;
53    int writing;
54    gchar *destdir;
55    gchar *destname;
56 };
57 typedef struct OpenFile OpenFile;
58 
59 static void
freeOpenFile(OpenFile * openFile)60 freeOpenFile(OpenFile *openFile)
61 {
62    gp_file_unref(openFile->file);
63    g_free(openFile);
64 }
65 
66 
67 /*
68  * gpresultToErrno:
69  *
70  * Maps libgphoto2 errors to errnos. For some errors, there isn't
71  * really a good mapping, so a best effort has to be made.
72  */
73 
74 static int
gpresultToErrno(int result)75 gpresultToErrno(int result)
76 {
77    switch (result) {
78    case GP_ERROR:
79       return -EPROTO;
80    case GP_ERROR_BAD_PARAMETERS:
81       return -EINVAL;
82    case GP_ERROR_NO_MEMORY:
83       return -ENOMEM;
84    case GP_ERROR_LIBRARY:
85       return -ENOSYS;
86    case GP_ERROR_UNKNOWN_PORT:
87       return -ENXIO;
88    case GP_ERROR_NOT_SUPPORTED:
89       return -EPROTONOSUPPORT;
90    case GP_ERROR_TIMEOUT:
91       return -ETIMEDOUT;
92    case GP_ERROR_IO:
93    case GP_ERROR_IO_SUPPORTED_SERIAL:
94    case GP_ERROR_IO_SUPPORTED_USB:
95    case GP_ERROR_IO_INIT:
96    case GP_ERROR_IO_READ:
97    case GP_ERROR_IO_WRITE:
98    case GP_ERROR_IO_UPDATE:
99    case GP_ERROR_IO_SERIAL_SPEED:
100    case GP_ERROR_IO_USB_CLEAR_HALT:
101    case GP_ERROR_IO_USB_FIND:
102    case GP_ERROR_IO_USB_CLAIM:
103    case GP_ERROR_IO_LOCK:
104       return -EIO;
105 
106    case GP_ERROR_CAMERA_BUSY:
107       return -EBUSY;
108    case GP_ERROR_FILE_NOT_FOUND:
109    case GP_ERROR_DIRECTORY_NOT_FOUND:
110       return -ENOENT;
111    case GP_ERROR_FILE_EXISTS:
112    case GP_ERROR_DIRECTORY_EXISTS:
113       return -EEXIST;
114    case GP_ERROR_PATH_NOT_ABSOLUTE:
115       return -ENOTDIR;
116    case GP_ERROR_CORRUPTED_DATA:
117       return -EIO;
118    case GP_ERROR_CANCEL:
119       return -ECANCELED;
120 
121    /* These are pretty dubious mappings. */
122    case GP_ERROR_MODEL_NOT_FOUND:
123       return -EPROTO;
124    case GP_ERROR_CAMERA_ERROR:
125       return -EPERM;
126    case GP_ERROR_OS_FAILURE:
127       return -EPIPE;
128    }
129    return -EINVAL;
130 }
131 
132 struct GPCtx {
133    Camera *camera;
134    GPContext *context;
135    CameraAbilitiesList *abilities;
136    int debug_func_id;
137 
138    gchar *directory;
139    GHashTable *files;
140    GHashTable *dirs;
141    GHashTable *reads;
142    GHashTable *writes;
143 };
144 typedef struct GPCtx GPCtx;
145 
146 static int
gphotofs_readdir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi)147 gphotofs_readdir(const char *path,
148                  void *buf,
149                  fuse_fill_dir_t filler,
150                  off_t offset,
151                  struct fuse_file_info *fi)
152 {
153    GPCtx *p;
154    CameraList *list = NULL;
155    int i;
156    int ret = 0;
157 
158    p = (GPCtx *)fuse_get_context()->private_data;
159 
160    filler(buf, ".", NULL, 0);
161    filler(buf, "..", NULL, 0);
162 
163    /* Read directories */
164    gp_list_new(&list);
165 
166    ret = gp_camera_folder_list_folders(p->camera, path, list, p->context);
167    if (ret != 0) {
168       goto error;
169    }
170 
171    for (i = 0; i < gp_list_count(list); i++) {
172       struct stat *stbuf;
173       const char *name;
174       gchar *key;
175 
176       stbuf = g_new0(struct stat, 1);
177       stbuf->st_mode = S_IFDIR | 0755;
178       /* This is not a correct number in general. */
179       stbuf->st_nlink = 2;
180       stbuf->st_uid = getuid();
181       stbuf->st_gid = getgid();
182 
183       gp_list_get_name(list, i, &name);
184       filler(buf, name, stbuf, 0);
185 
186       key = g_build_filename(path, name, NULL);
187 
188       g_hash_table_replace(p->dirs, key, stbuf);
189    }
190 
191    gp_list_free(list);
192    list = NULL;
193 
194    /* Read files */
195    gp_list_new(&list);
196 
197    ret = gp_camera_folder_list_files(p->camera, path, list, p->context);
198    if (ret != 0) {
199       goto error;
200    }
201 
202    for (i = 0; i < gp_list_count(list); i++) {
203       struct stat *stbuf;
204       const char *name;
205       gchar *key;
206       CameraFileInfo info;
207 
208       gp_list_get_name(list, i, &name);
209       ret = gp_camera_file_get_info(p->camera, path, name, &info, p->context);
210       if (ret != 0) {
211          goto error;
212       }
213 
214       stbuf = g_new0(struct stat, 1);
215       stbuf->st_mode = S_IFREG | 0644;
216       stbuf->st_nlink = 1;
217       stbuf->st_uid = getuid();
218       stbuf->st_gid = getgid();
219       stbuf->st_size = info.file.size;
220       stbuf->st_mtime = info.file.mtime;
221       stbuf->st_blocks = (info.file.size / 512) +
222                          (info.file.size % 512 > 0 ? 1 : 0);
223 
224       filler(buf, name, stbuf, 0);
225 
226       key = g_build_filename(path, name, NULL);
227 
228       g_hash_table_replace(p->files, key, stbuf);
229    }
230 
231 exit:
232    if (list) {
233       gp_list_free(list);
234    }
235    return ret;
236 
237  error:
238    ret = gpresultToErrno(ret);
239    goto exit;
240 }
241 
242 static int
dummyfiller(void * buf,const char * name,const struct stat * stbuf,off_t off)243 dummyfiller(void *buf, const char *name,
244             const struct stat *stbuf, off_t off
245 ) {
246    return 0;
247 }
248 
249 
250 static int
gphotofs_getattr(const char * path,struct stat * stbuf)251 gphotofs_getattr(const char *path,
252                  struct stat *stbuf)
253 {
254    int ret = 0;
255    GPCtx *p = (GPCtx *)fuse_get_context()->private_data;
256    struct stat *mystbuf = NULL;
257    gpointer value;
258    guint i;
259 
260    memset(stbuf, 0, sizeof(struct stat));
261    if(strcmp(path, "/") == 0) {
262       stbuf->st_mode = S_IFDIR | 0755;
263       stbuf->st_nlink = 2;
264       stbuf->st_uid = getuid();
265       stbuf->st_gid = getgid();
266       return 0;
267    }
268 
269    /*
270     * Due to the libgphoto2 api, the best way of verifying
271     * if a file exists is to iterate the contents of that
272     * directory; so if we don't know about the file already,
273     * turn around and call readdir on the directory and try
274     * again.
275     */
276    for (i = 2; i > 0; i--) {
277       gchar *dir;
278       value = g_hash_table_lookup(p->files, path);
279       if (!value) {
280 	 value = g_hash_table_lookup(p->dirs, path);
281       }
282       if (value) {
283 	 mystbuf = (struct stat *)value;
284 	 break;
285       }
286 
287       dir = g_path_get_dirname(path);
288       ret = gphotofs_readdir(dir, NULL, dummyfiller, 0, NULL);
289       g_free(dir);
290       if (ret != 0) {
291 	 return ret;
292       }
293    }
294 
295    if (mystbuf) {
296       stbuf->st_mode = mystbuf->st_mode;
297       stbuf->st_nlink = mystbuf->st_nlink;
298       stbuf->st_uid = mystbuf->st_uid;
299       stbuf->st_gid = mystbuf->st_gid;
300       stbuf->st_size = mystbuf->st_size;
301       stbuf->st_blocks = mystbuf->st_blocks;
302       stbuf->st_mtime = mystbuf->st_mtime;
303    } else {
304       ret = -ENOENT;
305    }
306    return ret;
307 }
308 
309 static int
gphotofs_open(const char * path,struct fuse_file_info * fi)310 gphotofs_open(const char *path,
311               struct fuse_file_info *fi)
312 {
313    GPCtx *p = (GPCtx *)fuse_get_context()->private_data;
314    OpenFile *openFile;
315 
316 
317    if ((fi->flags & 3) == O_RDONLY) {
318       openFile = g_hash_table_lookup(p->reads, path);
319       if (!openFile) {
320 	 gchar *dir = g_path_get_dirname(path);
321 	 gchar *file = g_path_get_basename(path);
322 	 CameraFile *cFile;
323 	 int ret;
324 
325 	 gp_file_new(&cFile);
326 	 ret = gp_camera_file_get(p->camera, dir, file, GP_FILE_TYPE_NORMAL,
327 				  cFile, p->context);
328 	 g_free(file);
329 	 g_free(dir);
330 
331 	 if (ret != 0) {
332 	    gp_file_unref (cFile);
333 	    return gpresultToErrno(ret);
334          }
335 
336 	 openFile = g_new0(OpenFile, 1);
337 	 openFile->file = cFile;
338 	 openFile->count = 1;
339 	 g_hash_table_replace(p->reads, g_strdup(path), openFile);
340       } else {
341 	 openFile->count++;
342       }
343       return 0;
344    }
345    if ((fi->flags & 3) == O_WRONLY) {
346       openFile = g_hash_table_lookup(p->writes, path);
347       if (!openFile) {
348 	 gchar *dir = g_path_get_dirname(path);
349 	 gchar *file = g_path_get_basename(path);
350 
351 	 openFile = g_new0(OpenFile, 1);
352 	 openFile->file = NULL;
353 	 openFile->count = 1;
354 	 openFile->size = 0;
355 	 openFile->writing = 1;
356 	 openFile->destdir = g_strdup(dir);
357 	 openFile->destname = g_strdup(file);
358 
359 	 openFile->buf = malloc(1);
360 	 if (!openFile->buf) return -1;
361 	 g_hash_table_replace(p->writes, g_strdup(path), openFile);
362 
363          g_free(dir);
364          g_free(file);
365       } else {
366 	 openFile->count++;
367       }
368       return 0;
369    }
370    return -EINVAL;
371 }
372 
373 static int
gphotofs_read(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fi)374 gphotofs_read(const char *path,
375               char *buf,
376               size_t size,
377               off_t offset,
378               struct fuse_file_info *fi)
379 {
380    GPCtx *p = (GPCtx *)fuse_get_context()->private_data;
381    OpenFile *openFile;
382    const char *data;
383    unsigned long int dataSize;
384    int ret;
385 
386    openFile = g_hash_table_lookup(p->reads, path);
387    ret = gp_file_get_data_and_size(openFile->file, &data, &dataSize);
388    if (ret == 0) {
389       if (offset < dataSize) {
390          if (offset + size > dataSize) {
391             size = dataSize - offset;
392          }
393          memcpy(buf, data + offset, size);
394          ret = size;
395       } else {
396          ret = 0;
397       }
398    } else {
399       ret = gpresultToErrno(ret);
400    }
401 
402    return ret;
403 }
404 
405 
406 /* ================================================================================== */
407 
408 
409 static int
gphotofs_write(const char * path,const char * wbuf,size_t size,off_t offset,struct fuse_file_info * fi)410 gphotofs_write(const char *path, const char *wbuf, size_t size,
411                        off_t offset, struct fuse_file_info *fi)
412 {
413    GPCtx *p = (GPCtx *)fuse_get_context()->private_data;
414    OpenFile *openFile = g_hash_table_lookup (p->writes, path);
415 
416    if (!openFile)
417       return -1;
418    if (offset + size > openFile->size) {
419       openFile->size = offset + size;
420       openFile->buf = realloc (openFile->buf, openFile->size);
421    }
422    memcpy(openFile->buf+offset, wbuf, size);
423    return size;
424 }
425 
426 static int
gphotofs_mkdir(const char * path,mode_t mode)427 gphotofs_mkdir(const char *path, mode_t mode)
428 {
429     int ret = 0;
430     GPCtx *p = (GPCtx *)fuse_get_context()->private_data;
431     gchar *dir = g_path_get_dirname(path);
432     gchar *file = g_path_get_basename(path);
433 
434     ret = gp_camera_folder_make_dir(p->camera, dir, file, p->context);
435     if (ret != 0) {
436        ret = gpresultToErrno(ret);
437     } else {
438        struct stat *stbuf;
439 
440        stbuf = g_new0(struct stat, 1);
441        stbuf->st_mode = S_IFDIR | 0555;
442        /* This is not a correct number in general. */
443        stbuf->st_nlink = 2;
444        stbuf->st_uid = getuid();
445        stbuf->st_gid = getgid();
446        g_hash_table_replace(p->dirs, g_strdup (path), stbuf);
447 
448     }
449     g_free(dir);
450     g_free(file);
451 
452     return ret;
453 }
454 
455 static int
gphotofs_rmdir(const char * path)456 gphotofs_rmdir(const char *path)
457 {
458     int ret = 0;
459 
460     GPCtx *p = (GPCtx *)fuse_get_context()->private_data;
461     gchar *dir = g_path_get_dirname(path);
462     gchar *file = g_path_get_basename(path);
463 
464     ret = gp_camera_folder_remove_dir(p->camera, dir, file, p->context);
465     if (ret != 0) {
466        ret = gpresultToErrno(ret);
467     } else {
468        g_hash_table_remove(p->dirs, path);
469     }
470     g_free(dir);
471     g_free(file);
472     return ret;
473 }
474 
475 static int
gphotofs_mknod(const char * path,mode_t mode,dev_t rdev)476 gphotofs_mknod(const char *path, mode_t mode, dev_t rdev)
477 {
478    GPCtx *p = (GPCtx *)fuse_get_context()->private_data;
479    gchar *dir = g_path_get_dirname(path);
480    gchar *file = g_path_get_basename(path);
481    char *data;
482    int res;
483    CameraFile *cfile;
484 
485    gp_file_new (&cfile);
486    data = malloc(1);
487    data[0] = 'c';
488    res = gp_file_set_data_and_size (cfile, data, 1);
489    if (res < 0) {
490       gp_file_unref (cfile);
491       return -1;
492    }
493    res = gp_camera_folder_put_file (p->camera, dir, file, GP_FILE_TYPE_NORMAL, cfile,
494 				    p->context);
495    gp_file_unref (cfile);
496    g_free(dir);
497    g_free(file);
498    return 0;
499 }
500 
501 
502 
gphotofs_flush(const char * path,struct fuse_file_info * fi)503 static int gphotofs_flush(const char *path, struct fuse_file_info *fi)
504 {
505    GPCtx *p = (GPCtx *)fuse_get_context()->private_data;
506    OpenFile *openFile = g_hash_table_lookup(p->writes, path);
507 
508    if (!openFile)
509       return 0;
510    if (openFile->writing) {
511       int res;
512       CameraFile *file;
513       char *data;
514       gp_file_new (&file);
515       data = malloc (openFile->size);
516       if (!data)
517 	 return -ENOMEM;
518       memcpy (data, openFile->buf, openFile->size);
519       /* The call below takes over responsbility of freeing data. */
520       res = gp_file_set_data_and_size (file, data, openFile->size);
521       if (res < 0) {
522 	 gp_file_unref (file);
523 	 return -1;
524       }
525       res = gp_camera_file_delete(p->camera, openFile->destdir, openFile->destname, p->context);
526       res = gp_camera_folder_put_file (p->camera, openFile->destdir, openFile->destname, GP_FILE_TYPE_NORMAL, file, p->context);
527       if (res < 0)
528 	 return -ENOSPC;
529       gp_file_unref (file);
530    }
531    return 0;
532 }
533 
gphotofs_fsync(const char * path,int isdatasync,struct fuse_file_info * fi)534 static int gphotofs_fsync(const char *path, int isdatasync,
535                        struct fuse_file_info *fi)
536 {
537     (void) isdatasync;
538     return gphotofs_flush(path, fi);
539 }
540 
541 
542 
gphotofs_chmod(const char * path,mode_t mode)543 static int gphotofs_chmod(const char *path, mode_t mode)
544 {
545     return 0;
546 }
547 
gphotofs_chown(const char * path,uid_t uid,gid_t gid)548 static int gphotofs_chown(const char *path, uid_t uid, gid_t gid)
549 {
550     return 0;
551 }
552 
gphotofs_statfs(const char * path,struct statvfs * stvfs)553 static int gphotofs_statfs(const char *path, struct statvfs *stvfs)
554 {
555    GPCtx *p = (GPCtx *)fuse_get_context()->private_data;
556    CameraStorageInformation *sifs;
557    int res, nrofsifs;
558 
559    res = gp_camera_get_storageinfo (p->camera, &sifs, &nrofsifs, p->context);
560    if (res < GP_OK)
561       return gpresultToErrno(res);
562    if (nrofsifs == 0)
563       return -ENOSYS;
564 
565    if (nrofsifs == 1) {
566       stvfs->f_bsize = 1024;
567       stvfs->f_blocks = sifs->capacitykbytes;
568       stvfs->f_bfree = sifs->freekbytes;
569       stvfs->f_bavail = sifs->freekbytes;
570       stvfs->f_files = -1;
571       stvfs->f_ffree = -1;
572       stvfs->f_favail = -1;
573    }
574    return 0;
575 }
576 
577 static int
gphotofs_release(const char * path,struct fuse_file_info * fi)578 gphotofs_release(const char *path,
579                  struct fuse_file_info *fi)
580 {
581    GPCtx *p = (GPCtx *)fuse_get_context()->private_data;
582    OpenFile *openFile = g_hash_table_lookup(p->reads, path);
583 
584    if (!openFile) openFile = g_hash_table_lookup(p->writes, path);
585 
586    if (openFile) {
587       openFile->count--;
588       if (openFile->count == 0) {
589          if (openFile->writing) {
590 	     free (openFile->buf);
591              g_hash_table_remove(p->writes, path);
592          } else  {
593              g_hash_table_remove(p->reads, path);
594          }
595       }
596    }
597 
598    return 0;
599 }
600 
601 static int
gphotofs_unlink(const char * path)602 gphotofs_unlink(const char *path)
603 {
604    GPCtx *p = (GPCtx *)fuse_get_context()->private_data;
605    gchar *dir = g_path_get_dirname(path);
606    gchar *file = g_path_get_basename(path);
607    int ret = 0;
608 
609    /* Don't allow deletion of open files. */
610    if (g_hash_table_lookup(p->reads, path)) {
611       ret = -EBUSY;
612       goto exit;
613    }
614 
615    ret = gp_camera_file_delete(p->camera, dir, file, p->context);
616    if (ret != 0) {
617       ret = gpresultToErrno(ret);
618       goto exit;
619    }
620 
621    g_hash_table_remove(p->files, path);
622  exit:
623    g_free(dir);
624    g_free(file);
625 
626    return ret;
627 
628 }
629 
630 static void
631 #ifdef __GNUC__
632                 __attribute__((__format__(printf,3,0)))
633 #endif
debug_func(GPLogLevel level,const char * domain,const char * format,va_list args,void * data)634 debug_func (GPLogLevel level, const char *domain, const char *format,
635             va_list args, void *data)
636 {
637    struct timeval tv;
638    long sec, usec;
639    FILE *logfile = data;
640 
641    gettimeofday (&tv,NULL);
642    sec = tv.tv_sec  - glob_tv_zero.tv_sec;
643    usec = tv.tv_usec - glob_tv_zero.tv_usec;
644    if (usec < 0) {sec--; usec += 1000000L;}
645    fprintf (logfile, "%li.%06li %s(%i): ", sec, usec, domain, level);
646    vfprintf (logfile, format, args);
647    fputc ('\n', logfile);
648    fflush (logfile);
649 }
650 
651 static void *
gphotofs_init()652 gphotofs_init()
653 {
654    int ret = GP_OK;
655    GPCtx *p = g_new0(GPCtx, 1);
656 
657 #if 0 /* enable for debugging */
658    int fd = -1;
659    FILE *f = NULL;
660 
661    fd = open("/tmp/gpfs.log",O_WRONLY|O_CREAT,0600);
662    if (fd != -1) {
663       f = fdopen(fd,"a");
664       if (f)
665          p->debug_func_id = gp_log_add_func (GP_LOG_ALL, debug_func, (void *) f);
666    }
667    fprintf (f, "log opened on pid %d\n", getpid());
668 #endif
669 
670    p->context = gp_context_new();
671    gettimeofday (&glob_tv_zero, NULL);
672 
673    setlocale (LC_CTYPE,"en_US.UTF-8"); /* for ptp2 driver to convert to utf-8 */
674 
675    gp_camera_new (&p->camera);
676 
677    gp_abilities_list_new(&p->abilities);
678    gp_abilities_list_load(p->abilities, p->context);
679 
680    if (sSpeed) {
681       GPPortInfo	info;
682       GPPortType	type;
683 
684       /* Make sure we've got a serial port. */
685       ret = gp_camera_get_port_info(p->camera, &info);
686       gp_port_info_get_type (info, &type);
687       if (ret != 0) {
688          goto error;
689       } else if (type != GP_PORT_SERIAL) {
690          g_fprintf(stderr, "%s\n", _("You can only specify speeds for serial ports."));
691          goto error;
692       }
693 
694       /* Set the speed. */
695       ret = gp_camera_set_port_speed(p->camera, sSpeed);
696       if (ret != 0) {
697          goto error;
698       }
699    }
700 
701    if (sModel) {
702       CameraAbilities a;
703       int m;
704 
705       m = gp_abilities_list_lookup_model(p->abilities, sModel);
706       if (m < 0) {
707          g_fprintf(stderr, _("Model %s was not recognised."), sModel);
708          g_fprintf(stderr, "\n");
709          goto error;
710       }
711 
712       ret = gp_abilities_list_get_abilities(p->abilities, m, &a);
713       if (ret != 0) {
714          goto error;
715       }
716 
717       ret = gp_camera_set_abilities(p->camera, a);
718       if (ret != 0) {
719          goto error;
720       }
721 
722       /* Marcus: why save it? puzzling. */
723       ret = gp_setting_set("gphoto2", "model", a.model);
724       if (ret != 0) {
725          goto error;
726       }
727    }
728 
729    if (sPort) {
730       GPPortInfo info;
731       GPPortInfoList *il = NULL;
732       int i;
733 
734       ret = gp_port_info_list_new(&il);
735       if (ret != 0) {
736          goto error;
737       }
738 
739       ret = gp_port_info_list_load(il);
740       if (ret != 0) {
741          goto error;
742       }
743 
744       i = gp_port_info_list_lookup_path(il, sPort);
745       if (i == GP_ERROR_UNKNOWN_PORT) {
746          g_fprintf(stderr,
747                    _("The port you specified ('%s') can not "
748                      "be found. Please specify one of the ports "
749                      "found by 'gphoto2 --list-ports' make sure "
750                      "the spelling is correct (i.e. with prefix "
751                      "'serial:' or 'usb:')."), sPort);
752          g_fprintf(stderr, "\n");
753          goto error;
754       } else if (i < 0) {
755          ret = i;
756          goto error;
757       } else {
758 	 char *xpath;
759          ret = gp_port_info_list_get_info(il, i, &info);
760          if (ret != 0) {
761             goto error;
762          }
763 
764          ret = gp_camera_set_port_info (p->camera, info);
765          if (ret != 0)
766             goto error;
767          /* Marcus: why save it? puzzling. */
768 	 gp_port_info_get_path (info, &xpath);
769          gp_setting_set("gphoto2", "port", xpath);
770 
771          gp_port_info_list_free(il);
772       }
773    }
774 
775    p->dirs = g_hash_table_new_full(g_str_hash, g_str_equal,
776                                    g_free, g_free);
777    p->files = g_hash_table_new_full(g_str_hash, g_str_equal,
778                                     g_free, g_free);
779    p->reads = g_hash_table_new_full(g_str_hash, g_str_equal,
780                                     g_free, (GDestroyNotify)freeOpenFile);
781    p->writes = g_hash_table_new_full(g_str_hash, g_str_equal,
782                                     g_free, (GDestroyNotify)freeOpenFile);
783 
784    return p;
785 
786  error:
787    if (ret != GP_OK) {
788       g_fprintf(stderr, _("Error initialising gphotofs: %s"),
789                 gp_result_as_string(ret));
790       g_fprintf(stderr, "\n");
791    }
792    exit(EXIT_FAILURE);
793 }
794 
795 static void
gphotofs_destroy(void * context)796 gphotofs_destroy(void *context)
797 {
798    if (!context) {
799       return;
800    }
801 
802    GPCtx *p = (GPCtx *)context;
803 
804    if (p->reads) {
805       g_hash_table_destroy(p->reads);
806    }
807    if (p->files) {
808       g_hash_table_destroy(p->files);
809    }
810    if (p->dirs) {
811       g_hash_table_destroy(p->dirs);
812    }
813    g_free(p->directory);
814 
815    if (p->abilities) {
816       gp_abilities_list_free(p->abilities);
817    }
818    if (p->camera) {
819       gp_camera_unref(p->camera);
820    }
821    if (p->context) {
822       gp_context_unref(p->context);
823    }
824    g_free(p);
825 }
826 
827 static struct fuse_operations gphotofs_oper = {
828     .init	= gphotofs_init,
829     .destroy	= gphotofs_destroy,
830     .readdir	= gphotofs_readdir,
831     .getattr	= gphotofs_getattr,
832     .open	= gphotofs_open,
833     .read	= gphotofs_read,
834     .release	= gphotofs_release,
835     .unlink	= gphotofs_unlink,
836 
837     .write	= gphotofs_write,
838     .mkdir	= gphotofs_mkdir,
839     .rmdir	= gphotofs_rmdir,
840     .mknod	= gphotofs_mknod,
841     .flush	= gphotofs_flush,
842     .fsync	= gphotofs_fsync,
843 
844     .chmod	= gphotofs_chmod,
845     .chown	= gphotofs_chown,
846 
847     .statfs	= gphotofs_statfs
848 };
849 
850 static GOptionEntry options[] =
851 {
852    { "port", 0, 0, G_OPTION_ARG_STRING, &sPort, N_("Specify port device"), "path" },
853    { "speed", 0, 0, G_OPTION_ARG_INT, &sSpeed, N_("Specify serial transfer speed"), "speed" },
854    { "camera", 0, 0, G_OPTION_ARG_STRING, &sModel, N_("Specify camera model"), "model" },
855    { "usbid", 0, 0, G_OPTION_ARG_STRING, &sUsbid, N_("(expert only) Override USB IDs"), "usbid" },
856    { "help-fuse", 'h', 0, G_OPTION_ARG_NONE, &sHelp, N_("Show FUSE help options"), NULL },
857 };
858 
859 int
main(int argc,char * argv[])860 main(int argc,
861      char *argv[])
862 {
863    GError *error = NULL;
864 
865    GOptionContext *context = g_option_context_new(_("- gphoto filesystem"));
866    g_option_context_add_main_entries(context, options, GETTEXT_PACKAGE);
867    g_option_context_set_ignore_unknown_options(context, TRUE);
868    g_option_context_parse(context, &argc, &argv, &error);
869 
870    if (sHelp) {
871       const char *fusehelp[] = { argv[0], "-ho", NULL};
872 
873       return fuse_main(2, (char **)fusehelp, &gphotofs_oper);
874    } else if (sUsbid) {
875       g_fprintf(stderr, "--usbid is not yet implemented\n");
876       return 1;
877    } else {
878      char **newargv = malloc ( (argc+2)*sizeof(char*));
879      memcpy (newargv+2,argv+1,sizeof(char*)*(argc-1));
880      newargv[0] = argv[0];
881      newargv[1] = "-s"; /* disable multithreading */
882 
883      return fuse_main(argc+1, newargv, &gphotofs_oper);
884    }
885 }
886