summaryrefslogtreecommitdiff
path: root/gnome-2-24/daemon/gvfsbackendgphoto2.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnome-2-24/daemon/gvfsbackendgphoto2.c')
-rw-r--r--gnome-2-24/daemon/gvfsbackendgphoto2.c3404
1 files changed, 0 insertions, 3404 deletions
diff --git a/gnome-2-24/daemon/gvfsbackendgphoto2.c b/gnome-2-24/daemon/gvfsbackendgphoto2.c
deleted file mode 100644
index f06605c1..00000000
--- a/gnome-2-24/daemon/gvfsbackendgphoto2.c
+++ /dev/null
@@ -1,3404 +0,0 @@
-
-/* GVFS gphoto2 file system driver
- *
- * Copyright (C) 2007-2008 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: David Zeuthen <davidz@redhat.com>
- */
-
-/* NOTE: since we link the libcdio libs (GPLv2) into our process space
- * the combined work is GPLv2. This source file, however, is LGPLv2+.
- */
-
-#include <config.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include <glib/gstdio.h>
-#include <glib/gi18n.h>
-#include <gio/gio.h>
-#include <gphoto2.h>
-#include <libhal.h>
-#include <dbus/dbus.h>
-#include <sys/time.h>
-
-#include "gvfsbackendgphoto2.h"
-#include "gvfsjobopenforread.h"
-#include "gvfsjobread.h"
-#include "gvfsjobseekread.h"
-#include "gvfsjobqueryinfo.h"
-#include "gvfsjobenumerate.h"
-#include "gvfsjobsetdisplayname.h"
-#include "gvfsjobopenforwrite.h"
-#include "gvfsjobwrite.h"
-#include "gvfsjobclosewrite.h"
-#include "gvfsjobcreatemonitor.h"
-#include "gvfsmonitor.h"
-#include "gvfsjobseekwrite.h"
-
-/* showing debug traces */
-#if 0
-#define DEBUG_SHOW_TRACES 1
-#endif
-
-/* showing libgphoto2 output */
-#if 0
-#define DEBUG_SHOW_LIBGPHOTO2_OUTPUT 1
-#endif
-
-/* use this to disable caching */
-#if 0
-#define DEBUG_NO_CACHING 1
-#endif
-
-/*--------------------------------------------------------------------------------------------------------------*/
-
-/* TODO:
- *
- * - write support
- * - it's in; we support writing. yay.
- * - though there's no way to rename an non-empty folder yet
- * - there's an assumption, for caching, that the device won't
- * be able to put files while we're using it. May have to
- * revisit that if such devices exist.
- * - one solution: make cache items valid for only five seconds or something
- *
- * - Note that most PTP devices (e.g. digital cameras) don't support writing
- * - Most MTP devices (e.g. digital audio players) do
- *
- * - However, some MTP devices are just busted when using ~ backup
- * style; see below. This is with my (davidz) Sandisk Sansa
- * e250. This is probably a firmware bug; when investigating
- * libgphoto2 reports everything as peachy.
- *
- * $ pwd
- * /home/davidz/.gvfs/gphoto2 mount on usb%3A001,051/foo
- * $ echo a > a
- * $ echo b > b
- * $ ls -l
- * total 0
- * -rw------- 1 davidz davidz 2 2008-03-02 13:22 a
- * -rw------- 1 davidz davidz 2 2008-03-02 13:22 b
- * $ cat a
- * a
- * $ cat b
- * b
- * $ mv b a
- * $ ls -l
- * total 0
- * -rw------- 1 davidz davidz 2 2008-03-02 13:22 a
- * $ cat a
- * b
- * $ rm a
- *
- * See, this worked fine.. Now see what happens if we
- * use different files names
- *
- * $ echo a > file.txt
- * $ echo b > file.txt~
- * $ ls -l
- * total 0
- * -rw------- 1 davidz davidz 2 2008-03-02 13:22 file.txt
- * -rw------- 1 davidz davidz 2 2008-03-02 13:22 file.txt~
- * $ cat file.txt
- * a
- * $ cat file.txt~
- * b
- * $ mv file.txt~ file.txt
- * $ ls -l
- * total 0
- * -rw------- 1 davidz davidz 0 1969-12-31 18:59 file.txt
- * $ cat file.txt
- * $
- *
- * Awesome. I hate hardware.
- *
- * - This is a bit bad as it affects most text editors (vim, emacs,
- * gedit) and it actually results in data loss. However, there's
- * little we can do about it.
- *
- * - Would be nice to test this on other MTP devices to verify
- * it's indeed a firmware bug in the Sansa Sandisk e250.
- *
- * - This shouldn't affect stuff like Banshee or Rhythmbox using
- * this backend for MTP support though; despite this bug basic
- * file operations works nicely.
- * - http://bugzilla.gnome.org/show_bug.cgi?id=520121
- *
- * - Need to test this with a native gio version of gedit that should
- * use replace() directly instead of fooling around with ~-style
- * backup files
- *
- * - support for multiple storage heads
- * - need a device that supports this
- * - should be different mounts so need to infect GHalVolumeMonitor with libgphoto2
- * - probably not a huge priority to add
- * - might help properly resolve the hack we're doing in ensure_ignore_prefix()
- * - http://bugzilla.gnome.org/show_bug.cgi?id=520123
- *
- * - adding a payload cache don't make much sense as libgphoto2 has a LRU cache already
- * - (see comment in the do_close_write() function)
- *
- * - Support PTP/IP devices nicely
- * - Need hardware for testing
- * - Should actually work out of the box; just try mounting e.g.
- * gphoto2://[ptpip:<something]/ from either Nautilus or via
- * gvfs-mount(1).
- * - Need to automatically unmount when the device stops answering
- * - May need authentication bits
- * - Need integration into network://
- * - does such devices use DNS-SD or UPNP?
- *
- */
-
-struct _GVfsBackendGphoto2
-{
- GVfsBackend parent_instance;
-
- /* a gphoto2 specific identifier for the gphoto2 camera such as usb:001,041 */
- char *gphoto2_port;
- GPContext *context;
- Camera *camera;
-
- /* see comment in ensure_ignore_prefix() */
- char *ignore_prefix;
-
- /* list of open files */
- int num_open_files_for_reading;
-
- DBusConnection *dbus_connection;
- LibHalContext *hal_ctx;
- char *hal_udi;
- char *hal_name;
- char *hal_icon_name;
-
- /* whether we can write to the device */
- gboolean can_write;
-
- /* This lock protects all members in this class that are not
- * used both on the main thread and on the IO thread.
- *
- * It is used, among other places, in the try_* functions to return
- * already cached data quickly (to e.g. enumerate and get file info
- * while we're reading or writing a file from the device).
- *
- * Must only be held for very short amounts of time (e.g. no IO).
- */
- GMutex *lock;
-
- /* CACHES */
-
- /* free_space is set to -1 if we don't know or have modified the
- * device since last time we read it. If -1 we can't do
- * try_query_fs_info() and will fall back to do_query_fs_info().
- */
- gint64 free_space;
- gint64 capacity;
-
- /* fully qualified path -> GFileInfo */
- GHashTable *info_cache;
-
- /* dir name -> CameraList of (sub-) directory names in given directory */
- GHashTable *dir_name_cache;
-
- /* dir name -> CameraList of file names in given directory */
- GHashTable *file_name_cache;
-
- /* monitors (only used on the IO thread) */
- GList *dir_monitor_proxies;
- GList *file_monitor_proxies;
-
- /* list of open write handles (only used on the IO thread) */
- GList *open_write_handles;
-};
-
-G_DEFINE_TYPE (GVfsBackendGphoto2, g_vfs_backend_gphoto2, G_VFS_TYPE_BACKEND);
-
-/* ------------------------------------------------------------------------------------------------- */
-
-typedef struct {
- /* this is the path of the dir/file including ignore_prefix */
- char *path;
- GVfsMonitor *vfs_monitor;
-} MonitorProxy;
-
-static void
-monitor_proxy_free (MonitorProxy *proxy)
-{
- g_free (proxy->path);
- /* vfs_monitor is owned by the gvfs core; see the functions
- * vfs_dir_monitor_destroyed() and do_create_monitor()
- */
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-typedef struct {
- /* filename as given from the vfs without ignore prefix e.g. /foo.txt */
- char *filename;
-
- /* filename with ignore prefix splitted into dir and name; e.g. "/store_00010001/" and "foo.txt" */
- char *dir;
- char *name;
-
- char *data;
- unsigned long int size;
- unsigned long int cursor;
- unsigned long int allocated_size;
-
- gboolean job_is_replace;
- gboolean job_is_append_to;
-
- gboolean delete_before;
-
- gboolean is_dirty;
-} WriteHandle;
-
-/* how much more memory to ask for when using g_realloc() when writing a file */
-#define WRITE_INCREMENT 4096
-
-typedef struct {
- CameraFile *file;
-
- const char *data;
- unsigned long int size;
- unsigned long int cursor;
-} ReadHandle;
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-DEBUG (const gchar *message, ...)
-{
-#ifdef DEBUG_SHOW_TRACES
- va_list args;
- va_start (args, message);
- g_vfprintf (stderr, message, args);
- va_end (args);
- g_fprintf (stderr, "\n");
- fflush (stderr);
-#endif
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static int commit_write_handle (GVfsBackendGphoto2 *gphoto2_backend, WriteHandle *write_handle);
-
-static void
-write_handle_free (WriteHandle *write_handle)
-{
- g_free (write_handle->filename);
- g_free (write_handle->dir);
- g_free (write_handle->name);
- g_free (write_handle->data);
- g_free (write_handle);
-}
-
-/* This must be called before reading from the device to ensure that
- * all pending writes are written to the device.
- *
- * Must only be called on the IO thread.
- */
-static void
-ensure_not_dirty (GVfsBackendGphoto2 *gphoto2_backend)
-{
- GList *l;
-
- for (l = gphoto2_backend->open_write_handles; l != NULL; l = l->next)
- {
- WriteHandle *write_handle = l->data;
-
- DEBUG ("ensure_not_dirty: looking at handle for '%s", write_handle->filename);
-
- if (write_handle->is_dirty)
- commit_write_handle (gphoto2_backend, write_handle);
- }
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-/* used when gphoto2 will take ownership of this data for it's LRU cache - and will use free(3) to free it */
-static char *
-dup_for_gphoto2 (char *gmem, unsigned long int size)
-{
- char *mem;
- mem = malloc (size);
- memcpy (mem, gmem, size);
- return mem;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-monitors_emit_internal (GVfsBackendGphoto2 *gphoto2_backend,
- const char *dir,
- const char *name,
- GFileMonitorEvent event,
- const char *event_name)
-{
- GList *l;
- char *filepath;
-
- g_return_if_fail (g_str_has_prefix (dir, gphoto2_backend->ignore_prefix));
-
- DEBUG ("monitors_emit_internal() %s for '%s' '%s'", event_name, dir, name);
-
- for (l = gphoto2_backend->dir_monitor_proxies; l != NULL; l = l->next)
- {
- MonitorProxy *proxy = l->data;
- if (strcmp (proxy->path, dir) == 0)
- {
- char *path;
- path = g_build_filename (dir + strlen (gphoto2_backend->ignore_prefix), name, NULL);
- g_vfs_monitor_emit_event (proxy->vfs_monitor, event, path, NULL);
- DEBUG (" emitted %s for '%s' on dir monitor for '%s'", event_name, path, dir);
- g_free (path);
- }
- }
-
- filepath = g_build_filename (dir, name, NULL);
- for (l = gphoto2_backend->file_monitor_proxies; l != NULL; l = l->next)
- {
- MonitorProxy *proxy = l->data;
- if (strcmp (proxy->path, filepath) == 0)
- {
- const char *path = filepath + strlen (gphoto2_backend->ignore_prefix);
- g_vfs_monitor_emit_event (proxy->vfs_monitor, event, path, NULL);
- DEBUG (" emitted %s for '%s' on file monitor", event_name, path);
- }
- }
- g_free (filepath);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-/* call this when a file/directory have been added to a directory */
-static void
-monitors_emit_created (GVfsBackendGphoto2 *gphoto2_backend, const char *dir, const char *name)
-{
- DEBUG ("monitors_emit_created(): '%s' '%s'", dir, name);
- monitors_emit_internal (gphoto2_backend, dir, name, G_FILE_MONITOR_EVENT_CREATED, "CREATED");
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-/* call this when a file/directory have been deleted from a directory */
-static void
-monitors_emit_deleted (GVfsBackendGphoto2 *gphoto2_backend, const char *dir, const char *name)
-{
- DEBUG ("monitors_emit_deleted(): '%s' '%s'", dir, name);
- monitors_emit_internal (gphoto2_backend, dir, name, G_FILE_MONITOR_EVENT_DELETED, "DELETED");
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-/* call this when a file/directory have been changed in a directory */
-static void
-monitors_emit_changed (GVfsBackendGphoto2 *gphoto2_backend, const char *dir, const char *name)
-{
- DEBUG ("monitors_emit_changed(): '%s' '%s'", dir, name);
- monitors_emit_internal (gphoto2_backend, dir, name, G_FILE_MONITOR_EVENT_CHANGED, "CHANGED");
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-caches_invalidate_all (GVfsBackendGphoto2 *gphoto2_backend)
-{
- DEBUG ("caches_invalidate_all()");
-
- g_mutex_lock (gphoto2_backend->lock);
- if (gphoto2_backend->dir_name_cache != NULL)
- g_hash_table_remove_all (gphoto2_backend->dir_name_cache);
- if (gphoto2_backend->file_name_cache != NULL)
- g_hash_table_remove_all (gphoto2_backend->file_name_cache);
- if (gphoto2_backend->info_cache != NULL)
- g_hash_table_remove_all (gphoto2_backend->info_cache);
- gphoto2_backend->capacity = -1;
- gphoto2_backend->free_space = -1;
- g_mutex_unlock (gphoto2_backend->lock);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-caches_invalidate_free_space (GVfsBackendGphoto2 *gphoto2_backend)
-{
- g_mutex_lock (gphoto2_backend->lock);
- gphoto2_backend->free_space = -1;
- g_mutex_unlock (gphoto2_backend->lock);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-caches_invalidate_dir (GVfsBackendGphoto2 *gphoto2_backend, const char *dir)
-{
- DEBUG ("caches_invalidate_dir() for '%s'", dir);
- g_mutex_lock (gphoto2_backend->lock);
- g_hash_table_remove (gphoto2_backend->dir_name_cache, dir);
- g_hash_table_remove (gphoto2_backend->file_name_cache, dir);
- g_hash_table_remove (gphoto2_backend->info_cache, dir);
- g_mutex_unlock (gphoto2_backend->lock);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-caches_invalidate_file (GVfsBackendGphoto2 *gphoto2_backend, const char *dir, const char *name)
-{
- char *full_name;
-
- full_name = g_build_filename (dir, name, NULL);
-
- g_mutex_lock (gphoto2_backend->lock);
- /* this is essentially: caches_invalidate_dir (gphoto2_backend, dir); */
- g_hash_table_remove (gphoto2_backend->dir_name_cache, dir);
- g_hash_table_remove (gphoto2_backend->file_name_cache, dir);
- g_hash_table_remove (gphoto2_backend->info_cache, dir);
-
- g_hash_table_remove (gphoto2_backend->info_cache, full_name);
- g_mutex_unlock (gphoto2_backend->lock);
-
- DEBUG ("caches_invalidate_file() for '%s'", full_name);
- g_free (full_name);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static GError *
-get_error_from_gphoto2 (const char *message, int rc)
-{
- GError *error;
-
- switch (rc)
- {
- case GP_ERROR_FILE_EXISTS:
- case GP_ERROR_DIRECTORY_EXISTS:
- /* Translator: %s represents a more specific error message and %d the specific error code */
- error = g_error_new (G_IO_ERROR,
- G_IO_ERROR_EXISTS, _("%s: %d: Directory or file exists"), message, rc);
- break;
-
- case GP_ERROR_FILE_NOT_FOUND:
- case GP_ERROR_DIRECTORY_NOT_FOUND:
- /* Translator: %s represents a more specific error message and %d the specific error code */
- error = g_error_new (G_IO_ERROR,
- G_IO_ERROR_NOT_FOUND, _("%s: %d: No such file or directory"), message, rc);
- break;
-
- case GP_ERROR_PATH_NOT_ABSOLUTE:
- /* Translator: %s represents a more specific error message and %d the specific error code */
- error = g_error_new (G_IO_ERROR,
- G_IO_ERROR_INVALID_FILENAME, _("%s: %d: Invalid filename"), message, rc);
- break;
-
- case GP_ERROR_NOT_SUPPORTED:
- /* Translator: %s represents a more specific error message and %d the specific error code */
- error = g_error_new (G_IO_ERROR,
- G_IO_ERROR_NOT_SUPPORTED, _("%s: %d: Not Supported"), message, rc);
- break;
-
- default:
- error = g_error_new (G_IO_ERROR,
- G_IO_ERROR_FAILED, "%s: %d: %s", message, rc, gp_result_as_string (rc));
- break;
- }
- return error;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-release_device (GVfsBackendGphoto2 *gphoto2_backend)
-{
- GList *l;
-
- g_free (gphoto2_backend->gphoto2_port);
- gphoto2_backend->gphoto2_port = NULL;
-
- if (gphoto2_backend->context != NULL)
- {
- gp_context_unref (gphoto2_backend->context);
- gphoto2_backend->context = NULL;
- }
-
- if (gphoto2_backend->camera != NULL)
- {
- gp_camera_unref (gphoto2_backend->camera);
- gphoto2_backend->camera = NULL;
- }
-
- if (gphoto2_backend->dbus_connection != NULL)
- {
- dbus_connection_close (gphoto2_backend->dbus_connection);
- dbus_connection_unref (gphoto2_backend->dbus_connection);
- gphoto2_backend->dbus_connection = NULL;
- }
-
- if (gphoto2_backend->hal_ctx != NULL)
- {
- libhal_ctx_free (gphoto2_backend->hal_ctx);
- gphoto2_backend->hal_ctx = NULL;
-
- }
- g_free (gphoto2_backend->hal_udi);
- gphoto2_backend->hal_udi = NULL;
- g_free (gphoto2_backend->hal_name);
- gphoto2_backend->hal_name = NULL;
- g_free (gphoto2_backend->hal_icon_name);
- gphoto2_backend->hal_icon_name = NULL;
-
- g_free (gphoto2_backend->ignore_prefix);
- gphoto2_backend->ignore_prefix = NULL;
-
- if (gphoto2_backend->info_cache != NULL)
- {
- g_hash_table_unref (gphoto2_backend->info_cache);
- gphoto2_backend->info_cache = NULL;
- }
- if (gphoto2_backend->dir_name_cache != NULL)
- {
- g_hash_table_unref (gphoto2_backend->dir_name_cache);
- gphoto2_backend->dir_name_cache = NULL;
- }
- if (gphoto2_backend->file_name_cache != NULL)
- {
- g_hash_table_unref (gphoto2_backend->file_name_cache);
- gphoto2_backend->file_name_cache = NULL;
- }
-
- for (l = gphoto2_backend->dir_monitor_proxies; l != NULL; l = l->next)
- {
- MonitorProxy *proxy = l->data;
- monitor_proxy_free (proxy);
- }
- g_list_free (gphoto2_backend->dir_monitor_proxies);
- gphoto2_backend->dir_monitor_proxies = NULL;
-
- for (l = gphoto2_backend->file_monitor_proxies; l != NULL; l = l->next)
- {
- MonitorProxy *proxy = l->data;
- monitor_proxy_free (proxy);
- }
- g_list_free (gphoto2_backend->file_monitor_proxies);
- gphoto2_backend->file_monitor_proxies = NULL;
-
- if (gphoto2_backend->lock != NULL)
- {
- g_mutex_free (gphoto2_backend->lock);
- gphoto2_backend->lock = NULL;
- }
- gphoto2_backend->capacity = -1;
- gphoto2_backend->free_space = -1;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-g_vfs_backend_gphoto2_finalize (GObject *object)
-{
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (object);
-
- DEBUG ("finalizing %p", object);
-
- release_device (gphoto2_backend);
-
- if (G_OBJECT_CLASS (g_vfs_backend_gphoto2_parent_class)->finalize)
- (*G_OBJECT_CLASS (g_vfs_backend_gphoto2_parent_class)->finalize) (object);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-#ifdef DEBUG_SHOW_LIBGPHOTO2_OUTPUT
-static void
-_gphoto2_logger_func (GPLogLevel level, const char *domain, const char *format, va_list args, void *data)
-{
- g_fprintf (stderr, "libgphoto2: %s: ", domain);
- g_vfprintf (stderr, message, args);
- va_end (args);
- g_fprintf (stderr, "\n");
-}
-#endif
-
-static void
-g_vfs_backend_gphoto2_init (GVfsBackendGphoto2 *gphoto2_backend)
-{
- GVfsBackend *backend = G_VFS_BACKEND (gphoto2_backend);
- GMountSpec *mount_spec;
-
- DEBUG ("initing %p", gphoto2_backend);
-
- g_vfs_backend_set_display_name (backend, "gphoto2");
-
- mount_spec = g_mount_spec_new ("gphoto2");
- g_vfs_backend_set_mount_spec (backend, mount_spec);
- g_mount_spec_unref (mount_spec);
-
-#ifdef DEBUG_SHOW_LIBGPHOTO2_OUTPUT
- gp_log_add_func (GP_LOG_ALL, _gphoto2_logger_func, NULL);
-#endif
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static char *
-compute_icon_name (GVfsBackendGphoto2 *gphoto2_backend)
-{
- char *result;
-
- if (gphoto2_backend->hal_icon_name == NULL)
- {
- result = g_strdup_printf ("camera-photo");
- }
- else
- {
- result = g_strdup (gphoto2_backend->hal_icon_name);
- }
-
- return result;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static char *
-compute_display_name (GVfsBackendGphoto2 *gphoto2_backend)
-{
- char *result;
-
- if (gphoto2_backend->hal_name == NULL)
- {
- /* Translator: %s represents the device, e.g. usb:001,042 */
- result = g_strdup_printf (_("Digital Camera (%s)"), gphoto2_backend->gphoto2_port);
- }
- else
- {
- result = g_strdup (gphoto2_backend->hal_name);
- }
-
- return result;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-find_udi_for_device (GVfsBackendGphoto2 *gphoto2_backend)
-{
- int num_camera_devices;
- int num_mtp_devices;
- int num_devices;
- char **camera_devices;
- char **mtp_devices;
- char **devices;
- int n, m;
- int usb_bus_num;
- int usb_device_num;
- char **tokens;
- char *endp;
- char *camera_x_content_types[] = {"x-content/image-dcf", NULL};
- char *music_player_x_content_types[] = {"x-content/audio-player", NULL};
-
- gphoto2_backend->hal_udi = NULL;
-
- /* parse the usb:001,041 string */
-
- if (!g_str_has_prefix (gphoto2_backend->gphoto2_port, "usb:"))
- {
- return;
- }
-
- tokens = g_strsplit (gphoto2_backend->gphoto2_port + 4, ",", 0);
- if (g_strv_length (tokens) != 2)
- {
- g_strfreev (tokens);
- return;
- }
-
- usb_bus_num = strtol (tokens[0], &endp, 10);
- if (*endp != '\0')
- {
- g_strfreev (tokens);
- return;
- }
-
- usb_device_num = strtol (tokens[1], &endp, 10);
- if (*endp != '\0')
- {
- g_strfreev (tokens);
- return;
- }
-
- g_strfreev (tokens);
-
- DEBUG ("Parsed '%s' into bus=%d device=%d", gphoto2_backend->gphoto2_port, usb_bus_num, usb_device_num);
-
- camera_devices = libhal_find_device_by_capability (gphoto2_backend->hal_ctx,
- "camera",
- &num_camera_devices,
- NULL);
- mtp_devices = libhal_find_device_by_capability (gphoto2_backend->hal_ctx,
- "portable_audio_player",
- &num_mtp_devices,
- NULL);
- for (m = 0; m < 2 && gphoto2_backend->hal_udi == NULL; m++)
- {
- devices = m == 0 ? camera_devices : mtp_devices;
- num_devices = m == 0 ? num_camera_devices : num_mtp_devices;
-
- if (devices != NULL)
- {
- for (n = 0; n < num_devices && gphoto2_backend->hal_udi == NULL; n++)
- {
- char *udi = devices[n];
- LibHalPropertySet *ps;
-
- ps = libhal_device_get_all_properties (gphoto2_backend->hal_ctx, udi, NULL);
- if (ps != NULL)
- {
- const char *subsystem;
-
- subsystem = libhal_ps_get_string (ps, "info.subsystem");
- if (subsystem != NULL && strcmp (subsystem, "usb") == 0)
- {
- int device_usb_bus_num;
- int device_usb_device_num;
- const char *icon_from_hal;
- const char *name_from_hal;
-
- device_usb_bus_num = libhal_ps_get_int32 (ps, "usb.bus_number");
- device_usb_device_num = libhal_ps_get_int32 (ps, "usb.linux.device_number");
- icon_from_hal = libhal_ps_get_string (ps, "info.desktop.icon");
- name_from_hal = libhal_ps_get_string (ps, "info.desktop.name");
-
- DEBUG ("looking at usb device '%s' with bus=%d, device=%d",
- udi, device_usb_bus_num, device_usb_device_num);
-
- if (device_usb_bus_num == usb_bus_num &&
- device_usb_device_num == usb_device_num)
- {
- char *name;
- const char *parent_udi;
- LibHalPropertySet *ps2;
-
- DEBUG ("udi '%s' is the one!", udi);
-
- /* IMPORTANT:
- *
- * Keep this naming code in sync with
- *
- * hal/ghalvolume;do_update_from_hal_for_camera()
- */
- name = NULL;
- parent_udi = libhal_ps_get_string (ps, "info.parent");
- if (name_from_hal != NULL)
- {
- name = g_strdup (name_from_hal);
- }
- else if (parent_udi != NULL)
- {
- ps2 = libhal_device_get_all_properties (gphoto2_backend->hal_ctx, parent_udi, NULL);
- if (ps2 != NULL)
- {
- const char *vendor;
- const char *product;
-
- vendor = libhal_ps_get_string (ps2, "usb_device.vendor");
- product = libhal_ps_get_string (ps2, "usb_device.product");
- if (vendor == NULL)
- {
- if (product != NULL)
- name = g_strdup (product);
- }
- else
- {
- if (product != NULL)
- name = g_strdup_printf ("%s %s", vendor, product);
- else
- {
- if (m == 0)
- /* Translator: %s is the vendor name, e.g. Panasonic */
- name = g_strdup_printf (_("%s Camera"), vendor);
- else
- /* Translator: %s is the vendor name, e.g. Panasonic */
- name = g_strdup_printf (_("%s Audio Player"), vendor);
- }
- }
- libhal_free_property_set (ps2);
- }
- }
- if (name == NULL)
- {
- if (m == 0)
- name = g_strdup (_("Camera"));
- else
- name = g_strdup (_("Audio Player"));
- }
-
- gphoto2_backend->hal_udi = g_strdup (udi);
- gphoto2_backend->hal_name = name;
- if (icon_from_hal != NULL)
- {
- gphoto2_backend->hal_icon_name = g_strdup (icon_from_hal);
- }
- else
- {
- if (m == 1)
- {
- gphoto2_backend->hal_icon_name = g_strdup ("multimedia-player");
- }
- else
- {
- gphoto2_backend->hal_icon_name = g_strdup ("camera-photo");
- }
- }
-
- /* TODO: should we sniff the files instead? */
- if (m == 0)
- {
- g_vfs_backend_set_x_content_types (G_VFS_BACKEND (gphoto2_backend),
- camera_x_content_types);
- }
- else
- {
- g_vfs_backend_set_x_content_types (G_VFS_BACKEND (gphoto2_backend),
- music_player_x_content_types);
- }
-
- }
-
- }
-
- libhal_free_property_set (ps);
- }
- }
- libhal_free_string_array (devices);
- }
- }
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-_hal_device_removed (LibHalContext *hal_ctx, const char *udi)
-{
- GVfsBackendGphoto2 *gphoto2_backend;
-
- gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (libhal_ctx_get_user_data (hal_ctx));
-
- if (gphoto2_backend->hal_udi != NULL && strcmp (udi, gphoto2_backend->hal_udi) == 0)
- {
- DEBUG ("we have been removed!");
-
- /* nuke all caches so we're a bit more valgrind friendly */
- caches_invalidate_all (gphoto2_backend);
-
- /* TODO: need a cleaner way to force unmount ourselves */
- exit (1);
- }
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-split_filename_with_ignore_prefix (GVfsBackendGphoto2 *gphoto2_backend, const char *filename, char **dir, char **name)
-{
- char *s;
-
- s = g_path_get_dirname (filename);
- if (s[0] == '/')
- *dir = g_strconcat (gphoto2_backend->ignore_prefix, s + 1, NULL);
- else
- *dir = g_strconcat (gphoto2_backend->ignore_prefix, s, NULL);
- g_free (s);
-
- if (strcmp (filename, "/") == 0)
- *name = g_strdup ("");
- else
- *name = g_path_get_basename (filename);
-
- s = *dir;
- if (s[strlen(s)] == '/')
- s[strlen(s)] = '\0';
-
- /*DEBUG ("split_filename_with_ignore_prefix: '%s' -> '%s' '%s'", filename, *dir, *name);*/
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static char *
-add_ignore_prefix (GVfsBackendGphoto2 *gphoto2_backend, const char *filename)
-{
- char *result;
-
- if (filename[0] == '/')
- result = g_strconcat (gphoto2_backend->ignore_prefix, filename + 1, NULL);
- else
- result = g_strconcat (gphoto2_backend->ignore_prefix, filename, NULL);
-
- /*DEBUG ("add_ignore_prefix: '%s' -> '%s'", filename, result);*/
- return result;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-/* the passed 'dir' variable must contain ignore_prefix */
-static gboolean
-file_get_info (GVfsBackendGphoto2 *gphoto2_backend,
- const char *dir,
- const char *name,
- GFileInfo *info,
- GError **error,
- gboolean try_cache_only)
-{
- int rc;
- gboolean ret;
- CameraFileInfo gp_info;
- char *full_path;
- GFileInfo *cached_info;
- GTimeVal mtime;
- char *mime_type;
- GIcon *icon;
- unsigned int n;
-
- ret = FALSE;
-
- full_path = g_build_filename (dir, name, NULL);
- DEBUG ("file_get_info() try_cache_only=%d dir='%s', name='%s'\n"
- " full_path='%s'",
- try_cache_only, dir, name, full_path, gphoto2_backend->ignore_prefix);
-
-
- /* first look up cache */
- g_mutex_lock (gphoto2_backend->lock);
- cached_info = g_hash_table_lookup (gphoto2_backend->info_cache, full_path);
- if (cached_info != NULL)
- {
- g_file_info_copy_into (cached_info, info);
- g_mutex_unlock (gphoto2_backend->lock);
- DEBUG (" Using cached info %p for '%s'", cached_info, full_path);
- ret = TRUE;
- goto out;
- }
- g_mutex_unlock (gphoto2_backend->lock);
-
- if (try_cache_only)
- goto out;
-
- ensure_not_dirty (gphoto2_backend);
-
- DEBUG (" No cached info for '%s'", full_path);
-
- /* Since we're caching stuff, make sure all information we store is set */
- g_file_info_unset_attribute_mask (info);
-
- /* handle root directory */
- if (strcmp (full_path, gphoto2_backend->ignore_prefix) == 0 || strcmp (full_path, "/") == 0)
- {
- char *display_name;
- display_name = compute_display_name (gphoto2_backend);
- g_file_info_set_display_name (info, display_name);
- g_file_info_set_name (info, display_name);
- g_free (display_name);
- g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
- g_file_info_set_content_type (info, "inode/directory");
- g_file_info_set_size (info, 0);
- icon = g_themed_icon_new ("folder");
- g_file_info_set_icon (info, icon);
- g_object_unref (icon);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, TRUE);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, gphoto2_backend->can_write);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, gphoto2_backend->can_write);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, TRUE);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, FALSE);
- ret = TRUE;
- DEBUG (" Generating info (root folder) for '%s'", full_path);
- goto add_to_cache;
- }
-
- rc = gp_camera_file_get_info (gphoto2_backend->camera,
- dir,
- name,
- &gp_info,
- gphoto2_backend->context);
- if (rc != 0)
- {
- CameraList *list;
- gboolean is_folder;
-
- /* gphoto2 doesn't know about this file.. it may be a folder; try that */
- is_folder = FALSE;
- gp_list_new (&list);
- rc = gp_camera_folder_list_folders (gphoto2_backend->camera,
- dir,
- list,
- gphoto2_backend->context);
- if (rc == 0)
- {
- for (n = 0; n < gp_list_count (list) && !is_folder; n++)
- {
- const char *folder_name;
- gp_list_get_name (list, n, &folder_name);
- if (strcmp (folder_name, name) == 0)
- {
- is_folder = TRUE;
- }
- }
- }
- gp_list_free (list);
-
- if (is_folder)
- {
- g_file_info_set_name (info, name);
- g_file_info_set_display_name (info, name);
- icon = g_themed_icon_new ("folder");
- g_file_info_set_icon (info, icon);
- g_object_unref (icon);
- g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
- g_file_info_set_content_type (info, "inode/directory");
- g_file_info_set_size (info, 0);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, TRUE);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, gphoto2_backend->can_write);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, gphoto2_backend->can_write);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, TRUE);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, gphoto2_backend->can_write);
- g_file_info_set_is_hidden (info, name != NULL && name[0] == '.');
- ret = TRUE;
- DEBUG (" Generating info (folder) for '%s'", full_path);
- goto add_to_cache;
- }
-
- /* nope, not a folder either.. error out.. */
- if (error != NULL)
- {
- *error = g_error_new (G_IO_ERROR,
- G_IO_ERROR_NOT_FOUND,
- _("No such file or directory"));
- }
- goto out;
- }
-
- g_file_info_set_name (info, name);
- g_file_info_set_display_name (info, name);
- g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR);
-
- if (gp_info.file.fields & GP_FILE_INFO_SIZE)
- {
- g_file_info_set_size (info, gp_info.file.size);
- }
- else
- {
- /* not really sure this is the right thing to do... */
- g_file_info_set_size (info, 0);
- }
-
- /* TODO: We really should sniff the file / look at file extensions
- * instead of relying on gp_info.file.type... but sniffing the file
- * is no fun since we (currently) can't do partial reads with the
- * libgphoto2 API :-/
- */
- mime_type = NULL;
- if (gp_info.file.fields & GP_FILE_INFO_TYPE)
- {
- /* application/x-unknown is a bogus mime type return by some
- * devices (such as Sandisk Sansa music players) - ignore it.
- */
- if (strcmp (gp_info.file.type, "application/x-unknown") != 0)
- {
- mime_type = g_strdup (gp_info.file.type);
- }
- }
- if (mime_type == NULL)
- mime_type = g_content_type_guess (name, NULL, 0, NULL);
- if (mime_type == NULL)
- mime_type = g_strdup ("application/octet-stream");
- g_file_info_set_content_type (info, mime_type);
-
- icon = g_content_type_get_icon (mime_type);
- DEBUG (" got icon %p for mime_type '%s'", icon, mime_type);
- if (icon != NULL)
- {
- g_file_info_set_icon (info, icon);
- g_object_unref (icon);
- }
- g_free (mime_type);
-
-
- if (gp_info.file.fields & GP_FILE_INFO_MTIME)
- mtime.tv_sec = gp_info.file.mtime;
- else
- mtime.tv_sec = 0;
- mtime.tv_usec = 0;
- g_file_info_set_modification_time (info, &mtime);
-
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, TRUE);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, gphoto2_backend->can_write);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, gphoto2_backend->can_write);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, FALSE);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, gphoto2_backend->can_write);
- g_file_info_set_is_hidden (info, name != NULL && name[0] == '.');
-
- if (gp_info.file.fields & GP_FILE_INFO_PERMISSIONS) {
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
- gp_info.file.permissions & GP_FILE_PERM_DELETE);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE,
- gp_info.file.permissions & GP_FILE_PERM_DELETE);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME,
- gp_info.file.permissions & GP_FILE_PERM_DELETE);
- }
-
- ret = TRUE;
- DEBUG (" Generating info (file) for '%s'", full_path);
-
- add_to_cache:
- /* add this sucker to the cache */
- if (ret == TRUE)
- {
-#ifndef DEBUG_NO_CACHING
- cached_info = g_file_info_dup (info);
- DEBUG (" Storing cached info %p for '%s'", cached_info, full_path);
- g_mutex_lock (gphoto2_backend->lock);
- g_hash_table_insert (gphoto2_backend->info_cache, g_strdup (full_path), cached_info);
- g_mutex_unlock (gphoto2_backend->lock);
-#endif
- }
-
- out:
- g_free (full_path);
- return ret;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static gboolean
-is_directory (GVfsBackendGphoto2 *gphoto2_backend, const char *dir, const char *name)
-{
- GFileInfo *info;
- gboolean ret;
-
- ret = FALSE;
-
- info = g_file_info_new ();
- if (!file_get_info (gphoto2_backend, dir, name, info, NULL, FALSE))
- goto out;
-
- if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
- ret = TRUE;
-
- out:
- g_object_unref (info);
- return ret;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static gboolean
-is_regular (GVfsBackendGphoto2 *gphoto2_backend, const char *dir, const char *name)
-{
- GFileInfo *info;
- gboolean ret;
-
- ret = FALSE;
-
- info = g_file_info_new ();
- if (!file_get_info (gphoto2_backend, dir, name, info, NULL, FALSE))
- goto out;
-
- if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR)
- ret = TRUE;
-
- out:
- g_object_unref (info);
- return ret;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static gboolean
-is_directory_empty (GVfsBackendGphoto2 *gphoto2_backend, const char *dir)
-{
- CameraList *list;
- gboolean ret;
- int rc;
- int num_dirs;
- int num_files;
-
- DEBUG ("is_directory_empty begin (%s)", dir);
-
- /* TODO: use cache */
-
- ret = FALSE;
- num_dirs = 0;
- num_files = 0;
-
- gp_list_new (&list);
- rc = gp_camera_folder_list_files (gphoto2_backend->camera,
- dir,
- list,
- gphoto2_backend->context);
- if (rc == 0)
- num_files = gp_list_count (list);
- gp_list_free (list);
-
- if (num_files > 0)
- goto out;
-
- gp_list_new (&list);
- rc = gp_camera_folder_list_folders (gphoto2_backend->camera,
- dir,
- list,
- gphoto2_backend->context);
- if (rc == 0)
- num_dirs = gp_list_count (list);
- gp_list_free (list);
-
- if (num_dirs == 0 && num_files == 0)
- ret = TRUE;
-
- out:
- DEBUG (" is_directory_empty (%s) -> %d", dir, ret);
- return ret;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-/* The PTP gphoto2 backend puts an annoying virtual store_00010001
- * directory in the root (in fact 00010001 can be any hexedecimal
- * digit).
- *
- * We want to skip that as the x-content detection expects to find the
- * DCIM/ folder. As such, this function tries to detect the presence
- * of such a folder in the root and, if found, sets a variable that is
- * prepended to any path passed to libgphoto2. This is cached for as
- * long as we got a connection to libgphoto2. If this operation fails
- * then the passed job will be cancelled and this function will return
- * FALSE.
- *
- * This function needs to be called from do_mount().
- */
-static gboolean
-ensure_ignore_prefix (GVfsBackendGphoto2 *gphoto2_backend, GVfsJob *job)
-{
- int rc;
- char *prefix;
- GError *error;
- CameraList *list;
-
- /* already set */
- if (gphoto2_backend->ignore_prefix != NULL)
- return TRUE;
-
- /* check folders in / - if there is exactly one folder of the form "store_" followed by eight
- * hexadecimal digits.. then use that as ignore_prefix.. otherwise don't use anything
- */
-
- gp_list_new (&list);
- rc = gp_camera_folder_list_folders (gphoto2_backend->camera,
- "/",
- list,
- gphoto2_backend->context);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Failed to get folder list"), rc);
- g_vfs_job_failed_from_error (job, error);
- g_error_free (error);
- return FALSE;
- }
-
- prefix = NULL;
-
- if (gp_list_count (list) == 1)
- {
- char *name;
- const char *s;
- unsigned int n;
-
- gp_list_get_name (list, 0, &s);
-
- name = g_ascii_strdown (s, -1);
- if (g_str_has_prefix (name, "store_") && strlen (name) == 14)
- {
- for (n = 6; n < 14; n++)
- {
- if (!g_ascii_isxdigit (name[n]))
- {
- break;
- }
- }
- if (n == 14)
- {
- prefix = g_strconcat ("/", name, "/", NULL);
- }
- }
-
- g_free (name);
- }
- gp_list_free (list);
-
- if (prefix == NULL)
- gphoto2_backend->ignore_prefix = g_strdup ("/");
- else
- gphoto2_backend->ignore_prefix = prefix;
-
- return TRUE;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-do_mount (GVfsBackend *backend,
- GVfsJobMount *job,
- GMountSpec *mount_spec,
- GMountSource *mount_source,
- gboolean is_automount)
-{
- char *fuse_name;
- char *display_name;
- char *icon_name;
- const char *host;
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- GError *error = NULL;
- GMountSpec *gphoto2_mount_spec;
- int rc;
- GPPortInfo info;
- GPPortInfoList *il = NULL;
- int n;
- DBusError dbus_error;
- CameraStorageInformation *storage_info;
- int num_storage_info;
-
- DEBUG ("do_mount %p", gphoto2_backend);
-
- /* setup libhal */
-
- dbus_error_init (&dbus_error);
- gphoto2_backend->dbus_connection = dbus_bus_get_private (DBUS_BUS_SYSTEM, &dbus_error);
- if (dbus_error_is_set (&dbus_error))
- {
- release_device (gphoto2_backend);
- dbus_error_free (&dbus_error);
- g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot connect to the system bus"));
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- return;
- }
-
- dbus_connection_set_exit_on_disconnect (gphoto2_backend->dbus_connection, FALSE);
-
- gphoto2_backend->hal_ctx = libhal_ctx_new ();
- if (gphoto2_backend->hal_ctx == NULL)
- {
- release_device (gphoto2_backend);
- g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot create libhal context"));
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- return;
- }
-
- _g_dbus_connection_integrate_with_main (gphoto2_backend->dbus_connection);
- libhal_ctx_set_dbus_connection (gphoto2_backend->hal_ctx, gphoto2_backend->dbus_connection);
-
- if (!libhal_ctx_init (gphoto2_backend->hal_ctx, &dbus_error))
- {
- release_device (gphoto2_backend);
- dbus_error_free (&dbus_error);
- g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot initialize libhal"));
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- return;
- }
-
- libhal_ctx_set_device_removed (gphoto2_backend->hal_ctx, _hal_device_removed);
- libhal_ctx_set_user_data (gphoto2_backend->hal_ctx, gphoto2_backend);
-
- /* setup gphoto2 */
-
- host = g_mount_spec_get (mount_spec, "host");
- DEBUG (" host='%s'", host);
- if (host == NULL || strlen (host) < 3 || host[0] != '[' || host[strlen (host) - 1] != ']')
- {
- g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("No device specified"));
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- release_device (gphoto2_backend);
- return;
- }
-
- gphoto2_backend->gphoto2_port = g_strdup (host + 1);
- gphoto2_backend->gphoto2_port[strlen (gphoto2_backend->gphoto2_port) - 1] = '\0';
-
- DEBUG (" decoded host='%s'", gphoto2_backend->gphoto2_port);
-
- find_udi_for_device (gphoto2_backend);
-
- gphoto2_backend->context = gp_context_new ();
- if (gphoto2_backend->context == NULL)
- {
- g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot create gphoto2 context"));
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- release_device (gphoto2_backend);
- return;
- }
-
- rc = gp_camera_new (&(gphoto2_backend->camera));
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Error creating camera"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- release_device (gphoto2_backend);
- return;
- }
-
-
- il = NULL;
-
- rc = gp_port_info_list_new (&il);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Error loading device information"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- release_device (gphoto2_backend);
- return;
- }
-
- rc = gp_port_info_list_load (il);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Error loading device information"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- release_device (gphoto2_backend);
- return;
- }
-
- DEBUG (" gphoto2_port='%s'", gphoto2_backend->gphoto2_port);
-
- n = gp_port_info_list_lookup_path (il, gphoto2_backend->gphoto2_port);
- if (n == GP_ERROR_UNKNOWN_PORT)
- {
- error = get_error_from_gphoto2 (_("Error looking up device information"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- release_device (gphoto2_backend);
- return;
- }
-
- rc = gp_port_info_list_get_info (il, n, &info);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Error getting device information"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- release_device (gphoto2_backend);
- return;
- }
-
- DEBUG (" '%s' '%s' '%s'", info.name, info.path, info.library_filename);
-
- /* set port */
- rc = gp_camera_set_port_info (gphoto2_backend->camera, info);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Error setting up camera communications port"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- release_device (gphoto2_backend);
- return;
- }
- gp_port_info_list_free(il);
-
- rc = gp_camera_init (gphoto2_backend->camera, gphoto2_backend->context);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Error initializing camera"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- release_device (gphoto2_backend);
- return;
- }
-
- if (!ensure_ignore_prefix (gphoto2_backend, G_VFS_JOB (job)))
- {
- release_device (gphoto2_backend);
- return;
- }
-
- /* Translator: %s represents the device, e.g. usb:001,042 */
- fuse_name = g_strdup_printf (_("gphoto2 mount on %s"), gphoto2_backend->gphoto2_port);
- icon_name = compute_icon_name (gphoto2_backend);
- display_name = compute_display_name (gphoto2_backend);
- g_vfs_backend_set_stable_name (backend, fuse_name);
- g_vfs_backend_set_display_name (backend, display_name);
- g_vfs_backend_set_icon_name (backend, icon_name);
- g_free (display_name);
- g_free (icon_name);
- g_free (fuse_name);
-
- gphoto2_backend->can_write = FALSE;
- rc = gp_camera_get_storageinfo (gphoto2_backend->camera, &storage_info, &num_storage_info, gphoto2_backend->context);
- if (rc == 0)
- {
- if (num_storage_info >= 1)
- {
- if (storage_info[0].fields & GP_STORAGEINFO_ACCESS && storage_info[0].access == GP_STORAGEINFO_AC_READWRITE)
- {
- gphoto2_backend->can_write = TRUE;
- }
- }
- }
- DEBUG (" can_write = %d", gphoto2_backend->can_write);
-
- g_vfs_job_succeeded (G_VFS_JOB (job));
-
- gphoto2_backend->free_space = -1;
-
- gphoto2_backend->lock = g_mutex_new ();
-
- gphoto2_mount_spec = g_mount_spec_new ("gphoto2");
- g_mount_spec_set (gphoto2_mount_spec, "host", host);
- g_vfs_backend_set_mount_spec (backend, gphoto2_mount_spec);
- g_mount_spec_unref (gphoto2_mount_spec);
-
- gphoto2_backend->info_cache = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- g_object_unref);
-
- gphoto2_backend->dir_name_cache = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- (GDestroyNotify) gp_list_unref);
-
- gphoto2_backend->file_name_cache = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- (GDestroyNotify) gp_list_unref);
-
- DEBUG (" mounted %p", gphoto2_backend);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static gboolean
-try_mount (GVfsBackend *backend,
- GVfsJobMount *job,
- GMountSpec *mount_spec,
- GMountSource *mount_source,
- gboolean is_automount)
-{
- const char *host;
- GError *error = NULL;
- GMountSpec *gphoto2_mount_spec;
-
- DEBUG ("try_mount %p", backend);
-
- /* TODO: Hmm.. apparently we have to set the mount spec in
- * try_mount(); doing it in mount() do_won't work..
- */
- host = g_mount_spec_get (mount_spec, "host");
- DEBUG (" host=%s", host);
- if (host == NULL)
- {
- g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("No camera specified"));
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- return TRUE;
- }
-
- gphoto2_mount_spec = g_mount_spec_new ("gphoto2");
- g_mount_spec_set (gphoto2_mount_spec, "host", host);
- g_vfs_backend_set_mount_spec (backend, gphoto2_mount_spec);
- g_mount_spec_unref (gphoto2_mount_spec);
- return FALSE;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-do_unmount (GVfsBackend *backend,
- GVfsJobUnmount *job)
-{
- GError *error;
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- int num_open_files;
-
- num_open_files = gphoto2_backend->num_open_files_for_reading + g_list_length (gphoto2_backend->open_write_handles);
-
- if (num_open_files > 0)
- {
- error = g_error_new (G_IO_ERROR, G_IO_ERROR_BUSY,
- ngettext("File system is busy: %d open file",
- "File system is busy: %d open files",
- num_open_files),
- num_open_files);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- return;
- }
-
- release_device (gphoto2_backend);
- g_vfs_job_succeeded (G_VFS_JOB (job));
-
- DEBUG ("unmounted %p", backend);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-free_read_handle (ReadHandle *read_handle)
-{
- if (read_handle->file != NULL)
- {
- gp_file_unref (read_handle->file);
- }
- g_free (read_handle);
-}
-
-static void
-do_open_for_read (GVfsBackend *backend,
- GVfsJobOpenForRead *job,
- const char *filename)
-{
- int rc;
- GError *error;
- ReadHandle *read_handle;
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- char *dir;
- char *name;
-
- DEBUG ("open_for_read (%s)", filename);
-
- ensure_not_dirty (gphoto2_backend);
-
- split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
-
- if (is_directory (gphoto2_backend, dir, name))
- {
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_IS_DIRECTORY,
- _("Can't open directory"));
- goto out;
- }
-
- if (!is_regular (gphoto2_backend, dir, name))
- {
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_NOT_FOUND,
- _("No such file"));
- goto out;
- }
-
- read_handle = g_new0 (ReadHandle, 1);
- rc = gp_file_new (&read_handle->file);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Error creating file object"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- free_read_handle (read_handle);
- goto out;
- }
-
- rc = gp_camera_file_get (gphoto2_backend->camera,
- dir,
- name,
- GP_FILE_TYPE_NORMAL,
- read_handle->file,
- gphoto2_backend->context);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Error getting file"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- free_read_handle (read_handle);
- goto out;
- }
-
- rc = gp_file_get_data_and_size (read_handle->file, &read_handle->data, &read_handle->size);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Error getting data from file"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- free_read_handle (read_handle);
- goto out;
- }
-
- DEBUG (" data=%p size=%ld handle=%p", read_handle->data, read_handle->size, read_handle);
-
- g_mutex_lock (gphoto2_backend->lock);
- gphoto2_backend->num_open_files_for_reading++;
- g_mutex_unlock (gphoto2_backend->lock);
-
- read_handle->cursor = 0;
-
- g_vfs_job_open_for_read_set_can_seek (job, TRUE);
- g_vfs_job_open_for_read_set_handle (job, GINT_TO_POINTER (read_handle));
- g_vfs_job_succeeded (G_VFS_JOB (job));
-
- out:
- g_free (name);
- g_free (dir);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static gboolean
-try_read (GVfsBackend *backend,
- GVfsJobRead *job,
- GVfsBackendHandle handle,
- char *buffer,
- gsize bytes_requested)
-{
- //GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- ReadHandle *read_handle = (ReadHandle *) handle;
- gsize bytes_left;
- gsize bytes_to_copy;
-
- DEBUG ("do_read() %d @ %ld of %ld, handle=%p", bytes_requested, read_handle->cursor, read_handle->size, handle);
-
- if (read_handle->cursor >= read_handle->size)
- {
- bytes_to_copy = 0;
- goto out;
- }
-
- bytes_left = read_handle->size - read_handle->cursor;
- if (bytes_requested > bytes_left)
- bytes_to_copy = bytes_left;
- else
- bytes_to_copy = bytes_requested;
-
- memcpy (buffer, read_handle->data + read_handle->cursor, bytes_to_copy);
- read_handle->cursor += bytes_to_copy;
-
- out:
-
- g_vfs_job_read_set_size (job, bytes_to_copy);
- g_vfs_job_succeeded (G_VFS_JOB (job));
- return TRUE;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static gboolean
-try_seek_on_read (GVfsBackend *backend,
- GVfsJobSeekRead *job,
- GVfsBackendHandle handle,
- goffset offset,
- GSeekType type)
-{
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- ReadHandle *read_handle = (ReadHandle *) handle;
- long new_offset;
-
- DEBUG ("seek_on_read() offset=%d, type=%d, handle=%p", (int)offset, type, handle);
-
- switch (type)
- {
- default:
- case G_SEEK_SET:
- new_offset = offset;
- break;
- case G_SEEK_CUR:
- new_offset = read_handle->cursor + offset;
- break;
- case G_SEEK_END:
- new_offset = read_handle->size + offset;
- break;
- }
-
- if (new_offset < 0 || new_offset > read_handle->size)
- {
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_FAILED,
- _("Error seeking in stream on camera %s"), gphoto2_backend->gphoto2_port);
- }
- else
- {
- read_handle->cursor = new_offset;
- g_vfs_job_seek_read_set_offset (job, offset);
- g_vfs_job_succeeded (G_VFS_JOB (job));
- }
- return TRUE;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-/* cannot be async because we unref the CameraFile */
-static void
-do_close_read (GVfsBackend *backend,
- GVfsJobCloseRead *job,
- GVfsBackendHandle handle)
-{
- ReadHandle *read_handle = (ReadHandle *) handle;
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
-
- DEBUG ("close_read() handle=%p", handle);
-
- free_read_handle (read_handle);
-
- g_mutex_lock (gphoto2_backend->lock);
- gphoto2_backend->num_open_files_for_reading--;
- g_mutex_unlock (gphoto2_backend->lock);
-
- g_vfs_job_succeeded (G_VFS_JOB (job));
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-do_query_info (GVfsBackend *backend,
- GVfsJobQueryInfo *job,
- const char *filename,
- GFileQueryInfoFlags flags,
- GFileInfo *info,
- GFileAttributeMatcher *matcher)
-{
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- GError *error;
- char *dir;
- char *name;
-
- DEBUG ("query_info (%s)", filename);
-
- split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
-
- error = NULL;
- if (!file_get_info (gphoto2_backend, dir, name, info, &error, FALSE))
- {
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- }
- else
- {
- g_vfs_job_succeeded (G_VFS_JOB (job));
- }
-
- g_free (name);
- g_free (dir);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static gboolean
-try_query_info (GVfsBackend *backend,
- GVfsJobQueryInfo *job,
- const char *filename,
- GFileQueryInfoFlags flags,
- GFileInfo *info,
- GFileAttributeMatcher *matcher)
-{
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- char *dir;
- char *name;
- gboolean ret;
-
- DEBUG ("try_query_info (%s)", filename);
-
- ret = FALSE;
-
- split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
-
- if (!file_get_info (gphoto2_backend, dir, name, info, NULL, TRUE))
- {
- DEBUG (" BUU no info from cache for try_query_info (%s)", filename);
- goto out;
- }
- DEBUG (" YAY got info from cache for try_query_info (%s)", filename);
-
- g_vfs_job_succeeded (G_VFS_JOB (job));
- ret = TRUE;
-
- out:
- g_free (name);
- g_free (dir);
- return ret;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-do_enumerate (GVfsBackend *backend,
- GVfsJobEnumerate *job,
- const char *given_filename,
- GFileAttributeMatcher *matcher,
- GFileQueryInfoFlags flags)
-{
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- GFileInfo *info;
- GList *l;
- GError *error;
- CameraList *list;
- int n;
- int rc;
- char *filename;
- gboolean using_cached_dir_list;
- gboolean using_cached_file_list;
- char *as_dir;
- char *as_name;
-
- l = NULL;
- using_cached_dir_list = FALSE;
- using_cached_file_list = FALSE;
-
- filename = add_ignore_prefix (gphoto2_backend, given_filename);
- DEBUG ("enumerate (%s)", given_filename);
-
- split_filename_with_ignore_prefix (gphoto2_backend, given_filename, &as_dir, &as_name);
- if (!is_directory (gphoto2_backend, as_dir, as_name))
- {
- if (is_regular (gphoto2_backend, as_dir, as_name))
- {
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_NOT_DIRECTORY,
- _("Not a directory"));
- }
- else
- {
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_NOT_FOUND,
- _("No such file or directory"));
- }
- g_free (as_dir);
- g_free (as_name);
- return;
- }
- g_free (as_dir);
- g_free (as_name);
-
- /* first, list the folders */
- g_mutex_lock (gphoto2_backend->lock);
- list = g_hash_table_lookup (gphoto2_backend->dir_name_cache, filename);
- if (list == NULL)
- {
- g_mutex_unlock (gphoto2_backend->lock);
-
- ensure_not_dirty (gphoto2_backend);
-
- DEBUG (" Generating dir list for dir '%s'", filename);
-
- gp_list_new (&list);
- rc = gp_camera_folder_list_folders (gphoto2_backend->camera,
- filename,
- list,
- gphoto2_backend->context);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Failed to get folder list"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- g_free (filename);
- return;
- }
- }
- else
- {
- DEBUG (" Using cached dir list for dir '%s'", filename);
- using_cached_dir_list = TRUE;
- gp_list_ref (list);
- g_mutex_unlock (gphoto2_backend->lock);
- }
- for (n = 0; n < gp_list_count (list); n++)
- {
- const char *name;
-
- gp_list_get_name (list, n, &name);
- DEBUG (" enum folder '%s'", name);
- info = g_file_info_new ();
- error = NULL;
- if (!file_get_info (gphoto2_backend, filename, name, info, &error, FALSE))
- {
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- g_list_foreach (l, (GFunc) g_object_unref, NULL);
- g_list_free (l);
- gp_list_free (list);
- return;
- }
- l = g_list_append (l, info);
- }
- if (!using_cached_dir_list)
- {
-#ifndef DEBUG_NO_CACHING
- g_mutex_lock (gphoto2_backend->lock);
- g_hash_table_insert (gphoto2_backend->dir_name_cache, g_strdup (filename), list);
- g_mutex_unlock (gphoto2_backend->lock);
-#endif
- }
- else
- {
- g_mutex_lock (gphoto2_backend->lock);
- gp_list_unref (list);
- g_mutex_unlock (gphoto2_backend->lock);
- }
-
-
- /* then list the files in each folder */
- g_mutex_lock (gphoto2_backend->lock);
- list = g_hash_table_lookup (gphoto2_backend->file_name_cache, filename);
- if (list == NULL)
- {
- g_mutex_unlock (gphoto2_backend->lock);
- ensure_not_dirty (gphoto2_backend);
-
- DEBUG (" Generating file list for dir '%s'", filename);
-
- gp_list_new (&list);
- rc = gp_camera_folder_list_files (gphoto2_backend->camera,
- filename,
- list,
- gphoto2_backend->context);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Failed to get file list"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- g_free (filename);
- return;
- }
- }
- else
- {
- DEBUG (" Using cached file list for dir '%s'", filename);
- using_cached_file_list = TRUE;
- gp_list_ref (list);
- g_mutex_unlock (gphoto2_backend->lock);
- }
- for (n = 0; n < gp_list_count (list); n++)
- {
- const char *name;
-
- gp_list_get_name (list, n, &name);
- DEBUG (" enum file '%s'", name);
-
- info = g_file_info_new ();
- error = NULL;
- if (!file_get_info (gphoto2_backend, filename, name, info, &error, FALSE))
- {
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- g_list_foreach (l, (GFunc) g_object_unref, NULL);
- g_list_free (l);
- gp_list_free (list);
- return;
- }
- l = g_list_append (l, info);
- }
- if (!using_cached_file_list)
- {
-#ifndef DEBUG_NO_CACHING
- g_mutex_lock (gphoto2_backend->lock);
- g_hash_table_insert (gphoto2_backend->file_name_cache, g_strdup (filename), list);
- g_mutex_unlock (gphoto2_backend->lock);
-#endif
- }
- else
- {
- g_mutex_lock (gphoto2_backend->lock);
- gp_list_unref (list);
- g_mutex_unlock (gphoto2_backend->lock);
- }
-
- /* and we're done */
-
- g_vfs_job_succeeded (G_VFS_JOB (job));
- g_vfs_job_enumerate_add_infos (job, l);
- g_list_foreach (l, (GFunc) g_object_unref, NULL);
- g_list_free (l);
- g_vfs_job_enumerate_done (job);
-
- g_free (filename);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static gboolean
-try_enumerate (GVfsBackend *backend,
- GVfsJobEnumerate *job,
- const char *given_filename,
- GFileAttributeMatcher *matcher,
- GFileQueryInfoFlags flags)
-{
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- GFileInfo *info;
- GList *l;
- GError *error;
- CameraList *list;
- int n;
- char *filename;
- const char *name;
-
- l = NULL;
-
- filename = add_ignore_prefix (gphoto2_backend, given_filename);
- DEBUG ("try_enumerate (%s)", given_filename);
-
- /* first, list the folders */
- g_mutex_lock (gphoto2_backend->lock);
- list = g_hash_table_lookup (gphoto2_backend->dir_name_cache, filename);
- if (list == NULL)
- {
- g_mutex_unlock (gphoto2_backend->lock);
- goto error_not_cached;
- }
- gp_list_ref (list);
- g_mutex_unlock (gphoto2_backend->lock);
- for (n = 0; n < gp_list_count (list); n++)
- {
- gp_list_get_name (list, n, &name);
- DEBUG (" try_enum folder '%s'", name);
- info = g_file_info_new ();
- if (!file_get_info (gphoto2_backend, filename, name, info, &error, TRUE))
- {
- g_mutex_lock (gphoto2_backend->lock);
- gp_list_unref (list);
- g_mutex_unlock (gphoto2_backend->lock);
- goto error_not_cached;
- }
- l = g_list_append (l, info);
- }
- g_mutex_lock (gphoto2_backend->lock);
- gp_list_unref (list);
- g_mutex_unlock (gphoto2_backend->lock);
-
- /* then list the files in each folder */
- g_mutex_lock (gphoto2_backend->lock);
- list = g_hash_table_lookup (gphoto2_backend->file_name_cache, filename);
- if (list == NULL)
- {
- g_mutex_unlock (gphoto2_backend->lock);
- goto error_not_cached;
- }
- gp_list_ref (list);
- g_mutex_unlock (gphoto2_backend->lock);
- for (n = 0; n < gp_list_count (list); n++)
- {
- gp_list_get_name (list, n, &name);
- DEBUG (" try_enum file '%s'", name);
-
- info = g_file_info_new ();
- if (!file_get_info (gphoto2_backend, filename, name, info, &error, TRUE))
- {
- g_mutex_lock (gphoto2_backend->lock);
- gp_list_unref (list);
- g_mutex_unlock (gphoto2_backend->lock);
- goto error_not_cached;
- }
- l = g_list_append (l, info);
- }
- g_mutex_lock (gphoto2_backend->lock);
- gp_list_unref (list);
- g_mutex_unlock (gphoto2_backend->lock);
-
- /* and we're done */
-
- g_vfs_job_succeeded (G_VFS_JOB (job));
- g_vfs_job_enumerate_add_infos (job, l);
- g_list_foreach (l, (GFunc) g_object_unref, NULL);
- g_list_free (l);
- g_vfs_job_enumerate_done (job);
-
- g_free (filename);
- DEBUG (" YAY got info from cache for try_enumerate (%s)", given_filename);
- return TRUE;
-
- error_not_cached:
- g_list_foreach (l, (GFunc) g_object_unref, NULL);
- g_list_free (l);
-
- g_free (filename);
- DEBUG (" BUU no info from cache for try_enumerate (%s)", given_filename);
- return FALSE;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-do_query_fs_info (GVfsBackend *backend,
- GVfsJobQueryFsInfo *job,
- const char *filename,
- GFileInfo *info,
- GFileAttributeMatcher *attribute_matcher)
-{
- int rc;
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- CameraStorageInformation *storage_info;
- int num_storage_info;
-
- DEBUG ("query_fs_info (%s)", filename);
-
- g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "gphoto2");
- g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, !gphoto2_backend->can_write);
-
- rc = gp_camera_get_storageinfo (gphoto2_backend->camera, &storage_info, &num_storage_info, gphoto2_backend->context);
- if (rc == 0)
- {
- if (num_storage_info >= 1)
- {
- /* for now we only support a single storage head */
- if (storage_info[0].fields & GP_STORAGEINFO_MAXCAPACITY)
- {
- g_mutex_lock (gphoto2_backend->lock);
- gphoto2_backend->capacity = storage_info[0].capacitykbytes * 1024;
- g_mutex_unlock (gphoto2_backend->lock);
- g_file_info_set_attribute_uint64 (info,
- G_FILE_ATTRIBUTE_FILESYSTEM_SIZE,
- (guint64) gphoto2_backend->capacity);
- }
-
- if (storage_info[0].fields & GP_STORAGEINFO_FREESPACEKBYTES)
- {
- g_mutex_lock (gphoto2_backend->lock);
- gphoto2_backend->free_space = storage_info[0].freekbytes * 1024;
- g_mutex_unlock (gphoto2_backend->lock);
- g_file_info_set_attribute_uint64 (info,
- G_FILE_ATTRIBUTE_FILESYSTEM_FREE,
- (guint64) gphoto2_backend->free_space);
- }
- }
- DEBUG (" got %d storage_info objects", num_storage_info);
- }
-
- g_vfs_job_succeeded (G_VFS_JOB (job));
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static gboolean
-try_query_fs_info (GVfsBackend *backend,
- GVfsJobQueryFsInfo *job,
- const char *filename,
- GFileInfo *info,
- GFileAttributeMatcher *attribute_matcher)
-{
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- gboolean ret;
- gint64 free_space;
- gint64 capacity;
-
- DEBUG ("try_query_fs_info (%s)", filename);
-
- ret = FALSE;
-
- g_mutex_lock (gphoto2_backend->lock);
- free_space = gphoto2_backend->free_space;
- capacity = gphoto2_backend->capacity;
- g_mutex_unlock (gphoto2_backend->lock);
-
- if (free_space == -1 || capacity == -1)
- {
- DEBUG (" BUU no info from cache for try_query_fs_info (%s)", filename);
- goto out;
- }
- DEBUG (" YAY got info from cache for try_query_fs_info (%s)", filename);
-
- g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "gphoto2");
- g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, !gphoto2_backend->can_write);
- g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, (guint64) capacity);
- g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, (guint64) free_space);
- g_vfs_job_succeeded (G_VFS_JOB (job));
-
- ret = TRUE;
- out:
- return ret;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-do_make_directory (GVfsBackend *backend,
- GVfsJobMakeDirectory *job,
- const char *filename)
-{
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- char *name;
- char *dir;
- int rc;
- GError *error;
-
- DEBUG ("make_directory (%s)", filename);
-
- ensure_not_dirty (gphoto2_backend);
-
- dir = NULL;
- name = NULL;
- error = NULL;
-
- if (!gphoto2_backend->can_write)
- {
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_NOT_SUPPORTED,
- _("Not supported"));
- goto out;
- }
-
- split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
-
- rc = gp_camera_folder_make_dir (gphoto2_backend->camera,
- dir,
- name,
- gphoto2_backend->context);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Error creating directory"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- goto out;
- }
-
- caches_invalidate_dir (gphoto2_backend, dir);
- caches_invalidate_free_space (gphoto2_backend);
- monitors_emit_created (gphoto2_backend, dir, name);
-
- g_vfs_job_succeeded (G_VFS_JOB (job));
-
- out:
- g_free (dir);
- g_free (name);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-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)
-{
- int rc;
- CameraFile *file;
- CameraFile *file_dest;
- const char *data;
- unsigned long int size;
-
- file = NULL;
- file_dest = NULL;
-
- DEBUG ("do_slow_file_rename_in_same_dir() '%s' '%s' -> '%s'", dir, name, new_name);
-
- rc = gp_file_new (&file);
- if (rc != 0)
- goto out;
-
- rc = gp_camera_file_get (gphoto2_backend->camera,
- dir,
- name,
- GP_FILE_TYPE_NORMAL,
- file,
- gphoto2_backend->context);
- if (rc != 0)
- goto out;
-
- rc = gp_file_get_data_and_size (file, &data, &size);
- if (rc != 0)
- goto out;
-
- rc = gp_file_new (&file_dest);
- if (rc != 0)
- goto out;
-
- rc = gp_file_copy (file_dest, file);
- if (rc != 0)
- goto out;
-
- rc = gp_file_set_name (file_dest, new_name);
- if (rc != 0)
- goto out;
-
- if (allow_overwrite)
- {
- gp_camera_file_delete (gphoto2_backend->camera,
- dir,
- new_name,
- gphoto2_backend->context);
- if (rc != 0)
- {
- DEBUG (" file delete failed as part of slow rename rc=%d", rc);
- goto out;
- }
- }
-
- rc = gp_camera_folder_put_file (gphoto2_backend->camera, dir, file_dest, gphoto2_backend->context);
- if (rc != 0)
- goto out;
-
- rc = gp_camera_file_delete (gphoto2_backend->camera,
- dir,
- name,
- gphoto2_backend->context);
- if (rc != 0)
- {
- /* at least try to clean up the newly created file... */
- gp_camera_file_delete (gphoto2_backend->camera,
- dir,
- new_name,
- gphoto2_backend->context);
- goto out;
- }
-
- out:
- if (file != NULL)
- gp_file_unref (file);
- if (file_dest != NULL)
- gp_file_unref (file_dest);
- return rc;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static int
-do_file_rename_in_same_dir (GVfsBackendGphoto2 *gphoto2_backend,
- const char *dir,
- const char *name,
- const char *new_name,
- gboolean allow_overwrite)
-{
- /* TODO: The libgphoto2 API speaks of just using
- * gp_camera_file_set_info() to achieve this. However this
- * fails on the devices that I own. So fall back to the slow
- * method for now. Patches welcome for someone with a device
- * where the above mentioned trick works.
- */
- return do_slow_file_rename_in_same_dir (gphoto2_backend, dir, name, new_name, allow_overwrite);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static int
-do_dir_rename_in_same_dir (GVfsBackendGphoto2 *gphoto2_backend,
- const char *dir,
- const char *name,
- const char *new_name)
-{
- int rc;
- char *dir_name;
-
- dir_name = g_build_filename (dir, name, NULL);
-
- DEBUG ("do_dir_rename_in_same_dir() '%s' '%s' -> '%s' ('%s')", dir, name, new_name, dir_name);
-
- /* TODO: Support non-empty folders by recursively renaming stuff.
- * Or that might be too dangerous as it's not exactly atomic.
- * And renaming files may be slow; see do_file_rename_in_same_dir() above.
- */
- if (is_directory_empty (gphoto2_backend, dir_name))
- {
- rc = gp_camera_folder_make_dir (gphoto2_backend->camera,
- dir,
- new_name,
- gphoto2_backend->context);
- if (rc != 0)
- goto out;
-
- rc = gp_camera_folder_remove_dir (gphoto2_backend->camera,
- dir,
- name,
- gphoto2_backend->context);
- if (rc != 0)
- goto out;
- }
- else
- {
- rc = GP_ERROR_NOT_SUPPORTED;
- }
-
- out:
- g_free (dir_name);
- return rc;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-do_set_display_name (GVfsBackend *backend,
- GVfsJobSetDisplayName *job,
- const char *filename,
- const char *display_name)
-{
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- char *name;
- char *dir;
- int rc;
- char *dir_name;
- GError *error;
- char *new_name;
-
- ensure_not_dirty (gphoto2_backend);
-
- DEBUG ("set_display_name() '%s' -> '%s'", filename, display_name);
-
- dir = NULL;
- name = NULL;
- dir_name = NULL;
- new_name = NULL;
-
- if (!gphoto2_backend->can_write)
- {
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_NOT_SUPPORTED,
- _("Not supported"));
- goto out;
- }
-
- split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
-
- /* refuse is desired name is already taken */
- if (is_directory (gphoto2_backend, dir, display_name) ||
- is_regular (gphoto2_backend, dir, display_name))
- {
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_EXISTS,
- _("Name already exists"));
- goto out;
- }
-
- /* ensure name is not too long - otherwise it might screw up enumerating
- * the folder on some devices
- */
- if (strlen (display_name) > 63)
- {
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_NOT_SUPPORTED,
- _("New name too long"));
- goto out;
- }
-
- if (is_directory (gphoto2_backend, dir, name))
- {
- /* dir renaming */
- rc = do_dir_rename_in_same_dir (gphoto2_backend, dir, name, display_name);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Error renaming dir"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- goto out;
- }
- caches_invalidate_file (gphoto2_backend, dir, name);
- }
- else
- {
- /* file renaming */
- rc = do_file_rename_in_same_dir (gphoto2_backend, dir, name, display_name, FALSE);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Error renaming file"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- goto out;
- }
- caches_invalidate_file (gphoto2_backend, dir, name);
- }
-
-
- /* emit on monitor */
- monitors_emit_deleted (gphoto2_backend, dir, name);
- monitors_emit_created (gphoto2_backend, dir, display_name);
-
- new_name = g_build_filename (dir + strlen (gphoto2_backend->ignore_prefix), display_name, NULL);
- g_vfs_job_set_display_name_set_new_path (job, new_name);
-
- g_vfs_job_succeeded (G_VFS_JOB (job));
-
- out:
- g_free (dir);
- g_free (name);
- g_free (dir_name);
- g_free (new_name);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-do_delete (GVfsBackend *backend,
- GVfsJobDelete *job,
- const char *filename)
-{
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- char *name;
- char *dir;
- int rc;
- GError *error;
- char *dir_name;
-
- ensure_not_dirty (gphoto2_backend);
-
- DEBUG ("delete() '%s'", filename);
-
- dir = NULL;
- name = NULL;
- dir_name = NULL;
-
- if (!gphoto2_backend->can_write)
- {
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_NOT_SUPPORTED,
- _("Not supported"));
- goto out;
- }
-
- split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
-
- if (is_directory (gphoto2_backend, dir, name))
- {
- dir_name = add_ignore_prefix (gphoto2_backend, filename);
- if (!is_directory_empty (gphoto2_backend, dir_name))
- {
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_NOT_EMPTY,
- _("Directory '%s' is not empty"), filename);
- goto out;
- }
- else
- {
- rc = gp_camera_folder_remove_dir (gphoto2_backend->camera,
- dir,
- name,
- gphoto2_backend->context);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Error deleting directory"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- goto out;
- }
- caches_invalidate_file (gphoto2_backend, dir, name);
- caches_invalidate_free_space (gphoto2_backend);
- monitors_emit_deleted (gphoto2_backend, dir, name);
- }
- }
- else
- {
- if (!is_regular (gphoto2_backend, dir, name))
- {
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_NOT_FOUND,
- _("No such file or directory"));
- goto out;
- }
-
- rc = gp_camera_file_delete (gphoto2_backend->camera,
- dir,
- name,
- gphoto2_backend->context);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Error deleting file"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- goto out;
- }
-
- caches_invalidate_file (gphoto2_backend, dir, name);
- caches_invalidate_free_space (gphoto2_backend);
- monitors_emit_deleted (gphoto2_backend, dir, name);
- }
-
- g_vfs_job_succeeded (G_VFS_JOB (job));
-
- out:
- g_free (dir);
- g_free (name);
- g_free (dir_name);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-do_create_internal (GVfsBackend *backend,
- GVfsJobOpenForWrite *job,
- const char *filename,
- GFileCreateFlags flags,
- gboolean job_is_replace,
- gboolean job_is_append_to)
-{
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- WriteHandle *handle;
- char *dir;
- char *name;
-
- ensure_not_dirty (gphoto2_backend);
-
- dir = NULL;
- name = NULL;
-
- if (!gphoto2_backend->can_write)
- {
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_NOT_SUPPORTED,
- _("Not supported"));
- goto out;
- }
-
- split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
-
- if (is_directory (gphoto2_backend, dir, name))
- {
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_IS_DIRECTORY,
- _("Can't write to directory"));
- goto out;
- }
-
- /* unless we're replacing or appending.. error out if file already exists */
- if (is_regular (gphoto2_backend, dir, name))
- {
- if (! (job_is_replace || job_is_append_to))
- {
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_EXISTS,
- _("File exists"));
- goto out;
- }
- }
- else
- {
- if (job_is_replace || job_is_append_to)
- {
- /* so we're not really replacing or appending; dont fail these
- * operations; just turn them into create instead...
- */
- job_is_replace = FALSE;
- job_is_append_to = FALSE;
- }
- }
-
- handle = g_new0 (WriteHandle, 1);
- handle->filename = g_strdup (filename);
- handle->dir = g_strdup (dir);
- handle->name = g_strdup (name);
- handle->job_is_replace = job_is_replace;
- handle->job_is_append_to = job_is_append_to;
- handle->is_dirty = TRUE;
-
- /* if we're appending to a file read in all of the file to memory */
- if (job_is_append_to)
- {
- int rc;
- GError *error;
- CameraFile *file;
- const char *data;
- unsigned long int size;
-
- rc = gp_file_new (&file);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Cannot allocate new file to append to"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- write_handle_free (handle);
- goto out;
- }
-
- rc = gp_camera_file_get (gphoto2_backend->camera,
- dir,
- name,
- GP_FILE_TYPE_NORMAL,
- file,
- gphoto2_backend->context);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Cannot read file to append to"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- write_handle_free (handle);
- gp_file_unref (file);
- goto out;
- }
-
- rc = gp_file_get_data_and_size (file, &data, &size);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Cannot get data of file to append to"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- write_handle_free (handle);
- gp_file_unref (file);
- goto out;
- }
-
- handle->data = g_malloc (size + WRITE_INCREMENT);
- handle->allocated_size = size + WRITE_INCREMENT;
- handle->size = size;
- handle->cursor = size;
- memcpy (handle->data, data, size);
- gp_file_unref (file);
-
- }
- else
- {
- handle->data = g_malloc (WRITE_INCREMENT);
- handle->allocated_size = WRITE_INCREMENT;
- }
-
- g_vfs_job_open_for_write_set_handle (job, handle);
- g_vfs_job_open_for_write_set_can_seek (job, TRUE);
-
- gphoto2_backend->open_write_handles = g_list_prepend (gphoto2_backend->open_write_handles, handle);
-
- DEBUG (" handle=%p", handle);
-
- /* make sure we invalidate the dir and the file */
- caches_invalidate_file (gphoto2_backend, dir, name);
-
- /* emit on the monitor - hopefully some client won't need info
- * about this (to avoid committing dirty bits midwrite) before
- * the write is done...
- */
- if (job_is_replace || job_is_append_to)
- monitors_emit_changed (gphoto2_backend, dir, name);
- else
- monitors_emit_created (gphoto2_backend, dir, name);
-
- g_vfs_job_succeeded (G_VFS_JOB (job));
-
- out:
- g_free (dir);
- g_free (name);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-do_create (GVfsBackend *backend,
- GVfsJobOpenForWrite *job,
- const char *filename,
- GFileCreateFlags flags)
-{
- DEBUG ("create() '%s' flags=0x%04x", filename, flags);
-
- return do_create_internal (backend, job, filename, flags, FALSE, FALSE);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-do_replace (GVfsBackend *backend,
- GVfsJobOpenForWrite *job,
- const char *filename,
- const char *etag,
- gboolean make_backup,
- GFileCreateFlags flags)
-{
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- char *dir;
- char *name;
-
- DEBUG ("replace() '%s' etag='%s' make_backup=%d flags=0x%04x", filename, etag, make_backup, flags);
-
- dir = NULL;
- name = NULL;
- split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
-
- /* write a new file
- * - will delete the existing one when done in do_close_write()
- */
- do_create_internal (backend, job, filename, flags, TRUE, FALSE);
-
- g_free (dir);
- g_free (name);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-do_append_to (GVfsBackend *backend,
- GVfsJobOpenForWrite *job,
- const char *filename,
- GFileCreateFlags flags)
-{
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- char *dir;
- char *name;
-
- DEBUG ("append_to() '%s' flags=0x%04x", filename, flags);
-
- dir = NULL;
- name = NULL;
- split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
-
- /* write a new file
- * - will read existing data in do_create_internal
- * - will delete the existing one when done in do_close_write()
- */
- do_create_internal (backend, job, filename, flags, FALSE, TRUE);
-
- g_free (dir);
- g_free (name);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-do_write (GVfsBackend *backend,
- GVfsJobWrite *job,
- GVfsBackendHandle _handle,
- char *buffer,
- gsize buffer_size)
-{
- WriteHandle *handle = _handle;
-
- DEBUG ("write() %p, '%s', %d bytes", handle, handle->filename, buffer_size);
-
- /* ensure we have enough room */
- if (handle->cursor + buffer_size > handle->allocated_size)
- {
- unsigned long int new_allocated_size;
- new_allocated_size = ((handle->cursor + buffer_size) / WRITE_INCREMENT + 1) * WRITE_INCREMENT;
- handle->data = g_realloc (handle->data, new_allocated_size);
- handle->allocated_size = new_allocated_size;
- DEBUG (" allocated_size is now %ld bytes)", handle->allocated_size);
- }
-
-
- memcpy (handle->data + handle->cursor, buffer, buffer_size);
- handle->cursor += buffer_size;
-
- if (handle->cursor > handle->size)
- handle->size = handle->cursor;
-
- /* this will make us dirty */
- handle->is_dirty = TRUE;
-
- g_vfs_job_write_set_written_size (job, buffer_size);
- g_vfs_job_succeeded (G_VFS_JOB (job));
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-do_seek_on_write (GVfsBackend *backend,
- GVfsJobSeekWrite *job,
- GVfsBackendHandle handle,
- goffset offset,
- GSeekType type)
-{
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- WriteHandle *write_handle = handle;
- long new_offset;
-
- DEBUG ("seek_on_write() %p '%s' offset=%d type=%d cursor=%ld size=%ld", write_handle, write_handle->filename, (int)offset, type, write_handle->cursor, write_handle->size);
-
- switch (type)
- {
- default:
- case G_SEEK_SET:
- new_offset = offset;
- break;
- case G_SEEK_CUR:
- new_offset = write_handle->cursor + offset;
- break;
- case G_SEEK_END:
- new_offset = write_handle->size + offset;
- break;
- }
-
- if (new_offset < 0 || new_offset > write_handle->size)
- {
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_FAILED,
- _("Error seeking in stream on camera %s"), gphoto2_backend->gphoto2_port);
- }
- else
- {
- write_handle->cursor = new_offset;
- g_vfs_job_seek_write_set_offset (job, offset);
- g_vfs_job_succeeded (G_VFS_JOB (job));
- }
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-/* this functions updates the device with the data currently in write_handle */
-static int
-commit_write_handle (GVfsBackendGphoto2 *gphoto2_backend, WriteHandle *write_handle)
-{
- int rc;
- CameraFile *file;
-
- DEBUG ("commit_write_handle() '%s' of size %ld", write_handle->filename, write_handle->size);
-
- /* no need to write as we're not dirty */
- if (!write_handle->is_dirty)
- {
- DEBUG (" not dirty => not writing");
- return 0;
- }
-
- if (write_handle->delete_before ||
- (write_handle->job_is_replace || write_handle->job_is_append_to))
- {
- /* OK, so this is not atomic. But there's no way we can make it
- * atomic until rename works properly - see comments in
- * do_set_display_name() and why have do_slow_rename()...
- *
- * So first delete the existing file...
- */
- rc = gp_camera_file_delete (gphoto2_backend->camera,
- write_handle->dir,
- write_handle->name,
- gphoto2_backend->context);
- if (rc != 0)
- goto out;
-
- DEBUG (" deleted '%s' '%s' for delete_before=%d, job_is_replace=%d, job_is_append_to=%d",
- write_handle->dir, write_handle->name,
- write_handle->delete_before, write_handle->job_is_replace, write_handle->job_is_append_to);
- }
-
- rc = gp_file_new (&file);
- if (rc != 0)
- goto out;
-
- gp_file_set_type (file, GP_FILE_TYPE_NORMAL);
- gp_file_set_name (file, write_handle->name);
- gp_file_set_mtime (file, time (NULL));
- gp_file_set_data_and_size (file,
- dup_for_gphoto2 (write_handle->data, write_handle->size),
- write_handle->size);
-
- rc = gp_camera_folder_put_file (gphoto2_backend->camera, write_handle->dir, file, gphoto2_backend->context);
- if (rc != 0)
- {
- gp_file_unref (file);
- goto out;
- }
-
- DEBUG (" successfully wrote '%s' of %ld bytes", write_handle->filename, write_handle->size);
- monitors_emit_changed (gphoto2_backend, write_handle->dir, write_handle->name);
-
- gp_file_unref (file);
-
- out:
- write_handle->is_dirty = FALSE;
- write_handle->delete_before = TRUE;
-
- caches_invalidate_file (gphoto2_backend, write_handle->dir, write_handle->name);
- caches_invalidate_free_space (gphoto2_backend);
-
- return rc;
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-do_close_write (GVfsBackend *backend,
- GVfsJobCloseWrite *job,
- GVfsBackendHandle handle)
-{
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- WriteHandle *write_handle = handle;
- GError *error;
- int rc;
-
- DEBUG ("close_write() %p '%s' %ld bytes total", write_handle, write_handle->filename, write_handle->size);
-
- rc = commit_write_handle (gphoto2_backend, write_handle);
- if (rc != 0)
- {
- error = get_error_from_gphoto2 (_("Error writing file"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- goto out;
- }
-
- monitors_emit_changed (gphoto2_backend, write_handle->dir, write_handle->name);
-
- g_vfs_job_succeeded (G_VFS_JOB (job));
-
- out:
- write_handle_free (write_handle);
- gphoto2_backend->open_write_handles = g_list_remove (gphoto2_backend->open_write_handles, write_handle);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-do_move (GVfsBackend *backend,
- GVfsJobMove *job,
- const char *source,
- const char *destination,
- GFileCopyFlags flags,
- GFileProgressCallback progress_callback,
- gpointer progress_callback_data)
-{
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- char *src_dir;
- char *src_name;
- char *dst_dir;
- char *dst_name;
- int rc;
- GError *error;
- gboolean mv_dir;
-
- DEBUG ("move() '%s' -> '%s' %04x)", source, destination, flags);
-
- ensure_not_dirty (gphoto2_backend);
-
- split_filename_with_ignore_prefix (gphoto2_backend, source, &src_dir, &src_name);
- split_filename_with_ignore_prefix (gphoto2_backend, destination, &dst_dir, &dst_name);
-
- /* this is an limited implementation that can only move files / folders in the same directory */
- if (strcmp (src_dir, dst_dir) != 0)
- {
- DEBUG (" not supported (not same directory)");
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_NOT_SUPPORTED,
- _("Not supported (not same directory)"));
- goto out;
- }
-
- mv_dir = FALSE;
- if (is_directory (gphoto2_backend, src_dir, src_name))
- {
- if (is_directory (gphoto2_backend, dst_dir, dst_name))
- {
- DEBUG (" not supported (src is dir; dst is dir)");
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_NOT_SUPPORTED,
- _("Not supported (src is dir, dst is dir)"));
- goto out;
- }
- else if (is_regular (gphoto2_backend, dst_dir, dst_name))
- {
- DEBUG (" not supported (src is dir; dst is existing file)");
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_NOT_SUPPORTED,
- _("Not supported (src is dir, dst is existing file)"));
- goto out;
- }
- mv_dir = TRUE;
- }
- else
- {
- if (is_directory (gphoto2_backend, dst_dir, dst_name))
- {
- DEBUG (" not supported (src is file; dst is dir)");
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_NOT_SUPPORTED,
- _("Not supported (src is file, dst is dir)"));
- goto out;
- }
- }
-
- /* ensure name is not too long - otherwise it might screw up enumerating
- * the folder on some devices
- */
- if (strlen (dst_name) > 63)
- {
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
- G_IO_ERROR_NOT_SUPPORTED,
- _("New name too long"));
- goto out;
- }
-
- if (mv_dir)
- {
- DEBUG (" renaming dir");
- rc = do_dir_rename_in_same_dir (gphoto2_backend, src_dir, src_name, dst_name);
- if (rc != 0)
- {
- DEBUG (" error renaming dir");
- error = get_error_from_gphoto2 (_("Error renaming dir"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- goto out;
- }
- }
- else
- {
- DEBUG (" renaming file");
- rc = do_file_rename_in_same_dir (gphoto2_backend, src_dir, src_name, dst_name, flags & G_FILE_COPY_OVERWRITE);
- if (rc != 0)
- {
- DEBUG (" error renaming file");
- error = get_error_from_gphoto2 (_("Error renaming file"), rc);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- goto out;
- }
- }
-
- caches_invalidate_file (gphoto2_backend, src_dir, src_name);
- monitors_emit_deleted (gphoto2_backend, src_dir, src_name);
- monitors_emit_created (gphoto2_backend, src_dir, dst_name);
-
- DEBUG (" success");
-
- g_vfs_job_succeeded (G_VFS_JOB (job));
-
- out:
- g_free (src_dir);
- g_free (src_name);
- g_free (dst_dir);
- g_free (dst_name);
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-vfs_dir_monitor_destroyed (gpointer user_data, GObject *where_the_object_was)
-{
- GList *l;
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (user_data);
-
- DEBUG ("vfs_dir_monitor_destroyed()");
-
- for (l = gphoto2_backend->dir_monitor_proxies; l != NULL; l = l->next)
- {
- MonitorProxy *proxy = l->data;
- if (G_OBJECT (proxy->vfs_monitor) == where_the_object_was)
- {
- gphoto2_backend->dir_monitor_proxies = g_list_remove (gphoto2_backend->dir_monitor_proxies, proxy);
- DEBUG (" Removed dead dir monitor for '%s'", proxy->path);
- monitor_proxy_free (proxy);
- break;
- }
- }
-}
-
-static void
-do_create_dir_monitor (GVfsBackend *backend,
- GVfsJobCreateMonitor *job,
- const char *filename,
- GFileMonitorFlags flags)
-{
- char *dir;
- char *name;
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- MonitorProxy *proxy;
-
- DEBUG ("create_dir_monitor (%s)", filename);
-
- split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
-
- proxy = g_new0 (MonitorProxy, 1);
- proxy->path = add_ignore_prefix (gphoto2_backend, filename);
- proxy->vfs_monitor = g_vfs_monitor_new (backend);
-
- gphoto2_backend->dir_monitor_proxies = g_list_prepend (gphoto2_backend->dir_monitor_proxies, proxy);
-
- g_vfs_job_create_monitor_set_monitor (job, proxy->vfs_monitor);
- g_object_weak_ref (G_OBJECT (proxy->vfs_monitor), vfs_dir_monitor_destroyed, gphoto2_backend);
- g_object_unref (proxy->vfs_monitor);
- g_vfs_job_succeeded (G_VFS_JOB (job));
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-vfs_file_monitor_destroyed (gpointer user_data, GObject *where_the_object_was)
-{
- GList *l;
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (user_data);
-
- DEBUG ("vfs_file_monitor_destroyed()");
-
- for (l = gphoto2_backend->file_monitor_proxies; l != NULL; l = l->next)
- {
- MonitorProxy *proxy = l->data;
- if (G_OBJECT (proxy->vfs_monitor) == where_the_object_was)
- {
- gphoto2_backend->dir_monitor_proxies = g_list_remove (gphoto2_backend->dir_monitor_proxies, proxy);
- DEBUG (" Removed dead file monitor for '%s'", proxy->path);
- monitor_proxy_free (proxy);
- break;
- }
- }
-}
-
-static void
-do_create_file_monitor (GVfsBackend *backend,
- GVfsJobCreateMonitor *job,
- const char *filename,
- GFileMonitorFlags flags)
-{
- char *dir;
- char *name;
- GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
- MonitorProxy *proxy;
-
- DEBUG ("create_file_monitor (%s)", filename);
-
- split_filename_with_ignore_prefix (gphoto2_backend, filename, &dir, &name);
-
- proxy = g_new0 (MonitorProxy, 1);
- proxy->path = add_ignore_prefix (gphoto2_backend, filename);
- proxy->vfs_monitor = g_vfs_monitor_new (backend);
-
- gphoto2_backend->file_monitor_proxies = g_list_prepend (gphoto2_backend->file_monitor_proxies, proxy);
-
- g_vfs_job_create_monitor_set_monitor (job, proxy->vfs_monitor);
- g_object_weak_ref (G_OBJECT (proxy->vfs_monitor), vfs_file_monitor_destroyed, gphoto2_backend);
- g_object_unref (proxy->vfs_monitor);
- g_vfs_job_succeeded (G_VFS_JOB (job));
-}
-
-/* ------------------------------------------------------------------------------------------------- */
-
-static void
-g_vfs_backend_gphoto2_class_init (GVfsBackendGphoto2Class *klass)
-{
- GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass);
-
- gobject_class->finalize = g_vfs_backend_gphoto2_finalize;
-
- backend_class->try_mount = try_mount;
- backend_class->mount = do_mount;
- backend_class->unmount = do_unmount;
- backend_class->open_for_read = do_open_for_read;
- backend_class->try_read = try_read;
- backend_class->try_seek_on_read = try_seek_on_read;
- backend_class->close_read = do_close_read;
- backend_class->query_info = do_query_info;
- backend_class->enumerate = do_enumerate;
- backend_class->query_fs_info = do_query_fs_info;
- backend_class->make_directory = do_make_directory;
- backend_class->set_display_name = do_set_display_name;
- backend_class->delete = do_delete;
- backend_class->create = do_create;
- backend_class->replace = do_replace;
- backend_class->append_to = do_append_to;
- backend_class->write = do_write;
- backend_class->close_write = do_close_write;
- backend_class->seek_on_write = do_seek_on_write;
- backend_class->move = do_move;
- backend_class->create_dir_monitor = do_create_dir_monitor;
- backend_class->create_file_monitor = do_create_file_monitor;
-
- /* fast sync versions that only succeed if info is in the cache */
- backend_class->try_query_info = try_query_info;
- backend_class->try_enumerate = try_enumerate;
- backend_class->try_query_fs_info = try_query_fs_info;
-}