gvfs/daemon/gvfsbackendburn.c

1064 lines
27 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 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., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#include <config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
#include <gio/gunixmounts.h>
#include "gvfsbackendburn.h"
#include "gvfsmonitor.h"
#include "gvfsjobopenforread.h"
#include "gvfsjobread.h"
#include "gvfsjobseekread.h"
#include "gvfsjobopenforwrite.h"
#include "gvfsjobwrite.h"
#include "gvfsjobclosewrite.h"
#include "gvfsjobseekwrite.h"
#include "gvfsjobsetdisplayname.h"
#include "gvfsjobmountmountable.h"
#include "gvfsjobqueryinfo.h"
#include "gvfsjobdelete.h"
#include "gvfsjobqueryfsinfo.h"
#include "gvfsjobqueryattributes.h"
#include "gvfsjobenumerate.h"
#include "gvfsjobcreatemonitor.h"
#include "gvfsdaemonprotocol.h"
/* TODO:
* Change notification
*
*/
typedef enum {
VIRTUAL_NODE_FILE,
VIRTUAL_NODE_DIRECTORY
} VirtualNodeType;
typedef struct {
char *filename;
VirtualNodeType type;
/* for files: */
char *backing_file; /* local filename */
gboolean owned_file;
GList *subscriptions;
/* for directories: */
GList *children;
volatile gint ref_count;
} VirtualNode;
struct _GVfsBackendBurn
{
GVfsBackend parent_instance;
char *tempdir;
VirtualNode *root_node;
GMountSpec *mount_spec;
};
G_DEFINE_TYPE (GVfsBackendBurn, g_vfs_backend_burn, G_VFS_TYPE_BACKEND)
static void virtual_node_unref (VirtualNode *node);
static VirtualNode *
virtual_node_new (const char *filename,
VirtualNodeType type)
{
VirtualNode *node;
node = g_slice_new0 (VirtualNode);
node->filename = g_strdup (filename);
node->type = type;
node->ref_count = 1;
return node;
}
static void
virtual_node_free (VirtualNode *node,
gboolean deep)
{
GList *l;
g_free (node->filename);
switch (node->type)
{
case VIRTUAL_NODE_FILE:
if (node->backing_file != NULL)
{
if (node->owned_file)
g_unlink (node->backing_file);
g_free (node->backing_file);
}
break;
case VIRTUAL_NODE_DIRECTORY:
if (deep)
{
for (l = node->children; l != NULL; l = l->next)
virtual_node_unref ((VirtualNode *)l->data);
}
break;
default:
g_assert_not_reached ();
break;
}
/*
while ((l = g_list_first (node->subscriptions)) != NULL) {
MappingSubscription *sub = l->data;
virtual_node_remove_subscription (node, sub);
}
*/
g_slice_free (VirtualNode, node);
}
static void
virtual_node_unref (VirtualNode *node)
{
g_return_if_fail (node != NULL);
g_return_if_fail (node->ref_count > 0);
if (g_atomic_int_dec_and_test (&node->ref_count))
virtual_node_free (node, TRUE);
}
static G_GNUC_UNUSED VirtualNode *
virtual_node_ref (VirtualNode *node)
{
g_return_val_if_fail (node != NULL, NULL);
g_return_val_if_fail (node->ref_count > 0, NULL);
g_atomic_int_inc (&node->ref_count);
return node;
}
static VirtualNode *
virtual_dir_lookup (VirtualNode *dir,
const char *filename)
{
GList *l;
VirtualNode *node;
g_assert (dir->type == VIRTUAL_NODE_DIRECTORY);
for (l = dir->children; l != NULL; l = l->next)
{
node = l->data;
if (strcmp (node->filename, filename) == 0)
return node;
}
return NULL;
}
static VirtualNode *
virtual_node_lookup (VirtualNode *root_dir,
const char *path,
VirtualNode **parent)
{
char *copy, *next, *copy_orig;
VirtualNode *node;
copy_orig = g_strdup (path);
copy = copy_orig;
if (parent != NULL)
*parent = NULL;
node = root_dir;
while (copy != NULL)
{
/* Skip initial/multiple slashes */
while (G_IS_DIR_SEPARATOR (*copy))
++copy;
if (*copy == 0)
break;
next = strchr (copy, G_DIR_SEPARATOR);
if (next)
{
*next = 0;
next++;
}
if (node->type != VIRTUAL_NODE_DIRECTORY)
{
/* Found a file in the middle of the path */
node = NULL;
break;
}
if (parent != NULL)
*parent = node;
node = virtual_dir_lookup (node, copy);
if (node == NULL)
break;
copy = next;
}
g_free (copy_orig);
return node;
}
static VirtualNode *
virtual_mkdir (VirtualNode *node,
const char *name)
{
VirtualNode *subdir;
g_assert (node->type == VIRTUAL_NODE_DIRECTORY);
if (virtual_dir_lookup (node, name) != NULL)
return NULL;
subdir = virtual_node_new (name, VIRTUAL_NODE_DIRECTORY);
/* list takes ownership of ref */
node->children = g_list_append (node->children, subdir);
return subdir;
}
static void
virtual_unlink (VirtualNode *dir,
VirtualNode *node)
{
g_assert (dir->type == VIRTUAL_NODE_DIRECTORY);
dir->children = g_list_remove (dir->children, node);
virtual_node_unref (node);
}
static VirtualNode *
virtual_create (GVfsBackendBurn *backend,
VirtualNode *dir,
const char *name,
const char *backing_file)
{
VirtualNode *file;
char *template;
int fd;
g_assert (dir->type == VIRTUAL_NODE_DIRECTORY);
if (virtual_dir_lookup (dir, name) != NULL)
return NULL;
file = virtual_node_new (name, VIRTUAL_NODE_FILE);
if (backing_file != NULL)
{
file->backing_file = g_strdup (backing_file);
file->owned_file = FALSE;
}
else
{
template = g_build_filename (backend->tempdir, "file.XXXXXX", NULL);
fd = g_mkstemp (template);
if (fd < 0)
{
g_free (template);
virtual_node_unref (file);
return NULL;
}
close (fd);
g_unlink (template);
file->backing_file = template;
file->owned_file = TRUE;
}
/* list takes ownership of ref */
dir->children = g_list_append (dir->children, file);
return file;
}
static void
g_vfs_backend_burn_finalize (GObject *object)
{
GVfsBackendBurn *backend;
backend = G_VFS_BACKEND_BURN (object);
g_mount_spec_unref (backend->mount_spec);
if (G_OBJECT_CLASS (g_vfs_backend_burn_parent_class)->finalize)
(*G_OBJECT_CLASS (g_vfs_backend_burn_parent_class)->finalize) (object);
}
static void
g_vfs_backend_burn_init (GVfsBackendBurn *burn_backend)
{
GVfsBackend *backend = G_VFS_BACKEND (burn_backend);
GMountSpec *mount_spec;
/* Translators: This is the name of the backend */
g_vfs_backend_set_display_name (backend, _("Burn"));
g_vfs_backend_set_icon_name (backend, "computer");
g_vfs_backend_set_symbolic_icon_name (backend, "computer-symbolic");
g_vfs_backend_set_user_visible (backend, FALSE);
mount_spec = g_mount_spec_new ("burn");
g_vfs_backend_set_mount_spec (backend, mount_spec);
burn_backend->mount_spec = mount_spec;
}
static gboolean
try_mount (GVfsBackend *backend,
GVfsJobMount *job,
GMountSpec *mount_spec,
GMountSource *mount_source,
gboolean is_automount)
{
GVfsBackendBurn *burn_backend = G_VFS_BACKEND_BURN (backend);
char *filename;
filename = g_build_filename (g_get_user_runtime_dir (), "gvfs-burn", NULL);
if (g_mkdir_with_parents (filename, 0700) < 0)
{
g_free (filename);
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_FAILED,
_("Unable to create temporary directory"));
return TRUE;
}
burn_backend->tempdir = filename;
burn_backend->root_node =
virtual_node_new (NULL, VIRTUAL_NODE_DIRECTORY);
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}
static gboolean
try_delete (GVfsBackend *backend,
GVfsJobDelete *job,
const char *filename)
{
char *dirname, *basename;
VirtualNode *file, *dir;
dirname = g_path_get_dirname (filename);
dir = virtual_node_lookup (G_VFS_BACKEND_BURN (backend)->root_node, dirname, NULL);
g_free (dirname);
if (dir == NULL ||
dir->type != VIRTUAL_NODE_DIRECTORY)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
_("No such file or directory"));
return TRUE;
}
basename = g_path_get_basename (filename);
file = virtual_dir_lookup (dir, basename);
g_free (basename);
if (file == NULL)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
_("No such file or directory"));
return TRUE;
}
if (file->type == VIRTUAL_NODE_DIRECTORY &&
file->children != NULL)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_NOT_EMPTY,
_("Directory not empty"));
return TRUE;
}
virtual_unlink (dir, file);
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}
static gboolean
try_open_for_read (GVfsBackend *backend,
GVfsJobOpenForRead *job,
const char *filename)
{
VirtualNode *node;
GFileInputStream *stream;
GFile *file;
GError *error;
node = virtual_node_lookup (G_VFS_BACKEND_BURN (backend)->root_node, filename, NULL);
if (node == NULL)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
_("No such file or directory"));
return TRUE;
}
if (node->type == VIRTUAL_NODE_DIRECTORY)
{
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
_("Cant copy file over directory"));
return TRUE;
}
file = g_file_new_for_path (node->backing_file);
error = NULL;
stream = g_file_read (file, G_VFS_JOB (job)->cancellable, &error);
g_object_unref (file);
if (stream)
{
g_vfs_job_open_for_read_set_can_seek (job, g_seekable_can_seek (G_SEEKABLE (stream)));
g_vfs_job_open_for_read_set_handle (job, stream);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
else
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
g_error_free (error);
}
return TRUE;
}
static void
do_read (GVfsBackend *backend,
GVfsJobRead *job,
GVfsBackendHandle _handle,
char *buffer,
gsize bytes_requested)
{
GError *error;
GFileInputStream *stream = _handle;
gssize s;
error = NULL;
s = g_input_stream_read (G_INPUT_STREAM(stream),
buffer, bytes_requested,
G_VFS_JOB (job)->cancellable, &error);
if (s >= 0)
{
g_vfs_job_read_set_size (job, s);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
else
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
g_error_free (error);
}
}
static void
do_seek_on_read (GVfsBackend *backend,
GVfsJobSeekRead *job,
GVfsBackendHandle _handle,
goffset offset,
GSeekType type)
{
GError *error;
GFileInputStream *stream = _handle;
error = NULL;
if (g_seekable_seek (G_SEEKABLE (stream), offset, type,
G_VFS_JOB (job)->cancellable, &error))
{
g_vfs_job_seek_read_set_offset (job, g_seekable_tell (G_SEEKABLE (stream)));
g_vfs_job_succeeded (G_VFS_JOB (job));
}
else
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
g_error_free (error);
}
}
static void
do_close_read (GVfsBackend *backend,
GVfsJobCloseRead *job,
GVfsBackendHandle _handle)
{
GError *error;
GFileInputStream *stream = _handle;
error = NULL;
if (g_input_stream_close (G_INPUT_STREAM(stream),
G_VFS_JOB (job)->cancellable, &error))
{
g_vfs_job_succeeded (G_VFS_JOB (job));
}
else
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
g_error_free (error);
}
g_object_unref (stream);
}
static char *
make_valid_utf8 (const char *name)
{
GString *string;
const gchar *remainder, *invalid;
gint remaining_bytes, valid_bytes;
string = NULL;
remainder = name;
remaining_bytes = strlen (name);
while (remaining_bytes != 0)
{
if (g_utf8_validate (remainder, remaining_bytes, &invalid))
break;
valid_bytes = invalid - remainder;
if (string == NULL)
string = g_string_sized_new (remaining_bytes);
g_string_append_len (string, remainder, valid_bytes);
/* append U+FFFD REPLACEMENT CHARACTER */
g_string_append (string, "\357\277\275");
remaining_bytes -= valid_bytes + 1;
remainder = invalid + 1;
}
if (string == NULL)
return g_strdup (name);
g_string_append (string, remainder);
g_warn_if_fail (g_utf8_validate (string->str, -1, NULL));
return g_string_free (string, FALSE);
}
static void
file_info_from_node (VirtualNode *node,
GFileInfo *info,
const char *attributes)
{
GIcon *icon;
GFile *file;
GFileInfo *file_info;
if (node->type == VIRTUAL_NODE_DIRECTORY)
{
const char *content_type = "inode/directory";
g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
icon = g_content_type_get_icon (content_type);
g_file_info_set_icon (info, icon);
g_object_unref (icon);
icon = g_content_type_get_symbolic_icon (content_type);
g_file_info_set_symbolic_icon (info, icon);
g_object_unref (icon);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, TRUE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, TRUE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
g_file_info_set_content_type (info, content_type);
}
else
{
file = g_file_new_for_path (node->backing_file);
file_info = g_file_query_info (file,
attributes,
0, /* Always follow symlinks */
NULL, NULL);
if (file_info)
{
g_file_info_copy_into (file_info, info);
g_object_unref (file_info);
}
g_file_info_set_attribute_byte_string (info,
"burn::backing-file",
node->backing_file);
}
if (node->filename != NULL)
{
char *utf8;
g_file_info_set_name (info, node->filename);
/* Ensure display name is utf8 */
utf8 = make_valid_utf8 (node->filename);
g_file_info_set_display_name (info, utf8);
g_free (utf8);
}
else
{
g_file_info_set_name (info, "/");
/* Translators: this is the display name of the backend */
g_file_info_set_display_name (info, _("CD/DVD Creator"));
}
}
static gboolean
try_enumerate (GVfsBackend *backend,
GVfsJobEnumerate *job,
const char *filename,
GFileAttributeMatcher *attribute_matcher,
GFileQueryInfoFlags flags)
{
VirtualNode *node, *child;
GFileInfo *info;
GList *l;
node = virtual_node_lookup (G_VFS_BACKEND_BURN (backend)->root_node, filename, NULL);
if (node == NULL)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
_("No such file or directory"));
return TRUE;
}
if (node->type != VIRTUAL_NODE_DIRECTORY)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_NOT_DIRECTORY,
_("The file is not a directory"));
return TRUE;
}
g_vfs_job_succeeded (G_VFS_JOB (job));
for (l = node->children; l != NULL; l = l->next)
{
child = l->data;
info = g_file_info_new ();
file_info_from_node (child, info, job->attributes);
g_vfs_job_enumerate_add_info (job, info);
g_object_unref (info);
}
g_vfs_job_enumerate_done (job);
return TRUE;
}
static gboolean
try_query_info (GVfsBackend *backend,
GVfsJobQueryInfo *job,
const char *filename,
GFileQueryInfoFlags flags,
GFileInfo *info,
GFileAttributeMatcher *matcher)
{
VirtualNode *node;
node = virtual_node_lookup (G_VFS_BACKEND_BURN (backend)->root_node, filename, NULL);
if (node == NULL)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
_("No such file or directory"));
return TRUE;
}
file_info_from_node (node, info, job->attributes);
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}
static gboolean
try_make_directory (GVfsBackend *backend,
GVfsJobMakeDirectory *job,
const char *filename)
{
char *dirname, *basename;
VirtualNode *file, *dir;
dirname = g_path_get_dirname (filename);
dir = virtual_node_lookup (G_VFS_BACKEND_BURN (backend)->root_node, dirname, NULL);
g_free (dirname);
if (dir == NULL)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
_("No such file or directory"));
return TRUE;
}
basename = g_path_get_basename (filename);
file = virtual_dir_lookup (dir, basename);
if (file != NULL)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_EXISTS,
_("File exists"));
g_free (basename);
return TRUE;
}
file = virtual_mkdir (dir, basename);
g_free (basename);
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}
static gboolean
try_set_display_name (GVfsBackend *backend,
GVfsJobSetDisplayName *job,
const char *filename,
const char *display_name)
{
VirtualNode *node, *dir;
char *target_path;
char *dirname;
node = virtual_node_lookup (G_VFS_BACKEND_BURN (backend)->root_node, filename, &dir);
if (node == NULL)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
_("No such file or directory"));
return TRUE;
}
if (virtual_dir_lookup (dir, display_name) != NULL)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_EXISTS,
_("File exists"));
return TRUE;
}
/* We use UTF8 for filenames */
g_free (node->filename);
node->filename = g_strdup (display_name);
dirname = g_path_get_dirname (filename);
target_path = g_build_filename (dirname, display_name, NULL);
g_vfs_job_set_display_name_set_new_path (job, target_path);
g_free (dirname);
g_free (target_path);
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}
static gboolean
try_push (GVfsBackend *backend,
GVfsJobPush *job,
const char *destination,
const char *local_path,
GFileCopyFlags flags,
gboolean remove_source,
GFileProgressCallback progress_callback,
gpointer progress_callback_data)
{
VirtualNode *file, *dir;
struct stat stat_buf;
char *dirname, *basename;
if (remove_source)
{
/* Fallback to copy & delete for now, fix that up later */
g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
_("Operation not supported"));
return TRUE;
}
if (g_stat (local_path, &stat_buf) == -1)
{
int errsv = errno;
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR,
g_io_error_from_errno (errsv),
"%s", g_strerror (errsv));
return TRUE;
}
dirname = g_path_get_dirname (destination);
dir = virtual_node_lookup (G_VFS_BACKEND_BURN (backend)->root_node, dirname, NULL);
g_free (dirname);
file = NULL;
if (dir == NULL)
{
/* Parent of created file doesn't exist */
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
_("No such file or directory"));
return TRUE;
}
basename = g_path_get_basename (destination);
file = virtual_dir_lookup (dir, basename);
g_free (basename);
if (S_ISDIR (stat_buf.st_mode))
{
/* The source is a directory, don't fail with WOULD_RECURSE immediately,
* as that is less useful to the app. Better check for errors on the
* target instead.
*/
if (file != NULL)
{
if (flags & G_FILE_COPY_OVERWRITE)
{
if (file->type == VIRTUAL_NODE_DIRECTORY)
{
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_WOULD_MERGE,
_("Cant copy directory over directory"));
return TRUE;
}
/* continue to would_recurse error */
}
else
{
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_EXISTS,
_("Target file exists"));
return TRUE;
}
}
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE,
_("Cant recursively copy directory"));
return TRUE;
}
if (file != NULL)
{
if (flags & G_FILE_COPY_OVERWRITE)
{
if (file->type == VIRTUAL_NODE_DIRECTORY)
{
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
_("Cant copy file over directory"));
return TRUE;
}
else
{
g_assert (file->type == VIRTUAL_NODE_FILE);
if (file->owned_file)
g_unlink (file->backing_file);
g_free (file->backing_file);
file->owned_file = FALSE;
file->backing_file = g_strdup (local_path);
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}
}
else
{
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_EXISTS,
_("File exists"));
return TRUE;
}
}
else
{
basename = g_path_get_basename (destination);
file = virtual_create (G_VFS_BACKEND_BURN (backend),
dir,
basename,
local_path);
g_free (basename);
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}
}
static gboolean
try_create_dir_monitor (GVfsBackend *backend,
GVfsJobCreateMonitor *job,
const char *filename,
GFileMonitorFlags flags)
{
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Operation not supported"));
return TRUE;
}
static gboolean
try_move (GVfsBackend *backend,
GVfsJobMove *job,
const char *source,
const char *destination,
GFileCopyFlags flags,
GFileProgressCallback progress_callback,
gpointer progress_callback_data)
{
VirtualNode *source_node, *dest_node, *root_node, *source_dir, *dest_dir;
root_node = G_VFS_BACKEND_BURN (backend)->root_node;
source_node = virtual_node_lookup (root_node, source, &source_dir);
if (source_node == NULL)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
_("No such file or directory"));
return TRUE;
}
dest_node = virtual_node_lookup (root_node, destination, &dest_dir);
if (dest_node != NULL)
{
if (flags & G_FILE_COPY_OVERWRITE)
{
if (dest_node->type == VIRTUAL_NODE_DIRECTORY)
{
if (source_node->type == VIRTUAL_NODE_DIRECTORY)
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_WOULD_MERGE,
_("File exists"));
else
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_IS_DIRECTORY,
_("File exists"));
return TRUE;
}
else
virtual_unlink (dest_dir, dest_node);
}
else
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_EXISTS,
_("File exists"));
return TRUE;
}
}
else if (dest_dir == NULL)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
_("No such file or directory"));
return TRUE;
}
g_free (source_node->filename);
source_node->filename = g_path_get_basename (destination);
if (source_dir != dest_dir)
{
source_dir->children = g_list_remove (source_dir->children, source_node);
dest_dir->children = g_list_append (dest_dir->children, source_node);
}
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}
static gboolean
try_query_fs_info (GVfsBackend *backend,
GVfsJobQueryFsInfo *job,
const char *filename,
GFileInfo *info,
GFileAttributeMatcher *matcher)
{
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "burn");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, FALSE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL);
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}
static void
g_vfs_backend_burn_class_init (GVfsBackendBurnClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass);
gobject_class->finalize = g_vfs_backend_burn_finalize;
backend_class->try_mount = try_mount;
backend_class->try_open_for_read = try_open_for_read;
backend_class->try_query_info = try_query_info;
backend_class->try_query_fs_info = try_query_fs_info;
backend_class->try_enumerate = try_enumerate;
backend_class->try_create_dir_monitor = try_create_dir_monitor;
backend_class->try_make_directory = try_make_directory;
backend_class->try_set_display_name = try_set_display_name;
backend_class->try_push = try_push;
backend_class->try_delete = try_delete;
backend_class->try_move = try_move;
backend_class->read = do_read;
backend_class->seek_on_read = do_seek_on_read;
backend_class->close_read = do_close_read;
}