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