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