diff options
Diffstat (limited to 'daemon/gvfsbackendgphoto2.c')
-rw-r--r-- | daemon/gvfsbackendgphoto2.c | 3404 |
1 files changed, 0 insertions, 3404 deletions
diff --git a/daemon/gvfsbackendgphoto2.c b/daemon/gvfsbackendgphoto2.c deleted file mode 100644 index f06605c1..00000000 --- a/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; -} |