1 
2 /* GVFS gphoto2 file system driver
3  *
4  * Copyright (C) 2007-2008 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General
17  * Public License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  * Author: David Zeuthen <davidz@redhat.com>
22  */
23 
24 #include <config.h>
25 
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <string.h>
32 #include <stdlib.h>
33 
34 #include <glib/gstdio.h>
35 #include <glib/gi18n.h>
36 #include <gio/gio.h>
37 #include <gio/gfiledescriptorbased.h>
38 #include <gphoto2.h>
39 #include <gudev/gudev.h>
40 #include <sys/time.h>
41 
42 #include "gvfsgphoto2utils.h"
43 #include "gvfsbackendgphoto2.h"
44 #include "gvfsjobopenforread.h"
45 #include "gvfsjobopeniconforread.h"
46 #include "gvfsjobread.h"
47 #include "gvfsjobseekread.h"
48 #include "gvfsjobqueryinfo.h"
49 #include "gvfsjobenumerate.h"
50 #include "gvfsjobsetdisplayname.h"
51 #include "gvfsjobopenforwrite.h"
52 #include "gvfsjobwrite.h"
53 #include "gvfsjobclosewrite.h"
54 #include "gvfsjobcreatemonitor.h"
55 #include "gvfsjobunmount.h"
56 #include "gvfsmonitor.h"
57 #include "gvfsjobseekwrite.h"
58 #include "gvfsicon.h"
59 
60 /* use this to disable caching */
61 #if 0
62 #define DEBUG_NO_CACHING 1
63 #endif
64 
65 /*--------------------------------------------------------------------------------------------------------------*/
66 
67 /* TODO:
68  *
69  *  - write support
70  *    - it's in; we support writing. yay.
71  *      - though there's no way to rename an non-empty folder yet
72  *    - there's an assumption, for caching, that the device won't
73  *      be able to put files while we're using it. May have to
74  *      revisit that if such devices exist.
75  *      - one solution: make cache items valid for only five seconds or something
76  *
77  *    - Note that most PTP devices (e.g. digital cameras) don't support writing
78  *      - Most MTP devices (e.g. digital audio players) do
79  *
80  *    - However, some MTP devices are just busted when using ~ backup
81  *      style; see below. This is with my (davidz) Sandisk Sansa
82  *      e250. This is probably a firmware bug; when investigating
83  *      libgphoto2 reports everything as peachy.
84  *
85  *         $ pwd
86  *         /home/davidz/.gvfs/gphoto2 mount on usb%3A001,051/foo
87  *         $ echo a > a
88  *         $ echo b > b
89  *         $ ls -l
90  *         total 0
91  *         -rw------- 1 davidz davidz 2 2008-03-02 13:22 a
92  *         -rw------- 1 davidz davidz 2 2008-03-02 13:22 b
93  *         $ cat a
94  *         a
95  *         $ cat b
96  *         b
97  *         $ mv b a
98  *         $ ls -l
99  *         total 0
100  *         -rw------- 1 davidz davidz 2 2008-03-02 13:22 a
101  *         $ cat a
102  *         b
103  *         $ rm a
104  *
105  *      See, this worked fine.. Now see what happens if we
106  *      use different files names
107  *
108  *         $ echo a > file.txt
109  *         $ echo b > file.txt~
110  *         $ ls -l
111  *         total 0
112  *         -rw------- 1 davidz davidz 2 2008-03-02 13:22 file.txt
113  *         -rw------- 1 davidz davidz 2 2008-03-02 13:22 file.txt~
114  *         $ cat file.txt
115  *         a
116  *         $ cat file.txt~
117  *         b
118  *         $ mv file.txt~ file.txt
119  *         $ ls -l
120  *         total 0
121  *         -rw------- 1 davidz davidz 0 1969-12-31 18:59 file.txt
122  *         $ cat file.txt
123  *         $
124  *
125  *      Awesome. I hate hardware.
126  *
127  *      - This is a bit bad as it affects most text editors (vim, emacs,
128  *        gedit) and it actually results in data loss. However, there's
129  *        little we can do about it.
130  *
131  *      - Would be nice to test this on other MTP devices to verify
132  *        it's indeed a firmware bug in the Sansa Sandisk e250.
133  *
134  *      - This shouldn't affect stuff like Banshee or Rhythmbox using
135  *        this backend for MTP support though; despite this bug basic
136  *        file operations works nicely.
137  *        - http://bugzilla.gnome.org/show_bug.cgi?id=520121
138  *
139  *      - Need to test this with a native gio version of gedit that should
140  *        use replace() directly instead of fooling around with ~-style
141  *        backup files
142  *
143  *  - adding a payload cache don't make much sense as libgphoto2 has a LRU cache already
144  *    - (see comment in the do_close_write() function)
145  *
146  *  - Support PTP/IP devices nicely
147  *    - Need hardware for testing
148  *    - Should actually work out of the box; just try mounting e.g.
149  *      gphoto2://[ptpip:<something]/ from either Nautilus or via
150  *      gvfs-mount(1).
151  *    - Need to automatically unmount when the device stops answering
152  *    - May need authentication bits
153  *    - Need integration into network://
154  *      - does such devices use DNS-SD or UPNP?
155  *
156  */
157 
158 struct _GVfsBackendGphoto2
159 {
160   GVfsBackend parent_instance;
161 
162   /* a gphoto2 specific identifier for the gphoto2 camera such as usb:001,041 */
163   char *gphoto2_port;
164   GPContext *context;
165   Camera *camera;
166 
167   /* see comment in ensure_ignore_prefix() */
168   char *ignore_prefix;
169 
170   GUdevClient *gudev_client;
171   GUdevDevice *udev_device;
172   char *icon_name;
173   char *symbolic_icon_name;
174 
175   /* whether we can write to the device */
176   gboolean can_write;
177   /* whether we can delete files from to the device */
178   gboolean can_delete;
179 
180   /* This lock protects all members in this class that are not
181    * used both on the main thread and on the IO thread.
182    *
183    * It is used, among other places, in the try_* functions to return
184    * already cached data quickly (to e.g. enumerate and get file info
185    * while we're reading or writing a file from the device).
186    *
187    * Must only be held for very short amounts of time (e.g. no IO).
188    */
189   GMutex lock;
190 
191   /* CACHES */
192 
193   /* free_space is set to -1 if we don't know or have modified the
194    * device since last time we read it. If -1 we can't do
195    * try_query_fs_info() and will fall back to do_query_fs_info().
196    */
197   gint64 free_space;
198   gint64 capacity;
199 
200   /* fully qualified path -> GFileInfo */
201   GHashTable *info_cache;
202 
203   /* dir name -> CameraList of (sub-) directory names in given directory */
204   GHashTable *dir_name_cache;
205 
206   /* dir name -> CameraList of file names in given directory */
207   GHashTable *file_name_cache;
208 
209   /* monitors (only used on the IO thread) */
210   GList *dir_monitor_proxies;
211   GList *file_monitor_proxies;
212 
213   /* list of open read handles (only used on the IO thread) */
214   GList *open_read_handles;
215 
216   /* list of open write handles (only used on the IO thread) */
217   GList *open_write_handles;
218 };
219 
220 G_DEFINE_TYPE (GVfsBackendGphoto2, g_vfs_backend_gphoto2, G_VFS_TYPE_BACKEND);
221 
222 /* ------------------------------------------------------------------------------------------------- */
223 
224 typedef struct {
225   /* this is the path of the dir/file including ignore_prefix */
226   char *path;
227   GVfsMonitor *vfs_monitor;
228 } MonitorProxy;
229 
230 static void
monitor_proxy_free(MonitorProxy * proxy)231 monitor_proxy_free (MonitorProxy *proxy)
232 {
233   g_free (proxy->path);
234   /* vfs_monitor is owned by the gvfs core; see the functions
235    * vfs_dir_monitor_destroyed() and do_create_monitor()
236    */
237 }
238 
239 /* ------------------------------------------------------------------------------------------------- */
240 
241 typedef struct {
242   /* filename as given from the vfs without ignore prefix e.g. /foo.txt */
243   char *filename;
244 
245   /* filename with ignore prefix splitted into dir and name; e.g. "/store_00010001/" and "foo.txt" */
246   char *dir;
247   char *name;
248 
249   char *data;
250   unsigned long int size;
251   unsigned long int cursor;
252   unsigned long int allocated_size;
253 
254   gboolean job_is_replace;
255   gboolean job_is_append_to;
256 
257   gboolean delete_before;
258 
259   gboolean is_dirty;
260 } WriteHandle;
261 
262 /* how much more memory to ask for when using g_realloc() when writing a file */
263 #define WRITE_INCREMENT 4096
264 
265 typedef struct {
266   CameraFile *file;
267 
268   const char *data;
269   unsigned long int size;
270   unsigned long int cursor;
271 } ReadHandle;
272 
273 /* ------------------------------------------------------------------------------------------------- */
274 
275 typedef struct {
276   goffset size;
277   float target;
278   GFileProgressCallback progress_callback;
279   gpointer progress_callback_data;
280 } PullContext;
281 
282 /* ------------------------------------------------------------------------------------------------- */
283 
284 static int commit_write_handle (GVfsBackendGphoto2 *gphoto2_backend, WriteHandle *write_handle);
285 
286 static void
write_handle_free(WriteHandle * write_handle)287 write_handle_free (WriteHandle *write_handle)
288 {
289   g_free (write_handle->filename);
290   g_free (write_handle->dir);
291   g_free (write_handle->name);
292   g_free (write_handle->data);
293   g_free (write_handle);
294 }
295 
296 /* This must be called before reading from the device to ensure that
297  * all pending writes are written to the device.
298  *
299  * Must only be called on the IO thread.
300  */
301 static void
ensure_not_dirty(GVfsBackendGphoto2 * gphoto2_backend)302 ensure_not_dirty (GVfsBackendGphoto2 *gphoto2_backend)
303 {
304   GList *l;
305 
306   for (l = gphoto2_backend->open_write_handles; l != NULL; l = l->next)
307     {
308       WriteHandle *write_handle = l->data;
309 
310       g_debug ("ensure_not_dirty: looking at handle for '%s'\n", write_handle->filename);
311 
312       if (write_handle->is_dirty)
313         commit_write_handle (gphoto2_backend, write_handle);
314     }
315 }
316 
317 /* ------------------------------------------------------------------------------------------------- */
318 
319 /* used when gphoto2 will take ownership of this data for it's LRU cache - and will use free(3) to free it */
320 static char *
dup_for_gphoto2(char * gmem,unsigned long int size)321 dup_for_gphoto2 (char *gmem, unsigned long int size)
322 {
323   char *mem;
324   mem = malloc (size);
325   memcpy (mem, gmem, size);
326   return mem;
327 }
328 
329 /* ------------------------------------------------------------------------------------------------- */
330 
331 static void
monitors_emit_internal(GVfsBackendGphoto2 * gphoto2_backend,const char * dir,const char * name,GFileMonitorEvent event,const char * event_name)332 monitors_emit_internal (GVfsBackendGphoto2 *gphoto2_backend,
333                         const char *dir,
334                         const char *name,
335                         GFileMonitorEvent event,
336                         const char *event_name)
337 {
338   GList *l;
339   char *filepath;
340 
341   g_return_if_fail (g_str_has_prefix (dir, gphoto2_backend->ignore_prefix));
342 
343   g_debug ("monitors_emit_internal() %s for '%s' '%s'\n", event_name, dir, name);
344 
345   for (l = gphoto2_backend->dir_monitor_proxies; l != NULL; l = l->next)
346     {
347       MonitorProxy *proxy = l->data;
348       if (strcmp (proxy->path, dir) == 0)
349         {
350           char *path;
351           path = g_build_filename (dir + strlen (gphoto2_backend->ignore_prefix), name, NULL);
352           g_vfs_monitor_emit_event (proxy->vfs_monitor, event, path, NULL);
353           g_debug ("  emitted %s for '%s' on dir monitor for '%s'\n", event_name, path, dir);
354           g_free (path);
355         }
356     }
357 
358   filepath = g_build_filename (dir, name, NULL);
359   for (l = gphoto2_backend->file_monitor_proxies; l != NULL; l = l->next)
360     {
361       MonitorProxy *proxy = l->data;
362       if (strcmp (proxy->path, filepath) == 0)
363         {
364           const char *path = filepath + strlen (gphoto2_backend->ignore_prefix);
365           g_vfs_monitor_emit_event (proxy->vfs_monitor, event, path, NULL);
366           g_debug ("  emitted %s for '%s' on file monitor\n", event_name, path);
367         }
368     }
369   g_free (filepath);
370 }
371 
372 /* ------------------------------------------------------------------------------------------------- */
373 
374 /* call this when a file/directory have been added to a directory */
375 static void
monitors_emit_created(GVfsBackendGphoto2 * gphoto2_backend,const char * dir,const char * name)376 monitors_emit_created (GVfsBackendGphoto2 *gphoto2_backend, const char *dir, const char *name)
377 {
378   g_debug ("monitors_emit_created(): '%s' '%s'\n", dir, name);
379   monitors_emit_internal (gphoto2_backend, dir, name, G_FILE_MONITOR_EVENT_CREATED, "CREATED");
380 }
381 
382 /* ------------------------------------------------------------------------------------------------- */
383 
384 /* call this when a file/directory have been deleted from a directory */
385 static void
monitors_emit_deleted(GVfsBackendGphoto2 * gphoto2_backend,const char * dir,const char * name)386 monitors_emit_deleted (GVfsBackendGphoto2 *gphoto2_backend, const char *dir, const char *name)
387 {
388   g_debug ("monitors_emit_deleted(): '%s' '%s'\n", dir, name);
389   monitors_emit_internal (gphoto2_backend, dir, name, G_FILE_MONITOR_EVENT_DELETED, "DELETED");
390 }
391 
392 /* ------------------------------------------------------------------------------------------------- */
393 
394 /* call this when a file/directory have been changed in a directory */
395 static void
monitors_emit_changed(GVfsBackendGphoto2 * gphoto2_backend,const char * dir,const char * name)396 monitors_emit_changed (GVfsBackendGphoto2 *gphoto2_backend, const char *dir, const char *name)
397 {
398   g_debug ("monitors_emit_changed(): '%s' '%s'\n", dir, name);
399   monitors_emit_internal (gphoto2_backend, dir, name, G_FILE_MONITOR_EVENT_CHANGED, "CHANGED");
400 }
401 
402 /* ------------------------------------------------------------------------------------------------- */
403 
404 static void
caches_invalidate_all(GVfsBackendGphoto2 * gphoto2_backend)405 caches_invalidate_all (GVfsBackendGphoto2 *gphoto2_backend)
406 {
407   g_debug ("caches_invalidate_all()\n");
408 
409   g_mutex_lock (&gphoto2_backend->lock);
410   if (gphoto2_backend->dir_name_cache != NULL)
411     g_hash_table_remove_all (gphoto2_backend->dir_name_cache);
412   if (gphoto2_backend->file_name_cache != NULL)
413     g_hash_table_remove_all (gphoto2_backend->file_name_cache);
414   if (gphoto2_backend->info_cache != NULL)
415     g_hash_table_remove_all (gphoto2_backend->info_cache);
416   gphoto2_backend->capacity = -1;
417   gphoto2_backend->free_space = -1;
418   g_mutex_unlock (&gphoto2_backend->lock);
419 }
420 
421 /* ------------------------------------------------------------------------------------------------- */
422 
423 static void
caches_invalidate_free_space(GVfsBackendGphoto2 * gphoto2_backend)424 caches_invalidate_free_space (GVfsBackendGphoto2 *gphoto2_backend)
425 {
426   g_mutex_lock (&gphoto2_backend->lock);
427   gphoto2_backend->free_space = -1;
428   g_mutex_unlock (&gphoto2_backend->lock);
429 }
430 
431 /* ------------------------------------------------------------------------------------------------- */
432 
433 static void
caches_invalidate_dir(GVfsBackendGphoto2 * gphoto2_backend,const char * dir)434 caches_invalidate_dir (GVfsBackendGphoto2 *gphoto2_backend, const char *dir)
435 {
436   g_debug ("caches_invalidate_dir() for '%s'\n", dir);
437   g_mutex_lock (&gphoto2_backend->lock);
438   g_hash_table_remove (gphoto2_backend->dir_name_cache, dir);
439   g_hash_table_remove (gphoto2_backend->file_name_cache, dir);
440   g_hash_table_remove (gphoto2_backend->info_cache, dir);
441   g_mutex_unlock (&gphoto2_backend->lock);
442 }
443 
444 /* ------------------------------------------------------------------------------------------------- */
445 
446 static void
caches_invalidate_file(GVfsBackendGphoto2 * gphoto2_backend,const char * dir,const char * name)447 caches_invalidate_file (GVfsBackendGphoto2 *gphoto2_backend, const char *dir, const char *name)
448 {
449   char *full_name;
450 
451   full_name = g_build_filename (dir, name, NULL);
452 
453   g_mutex_lock (&gphoto2_backend->lock);
454   /* this is essentially: caches_invalidate_dir (gphoto2_backend, dir); */
455   g_hash_table_remove (gphoto2_backend->dir_name_cache, dir);
456   g_hash_table_remove (gphoto2_backend->file_name_cache, dir);
457   g_hash_table_remove (gphoto2_backend->info_cache, dir);
458 
459   g_hash_table_remove (gphoto2_backend->info_cache, full_name);
460   g_mutex_unlock (&gphoto2_backend->lock);
461 
462   g_debug ("caches_invalidate_file() for '%s'\n", full_name);
463   g_free (full_name);
464 }
465 
466 /* ------------------------------------------------------------------------------------------------- */
467 
468 static GError *
get_error_from_gphoto2(const char * message,int rc)469 get_error_from_gphoto2 (const char *message, int rc)
470 {
471   GError *error;
472 
473   switch (rc)
474     {
475     case GP_ERROR_FILE_EXISTS:
476     case GP_ERROR_DIRECTORY_EXISTS:
477       error = g_error_new (G_IO_ERROR,
478                            /* Translator: %s represents a more specific error message and %d the specific error code */
479                            G_IO_ERROR_EXISTS, _("%s: %d: Directory or file exists"), message, rc);
480       break;
481 
482     case GP_ERROR_FILE_NOT_FOUND:
483     case GP_ERROR_DIRECTORY_NOT_FOUND:
484       error = g_error_new (G_IO_ERROR,
485                            /* Translator: %s represents a more specific error message and %d the specific error code */
486                            G_IO_ERROR_NOT_FOUND, _("%s: %d: No such file or directory"), message, rc);
487       break;
488 
489     case GP_ERROR_PATH_NOT_ABSOLUTE:
490       error = g_error_new (G_IO_ERROR,
491                            /* Translator: %s represents a more specific error message and %d the specific error code */
492                            G_IO_ERROR_INVALID_FILENAME, _("%s: %d: Invalid filename"), message, rc);
493       break;
494 
495     case GP_ERROR_NOT_SUPPORTED:
496       error = g_error_new (G_IO_ERROR,
497                            /* Translator: %s represents a more specific error message and %d the specific error code */
498                            G_IO_ERROR_NOT_SUPPORTED, _("%s: %d: Not Supported"), message, rc);
499       break;
500 
501     default:
502       error = g_error_new (G_IO_ERROR,
503                            G_IO_ERROR_FAILED, "%s: %d: %s", message, rc, gp_result_as_string (rc));
504       break;
505     }
506   return error;
507 }
508 
509 /* ------------------------------------------------------------------------------------------------- */
510 
511 static void
release_device(GVfsBackendGphoto2 * gphoto2_backend)512 release_device (GVfsBackendGphoto2 *gphoto2_backend)
513 {
514   GList *l;
515 
516   g_free (gphoto2_backend->gphoto2_port);
517   gphoto2_backend->gphoto2_port = NULL;
518 
519   if (gphoto2_backend->context != NULL)
520     {
521       gp_context_unref (gphoto2_backend->context);
522       gphoto2_backend->context = NULL;
523     }
524 
525   if (gphoto2_backend->camera != NULL)
526     {
527       gp_camera_unref (gphoto2_backend->camera);
528       gphoto2_backend->camera = NULL;
529     }
530 
531   if (gphoto2_backend->gudev_client != NULL)
532     g_object_unref (gphoto2_backend->gudev_client);
533   if (gphoto2_backend->udev_device != NULL)
534     g_object_unref (gphoto2_backend->udev_device);
535 
536   g_free (gphoto2_backend->icon_name);
537   gphoto2_backend->icon_name = NULL;
538   g_free (gphoto2_backend->symbolic_icon_name);
539   gphoto2_backend->symbolic_icon_name = NULL;
540 
541   g_free (gphoto2_backend->ignore_prefix);
542   gphoto2_backend->ignore_prefix = NULL;
543 
544   if (gphoto2_backend->info_cache != NULL)
545     {
546       g_hash_table_unref (gphoto2_backend->info_cache);
547       gphoto2_backend->info_cache = NULL;
548     }
549   if (gphoto2_backend->dir_name_cache != NULL)
550     {
551       g_hash_table_unref (gphoto2_backend->dir_name_cache);
552       gphoto2_backend->dir_name_cache = NULL;
553     }
554   if (gphoto2_backend->file_name_cache != NULL)
555     {
556       g_hash_table_unref (gphoto2_backend->file_name_cache);
557       gphoto2_backend->file_name_cache = NULL;
558     }
559 
560   for (l = gphoto2_backend->dir_monitor_proxies; l != NULL; l = l->next)
561     {
562       MonitorProxy *proxy = l->data;
563       monitor_proxy_free (proxy);
564     }
565   g_list_free (gphoto2_backend->dir_monitor_proxies);
566   gphoto2_backend->dir_monitor_proxies = NULL;
567 
568   for (l = gphoto2_backend->file_monitor_proxies; l != NULL; l = l->next)
569     {
570       MonitorProxy *proxy = l->data;
571       monitor_proxy_free (proxy);
572     }
573   g_list_free (gphoto2_backend->file_monitor_proxies);
574   gphoto2_backend->file_monitor_proxies = NULL;
575 
576   gphoto2_backend->capacity = -1;
577   gphoto2_backend->free_space = -1;
578 }
579 
580 /* ------------------------------------------------------------------------------------------------- */
581 
582 static void
g_vfs_backend_gphoto2_finalize(GObject * object)583 g_vfs_backend_gphoto2_finalize (GObject *object)
584 {
585   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (object);
586 
587   g_debug ("finalizing %p\n", object);
588 
589   release_device (gphoto2_backend);
590   g_mutex_clear (&gphoto2_backend->lock);
591 
592   if (G_OBJECT_CLASS (g_vfs_backend_gphoto2_parent_class)->finalize)
593     (*G_OBJECT_CLASS (g_vfs_backend_gphoto2_parent_class)->finalize) (object);
594 }
595 
596 /* ------------------------------------------------------------------------------------------------- */
597 
598 static void
_gphoto2_logger_func(GPLogLevel level,const char * domain,const char * str,void * data)599 _gphoto2_logger_func (GPLogLevel level, const char *domain, const char *str, void *data)
600 {
601   g_print ("%s: %s\n", domain, str);
602 }
603 
604 static void
g_vfs_backend_gphoto2_init(GVfsBackendGphoto2 * gphoto2_backend)605 g_vfs_backend_gphoto2_init (GVfsBackendGphoto2 *gphoto2_backend)
606 {
607   GVfsBackend *backend = G_VFS_BACKEND (gphoto2_backend);
608   GMountSpec *mount_spec;
609   const char *debug;
610   GPLogLevel level = -1;
611 
612   g_debug ("initing %p\n", gphoto2_backend);
613 
614   g_mutex_init (&gphoto2_backend->lock);
615 
616   g_vfs_backend_set_display_name (backend, "gphoto2");
617   g_vfs_backend_handle_readonly_lockdown (G_VFS_BACKEND (backend));
618 
619   mount_spec = g_mount_spec_new ("gphoto2");
620   g_vfs_backend_set_mount_spec (backend, mount_spec);
621   g_mount_spec_unref (mount_spec);
622 
623   debug = g_getenv ("GVFS_GPHOTO2_DEBUG");
624   if (debug)
625     {
626       if (g_ascii_strcasecmp ("all", debug) == 0 || g_ascii_strcasecmp ("data", debug) == 0)
627         level = GP_LOG_DATA;
628       else if (g_ascii_strcasecmp ("debug", debug) == 0)
629         level = GP_LOG_DEBUG;
630       else if (g_ascii_strcasecmp ("verbose", debug) == 0)
631         level = GP_LOG_VERBOSE;
632       else
633         level = GP_LOG_ERROR;
634 
635       gp_log_add_func (level, _gphoto2_logger_func, NULL);
636     }
637 }
638 
639 /* ------------------------------------------------------------------------------------------------- */
640 
641 static char *
compute_icon_name(GVfsBackendGphoto2 * gphoto2_backend)642 compute_icon_name (GVfsBackendGphoto2 *gphoto2_backend)
643 {
644   char *result;
645 
646   if (gphoto2_backend->icon_name == NULL)
647     {
648       result = g_strdup_printf ("camera-photo");
649     }
650   else
651     {
652       result = g_strdup (gphoto2_backend->icon_name);
653     }
654 
655   return result;
656 }
657 
658 static char *
compute_symbolic_icon_name(GVfsBackendGphoto2 * gphoto2_backend)659 compute_symbolic_icon_name (GVfsBackendGphoto2 *gphoto2_backend)
660 {
661   char *result;
662 
663   if (gphoto2_backend->symbolic_icon_name == NULL)
664     {
665       result = g_strdup_printf ("camera-photo-symbolic");
666     }
667   else
668     {
669       result = g_strdup (gphoto2_backend->symbolic_icon_name);
670     }
671 
672   return result;
673 }
674 
675 /* ------------------------------------------------------------------------------------------------- */
676 
677 static char *
compute_display_name(GVfsBackendGphoto2 * gphoto2_backend)678 compute_display_name (GVfsBackendGphoto2 *gphoto2_backend)
679 {
680   char *result = NULL;
681 
682   if (gphoto2_backend->udev_device != NULL)
683     result = g_vfs_get_volume_name (gphoto2_backend->udev_device, "ID_GPHOTO2");
684   if (result == NULL )
685     {
686       /* Translator: %s represents the device, e.g. usb:001,042  */
687       result = g_strdup_printf (_("Digital Camera (%s)"), gphoto2_backend->gphoto2_port);
688     }
689 
690   return result;
691 }
692 
693 /* ------------------------------------------------------------------------------------------------- */
694 
695 static void
setup_for_device(GVfsBackendGphoto2 * gphoto2_backend)696 setup_for_device (GVfsBackendGphoto2 *gphoto2_backend)
697 {
698   gchar *devname;
699   char *comma;
700 
701   /* turn usb:001,041 string into an udev device name */
702   if (!g_str_has_prefix (gphoto2_backend->gphoto2_port, "usb:"))
703     return;
704   devname = g_strconcat ("/dev/bus/usb/", gphoto2_backend->gphoto2_port+4, NULL);
705   if ((comma = strchr (devname, ',')) == NULL)
706     {
707       g_free (devname);
708       return;
709     }
710   *comma = '/';
711   g_debug ("Parsed '%s' into device name %s\n", gphoto2_backend->gphoto2_port, devname);
712 
713   /* find corresponding GUdevDevice */
714   gphoto2_backend->udev_device = g_udev_client_query_by_device_file (gphoto2_backend->gudev_client, devname);
715   g_free (devname);
716   if (gphoto2_backend->udev_device)
717     {
718       g_debug ("-> sysfs path %s, subsys %s, name %s\n", g_udev_device_get_sysfs_path (gphoto2_backend->udev_device), g_udev_device_get_subsystem (gphoto2_backend->udev_device), g_udev_device_get_name (gphoto2_backend->udev_device));
719 
720       gphoto2_backend->icon_name = g_vfs_get_volume_icon (gphoto2_backend->udev_device);
721       gphoto2_backend->symbolic_icon_name = g_vfs_get_volume_symbolic_icon (gphoto2_backend->udev_device);
722     }
723   else
724       g_debug ("-> did not find matching udev device\n");
725 
726   g_vfs_backend_set_x_content_types (G_VFS_BACKEND (gphoto2_backend),
727                                      g_vfs_get_x_content_types (gphoto2_backend->udev_device));
728 }
729 
730 static void
on_uevent(GUdevClient * client,gchar * action,GUdevDevice * device,gpointer user_data)731 on_uevent (GUdevClient *client, gchar *action, GUdevDevice *device, gpointer user_data)
732 {
733   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (user_data);
734 
735   g_debug ("on_uevent action %s, device %s\n", action, g_udev_device_get_device_file (device));
736 
737   if (gphoto2_backend->udev_device != NULL &&
738       g_strcmp0 (g_udev_device_get_device_file (gphoto2_backend->udev_device), g_udev_device_get_device_file (device)) == 0 &&
739       strcmp (action, "remove") == 0)
740     {
741       g_debug ("we have been removed!\n");
742 
743       /* nuke all caches so we're a bit more valgrind friendly */
744       caches_invalidate_all (gphoto2_backend);
745 
746       g_vfs_backend_force_unmount (G_VFS_BACKEND (gphoto2_backend));
747 
748       g_signal_handlers_disconnect_by_func (gphoto2_backend->gudev_client, on_uevent, gphoto2_backend);
749     }
750 }
751 
752 /* ------------------------------------------------------------------------------------------------- */
753 
754 static void
split_filename_with_ignore_prefix(GVfsBackendGphoto2 * gphoto2_backend,const char * filename,char ** dir,char ** name)755 split_filename_with_ignore_prefix (GVfsBackendGphoto2 *gphoto2_backend, const char *filename, char **dir, char **name)
756 {
757   char *s;
758 
759   s = g_path_get_dirname (filename);
760   if (s[0] == '/')
761     *dir = g_strconcat (gphoto2_backend->ignore_prefix, s + 1, NULL);
762   else
763     *dir = g_strconcat (gphoto2_backend->ignore_prefix, s, NULL);
764   g_free (s);
765 
766   if (strcmp (filename, "/") == 0)
767     *name = g_strdup ("");
768   else
769     *name = g_path_get_basename (filename);
770 
771   s = *dir;
772   if (s[strlen(s)] == '/')
773     s[strlen(s)] = '\0';
774 
775   /*g_debug ("split_filename_with_ignore_prefix: '%s' -> '%s' '%s'\n", filename, *dir, *name);*/
776 }
777 
778 /* ------------------------------------------------------------------------------------------------- */
779 
780 static char *
add_ignore_prefix(GVfsBackendGphoto2 * gphoto2_backend,const char * filename)781 add_ignore_prefix (GVfsBackendGphoto2 *gphoto2_backend, const char *filename)
782 {
783   char *result;
784 
785   if (filename[0] == '/')
786     result = g_strconcat (gphoto2_backend->ignore_prefix, filename + 1, NULL);
787   else
788     result = g_strconcat (gphoto2_backend->ignore_prefix, filename, NULL);
789 
790   /*g_debug ("add_ignore_prefix: '%s' -> '%s'\n", filename, result);*/
791   return result;
792 }
793 
794 /* ------------------------------------------------------------------------------------------------- */
795 
796 /* the passed 'dir' variable must contain ignore_prefix */
797 static gboolean
file_get_info(GVfsBackendGphoto2 * gphoto2_backend,const char * dir,const char * name,GFileInfo * info,GError ** error,gboolean try_cache_only)798 file_get_info (GVfsBackendGphoto2 *gphoto2_backend,
799                const char *dir,
800                const char *name,
801                GFileInfo *info,
802                GError **error,
803                gboolean try_cache_only)
804 {
805   int rc;
806   gboolean ret;
807   CameraFileInfo gp_info;
808   char *full_path;
809   GFileInfo *cached_info;
810   char *mime_type;
811   gboolean uncertain_content_type;
812   GIcon *icon;
813   unsigned int n;
814 
815   ret = FALSE;
816 
817   full_path = g_build_filename (dir, name, NULL);
818   g_debug ("file_get_info() try_cache_only=%d dir='%s', name='%s'\n"
819            "                full_path='%s' ignore_prefix='%s'\n",
820            try_cache_only, dir, name, full_path, gphoto2_backend->ignore_prefix);
821 
822 
823   /* first look up cache */
824   g_mutex_lock (&gphoto2_backend->lock);
825   cached_info = g_hash_table_lookup (gphoto2_backend->info_cache, full_path);
826   if (cached_info != NULL)
827     {
828       g_file_info_copy_into (cached_info, info);
829       g_mutex_unlock (&gphoto2_backend->lock);
830       g_debug ("  Using cached info %p for '%s'\n", cached_info, full_path);
831       ret = TRUE;
832       goto out;
833     }
834   g_mutex_unlock (&gphoto2_backend->lock);
835 
836   if (try_cache_only)
837     goto out;
838 
839   ensure_not_dirty (gphoto2_backend);
840 
841   g_debug ("  No cached info for '%s'\n", full_path);
842 
843   /* Since we're caching stuff, make sure all information we store is set */
844   g_file_info_unset_attribute_mask (info);
845 
846   /* handle root directory */
847   if (strcmp (full_path, gphoto2_backend->ignore_prefix) == 0 || strcmp (full_path, "/") == 0)
848     {
849       const char *directory_mime_type = "inode/directory";
850       char *display_name;
851       display_name = compute_display_name (gphoto2_backend);
852       g_file_info_set_display_name (info, display_name);
853       g_file_info_set_name (info, display_name);
854       g_free (display_name);
855       g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
856       g_file_info_set_content_type (info, directory_mime_type);
857       g_file_info_set_size (info, 0);
858       icon = g_content_type_get_icon (directory_mime_type);
859       g_file_info_set_icon (info, icon);
860       g_object_unref (icon);
861       icon = g_content_type_get_symbolic_icon (directory_mime_type);
862       g_file_info_set_symbolic_icon (info, icon);
863       g_object_unref (icon);
864       g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, TRUE);
865       g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, gphoto2_backend->can_write);
866       g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, gphoto2_backend->can_delete);
867       g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, TRUE);
868       g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
869       g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, FALSE);
870       ret = TRUE;
871       g_debug ("  Generating info (root folder) for '%s'\n", full_path);
872       goto add_to_cache;
873     }
874 
875   rc = gp_camera_file_get_info (gphoto2_backend->camera,
876                                 dir,
877                                 name,
878                                 &gp_info,
879                                 gphoto2_backend->context);
880   if (rc != 0)
881     {
882       CameraList *list;
883       gboolean is_folder;
884 
885       /* gphoto2 doesn't know about this file.. it may be a folder; try that */
886       is_folder = FALSE;
887       gp_list_new (&list);
888       rc = gp_camera_folder_list_folders (gphoto2_backend->camera,
889                                         dir,
890                                         list,
891                                         gphoto2_backend->context);
892       if (rc == 0)
893         {
894           for (n = 0; n < gp_list_count (list) && !is_folder; n++)
895             {
896               const char *folder_name;
897               gp_list_get_name (list, n, &folder_name);
898               if (strcmp (folder_name, name) == 0)
899                 {
900                   is_folder = TRUE;
901                 }
902             }
903         }
904       gp_list_free (list);
905 
906       if (is_folder)
907         {
908           const char *directory_mime_type = "inode/directory";
909 
910           g_file_info_set_name (info, name);
911           g_file_info_set_display_name (info, name);
912           icon = g_content_type_get_icon (directory_mime_type);
913           g_file_info_set_icon (info, icon);
914           g_object_unref (icon);
915           icon = g_content_type_get_symbolic_icon (directory_mime_type);
916           g_file_info_set_symbolic_icon (info, icon);
917           g_object_unref (icon);
918           g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
919           g_file_info_set_content_type (info, directory_mime_type);
920           g_file_info_set_size (info, 0);
921           g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, TRUE);
922           g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, gphoto2_backend->can_write);
923           g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, gphoto2_backend->can_delete);
924           g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, TRUE);
925           g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
926           g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, gphoto2_backend->can_write);
927           g_file_info_set_is_hidden (info, name != NULL && name[0] == '.');
928           ret = TRUE;
929           g_debug ("  Generating info (folder) for '%s'\n", full_path);
930           goto add_to_cache;
931         }
932 
933       /* nope, not a folder either.. error out.. */
934       if (error != NULL)
935         {
936           *error = g_error_new (G_IO_ERROR,
937                                 G_IO_ERROR_NOT_FOUND,
938                                 _("No such file or directory"));
939         }
940       goto out;
941     }
942 
943   g_file_info_set_name (info, name);
944   g_file_info_set_display_name (info, name);
945   g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR);
946 
947   if (gp_info.file.fields & GP_FILE_INFO_SIZE)
948     {
949       g_file_info_set_size (info, gp_info.file.size);
950     }
951   else
952     {
953       /* not really sure this is the right thing to do... */
954       g_file_info_set_size (info, 0);
955     }
956 
957   /* TODO: We really should sniff the file / look at file extensions
958    * instead of relying on gp_info.file.type... but sniffing the file
959    * is no fun since we (currently) can't do partial reads with the
960    * libgphoto2 API :-/
961    */
962   mime_type = NULL;
963   uncertain_content_type = FALSE;
964   if (gp_info.file.fields & GP_FILE_INFO_TYPE)
965     {
966       /* application/x-unknown is a bogus mime type return by some
967        * devices (such as Sandisk Sansa music players) - ignore it.
968        */
969       if (strcmp (gp_info.file.type, "application/x-unknown") != 0)
970         {
971           mime_type = g_strdup (gp_info.file.type);
972         }
973     }
974   if (mime_type == NULL)
975     mime_type = g_content_type_guess (name, NULL, 0, &uncertain_content_type);
976   if (mime_type == NULL)
977     mime_type = g_strdup ("application/octet-stream");
978   if (!uncertain_content_type)
979     g_file_info_set_content_type (info, mime_type);
980   g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE, mime_type);
981 
982   /* we offer thumbnails for both pics and video (see bgo #585853) */
983   if (g_str_has_prefix (mime_type, "image") || g_str_has_prefix (mime_type, "video"))
984     {
985       char *icon_id;
986       GIcon *icon;
987       GMountSpec *mount_spec;
988 
989       mount_spec = g_vfs_backend_get_mount_spec (G_VFS_BACKEND (gphoto2_backend));
990       icon_id = g_strdup_printf ("preview:%s/%s", dir + strlen (gphoto2_backend->ignore_prefix), name);
991       icon = g_vfs_icon_new (mount_spec,
992                              icon_id);
993       g_file_info_set_attribute_object (info,
994                                         G_FILE_ATTRIBUTE_PREVIEW_ICON,
995                                         G_OBJECT (icon));
996       g_object_unref (icon);
997       g_free (icon_id);
998     }
999 
1000   icon = g_content_type_get_icon (mime_type);
1001   g_debug ("  got icon %p for mime_type '%s'\n", icon, mime_type);
1002   if (icon != NULL)
1003     {
1004       g_file_info_set_icon (info, icon);
1005       g_object_unref (icon);
1006     }
1007   icon = g_content_type_get_symbolic_icon (mime_type);
1008   g_debug ("  got symbolic icon %p for mime_type '%s'\n", icon, mime_type);
1009   if (icon != NULL)
1010     {
1011       g_file_info_set_symbolic_icon (info, icon);
1012       g_object_unref (icon);
1013     }
1014   g_free (mime_type);
1015 
1016   if (gp_info.file.fields & GP_FILE_INFO_MTIME) {
1017     g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED, gp_info.file.mtime);
1018     g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, 0);
1019   }
1020 
1021   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, TRUE);
1022   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, gphoto2_backend->can_write);
1023   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, gphoto2_backend->can_delete);
1024   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, FALSE);
1025   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
1026   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, gphoto2_backend->can_write);
1027   g_file_info_set_is_hidden (info, name != NULL && name[0] == '.');
1028 
1029   if (gp_info.file.fields & GP_FILE_INFO_PERMISSIONS) {
1030     g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
1031                                        gp_info.file.permissions & GP_FILE_PERM_DELETE);
1032     g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE,
1033                                        gp_info.file.permissions & GP_FILE_PERM_DELETE);
1034     g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME,
1035                                        gp_info.file.permissions & GP_FILE_PERM_DELETE);
1036   }
1037 
1038   ret = TRUE;
1039   g_debug ("  Generating info (file) for '%s'\n", full_path);
1040 
1041  add_to_cache:
1042   /* add this sucker to the cache */
1043   if (ret == TRUE)
1044     {
1045 #ifndef DEBUG_NO_CACHING
1046       cached_info = g_file_info_dup (info);
1047       g_debug ("  Storing cached info %p for '%s'\n", cached_info, full_path);
1048       g_mutex_lock (&gphoto2_backend->lock);
1049       g_hash_table_insert (gphoto2_backend->info_cache, g_strdup (full_path), cached_info);
1050       g_mutex_unlock (&gphoto2_backend->lock);
1051 #endif
1052     }
1053 
1054  out:
1055   g_free (full_path);
1056   return ret;
1057 }
1058 
1059 /* ------------------------------------------------------------------------------------------------- */
1060 
1061 static gboolean
is_directory(GVfsBackendGphoto2 * gphoto2_backend,const char * dir,const char * name)1062 is_directory (GVfsBackendGphoto2 *gphoto2_backend, const char *dir, const char *name)
1063 {
1064   GFileInfo *info;
1065   gboolean ret;
1066 
1067   ret = FALSE;
1068 
1069   info = g_file_info_new ();
1070   if (!file_get_info (gphoto2_backend, dir, name, info, NULL, FALSE))
1071     goto out;
1072 
1073   if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
1074     ret = TRUE;
1075 
1076  out:
1077   g_object_unref (info);
1078   return ret;
1079 }
1080 
1081 /* ------------------------------------------------------------------------------------------------- */
1082 
1083 static gboolean
is_regular(GVfsBackendGphoto2 * gphoto2_backend,const char * dir,const char * name)1084 is_regular (GVfsBackendGphoto2 *gphoto2_backend, const char *dir, const char *name)
1085 {
1086   GFileInfo *info;
1087   gboolean ret;
1088 
1089   ret = FALSE;
1090 
1091   info = g_file_info_new ();
1092   if (!file_get_info (gphoto2_backend, dir, name, info, NULL, FALSE))
1093     goto out;
1094 
1095   if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR)
1096     ret = TRUE;
1097 
1098  out:
1099   g_object_unref (info);
1100   return ret;
1101 }
1102 
1103 /* ------------------------------------------------------------------------------------------------- */
1104 
1105 static gboolean
is_directory_empty(GVfsBackendGphoto2 * gphoto2_backend,const char * dir)1106 is_directory_empty (GVfsBackendGphoto2 *gphoto2_backend, const char *dir)
1107 {
1108   CameraList *list;
1109   gboolean ret;
1110   int rc;
1111   int num_dirs;
1112   int num_files;
1113 
1114   g_debug ("is_directory_empty begin (%s)\n", dir);
1115 
1116   /* TODO: use cache */
1117 
1118   ret = FALSE;
1119   num_dirs = 0;
1120   num_files = 0;
1121 
1122   gp_list_new (&list);
1123   rc = gp_camera_folder_list_files (gphoto2_backend->camera,
1124                                     dir,
1125                                     list,
1126                                     gphoto2_backend->context);
1127   if (rc == 0)
1128     num_files = gp_list_count (list);
1129   gp_list_free (list);
1130 
1131   if (num_files > 0)
1132     goto out;
1133 
1134   gp_list_new (&list);
1135   rc = gp_camera_folder_list_folders (gphoto2_backend->camera,
1136                                       dir,
1137                                       list,
1138                                       gphoto2_backend->context);
1139   if (rc == 0)
1140     num_dirs = gp_list_count (list);
1141   gp_list_free (list);
1142 
1143   if (num_dirs == 0 && num_files == 0)
1144     ret = TRUE;
1145 
1146  out:
1147   g_debug ("  is_directory_empty (%s) -> %d\n", dir, ret);
1148   return ret;
1149 }
1150 
1151 /* ------------------------------------------------------------------------------------------------- */
1152 
1153 /* If we only have a single storage head, the gphoto2 volume monitor
1154  * will not use activation roots into our mount. This is mainly to
1155  * work around buggy devices where the basedir of the storage head
1156  * changes on every camera initialization, e.g. the iPhone.
1157  *
1158  * So, if we have only one storage head, do use basedir of that
1159  * head as ignore_prefix.
1160  *
1161  * See also update_cameras() in ggphoto2volumemonitor.c.
1162  *
1163  * This function needs to be called from do_mount().
1164  */
1165 static gboolean
ensure_ignore_prefix(GVfsBackendGphoto2 * gphoto2_backend,GVfsJob * job)1166 ensure_ignore_prefix (GVfsBackendGphoto2 *gphoto2_backend, GVfsJob *job)
1167 {
1168   gchar *prefix;
1169   CameraStorageInformation *storage_info, *head;
1170   int num_storage_info, i;
1171 
1172   /* already set */
1173   if (gphoto2_backend->ignore_prefix != NULL)
1174     return TRUE;
1175 
1176   prefix = NULL;
1177 
1178   if (gp_camera_get_storageinfo (gphoto2_backend->camera,
1179                                  &storage_info,
1180                                  &num_storage_info,
1181                                  gphoto2_backend->context) != 0)
1182     goto out;
1183 
1184   head = NULL;
1185   for (i = 0; i < num_storage_info; i++)
1186     {
1187       /* Ignore storage with no capacity (see bug 570888) */
1188       if ((storage_info[i].fields & GP_STORAGEINFO_MAXCAPACITY) &&
1189           storage_info[i].capacitykbytes == 0)
1190         continue;
1191 
1192       /* Multiple heads, don't ignore */
1193       if (head != NULL)
1194 	goto out;
1195 
1196       head = &storage_info[i];
1197     }
1198 
1199   /* Some cameras, such as the Canon 5D, won't report the basedir */
1200   if (head && head->fields & GP_STORAGEINFO_BASE)
1201     prefix = g_strdup_printf ("%s/", head->basedir);
1202 
1203  out:
1204 
1205   if (prefix == NULL)
1206     gphoto2_backend->ignore_prefix = g_strdup ("/");
1207   else
1208     gphoto2_backend->ignore_prefix = prefix;
1209 
1210   g_debug ("Using ignore_prefix='%s'\n", gphoto2_backend->ignore_prefix);
1211 
1212   return TRUE;
1213 }
1214 
1215 /* ------------------------------------------------------------------------------------------------- */
1216 
1217 static char *
get_port_from_host(GVfsJob * job,GUdevClient * gudev_client,const char * host)1218 get_port_from_host (GVfsJob *job,
1219                     GUdevClient *gudev_client,
1220                     const char *host)
1221 {
1222   guint32 bus_num = 0, dev_num = 0;
1223   GList *devices, *l;
1224 
1225   /* find corresponding GUdevDevice */
1226   devices = g_udev_client_query_by_subsystem (gudev_client, "usb");
1227   for (l = devices; l != NULL; l = l->next)
1228     {
1229       const char *id = g_udev_device_get_property (l->data, "ID_SERIAL");
1230       if (g_strcmp0 (id, host) == 0)
1231         {
1232           bus_num = g_ascii_strtoull (g_udev_device_get_property (l->data, "BUSNUM"),
1233                                       NULL, 10);
1234           dev_num = g_ascii_strtoull (g_udev_device_get_property (l->data, "DEVNUM"),
1235                                       NULL, 10);
1236           break;
1237         }
1238     }
1239   g_list_free_full (devices, g_object_unref);
1240 
1241   if (bus_num && dev_num)
1242     {
1243       return g_strdup_printf ("usb:%03u,%03u", bus_num, dev_num);
1244     }
1245 
1246   /* For compatibility, handle old style host specifications. */
1247   if (g_str_has_prefix (host, "[usb:") && host[strlen (host) - 1] == ']')
1248     {
1249       char *port = g_strdup (host + 1);
1250       port[strlen (port) -1] = '\0';
1251       return port;
1252     }
1253 
1254   g_vfs_job_failed_literal (G_VFS_JOB (job),
1255                             G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1256                             _("Couldn’t find matching udev device."));
1257   return NULL;
1258 }
1259 
1260 static void
do_mount(GVfsBackend * backend,GVfsJobMount * job,GMountSpec * mount_spec,GMountSource * mount_source,gboolean is_automount)1261 do_mount (GVfsBackend *backend,
1262 	  GVfsJobMount *job,
1263 	  GMountSpec *mount_spec,
1264 	  GMountSource *mount_source,
1265 	  gboolean is_automount)
1266 {
1267   char *fuse_name;
1268   char *display_name;
1269   char *icon_name;
1270   const char *host;
1271   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
1272   GError *error = NULL;
1273   GMountSpec *gphoto2_mount_spec;
1274   int rc;
1275   GPPortInfo info;
1276   GPPortInfoList *il = NULL;
1277   int n;
1278   CameraStorageInformation *storage_info;
1279   int num_storage_info;
1280 
1281   g_debug ("do_mount %p\n", gphoto2_backend);
1282 
1283   /* setup gudev */
1284   const char *subsystems[] = {"usb", NULL};
1285 
1286   gphoto2_backend->gudev_client = g_udev_client_new (subsystems);
1287   if (gphoto2_backend->gudev_client == NULL)
1288     {
1289       g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot create gudev client"));
1290       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1291       g_error_free (error);
1292       return;
1293     }
1294 
1295   g_signal_connect (gphoto2_backend->gudev_client, "uevent", G_CALLBACK (on_uevent), gphoto2_backend);
1296 
1297   /* setup gphoto2 */
1298 
1299   host = g_mount_spec_get (mount_spec, "host");
1300   g_debug ("  host='%s'\n", host);
1301 
1302   char *port = get_port_from_host (G_VFS_JOB (job),
1303                                    gphoto2_backend->gudev_client,
1304                                    host);
1305   if (port == NULL)
1306     {
1307       /* Job already set to failed */
1308       return;
1309     }
1310   gphoto2_backend->gphoto2_port = port;
1311 
1312   g_debug ("  decoded host='%s'\n", gphoto2_backend->gphoto2_port);
1313 
1314   setup_for_device (gphoto2_backend);
1315 
1316   gphoto2_backend->context = gp_context_new ();
1317   if (gphoto2_backend->context == NULL)
1318     {
1319       g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot create gphoto2 context"));
1320       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1321       g_error_free (error);
1322       return;
1323     }
1324 
1325   rc = gp_camera_new (&(gphoto2_backend->camera));
1326   if (rc != 0)
1327     {
1328       error = get_error_from_gphoto2 (_("Error creating camera"), rc);
1329       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1330       g_error_free (error);
1331       return;
1332     }
1333 
1334 
1335   il = NULL;
1336 
1337   rc = gp_port_info_list_new (&il);
1338   if (rc != 0)
1339     {
1340       error = get_error_from_gphoto2 (_("Error loading device information"), rc);
1341       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1342       g_error_free (error);
1343       return;
1344     }
1345 
1346   rc = gp_port_info_list_load (il);
1347   if (rc != 0)
1348     {
1349       error = get_error_from_gphoto2 (_("Error loading device information"), rc);
1350       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1351       g_error_free (error);
1352       return;
1353     }
1354 
1355   g_debug ("  gphoto2_port='%s'\n", gphoto2_backend->gphoto2_port);
1356 
1357   n = gp_port_info_list_lookup_path (il, gphoto2_backend->gphoto2_port);
1358   if (n == GP_ERROR_UNKNOWN_PORT)
1359     {
1360       error = get_error_from_gphoto2 (_("Error looking up device information"), rc);
1361       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1362       g_error_free (error);
1363       return;
1364     }
1365 
1366   rc = gp_port_info_list_get_info (il, n, &info);
1367   if (rc != 0)
1368     {
1369       error = get_error_from_gphoto2 (_("Error getting device information"), rc);
1370       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1371       g_error_free (error);
1372       return;
1373     }
1374 
1375 #ifndef HAVE_GPHOTO25
1376   g_debug ("  '%s' '%s' '%s'\n",  info.name, info.path, info.library_filename);
1377 #endif
1378 
1379   /* set port */
1380   rc = gp_camera_set_port_info (gphoto2_backend->camera, info);
1381   if (rc != 0)
1382     {
1383       error = get_error_from_gphoto2 (_("Error setting up camera communications port"), rc);
1384       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1385       g_error_free (error);
1386       return;
1387     }
1388   gp_port_info_list_free(il);
1389 
1390   rc = gp_camera_init (gphoto2_backend->camera, gphoto2_backend->context);
1391   if (rc != 0)
1392     {
1393       error = get_error_from_gphoto2 (_("Error initializing camera"), rc);
1394       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1395       g_error_free (error);
1396       return;
1397     }
1398 
1399   if (!ensure_ignore_prefix (gphoto2_backend, G_VFS_JOB (job)))
1400     return;
1401 
1402   /* Translator: %s represents the device, e.g. usb:001,042. 'gphoto2' is the name of the
1403      backend and shouldn't be translated. */
1404   fuse_name = g_strdup_printf (_("gphoto2 mount on %s"), gphoto2_backend->gphoto2_port);
1405   display_name = compute_display_name (gphoto2_backend);
1406   g_vfs_backend_set_stable_name (backend, fuse_name);
1407   g_vfs_backend_set_display_name (backend, display_name);
1408 
1409   icon_name = compute_icon_name (gphoto2_backend);
1410   g_vfs_backend_set_icon_name (backend, icon_name);
1411   g_free (icon_name);
1412 
1413   icon_name = compute_symbolic_icon_name (gphoto2_backend);
1414   g_vfs_backend_set_symbolic_icon_name (backend, icon_name);
1415   g_free (icon_name);
1416 
1417   g_free (display_name);
1418   g_free (fuse_name);
1419 
1420   gphoto2_backend->can_write = FALSE;
1421   gphoto2_backend->can_delete = FALSE;
1422   rc = gp_camera_get_storageinfo (gphoto2_backend->camera, &storage_info, &num_storage_info, gphoto2_backend->context);
1423   if (rc == 0)
1424     {
1425       if (num_storage_info >= 1)
1426         {
1427           if (storage_info[0].fields & GP_STORAGEINFO_ACCESS && storage_info[0].access == GP_STORAGEINFO_AC_READWRITE)
1428             {
1429               gphoto2_backend->can_write = TRUE;
1430               gphoto2_backend->can_delete = TRUE;
1431             }
1432           if (storage_info[0].fields & GP_STORAGEINFO_ACCESS && storage_info[0].access == GP_STORAGEINFO_AC_READONLY_WITH_DELETE)
1433             {
1434               gphoto2_backend->can_delete = TRUE;
1435             }
1436         }
1437     }
1438   g_debug ("  can_write = %d\n", gphoto2_backend->can_write);
1439   g_debug ("  can_delete = %d\n", gphoto2_backend->can_delete);
1440 
1441   g_vfs_job_succeeded (G_VFS_JOB (job));
1442 
1443   gphoto2_backend->free_space = -1;
1444 
1445   gphoto2_mount_spec = g_mount_spec_new ("gphoto2");
1446   g_mount_spec_set (gphoto2_mount_spec, "host", host);
1447   g_vfs_backend_set_mount_spec (backend, gphoto2_mount_spec);
1448   g_mount_spec_unref (gphoto2_mount_spec);
1449 
1450   gphoto2_backend->info_cache = g_hash_table_new_full (g_str_hash,
1451                                                        g_str_equal,
1452                                                        g_free,
1453                                                        g_object_unref);
1454 
1455   gphoto2_backend->dir_name_cache = g_hash_table_new_full (g_str_hash,
1456                                                            g_str_equal,
1457                                                            g_free,
1458                                                            (GDestroyNotify) gp_list_unref);
1459 
1460   gphoto2_backend->file_name_cache = g_hash_table_new_full (g_str_hash,
1461                                                             g_str_equal,
1462                                                             g_free,
1463                                                             (GDestroyNotify) gp_list_unref);
1464 
1465   g_debug ("  mounted %p\n", gphoto2_backend);
1466 }
1467 
1468 static void
do_unmount(GVfsBackend * backend,GVfsJobUnmount * job,GMountUnmountFlags flags,GMountSource * mount_source)1469 do_unmount (GVfsBackend *backend, GVfsJobUnmount *job,
1470             GMountUnmountFlags flags,
1471             GMountSource *mount_source)
1472 {
1473   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
1474 
1475   g_signal_handlers_disconnect_by_func (gphoto2_backend->gudev_client, on_uevent, gphoto2_backend);
1476 
1477   g_vfs_job_succeeded (G_VFS_JOB (job));
1478 }
1479 
1480 /* ------------------------------------------------------------------------------------------------- */
1481 
1482 static gboolean
try_mount(GVfsBackend * backend,GVfsJobMount * job,GMountSpec * mount_spec,GMountSource * mount_source,gboolean is_automount)1483 try_mount (GVfsBackend *backend,
1484            GVfsJobMount *job,
1485            GMountSpec *mount_spec,
1486            GMountSource *mount_source,
1487            gboolean is_automount)
1488 {
1489   const char *host;
1490   GError *error = NULL;
1491   GMountSpec *gphoto2_mount_spec;
1492 
1493   g_debug ("try_mount %p\n", backend);
1494 
1495   /* TODO: Hmm.. apparently we have to set the mount spec in
1496    * try_mount(); doing it in mount() do_won't work..
1497    */
1498   host = g_mount_spec_get (mount_spec, "host");
1499   g_debug ("  host=%s\n", host);
1500   if (host == NULL)
1501     {
1502       g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("No camera specified"));
1503       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1504       g_error_free (error);
1505       return TRUE;
1506     }
1507 
1508   gphoto2_mount_spec = g_mount_spec_new ("gphoto2");
1509   g_mount_spec_set (gphoto2_mount_spec, "host", host);
1510   g_vfs_backend_set_mount_spec (backend, gphoto2_mount_spec);
1511   g_mount_spec_unref (gphoto2_mount_spec);
1512   return FALSE;
1513 }
1514 
1515 /* ------------------------------------------------------------------------------------------------- */
1516 
1517 static void
free_read_handle(ReadHandle * read_handle)1518 free_read_handle (ReadHandle *read_handle)
1519 {
1520   if (read_handle->file != NULL)
1521     {
1522       gp_file_unref (read_handle->file);
1523     }
1524   g_free (read_handle);
1525 }
1526 
1527 static void
do_open_for_read_real(GVfsBackend * backend,GVfsJobOpenForRead * job,const char * filename,gboolean get_preview)1528 do_open_for_read_real (GVfsBackend *backend,
1529                        GVfsJobOpenForRead *job,
1530                        const char *filename,
1531                        gboolean get_preview)
1532 {
1533   int rc;
1534   GError *error;
1535   ReadHandle *read_handle;
1536   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
1537   char *dir;
1538   char *name;
1539 
1540   ensure_not_dirty (gphoto2_backend);
1541 
1542   split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
1543 
1544   if (is_directory (gphoto2_backend, dir, name))
1545     {
1546       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
1547                         G_IO_ERROR_IS_DIRECTORY,
1548                         _("Can’t open directory"));
1549       goto out;
1550     }
1551 
1552   if (!is_regular (gphoto2_backend, dir, name))
1553     {
1554       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
1555                         G_IO_ERROR_NOT_FOUND,
1556                         _("No such file"));
1557       goto out;
1558     }
1559 
1560   read_handle = g_new0 (ReadHandle, 1);
1561   rc = gp_file_new (&read_handle->file);
1562   if (rc != 0)
1563     {
1564       error = get_error_from_gphoto2 (_("Error creating file object"), rc);
1565       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1566       g_error_free (error);
1567       free_read_handle (read_handle);
1568       goto out;
1569     }
1570 
1571   rc = gp_camera_file_get (gphoto2_backend->camera,
1572                            dir,
1573                            name,
1574                            get_preview ? GP_FILE_TYPE_PREVIEW : GP_FILE_TYPE_NORMAL,
1575                            read_handle->file,
1576                            gphoto2_backend->context);
1577   if (rc != 0)
1578     {
1579       error = get_error_from_gphoto2 (_("Error getting file"), rc);
1580       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1581       g_error_free (error);
1582       free_read_handle (read_handle);
1583       goto out;
1584     }
1585 
1586   rc = gp_file_get_data_and_size (read_handle->file, &read_handle->data, &read_handle->size);
1587   if (rc != 0)
1588     {
1589       error = get_error_from_gphoto2 (_("Error getting data from file"), rc);
1590       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1591       g_error_free (error);
1592       free_read_handle (read_handle);
1593       goto out;
1594     }
1595 
1596   g_debug ("  data=%p size=%ld handle=%p get_preview=%d\n",
1597          read_handle->data, read_handle->size, read_handle, get_preview);
1598 
1599   g_mutex_lock (&gphoto2_backend->lock);
1600   gphoto2_backend->open_read_handles = g_list_prepend (gphoto2_backend->open_read_handles, read_handle);
1601   g_mutex_unlock (&gphoto2_backend->lock);
1602 
1603   read_handle->cursor = 0;
1604 
1605   g_vfs_job_open_for_read_set_can_seek (job, TRUE);
1606   g_vfs_job_open_for_read_set_handle (job, GINT_TO_POINTER (read_handle));
1607   g_vfs_job_succeeded (G_VFS_JOB (job));
1608 
1609  out:
1610   g_free (name);
1611   g_free (dir);
1612 }
1613 
1614 
1615 static void
do_open_for_read(GVfsBackend * backend,GVfsJobOpenForRead * job,const char * filename)1616 do_open_for_read (GVfsBackend *backend,
1617                   GVfsJobOpenForRead *job,
1618                   const char *filename)
1619 {
1620   g_debug ("open_for_read (%s)\n", filename);
1621 
1622   do_open_for_read_real (backend,
1623                          job,
1624                          filename,
1625                          FALSE);
1626 }
1627 
1628 static void
do_open_icon_for_read(GVfsBackend * backend,GVfsJobOpenIconForRead * job,const char * icon_id)1629 do_open_icon_for_read (GVfsBackend *backend,
1630                        GVfsJobOpenIconForRead *job,
1631                        const char *icon_id)
1632 {
1633   g_debug ("open_icon_for_read (%s)\n", icon_id);
1634 
1635   if (g_str_has_prefix (icon_id, "preview:"))
1636     {
1637       do_open_for_read_real (backend,
1638                              G_VFS_JOB_OPEN_FOR_READ (job),
1639                              icon_id + sizeof ("preview:") - 1,
1640                              TRUE);
1641     }
1642   else
1643     {
1644       g_vfs_job_failed (G_VFS_JOB (job),
1645                         G_IO_ERROR,
1646                         G_IO_ERROR_INVALID_ARGUMENT,
1647                         _("Malformed icon identifier “%s”"),
1648                         icon_id);
1649     }
1650 }
1651 
1652 /* ------------------------------------------------------------------------------------------------- */
1653 
1654 static gboolean
try_read(GVfsBackend * backend,GVfsJobRead * job,GVfsBackendHandle handle,char * buffer,gsize bytes_requested)1655 try_read (GVfsBackend *backend,
1656           GVfsJobRead *job,
1657           GVfsBackendHandle handle,
1658           char *buffer,
1659           gsize bytes_requested)
1660 {
1661   //GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
1662   ReadHandle *read_handle = (ReadHandle *) handle;
1663   gsize bytes_left;
1664   gsize bytes_to_copy;
1665 
1666   g_debug ("do_read() %" G_GSIZE_FORMAT " @ %ld of %" G_GSIZE_FORMAT ", handle=%p\n", bytes_requested, read_handle->cursor, read_handle->size, handle);
1667 
1668   if (read_handle->cursor >= read_handle->size)
1669     {
1670       bytes_to_copy = 0;
1671       goto out;
1672     }
1673 
1674   bytes_left = read_handle->size - read_handle->cursor;
1675   if (bytes_requested > bytes_left)
1676     bytes_to_copy = bytes_left;
1677   else
1678     bytes_to_copy = bytes_requested;
1679 
1680   memcpy (buffer, read_handle->data + read_handle->cursor, bytes_to_copy);
1681   read_handle->cursor += bytes_to_copy;
1682 
1683  out:
1684 
1685   g_vfs_job_read_set_size (job, bytes_to_copy);
1686   g_vfs_job_succeeded (G_VFS_JOB (job));
1687   return TRUE;
1688 }
1689 
1690 /* ------------------------------------------------------------------------------------------------- */
1691 
1692 static gboolean
try_seek_on_read(GVfsBackend * backend,GVfsJobSeekRead * job,GVfsBackendHandle handle,goffset offset,GSeekType type)1693 try_seek_on_read (GVfsBackend *backend,
1694                   GVfsJobSeekRead *job,
1695                   GVfsBackendHandle handle,
1696                   goffset    offset,
1697                   GSeekType  type)
1698 {
1699   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
1700   ReadHandle *read_handle = (ReadHandle *) handle;
1701   long new_offset;
1702 
1703   g_debug ("seek_on_read() offset=%d, type=%d, handle=%p\n", (int)offset, type, handle);
1704 
1705   switch (type)
1706     {
1707     default:
1708     case G_SEEK_SET:
1709       new_offset = offset;
1710       break;
1711     case G_SEEK_CUR:
1712       new_offset = read_handle->cursor + offset;
1713       break;
1714     case G_SEEK_END:
1715       new_offset = read_handle->size + offset;
1716       break;
1717     }
1718 
1719   if (new_offset < 0)
1720     {
1721       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
1722                         G_IO_ERROR_FAILED,
1723                         _("Error seeking in stream on camera %s"), gphoto2_backend->gphoto2_port);
1724     }
1725   else
1726     {
1727       read_handle->cursor = new_offset;
1728       g_vfs_job_seek_read_set_offset (job, new_offset);
1729       g_vfs_job_succeeded (G_VFS_JOB (job));
1730     }
1731   return TRUE;
1732 }
1733 
1734 /* ------------------------------------------------------------------------------------------------- */
1735 
1736 /* cannot be async because we unref the CameraFile */
1737 static void
do_close_read(GVfsBackend * backend,GVfsJobCloseRead * job,GVfsBackendHandle handle)1738 do_close_read (GVfsBackend *backend,
1739                 GVfsJobCloseRead *job,
1740                 GVfsBackendHandle handle)
1741 {
1742   ReadHandle *read_handle = (ReadHandle *) handle;
1743   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
1744 
1745   g_debug ("close_read() handle=%p\n", handle);
1746 
1747   g_mutex_lock (&gphoto2_backend->lock);
1748   gphoto2_backend->open_read_handles = g_list_remove (gphoto2_backend->open_read_handles, read_handle);
1749   g_mutex_unlock (&gphoto2_backend->lock);
1750 
1751   free_read_handle (read_handle);
1752 
1753   g_vfs_job_succeeded (G_VFS_JOB (job));
1754 }
1755 
1756 /* ------------------------------------------------------------------------------------------------- */
1757 
1758 static void
do_query_info(GVfsBackend * backend,GVfsJobQueryInfo * job,const char * filename,GFileQueryInfoFlags flags,GFileInfo * info,GFileAttributeMatcher * matcher)1759 do_query_info (GVfsBackend *backend,
1760 	       GVfsJobQueryInfo *job,
1761 	       const char *filename,
1762 	       GFileQueryInfoFlags flags,
1763 	       GFileInfo *info,
1764 	       GFileAttributeMatcher *matcher)
1765 {
1766   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
1767   GError *error;
1768   char *dir;
1769   char *name;
1770 
1771   g_debug ("query_info (%s)\n", filename);
1772 
1773   split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
1774 
1775   error = NULL;
1776   if (!file_get_info (gphoto2_backend, dir, name, info, &error, FALSE))
1777     {
1778       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1779       g_error_free (error);
1780     }
1781   else
1782     {
1783       g_vfs_job_succeeded (G_VFS_JOB (job));
1784     }
1785 
1786   g_free (name);
1787   g_free (dir);
1788 }
1789 
1790 /* ------------------------------------------------------------------------------------------------- */
1791 
1792 static gboolean
try_query_info(GVfsBackend * backend,GVfsJobQueryInfo * job,const char * filename,GFileQueryInfoFlags flags,GFileInfo * info,GFileAttributeMatcher * matcher)1793 try_query_info (GVfsBackend *backend,
1794                 GVfsJobQueryInfo *job,
1795                 const char *filename,
1796                 GFileQueryInfoFlags flags,
1797                 GFileInfo *info,
1798                 GFileAttributeMatcher *matcher)
1799 {
1800   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
1801   char *dir;
1802   char *name;
1803   gboolean ret;
1804 
1805   g_debug ("try_query_info (%s)\n", filename);
1806 
1807   ret = FALSE;
1808 
1809   split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
1810 
1811   if (!file_get_info (gphoto2_backend, dir, name, info, NULL, TRUE))
1812     {
1813       g_debug ("  BUU no info from cache for try_query_info (%s)\n", filename);
1814       goto out;
1815     }
1816   g_debug ("  YAY got info from cache for try_query_info (%s)\n", filename);
1817 
1818   g_vfs_job_succeeded (G_VFS_JOB (job));
1819   ret = TRUE;
1820 
1821  out:
1822   g_free (name);
1823   g_free (dir);
1824   return ret;
1825 }
1826 
1827 /* ------------------------------------------------------------------------------------------------- */
1828 
1829 static void
do_enumerate(GVfsBackend * backend,GVfsJobEnumerate * job,const char * given_filename,GFileAttributeMatcher * matcher,GFileQueryInfoFlags flags)1830 do_enumerate (GVfsBackend *backend,
1831               GVfsJobEnumerate *job,
1832               const char *given_filename,
1833               GFileAttributeMatcher *matcher,
1834               GFileQueryInfoFlags flags)
1835 {
1836   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
1837   GFileInfo *info;
1838   GList *l;
1839   GError *error;
1840   CameraList *list;
1841   int n;
1842   int rc;
1843   char *filename;
1844   gboolean using_cached_dir_list;
1845   gboolean using_cached_file_list;
1846   char *as_dir;
1847   char *as_name;
1848 
1849   l = NULL;
1850   using_cached_dir_list = FALSE;
1851   using_cached_file_list = FALSE;
1852 
1853   filename = add_ignore_prefix (gphoto2_backend, given_filename);
1854   g_debug ("enumerate ('%s', with_prefix='%s')\n", given_filename, filename);
1855 
1856   split_filename_with_ignore_prefix (gphoto2_backend, given_filename, &as_dir, &as_name);
1857   if (!is_directory (gphoto2_backend, as_dir, as_name))
1858     {
1859       if (is_regular (gphoto2_backend, as_dir, as_name))
1860         {
1861           g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
1862                             G_IO_ERROR_NOT_DIRECTORY,
1863                             _("Not a directory"));
1864         }
1865       else
1866         {
1867           g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
1868                             G_IO_ERROR_NOT_FOUND,
1869                             _("No such file or directory"));
1870         }
1871       g_free (as_dir);
1872       g_free (as_name);
1873       return;
1874     }
1875   g_free (as_dir);
1876   g_free (as_name);
1877 
1878   /* first, list the folders */
1879   g_mutex_lock (&gphoto2_backend->lock);
1880   list = g_hash_table_lookup (gphoto2_backend->dir_name_cache, filename);
1881   if (list == NULL)
1882     {
1883       g_mutex_unlock (&gphoto2_backend->lock);
1884 
1885       ensure_not_dirty (gphoto2_backend);
1886 
1887       g_debug ("  Generating dir list for dir '%s'\n", filename);
1888 
1889       gp_list_new (&list);
1890       rc = gp_camera_folder_list_folders (gphoto2_backend->camera,
1891                                           filename,
1892                                           list,
1893                                           gphoto2_backend->context);
1894       if (rc != 0)
1895         {
1896           error = get_error_from_gphoto2 (_("Failed to get folder list"), rc);
1897           g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1898           g_error_free (error);
1899           g_free (filename);
1900           return;
1901         }
1902     }
1903   else
1904     {
1905       g_debug ("  Using cached dir list for dir '%s'\n", filename);
1906       using_cached_dir_list = TRUE;
1907       gp_list_ref (list);
1908       g_mutex_unlock (&gphoto2_backend->lock);
1909     }
1910   for (n = 0; n < gp_list_count (list); n++)
1911     {
1912       const char *name;
1913 
1914       gp_list_get_name (list, n, &name);
1915       g_debug ("  enum folder '%s'\n", name);
1916       info = g_file_info_new ();
1917       error = NULL;
1918       if (!file_get_info (gphoto2_backend, filename, name, info, &error, FALSE))
1919         {
1920           g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1921           g_error_free (error);
1922           g_list_free_full (l, g_object_unref);
1923           gp_list_free (list);
1924           return;
1925         }
1926       l = g_list_append (l, info);
1927     }
1928   if (!using_cached_dir_list)
1929     {
1930 #ifndef DEBUG_NO_CACHING
1931       g_mutex_lock (&gphoto2_backend->lock);
1932       g_hash_table_insert (gphoto2_backend->dir_name_cache, g_strdup (filename), list);
1933       g_mutex_unlock (&gphoto2_backend->lock);
1934 #endif
1935     }
1936   else
1937     {
1938       g_mutex_lock (&gphoto2_backend->lock);
1939       gp_list_unref (list);
1940       g_mutex_unlock (&gphoto2_backend->lock);
1941     }
1942 
1943 
1944   /* then list the files in each folder */
1945   g_mutex_lock (&gphoto2_backend->lock);
1946   list = g_hash_table_lookup (gphoto2_backend->file_name_cache, filename);
1947   if (list == NULL)
1948     {
1949       g_mutex_unlock (&gphoto2_backend->lock);
1950       ensure_not_dirty (gphoto2_backend);
1951 
1952       g_debug ("  Generating file list for dir '%s'\n", filename);
1953 
1954       gp_list_new (&list);
1955       rc = gp_camera_folder_list_files (gphoto2_backend->camera,
1956                                         filename,
1957                                         list,
1958                                         gphoto2_backend->context);
1959       if (rc != 0)
1960         {
1961           error = get_error_from_gphoto2 (_("Failed to get file list"), rc);
1962           g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1963           g_error_free (error);
1964           g_free (filename);
1965           return;
1966         }
1967     }
1968   else
1969     {
1970       g_debug ("  Using cached file list for dir '%s'\n", filename);
1971       using_cached_file_list = TRUE;
1972       gp_list_ref (list);
1973       g_mutex_unlock (&gphoto2_backend->lock);
1974     }
1975   for (n = 0; n < gp_list_count (list); n++)
1976     {
1977       const char *name;
1978 
1979       gp_list_get_name (list, n, &name);
1980       g_debug ("  enum file '%s'\n", name);
1981 
1982       info = g_file_info_new ();
1983       error = NULL;
1984       if (!file_get_info (gphoto2_backend, filename, name, info, &error, FALSE))
1985         {
1986           g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1987           g_error_free (error);
1988           g_list_free_full (l, g_object_unref);
1989           gp_list_free (list);
1990           return;
1991         }
1992       l = g_list_append (l, info);
1993     }
1994   if (!using_cached_file_list)
1995     {
1996 #ifndef DEBUG_NO_CACHING
1997       g_mutex_lock (&gphoto2_backend->lock);
1998       g_hash_table_insert (gphoto2_backend->file_name_cache, g_strdup (filename), list);
1999       g_mutex_unlock (&gphoto2_backend->lock);
2000 #endif
2001     }
2002   else
2003     {
2004       g_mutex_lock (&gphoto2_backend->lock);
2005       gp_list_unref (list);
2006       g_mutex_unlock (&gphoto2_backend->lock);
2007     }
2008 
2009   /* and we're done */
2010 
2011   g_vfs_job_succeeded (G_VFS_JOB (job));
2012   g_vfs_job_enumerate_add_infos (job, l);
2013   g_list_free_full (l, g_object_unref);
2014   g_vfs_job_enumerate_done (job);
2015 
2016   g_free (filename);
2017 }
2018 
2019 /* ------------------------------------------------------------------------------------------------- */
2020 
2021 static gboolean
try_enumerate(GVfsBackend * backend,GVfsJobEnumerate * job,const char * given_filename,GFileAttributeMatcher * matcher,GFileQueryInfoFlags flags)2022 try_enumerate (GVfsBackend *backend,
2023                GVfsJobEnumerate *job,
2024                const char *given_filename,
2025                GFileAttributeMatcher *matcher,
2026                GFileQueryInfoFlags flags)
2027 {
2028   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
2029   GFileInfo *info;
2030   GList *l;
2031   GError *error;
2032   CameraList *list;
2033   int n;
2034   char *filename;
2035   const char *name;
2036 
2037   l = NULL;
2038 
2039   filename = add_ignore_prefix (gphoto2_backend, given_filename);
2040   g_debug ("try_enumerate (%s)\n", given_filename);
2041 
2042   /* first, list the folders */
2043   g_mutex_lock (&gphoto2_backend->lock);
2044   list = g_hash_table_lookup (gphoto2_backend->dir_name_cache, filename);
2045   if (list == NULL)
2046     {
2047       g_mutex_unlock (&gphoto2_backend->lock);
2048       goto error_not_cached;
2049     }
2050   gp_list_ref (list);
2051   g_mutex_unlock (&gphoto2_backend->lock);
2052   for (n = 0; n < gp_list_count (list); n++)
2053     {
2054       gp_list_get_name (list, n, &name);
2055       g_debug ("  try_enum folder '%s'\n", name);
2056       info = g_file_info_new ();
2057       if (!file_get_info (gphoto2_backend, filename, name, info, &error, TRUE))
2058         {
2059           g_mutex_lock (&gphoto2_backend->lock);
2060           gp_list_unref (list);
2061           g_mutex_unlock (&gphoto2_backend->lock);
2062           goto error_not_cached;
2063         }
2064       l = g_list_append (l, info);
2065     }
2066   g_mutex_lock (&gphoto2_backend->lock);
2067   gp_list_unref (list);
2068   g_mutex_unlock (&gphoto2_backend->lock);
2069 
2070   /* then list the files in each folder */
2071   g_mutex_lock (&gphoto2_backend->lock);
2072   list = g_hash_table_lookup (gphoto2_backend->file_name_cache, filename);
2073   if (list == NULL)
2074     {
2075       g_mutex_unlock (&gphoto2_backend->lock);
2076       goto error_not_cached;
2077     }
2078   gp_list_ref (list);
2079   g_mutex_unlock (&gphoto2_backend->lock);
2080   for (n = 0; n < gp_list_count (list); n++)
2081     {
2082       gp_list_get_name (list, n, &name);
2083       g_debug ("  try_enum file '%s'\n", name);
2084 
2085       info = g_file_info_new ();
2086       if (!file_get_info (gphoto2_backend, filename, name, info, &error, TRUE))
2087         {
2088           g_mutex_lock (&gphoto2_backend->lock);
2089           gp_list_unref (list);
2090           g_mutex_unlock (&gphoto2_backend->lock);
2091           goto error_not_cached;
2092         }
2093       l = g_list_append (l, info);
2094     }
2095   g_mutex_lock (&gphoto2_backend->lock);
2096   gp_list_unref (list);
2097   g_mutex_unlock (&gphoto2_backend->lock);
2098 
2099   /* and we're done */
2100 
2101   g_vfs_job_succeeded (G_VFS_JOB (job));
2102   g_vfs_job_enumerate_add_infos (job, l);
2103   g_list_free_full (l, g_object_unref);
2104   g_vfs_job_enumerate_done (job);
2105 
2106   g_free (filename);
2107   g_debug ("  YAY got info from cache for try_enumerate (%s)\n", given_filename);
2108   return TRUE;
2109 
2110  error_not_cached:
2111   g_list_free_full (l, g_object_unref);
2112 
2113   g_free (filename);
2114   g_debug ("  BUU no info from cache for try_enumerate (%s)\n", given_filename);
2115   return FALSE;
2116 }
2117 
2118 /* ------------------------------------------------------------------------------------------------- */
2119 
2120 static void
do_query_fs_info(GVfsBackend * backend,GVfsJobQueryFsInfo * job,const char * filename,GFileInfo * info,GFileAttributeMatcher * attribute_matcher)2121 do_query_fs_info (GVfsBackend *backend,
2122 		  GVfsJobQueryFsInfo *job,
2123 		  const char *filename,
2124 		  GFileInfo *info,
2125 		  GFileAttributeMatcher *attribute_matcher)
2126 {
2127   int rc;
2128   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
2129   CameraStorageInformation *storage_info;
2130   int num_storage_info;
2131 
2132   g_debug ("query_fs_info (%s)\n", filename);
2133 
2134   g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "gphoto2");
2135   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, FALSE);
2136   g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_NEVER);
2137   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, !gphoto2_backend->can_write);
2138 
2139   rc = gp_camera_get_storageinfo (gphoto2_backend->camera, &storage_info, &num_storage_info, gphoto2_backend->context);
2140   if (rc == 0)
2141     {
2142       if (num_storage_info >= 1)
2143         {
2144           /* for now we only support a single storage head */
2145           if (storage_info[0].fields & GP_STORAGEINFO_MAXCAPACITY)
2146             {
2147               g_mutex_lock (&gphoto2_backend->lock);
2148               gphoto2_backend->capacity = storage_info[0].capacitykbytes * 1024;
2149               g_mutex_unlock (&gphoto2_backend->lock);
2150               g_file_info_set_attribute_uint64 (info,
2151                                                 G_FILE_ATTRIBUTE_FILESYSTEM_SIZE,
2152                                                 (guint64) gphoto2_backend->capacity);
2153             }
2154 
2155           if (storage_info[0].fields & GP_STORAGEINFO_FREESPACEKBYTES)
2156             {
2157               g_mutex_lock (&gphoto2_backend->lock);
2158               gphoto2_backend->free_space = storage_info[0].freekbytes * 1024;
2159               g_mutex_unlock (&gphoto2_backend->lock);
2160               g_file_info_set_attribute_uint64 (info,
2161                                                 G_FILE_ATTRIBUTE_FILESYSTEM_FREE,
2162                                                 (guint64) gphoto2_backend->free_space);
2163             }
2164         }
2165       g_debug ("  got %d storage_info objects\n", num_storage_info);
2166     }
2167 
2168   g_vfs_job_succeeded (G_VFS_JOB (job));
2169 }
2170 
2171 /* ------------------------------------------------------------------------------------------------- */
2172 
2173 static gboolean
try_query_fs_info(GVfsBackend * backend,GVfsJobQueryFsInfo * job,const char * filename,GFileInfo * info,GFileAttributeMatcher * attribute_matcher)2174 try_query_fs_info (GVfsBackend *backend,
2175 		  GVfsJobQueryFsInfo *job,
2176 		  const char *filename,
2177 		  GFileInfo *info,
2178 		  GFileAttributeMatcher *attribute_matcher)
2179 {
2180   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
2181   gboolean ret;
2182   gint64 free_space;
2183   gint64 capacity;
2184 
2185   g_debug ("try_query_fs_info (%s)\n", filename);
2186 
2187   ret = FALSE;
2188 
2189   g_mutex_lock (&gphoto2_backend->lock);
2190   free_space = gphoto2_backend->free_space;
2191   capacity = gphoto2_backend->capacity;
2192   g_mutex_unlock (&gphoto2_backend->lock);
2193 
2194   if (free_space == -1 || capacity == -1)
2195     {
2196       g_debug ("  BUU no info from cache for try_query_fs_info (%s)\n", filename);
2197       goto out;
2198     }
2199   g_debug ("  YAY got info from cache for try_query_fs_info (%s)\n", filename);
2200 
2201   g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "gphoto2");
2202   g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_NEVER);
2203   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, !gphoto2_backend->can_write);
2204   g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, (guint64) capacity);
2205   g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, (guint64) free_space);
2206   g_vfs_job_succeeded (G_VFS_JOB (job));
2207 
2208   ret = TRUE;
2209  out:
2210   return ret;
2211 }
2212 
2213 /* ------------------------------------------------------------------------------------------------- */
2214 
2215 static void
do_make_directory(GVfsBackend * backend,GVfsJobMakeDirectory * job,const char * filename)2216 do_make_directory (GVfsBackend *backend,
2217                    GVfsJobMakeDirectory *job,
2218                    const char *filename)
2219 {
2220   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
2221   char *name;
2222   char *dir;
2223   int rc;
2224   GError *error;
2225 
2226   g_debug ("make_directory (%s)\n", filename);
2227 
2228   ensure_not_dirty (gphoto2_backend);
2229 
2230   dir = NULL;
2231   name = NULL;
2232   error = NULL;
2233 
2234   if (!gphoto2_backend->can_write)
2235     {
2236       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
2237                         G_IO_ERROR_NOT_SUPPORTED,
2238                         _("Operation not supported"));
2239       goto out;
2240     }
2241 
2242   split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
2243 
2244   rc = gp_camera_folder_make_dir (gphoto2_backend->camera,
2245                                   dir,
2246                                   name,
2247                                   gphoto2_backend->context);
2248   if (rc != 0)
2249     {
2250       error = get_error_from_gphoto2 (_("Error creating directory"), rc);
2251       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
2252       g_error_free (error);
2253       goto out;
2254     }
2255 
2256   caches_invalidate_dir (gphoto2_backend, dir);
2257   caches_invalidate_free_space (gphoto2_backend);
2258   monitors_emit_created (gphoto2_backend, dir, name);
2259 
2260   g_vfs_job_succeeded (G_VFS_JOB (job));
2261 
2262  out:
2263   g_free (dir);
2264   g_free (name);
2265 }
2266 
2267 /* ------------------------------------------------------------------------------------------------- */
2268 
2269 static int
do_slow_file_rename_in_same_dir(GVfsBackendGphoto2 * gphoto2_backend,const char * dir,const char * name,const char * new_name,gboolean allow_overwrite)2270 do_slow_file_rename_in_same_dir (GVfsBackendGphoto2 *gphoto2_backend,
2271                                  const char *dir,
2272                                  const char *name,
2273                                  const char *new_name,
2274                                  gboolean allow_overwrite)
2275 {
2276   int rc;
2277   CameraFile *file;
2278   CameraFile *file_dest;
2279   const char *data;
2280   unsigned long int size;
2281 
2282   file = NULL;
2283   file_dest = NULL;
2284 
2285   g_debug ("do_slow_file_rename_in_same_dir() '%s' '%s' -> '%s'\n", dir, name, new_name);
2286 
2287   rc = gp_file_new (&file);
2288   if (rc != 0)
2289     goto out;
2290 
2291   rc = gp_camera_file_get (gphoto2_backend->camera,
2292                            dir,
2293                            name,
2294                            GP_FILE_TYPE_NORMAL,
2295                            file,
2296                            gphoto2_backend->context);
2297   if (rc != 0)
2298     goto out;
2299 
2300   rc = gp_file_get_data_and_size (file, &data, &size);
2301   if (rc != 0)
2302     goto out;
2303 
2304   rc = gp_file_new (&file_dest);
2305   if (rc != 0)
2306     goto out;
2307 
2308   rc = gp_file_copy (file_dest, file);
2309   if (rc != 0)
2310     goto out;
2311 
2312   rc = gp_file_set_name (file_dest, new_name);
2313   if (rc != 0)
2314     goto out;
2315 
2316   if (allow_overwrite)
2317     {
2318       gp_camera_file_delete (gphoto2_backend->camera,
2319                              dir,
2320                              new_name,
2321                              gphoto2_backend->context);
2322       if (rc != 0)
2323         {
2324           g_debug ("  file delete failed as part of slow rename rc=%d\n", rc);
2325           goto out;
2326         }
2327     }
2328 
2329 #ifdef HAVE_GPHOTO25
2330   rc = gp_camera_folder_put_file (gphoto2_backend->camera, dir, new_name, GP_FILE_TYPE_NORMAL, file_dest, gphoto2_backend->context);
2331 #else
2332   rc = gp_camera_folder_put_file (gphoto2_backend->camera, dir, file_dest, gphoto2_backend->context);
2333 #endif
2334   if (rc != 0)
2335     goto out;
2336 
2337   rc = gp_camera_file_delete (gphoto2_backend->camera,
2338                               dir,
2339                               name,
2340                               gphoto2_backend->context);
2341   if (rc != 0)
2342     {
2343       /* at least try to clean up the newly created file... */
2344       gp_camera_file_delete (gphoto2_backend->camera,
2345                              dir,
2346                              new_name,
2347                              gphoto2_backend->context);
2348       goto out;
2349     }
2350 
2351  out:
2352   if (file != NULL)
2353     gp_file_unref (file);
2354   if (file_dest != NULL)
2355     gp_file_unref (file_dest);
2356   return rc;
2357 }
2358 
2359 /* ------------------------------------------------------------------------------------------------- */
2360 
2361 static int
do_file_rename_in_same_dir(GVfsBackendGphoto2 * gphoto2_backend,const char * dir,const char * name,const char * new_name,gboolean allow_overwrite)2362 do_file_rename_in_same_dir (GVfsBackendGphoto2 *gphoto2_backend,
2363                             const char *dir,
2364                             const char *name,
2365                             const char *new_name,
2366                             gboolean allow_overwrite)
2367 {
2368   /* TODO: The libgphoto2 API speaks of just using
2369    *       gp_camera_file_set_info() to achieve this. However this
2370    *       fails on the devices that I own. So fall back to the slow
2371    *       method for now. Patches welcome for someone with a device
2372    *       where the above mentioned trick works.
2373    */
2374   return do_slow_file_rename_in_same_dir (gphoto2_backend, dir, name, new_name, allow_overwrite);
2375 }
2376 
2377 /* ------------------------------------------------------------------------------------------------- */
2378 
2379 static int
do_dir_rename_in_same_dir(GVfsBackendGphoto2 * gphoto2_backend,const char * dir,const char * name,const char * new_name)2380 do_dir_rename_in_same_dir (GVfsBackendGphoto2 *gphoto2_backend,
2381                            const char *dir,
2382                            const char *name,
2383                            const char *new_name)
2384 {
2385   int rc;
2386   char *dir_name;
2387 
2388   dir_name = g_build_filename (dir, name, NULL);
2389 
2390   g_debug ("do_dir_rename_in_same_dir() '%s' '%s' -> '%s' ('%s')\n", dir, name, new_name, dir_name);
2391 
2392   /* TODO: Support non-empty folders by recursively renaming stuff.
2393    *       Or that might be too dangerous as it's not exactly atomic.
2394    *       And renaming files may be slow; see do_file_rename_in_same_dir() above.
2395    */
2396   if (is_directory_empty (gphoto2_backend, dir_name))
2397     {
2398       rc = gp_camera_folder_make_dir (gphoto2_backend->camera,
2399                                       dir,
2400                                       new_name,
2401                                       gphoto2_backend->context);
2402       if (rc != 0)
2403         goto out;
2404 
2405       rc = gp_camera_folder_remove_dir (gphoto2_backend->camera,
2406                                         dir,
2407                                         name,
2408                                         gphoto2_backend->context);
2409       if (rc != 0)
2410         goto out;
2411     }
2412   else
2413     {
2414       rc = GP_ERROR_NOT_SUPPORTED;
2415     }
2416 
2417  out:
2418   g_free (dir_name);
2419   return rc;
2420 }
2421 
2422 /* ------------------------------------------------------------------------------------------------- */
2423 
2424 static void
do_set_display_name(GVfsBackend * backend,GVfsJobSetDisplayName * job,const char * filename,const char * display_name)2425 do_set_display_name (GVfsBackend *backend,
2426                      GVfsJobSetDisplayName *job,
2427                      const char *filename,
2428                      const char *display_name)
2429 {
2430   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
2431   char *name;
2432   char *dir;
2433   int rc;
2434   char *dir_name;
2435   GError *error;
2436   char *new_name;
2437 
2438   ensure_not_dirty (gphoto2_backend);
2439 
2440   g_debug ("set_display_name() '%s' -> '%s'\n", filename, display_name);
2441 
2442   dir = NULL;
2443   name = NULL;
2444   dir_name = NULL;
2445   new_name = NULL;
2446 
2447   if (!gphoto2_backend->can_write)
2448     {
2449       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
2450                         G_IO_ERROR_NOT_SUPPORTED,
2451                         _("Operation not supported"));
2452       goto out;
2453     }
2454 
2455   split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
2456 
2457   /* refuse is desired name is already taken */
2458   if (is_directory (gphoto2_backend, dir, display_name) ||
2459       is_regular (gphoto2_backend, dir, display_name))
2460     {
2461       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
2462                         G_IO_ERROR_EXISTS,
2463                         _("Name already exists"));
2464       goto out;
2465     }
2466 
2467   /* ensure name is not too long - otherwise it might screw up enumerating
2468    * the folder on some devices
2469    */
2470   if (strlen (display_name) > 63)
2471     {
2472       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
2473                         G_IO_ERROR_NOT_SUPPORTED,
2474                         _("New name too long"));
2475       goto out;
2476     }
2477 
2478   if (is_directory (gphoto2_backend, dir, name))
2479     {
2480       /* dir renaming */
2481       rc = do_dir_rename_in_same_dir (gphoto2_backend, dir, name, display_name);
2482       if (rc != 0)
2483         {
2484           error = get_error_from_gphoto2 (_("Error renaming directory"), rc);
2485           g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
2486           g_error_free (error);
2487           goto out;
2488         }
2489       caches_invalidate_file (gphoto2_backend, dir, name);
2490     }
2491   else
2492     {
2493       /* file renaming */
2494       rc = do_file_rename_in_same_dir (gphoto2_backend, dir, name, display_name, FALSE);
2495       if (rc != 0)
2496         {
2497           error = get_error_from_gphoto2 (_("Error renaming file"), rc);
2498           g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
2499           g_error_free (error);
2500           goto out;
2501         }
2502       caches_invalidate_file (gphoto2_backend, dir, name);
2503     }
2504 
2505 
2506   /* emit on monitor */
2507   monitors_emit_deleted (gphoto2_backend, dir, name);
2508   monitors_emit_created (gphoto2_backend, dir, display_name);
2509 
2510   new_name = g_build_filename (dir + strlen (gphoto2_backend->ignore_prefix), display_name, NULL);
2511   g_vfs_job_set_display_name_set_new_path (job, new_name);
2512 
2513   g_vfs_job_succeeded (G_VFS_JOB (job));
2514 
2515  out:
2516   g_free (dir);
2517   g_free (name);
2518   g_free (dir_name);
2519   g_free (new_name);
2520 }
2521 
2522 /* ------------------------------------------------------------------------------------------------- */
2523 
2524 static void
do_delete(GVfsBackend * backend,GVfsJobDelete * job,const char * filename)2525 do_delete (GVfsBackend *backend,
2526            GVfsJobDelete *job,
2527            const char *filename)
2528 {
2529   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
2530   char *name;
2531   char *dir;
2532   int rc;
2533   GError *error;
2534   char *dir_name;
2535 
2536   ensure_not_dirty (gphoto2_backend);
2537 
2538   g_debug ("delete() '%s'\n", filename);
2539 
2540   dir = NULL;
2541   name = NULL;
2542   dir_name = NULL;
2543 
2544   if (!gphoto2_backend->can_delete)
2545     {
2546       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
2547                         G_IO_ERROR_NOT_SUPPORTED,
2548                         _("Operation not supported"));
2549       goto out;
2550     }
2551 
2552   split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
2553 
2554   if (is_directory (gphoto2_backend, dir, name))
2555     {
2556       dir_name = add_ignore_prefix (gphoto2_backend, filename);
2557       if (!is_directory_empty (gphoto2_backend, dir_name))
2558         {
2559           g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
2560                             G_IO_ERROR_NOT_EMPTY,
2561                             _("Directory “%s” is not empty"), filename);
2562           goto out;
2563         }
2564       else
2565         {
2566           rc = gp_camera_folder_remove_dir (gphoto2_backend->camera,
2567                                             dir,
2568                                             name,
2569                                             gphoto2_backend->context);
2570           if (rc != 0)
2571             {
2572               error = get_error_from_gphoto2 (_("Error deleting directory"), rc);
2573               g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
2574               g_error_free (error);
2575               goto out;
2576             }
2577           caches_invalidate_file (gphoto2_backend, dir, name);
2578           caches_invalidate_free_space (gphoto2_backend);
2579           monitors_emit_deleted (gphoto2_backend, dir, name);
2580         }
2581     }
2582   else
2583     {
2584       if (!is_regular (gphoto2_backend, dir, name))
2585         {
2586           g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
2587                             G_IO_ERROR_NOT_FOUND,
2588                             _("No such file or directory"));
2589           goto out;
2590         }
2591 
2592       rc = gp_camera_file_delete (gphoto2_backend->camera,
2593                                   dir,
2594                                   name,
2595                                   gphoto2_backend->context);
2596       if (rc != 0)
2597         {
2598           error = get_error_from_gphoto2 (_("Error deleting file"), rc);
2599           g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
2600           g_error_free (error);
2601           goto out;
2602         }
2603 
2604       caches_invalidate_file (gphoto2_backend, dir, name);
2605       caches_invalidate_free_space (gphoto2_backend);
2606       monitors_emit_deleted (gphoto2_backend, dir, name);
2607     }
2608 
2609   g_vfs_job_succeeded (G_VFS_JOB (job));
2610 
2611  out:
2612   g_free (dir);
2613   g_free (name);
2614   g_free (dir_name);
2615 }
2616 
2617 /* ------------------------------------------------------------------------------------------------- */
2618 
2619 static void
do_create_internal(GVfsBackend * backend,GVfsJobOpenForWrite * job,const char * filename,GFileCreateFlags flags,gboolean job_is_replace,gboolean job_is_append_to)2620 do_create_internal (GVfsBackend *backend,
2621                     GVfsJobOpenForWrite *job,
2622                     const char *filename,
2623                     GFileCreateFlags flags,
2624                     gboolean job_is_replace,
2625                     gboolean job_is_append_to)
2626 {
2627   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
2628   WriteHandle *handle;
2629   char *dir;
2630   char *name;
2631 
2632   ensure_not_dirty (gphoto2_backend);
2633 
2634   dir = NULL;
2635   name = NULL;
2636 
2637   if (!gphoto2_backend->can_write)
2638     {
2639       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
2640                         G_IO_ERROR_NOT_SUPPORTED,
2641                         _("Operation not supported"));
2642       goto out;
2643     }
2644 
2645   split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
2646 
2647   if (is_directory (gphoto2_backend, dir, name))
2648     {
2649       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
2650                         G_IO_ERROR_IS_DIRECTORY,
2651                         _("Can’t write to directory"));
2652       goto out;
2653     }
2654 
2655   /* unless we're replacing or appending.. error out if file already exists */
2656   if (is_regular (gphoto2_backend, dir, name))
2657     {
2658       if (! (job_is_replace || job_is_append_to))
2659         {
2660           g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
2661                             G_IO_ERROR_EXISTS,
2662                             _("File exists"));
2663           goto out;
2664         }
2665     }
2666   else
2667     {
2668       if (job_is_replace || job_is_append_to)
2669         {
2670           /* so we're not really replacing or appending; dont fail these
2671            * operations; just turn them into create instead...
2672            */
2673           job_is_replace = FALSE;
2674           job_is_append_to = FALSE;
2675         }
2676     }
2677 
2678   handle = g_new0 (WriteHandle, 1);
2679   handle->filename = g_strdup (filename);
2680   handle->dir = g_strdup (dir);
2681   handle->name = g_strdup (name);
2682   handle->job_is_replace = job_is_replace;
2683   handle->job_is_append_to = job_is_append_to;
2684   handle->is_dirty = TRUE;
2685 
2686   /* if we're appending to a file read in all of the file to memory */
2687   if (job_is_append_to)
2688     {
2689       int rc;
2690       GError *error;
2691       CameraFile *file;
2692       const char *data;
2693       unsigned long int size;
2694 
2695       rc = gp_file_new (&file);
2696       if (rc != 0)
2697         {
2698           error = get_error_from_gphoto2 (_("Cannot allocate new file to append to"), rc);
2699           g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
2700           g_error_free (error);
2701           write_handle_free (handle);
2702           goto out;
2703         }
2704 
2705       rc = gp_camera_file_get (gphoto2_backend->camera,
2706                                dir,
2707                                name,
2708                                GP_FILE_TYPE_NORMAL,
2709                                file,
2710                                gphoto2_backend->context);
2711       if (rc != 0)
2712         {
2713           error = get_error_from_gphoto2 (_("Cannot read file to append to"), rc);
2714           g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
2715           g_error_free (error);
2716           write_handle_free (handle);
2717           gp_file_unref (file);
2718           goto out;
2719         }
2720 
2721       rc = gp_file_get_data_and_size (file, &data, &size);
2722       if (rc != 0)
2723         {
2724           error = get_error_from_gphoto2 (_("Cannot get data of file to append to"), rc);
2725           g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
2726           g_error_free (error);
2727           write_handle_free (handle);
2728           gp_file_unref (file);
2729           goto out;
2730         }
2731 
2732       handle->data = g_malloc (size + WRITE_INCREMENT);
2733       handle->allocated_size = size + WRITE_INCREMENT;
2734       handle->size = size;
2735       handle->cursor = size;
2736       memcpy (handle->data, data, size);
2737       gp_file_unref (file);
2738 
2739     }
2740   else
2741     {
2742       handle->data = g_malloc (WRITE_INCREMENT);
2743       handle->allocated_size = WRITE_INCREMENT;
2744     }
2745 
2746   g_vfs_job_open_for_write_set_handle (job, handle);
2747   g_vfs_job_open_for_write_set_can_seek (job, TRUE);
2748   g_vfs_job_open_for_write_set_can_truncate (job, TRUE);
2749 
2750   gphoto2_backend->open_write_handles = g_list_prepend (gphoto2_backend->open_write_handles, handle);
2751 
2752   g_debug ("  handle=%p\n", handle);
2753 
2754   /* make sure we invalidate the dir and the file */
2755   caches_invalidate_file (gphoto2_backend, dir, name);
2756 
2757   /* emit on the monitor - hopefully some client won't need info
2758    * about this (to avoid committing dirty bits midwrite) before
2759    * the write is done...
2760    */
2761   if (job_is_replace || job_is_append_to)
2762     monitors_emit_changed (gphoto2_backend, dir, name);
2763   else
2764     monitors_emit_created (gphoto2_backend, dir, name);
2765 
2766   g_vfs_job_succeeded (G_VFS_JOB (job));
2767 
2768  out:
2769   g_free (dir);
2770   g_free (name);
2771 }
2772 
2773 /* ------------------------------------------------------------------------------------------------- */
2774 
2775 static void
do_create(GVfsBackend * backend,GVfsJobOpenForWrite * job,const char * filename,GFileCreateFlags flags)2776 do_create (GVfsBackend *backend,
2777            GVfsJobOpenForWrite *job,
2778            const char *filename,
2779            GFileCreateFlags flags)
2780 {
2781   g_debug ("create() '%s' flags=0x%04x\n", filename, flags);
2782 
2783   return do_create_internal (backend, job, filename, flags, FALSE, FALSE);
2784 }
2785 
2786 /* ------------------------------------------------------------------------------------------------- */
2787 
2788 static void
do_replace(GVfsBackend * backend,GVfsJobOpenForWrite * job,const char * filename,const char * etag,gboolean make_backup,GFileCreateFlags flags)2789 do_replace (GVfsBackend *backend,
2790             GVfsJobOpenForWrite *job,
2791             const char *filename,
2792             const char *etag,
2793             gboolean make_backup,
2794             GFileCreateFlags flags)
2795 {
2796   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
2797   char *dir;
2798   char *name;
2799 
2800   g_debug ("replace() '%s' etag='%s' make_backup=%d flags=0x%04x\n", filename, etag, make_backup, flags);
2801 
2802   dir = NULL;
2803   name = NULL;
2804   split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
2805 
2806   /* write a new file
2807    * - will delete the existing one when done in do_close_write()
2808    */
2809   do_create_internal (backend, job, filename, flags, TRUE, FALSE);
2810 
2811   g_free (dir);
2812   g_free (name);
2813 }
2814 
2815 /* ------------------------------------------------------------------------------------------------- */
2816 
2817 static void
do_append_to(GVfsBackend * backend,GVfsJobOpenForWrite * job,const char * filename,GFileCreateFlags flags)2818 do_append_to (GVfsBackend *backend,
2819               GVfsJobOpenForWrite *job,
2820               const char *filename,
2821               GFileCreateFlags flags)
2822 {
2823   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
2824   char *dir;
2825   char *name;
2826 
2827   g_debug ("append_to() '%s' flags=0x%04x\n", filename, flags);
2828 
2829   dir = NULL;
2830   name = NULL;
2831   split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
2832 
2833   /* write a new file
2834    * - will read existing data in do_create_internal
2835    * - will delete the existing one when done in do_close_write()
2836    */
2837   do_create_internal (backend, job, filename, flags, FALSE, TRUE);
2838 
2839   g_free (dir);
2840   g_free (name);
2841 }
2842 
2843 /* ------------------------------------------------------------------------------------------------- */
2844 
2845 static void
do_write(GVfsBackend * backend,GVfsJobWrite * job,GVfsBackendHandle _handle,char * buffer,gsize buffer_size)2846 do_write (GVfsBackend *backend,
2847           GVfsJobWrite *job,
2848           GVfsBackendHandle _handle,
2849           char *buffer,
2850           gsize buffer_size)
2851 {
2852   WriteHandle *handle = _handle;
2853 
2854   g_debug ("write() %p, '%s', %" G_GSIZE_FORMAT " bytes\n", handle, handle->filename, buffer_size);
2855 
2856   /* ensure we have enough room */
2857   if (handle->cursor + buffer_size > handle->allocated_size)
2858     {
2859       unsigned long int new_allocated_size;
2860       new_allocated_size = ((handle->cursor + buffer_size) / WRITE_INCREMENT + 1) * WRITE_INCREMENT;
2861       handle->data = g_realloc (handle->data, new_allocated_size);
2862       handle->allocated_size = new_allocated_size;
2863       g_debug ("    allocated_size is now %ld bytes)\n", handle->allocated_size);
2864     }
2865 
2866 
2867   memcpy (handle->data + handle->cursor, buffer, buffer_size);
2868   handle->cursor += buffer_size;
2869 
2870   if (handle->cursor > handle->size)
2871     handle->size = handle->cursor;
2872 
2873   /* this will make us dirty */
2874   handle->is_dirty = TRUE;
2875 
2876   g_vfs_job_write_set_written_size (job, buffer_size);
2877   g_vfs_job_succeeded (G_VFS_JOB (job));
2878 }
2879 
2880 /* ------------------------------------------------------------------------------------------------- */
2881 
2882 static void
do_seek_on_write(GVfsBackend * backend,GVfsJobSeekWrite * job,GVfsBackendHandle handle,goffset offset,GSeekType type)2883 do_seek_on_write (GVfsBackend *backend,
2884                   GVfsJobSeekWrite *job,
2885                   GVfsBackendHandle handle,
2886                   goffset    offset,
2887                   GSeekType  type)
2888 {
2889   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
2890   WriteHandle *write_handle = handle;
2891   long new_offset;
2892 
2893   g_debug ("seek_on_write() %p '%s' offset=%d type=%d cursor=%ld size=%ld\n", write_handle, write_handle->filename, (int)offset, type, write_handle->cursor, write_handle->size);
2894 
2895   switch (type)
2896     {
2897     default:
2898     case G_SEEK_SET:
2899       new_offset = offset;
2900       break;
2901     case G_SEEK_CUR:
2902       new_offset = write_handle->cursor + offset;
2903       break;
2904     case G_SEEK_END:
2905       new_offset = write_handle->size + offset;
2906       break;
2907     }
2908 
2909   if (new_offset < 0)
2910     {
2911       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
2912 			G_IO_ERROR_FAILED,
2913 			_("Error seeking in stream on camera %s"), gphoto2_backend->gphoto2_port);
2914     }
2915   else
2916     {
2917       write_handle->cursor = new_offset;
2918       g_vfs_job_seek_write_set_offset (job, new_offset);
2919       g_vfs_job_succeeded (G_VFS_JOB (job));
2920     }
2921 }
2922 
2923 /* ------------------------------------------------------------------------------------------------- */
2924 
2925 static void
do_truncate(GVfsBackend * backend,GVfsJobTruncate * job,GVfsBackendHandle _handle,goffset size)2926 do_truncate (GVfsBackend *backend,
2927              GVfsJobTruncate *job,
2928              GVfsBackendHandle _handle,
2929              goffset size)
2930 {
2931   WriteHandle *handle = _handle;
2932 
2933   g_debug ("truncate() %p, '%s', %ld bytes\n", handle, handle->filename, size);
2934 
2935   /* ensure we have enough room */
2936   if (size > handle->allocated_size)
2937     {
2938       unsigned long int new_allocated_size;
2939       new_allocated_size = (size / WRITE_INCREMENT + 1) * WRITE_INCREMENT;
2940       handle->data = g_realloc (handle->data, new_allocated_size);
2941       handle->allocated_size = new_allocated_size;
2942       g_debug ("    allocated_size is now %ld bytes)\n", handle->allocated_size);
2943     }
2944 
2945 
2946   if (size > handle->size)
2947     memset(handle->data + handle->size, 0, size - handle->size);
2948 
2949   handle->size = size;
2950 
2951   /* this will make us dirty */
2952   handle->is_dirty = TRUE;
2953 
2954   g_vfs_job_succeeded (G_VFS_JOB (job));
2955 }
2956 
2957 /* ------------------------------------------------------------------------------------------------- */
2958 
2959 /* this functions updates the device with the data currently in write_handle */
2960 static int
commit_write_handle(GVfsBackendGphoto2 * gphoto2_backend,WriteHandle * write_handle)2961 commit_write_handle (GVfsBackendGphoto2 *gphoto2_backend, WriteHandle *write_handle)
2962 {
2963   int rc;
2964   CameraFile *file;
2965 
2966   g_debug ("commit_write_handle() '%s' of size %ld\n", write_handle->filename, write_handle->size);
2967 
2968   /* no need to write as we're not dirty */
2969   if (!write_handle->is_dirty)
2970     {
2971       g_debug ("  not dirty => not writing\n");
2972       return 0;
2973     }
2974 
2975   if (write_handle->delete_before ||
2976       (write_handle->job_is_replace || write_handle->job_is_append_to))
2977     {
2978       /* OK, so this is not atomic. But there's no way we can make it
2979        * atomic until rename works properly - see comments in
2980        * do_set_display_name() and why have do_slow_rename()...
2981        *
2982        * So first delete the existing file...
2983        */
2984       rc = gp_camera_file_delete (gphoto2_backend->camera,
2985                                   write_handle->dir,
2986                                   write_handle->name,
2987                                   gphoto2_backend->context);
2988       if (rc != 0)
2989         goto out;
2990 
2991       g_debug ("  deleted '%s' '%s' for delete_before=%d, job_is_replace=%d, job_is_append_to=%d\n",
2992                write_handle->dir, write_handle->name,
2993                write_handle->delete_before, write_handle->job_is_replace, write_handle->job_is_append_to);
2994     }
2995 
2996   rc = gp_file_new (&file);
2997   if (rc != 0)
2998     goto out;
2999 
3000   gp_file_set_name (file, write_handle->name);
3001   gp_file_set_mtime (file, time (NULL));
3002   gp_file_set_data_and_size (file,
3003                              dup_for_gphoto2 (write_handle->data, write_handle->size),
3004                              write_handle->size);
3005 
3006 #ifdef HAVE_GPHOTO25
3007   rc = gp_camera_folder_put_file (gphoto2_backend->camera, write_handle->dir, write_handle->name, GP_FILE_TYPE_NORMAL, file, gphoto2_backend->context);
3008 #else
3009   gp_file_set_type (file, GP_FILE_TYPE_NORMAL);
3010   rc = gp_camera_folder_put_file (gphoto2_backend->camera, write_handle->dir, file, gphoto2_backend->context);
3011 #endif
3012   if (rc != 0)
3013     {
3014       gp_file_unref (file);
3015       goto out;
3016     }
3017 
3018   g_debug ("  successfully wrote '%s' of %ld bytes\n", write_handle->filename, write_handle->size);
3019   monitors_emit_changed (gphoto2_backend, write_handle->dir, write_handle->name);
3020 
3021   gp_file_unref (file);
3022 
3023  out:
3024   write_handle->is_dirty = FALSE;
3025   write_handle->delete_before = TRUE;
3026 
3027   caches_invalidate_file (gphoto2_backend, write_handle->dir, write_handle->name);
3028   caches_invalidate_free_space (gphoto2_backend);
3029 
3030   return rc;
3031 }
3032 
3033 /* ------------------------------------------------------------------------------------------------- */
3034 
3035 static void
do_close_write(GVfsBackend * backend,GVfsJobCloseWrite * job,GVfsBackendHandle handle)3036 do_close_write (GVfsBackend *backend,
3037                 GVfsJobCloseWrite *job,
3038                 GVfsBackendHandle handle)
3039 {
3040   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
3041   WriteHandle *write_handle = handle;
3042   GError *error;
3043   int rc;
3044 
3045   g_debug ("close_write() %p '%s' %ld bytes total\n", write_handle, write_handle->filename, write_handle->size);
3046 
3047   rc = commit_write_handle (gphoto2_backend, write_handle);
3048   if (rc != 0)
3049     {
3050       error = get_error_from_gphoto2 (_("Error writing file"), rc);
3051       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
3052       g_error_free (error);
3053       goto out;
3054     }
3055 
3056   monitors_emit_changed (gphoto2_backend, write_handle->dir, write_handle->name);
3057 
3058   g_vfs_job_succeeded (G_VFS_JOB (job));
3059 
3060  out:
3061   write_handle_free (write_handle);
3062   gphoto2_backend->open_write_handles = g_list_remove (gphoto2_backend->open_write_handles, write_handle);
3063 }
3064 
3065 /* ------------------------------------------------------------------------------------------------- */
3066 
3067 static void
do_move(GVfsBackend * backend,GVfsJobMove * job,const char * source,const char * destination,GFileCopyFlags flags,GFileProgressCallback progress_callback,gpointer progress_callback_data)3068 do_move (GVfsBackend *backend,
3069          GVfsJobMove *job,
3070          const char *source,
3071          const char *destination,
3072          GFileCopyFlags flags,
3073          GFileProgressCallback progress_callback,
3074          gpointer progress_callback_data)
3075 {
3076   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
3077   char *src_dir;
3078   char *src_name;
3079   char *dst_dir;
3080   char *dst_name;
3081   int rc;
3082   GError *error;
3083   gboolean mv_dir;
3084 
3085   g_debug ("move() '%s' -> '%s' %04x)\n", source, destination, flags);
3086 
3087   ensure_not_dirty (gphoto2_backend);
3088 
3089   split_filename_with_ignore_prefix (gphoto2_backend, source, &src_dir, &src_name);
3090   split_filename_with_ignore_prefix (gphoto2_backend, destination, &dst_dir, &dst_name);
3091 
3092   /* this is an limited implementation that can only move files / folders in the same directory */
3093   if (strcmp (src_dir, dst_dir) != 0)
3094     {
3095       g_debug ("  not supported (not same directory)\n");
3096       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
3097                         G_IO_ERROR_NOT_SUPPORTED,
3098                         _("Not supported (not same directory)"));
3099       goto out;
3100     }
3101 
3102   mv_dir = FALSE;
3103   if (is_directory (gphoto2_backend, src_dir, src_name))
3104     {
3105       if (is_directory (gphoto2_backend, dst_dir, dst_name))
3106         {
3107           g_debug ("  not supported (src is dir; dst is dir)\n");
3108           g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
3109                             G_IO_ERROR_NOT_SUPPORTED,
3110                             _("Not supported (the source is a directory, the destination is a directory too)"));
3111           goto out;
3112         }
3113       else if (is_regular (gphoto2_backend, dst_dir, dst_name))
3114         {
3115           g_debug ("  not supported (src is dir; dst is existing file)\n");
3116           g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
3117                             G_IO_ERROR_NOT_SUPPORTED,
3118                             _("Not supported (the source is a directory, but the destination is an existing file)"));
3119           goto out;
3120         }
3121       mv_dir = TRUE;
3122     }
3123   else
3124     {
3125       if (is_directory (gphoto2_backend, dst_dir, dst_name))
3126         {
3127           g_debug ("  not supported (src is file; dst is dir)\n");
3128           g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
3129                             G_IO_ERROR_NOT_SUPPORTED,
3130                             _("Not supported (the source is a file, but the destination is a directory)"));
3131           goto out;
3132         }
3133     }
3134 
3135   /* ensure name is not too long - otherwise it might screw up enumerating
3136    * the folder on some devices
3137    */
3138   if (strlen (dst_name) > 63)
3139     {
3140       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
3141                         G_IO_ERROR_NOT_SUPPORTED,
3142                         _("New name too long"));
3143       goto out;
3144     }
3145 
3146   if (mv_dir)
3147     {
3148       g_debug ("  renaming dir\n");
3149       rc = do_dir_rename_in_same_dir (gphoto2_backend, src_dir, src_name, dst_name);
3150       if (rc != 0)
3151         {
3152           g_debug ("  error renaming dir\n");
3153           error = get_error_from_gphoto2 (_("Error renaming directory"), rc);
3154           g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
3155           g_error_free (error);
3156           goto out;
3157         }
3158     }
3159   else
3160     {
3161       g_debug ("  renaming file\n");
3162       rc = do_file_rename_in_same_dir (gphoto2_backend, src_dir, src_name, dst_name, flags & G_FILE_COPY_OVERWRITE);
3163       if (rc != 0)
3164         {
3165           g_debug ("  error renaming file\n");
3166           error = get_error_from_gphoto2 (_("Error renaming file"), rc);
3167           g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
3168           g_error_free (error);
3169           goto out;
3170         }
3171     }
3172 
3173   caches_invalidate_file (gphoto2_backend, src_dir, src_name);
3174   monitors_emit_deleted (gphoto2_backend, src_dir, src_name);
3175   monitors_emit_created (gphoto2_backend, src_dir, dst_name);
3176 
3177   g_debug ("  success\n");
3178 
3179   g_vfs_job_succeeded (G_VFS_JOB (job));
3180 
3181  out:
3182   g_free (src_dir);
3183   g_free (src_name);
3184   g_free (dst_dir);
3185   g_free (dst_name);
3186 }
3187 
3188 static unsigned int
ctx_progress_start_func(GPContext * context,float target,const char * str,void * data)3189 ctx_progress_start_func (GPContext *context,
3190                          float target,
3191                          const char *str,
3192                          void *data)
3193 {
3194   PullContext *pc = data;
3195   pc->target = target;
3196   return 0;
3197 }
3198 
3199 static void
ctx_progress_update_func(GPContext * context,unsigned int id,float current,void * data)3200 ctx_progress_update_func (GPContext *context,
3201                           unsigned int id,
3202                           float current,
3203                           void *data)
3204 {
3205   PullContext *pc = data;
3206   if (pc->progress_callback)
3207     pc->progress_callback ((current / pc->target) * pc->size, pc->size,
3208                            pc->progress_callback_data);
3209 }
3210 
3211 static void
ctx_progress_stop_func(GPContext * context,unsigned int id,void * data)3212 ctx_progress_stop_func (GPContext *context,
3213                         unsigned int id,
3214                         void *data)
3215 {
3216   PullContext *pc = data;
3217   if (pc->progress_callback)
3218     pc->progress_callback (pc->size, pc->size, pc->progress_callback_data);
3219 }
3220 
3221 static void
do_pull(GVfsBackend * backend,GVfsJobPull * job,const char * source,const char * local_path,GFileCopyFlags flags,gboolean remove_source,GFileProgressCallback progress_callback,gpointer progress_callback_data)3222 do_pull (GVfsBackend *backend,
3223          GVfsJobPull *job,
3224          const char *source,
3225          const char *local_path,
3226          GFileCopyFlags flags,
3227          gboolean remove_source,
3228          GFileProgressCallback progress_callback,
3229          gpointer progress_callback_data)
3230 {
3231   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
3232   GFileInfo *info = g_file_info_new ();
3233   GError *error = NULL;
3234   PullContext pc;
3235   CameraFile *file;
3236   GFile *dest = NULL;
3237   GFileDescriptorBased *fdstream;
3238   char *dir, *name;
3239   guint64 mtime;
3240   int rc;
3241 
3242   ensure_not_dirty (gphoto2_backend);
3243 
3244   split_filename_with_ignore_prefix (gphoto2_backend, source, &dir, &name);
3245 
3246   if (remove_source && (flags & G_FILE_COPY_NO_FALLBACK_FOR_MOVE))
3247     {
3248       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
3249                         G_IO_ERROR_NOT_SUPPORTED,
3250                         _("Operation not supported"));
3251       goto out;
3252     }
3253 
3254   if (remove_source && !gphoto2_backend->can_delete)
3255     {
3256       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
3257                         G_IO_ERROR_NOT_SUPPORTED,
3258                         _("Operation not supported"));
3259       goto out;
3260     }
3261 
3262   /* Fallback to the default implementation unless we have a regular file */
3263   if (!file_get_info (gphoto2_backend, dir, name, info, &error, FALSE) ||
3264       g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR)
3265     {
3266       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
3267                         G_IO_ERROR_NOT_SUPPORTED,
3268                         _("Operation not supported"));
3269       goto out;
3270     }
3271 
3272   dest = g_file_new_for_path (local_path);
3273   if (flags & G_FILE_COPY_OVERWRITE)
3274     {
3275       fdstream = G_FILE_DESCRIPTOR_BASED (
3276                      g_file_replace (dest,
3277                                      NULL,
3278                                      flags & G_FILE_COPY_BACKUP ? TRUE : FALSE,
3279                                      G_FILE_CREATE_REPLACE_DESTINATION,
3280                                      G_VFS_JOB (job)->cancellable, &error));
3281     }
3282   else
3283     {
3284       fdstream = G_FILE_DESCRIPTOR_BASED (
3285                      g_file_create (dest,
3286                                     G_FILE_CREATE_NONE,
3287                                     G_VFS_JOB (job)->cancellable, &error));
3288     }
3289 
3290   if (!fdstream)
3291     {
3292       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
3293       goto out;
3294     }
3295 
3296   rc = gp_file_new_from_fd (&file, g_file_descriptor_based_get_fd (fdstream));
3297   if (rc != 0)
3298     {
3299       error = get_error_from_gphoto2 (_("Error creating file object"), rc);
3300       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
3301       g_object_unref (fdstream);
3302       goto out;
3303     }
3304 
3305   pc.size = g_file_info_get_size (info);
3306   pc.progress_callback = progress_callback;
3307   pc.progress_callback_data = progress_callback_data;
3308 
3309   gp_context_set_progress_funcs (gphoto2_backend->context,
3310                                  ctx_progress_start_func,
3311                                  ctx_progress_update_func,
3312                                  ctx_progress_stop_func,
3313                                  &pc);
3314 
3315   rc = gp_camera_file_get (gphoto2_backend->camera,
3316                            dir,
3317                            name,
3318                            GP_FILE_TYPE_NORMAL,
3319                            file,
3320                            gphoto2_backend->context);
3321 
3322   gp_context_set_progress_funcs (gphoto2_backend->context, NULL, NULL, NULL, NULL);
3323 
3324   /* gp_camera_file_get() closes the fd so we just unref here */
3325   g_object_unref (fdstream);
3326   gp_file_unref (file);
3327 
3328   if (rc != 0)
3329     {
3330       error = get_error_from_gphoto2 (_("Error getting file"), rc);
3331       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
3332       goto out;
3333     }
3334 
3335   /* Ignore errors here. Failure to copy metadata is not a hard error */
3336   mtime = g_file_info_get_attribute_uint64 (info,
3337                                             G_FILE_ATTRIBUTE_TIME_MODIFIED);
3338   g_file_set_attribute_uint64 (dest,
3339                                G_FILE_ATTRIBUTE_TIME_MODIFIED, mtime,
3340                                G_FILE_QUERY_INFO_NONE,
3341                                G_VFS_JOB (job)->cancellable, NULL);
3342 
3343   if (remove_source)
3344     {
3345       rc = gp_camera_file_delete (gphoto2_backend->camera,
3346                                   dir,
3347                                   name,
3348                                   gphoto2_backend->context);
3349       if (rc != 0)
3350         {
3351           error = get_error_from_gphoto2 (_("Error deleting file"), rc);
3352           g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
3353           goto out;
3354         }
3355 
3356       caches_invalidate_file (gphoto2_backend, dir, name);
3357       caches_invalidate_free_space (gphoto2_backend);
3358       monitors_emit_deleted (gphoto2_backend, dir, name);
3359     }
3360 
3361   g_vfs_job_succeeded (G_VFS_JOB (job));
3362 
3363 out:
3364   g_object_unref (info);
3365   g_clear_object (&dest);
3366   g_free (name);
3367   g_free (dir);
3368   g_clear_error (&error);
3369 }
3370 
3371 /* ------------------------------------------------------------------------------------------------- */
3372 
3373 static void
vfs_dir_monitor_destroyed(gpointer user_data,GObject * where_the_object_was)3374 vfs_dir_monitor_destroyed (gpointer user_data, GObject *where_the_object_was)
3375 {
3376   GList *l;
3377   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (user_data);
3378 
3379   g_debug ("vfs_dir_monitor_destroyed()\n");
3380 
3381   for (l = gphoto2_backend->dir_monitor_proxies; l != NULL; l = l->next)
3382     {
3383       MonitorProxy *proxy = l->data;
3384       if (G_OBJECT (proxy->vfs_monitor) == where_the_object_was)
3385         {
3386           gphoto2_backend->dir_monitor_proxies = g_list_remove (gphoto2_backend->dir_monitor_proxies, proxy);
3387           g_debug ("  Removed dead dir monitor for '%s'\n", proxy->path);
3388           monitor_proxy_free (proxy);
3389           break;
3390         }
3391     }
3392 }
3393 
3394 static void
do_create_dir_monitor(GVfsBackend * backend,GVfsJobCreateMonitor * job,const char * filename,GFileMonitorFlags flags)3395 do_create_dir_monitor (GVfsBackend *backend,
3396                        GVfsJobCreateMonitor *job,
3397                        const char *filename,
3398                        GFileMonitorFlags flags)
3399 {
3400   char *dir;
3401   char *name;
3402   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
3403   MonitorProxy *proxy;
3404 
3405   g_debug ("create_dir_monitor (%s)\n", filename);
3406 
3407   split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
3408 
3409   proxy = g_new0 (MonitorProxy, 1);
3410   proxy->path = add_ignore_prefix (gphoto2_backend, filename);
3411   proxy->vfs_monitor = g_vfs_monitor_new (backend);
3412 
3413   gphoto2_backend->dir_monitor_proxies = g_list_prepend (gphoto2_backend->dir_monitor_proxies, proxy);
3414 
3415   g_vfs_job_create_monitor_set_monitor (job, proxy->vfs_monitor);
3416   g_object_weak_ref (G_OBJECT (proxy->vfs_monitor), vfs_dir_monitor_destroyed, gphoto2_backend);
3417   g_object_unref (proxy->vfs_monitor);
3418   g_vfs_job_succeeded (G_VFS_JOB (job));
3419 }
3420 
3421 /* ------------------------------------------------------------------------------------------------- */
3422 
3423 static void
vfs_file_monitor_destroyed(gpointer user_data,GObject * where_the_object_was)3424 vfs_file_monitor_destroyed (gpointer user_data, GObject *where_the_object_was)
3425 {
3426   GList *l;
3427   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (user_data);
3428 
3429   g_debug ("vfs_file_monitor_destroyed()\n");
3430 
3431   for (l = gphoto2_backend->file_monitor_proxies; l != NULL; l = l->next)
3432     {
3433       MonitorProxy *proxy = l->data;
3434       if (G_OBJECT (proxy->vfs_monitor) == where_the_object_was)
3435         {
3436           gphoto2_backend->dir_monitor_proxies = g_list_remove (gphoto2_backend->dir_monitor_proxies, proxy);
3437           g_debug ("  Removed dead file monitor for '%s'\n", proxy->path);
3438           monitor_proxy_free (proxy);
3439           break;
3440         }
3441     }
3442 }
3443 
3444 static void
do_create_file_monitor(GVfsBackend * backend,GVfsJobCreateMonitor * job,const char * filename,GFileMonitorFlags flags)3445 do_create_file_monitor (GVfsBackend *backend,
3446                         GVfsJobCreateMonitor *job,
3447                         const char *filename,
3448                         GFileMonitorFlags flags)
3449 {
3450   char *dir;
3451   char *name;
3452   GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
3453   MonitorProxy *proxy;
3454 
3455   g_debug ("create_file_monitor (%s)\n", filename);
3456 
3457   split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
3458 
3459   proxy = g_new0 (MonitorProxy, 1);
3460   proxy->path = add_ignore_prefix (gphoto2_backend, filename);
3461   proxy->vfs_monitor = g_vfs_monitor_new (backend);
3462 
3463   gphoto2_backend->file_monitor_proxies = g_list_prepend (gphoto2_backend->file_monitor_proxies, proxy);
3464 
3465   g_vfs_job_create_monitor_set_monitor (job, proxy->vfs_monitor);
3466   g_object_weak_ref (G_OBJECT (proxy->vfs_monitor), vfs_file_monitor_destroyed, gphoto2_backend);
3467   g_object_unref (proxy->vfs_monitor);
3468   g_vfs_job_succeeded (G_VFS_JOB (job));
3469 }
3470 
3471 /* ------------------------------------------------------------------------------------------------- */
3472 
3473 static void
g_vfs_backend_gphoto2_class_init(GVfsBackendGphoto2Class * klass)3474 g_vfs_backend_gphoto2_class_init (GVfsBackendGphoto2Class *klass)
3475 {
3476   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
3477   GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass);
3478 
3479   gobject_class->finalize = g_vfs_backend_gphoto2_finalize;
3480 
3481   backend_class->try_mount = try_mount;
3482   backend_class->mount = do_mount;
3483   backend_class->unmount = do_unmount;
3484   backend_class->open_icon_for_read = do_open_icon_for_read;
3485   backend_class->open_for_read = do_open_for_read;
3486   backend_class->try_read = try_read;
3487   backend_class->try_seek_on_read = try_seek_on_read;
3488   backend_class->close_read = do_close_read;
3489   backend_class->query_info = do_query_info;
3490   backend_class->enumerate = do_enumerate;
3491   backend_class->query_fs_info = do_query_fs_info;
3492   backend_class->make_directory = do_make_directory;
3493   backend_class->set_display_name = do_set_display_name;
3494   backend_class->delete = do_delete;
3495   backend_class->create = do_create;
3496   backend_class->replace = do_replace;
3497   backend_class->append_to = do_append_to;
3498   backend_class->write = do_write;
3499   backend_class->close_write = do_close_write;
3500   backend_class->seek_on_write = do_seek_on_write;
3501   backend_class->truncate = do_truncate;
3502   backend_class->move = do_move;
3503   backend_class->pull = do_pull;
3504   backend_class->create_dir_monitor = do_create_dir_monitor;
3505   backend_class->create_file_monitor = do_create_file_monitor;
3506 
3507   /* fast sync versions that only succeed if info is in the cache */
3508   backend_class->try_query_info = try_query_info;
3509   backend_class->try_enumerate = try_enumerate;
3510   backend_class->try_query_fs_info = try_query_fs_info;
3511 }
3512