gvfs/daemon/gvfsbackendafp.c

2242 lines
66 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) Carl-Anton Ingmarsson 2011 <ca.ingmarsson@gmail.com>
*
* 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: Carl-Anton Ingmarsson <ca.ingmarsson@gmail.com>
*/
#include <config.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <glib/gstdio.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
#ifdef HAVE_GCRYPT
#include <gcrypt.h>
#endif
#include "gvfsjobmount.h"
#include "gvfsjobunmount.h"
#include "gvfsjobenumerate.h"
#include "gvfsjobqueryinfo.h"
#include "gvfsjobqueryfsinfo.h"
#include "gvfsjobsetattribute.h"
#include "gvfsjobqueryattributes.h"
#include "gvfsjobopenforread.h"
#include "gvfsjobcloseread.h"
#include "gvfsjobread.h"
#include "gvfsjobseekread.h"
#include "gvfsjobseekwrite.h"
#include "gvfsjobtruncate.h"
#include "gvfsjobopenforwrite.h"
#include "gvfsjobwrite.h"
#include "gvfsjobclosewrite.h"
#include "gvfsjobdelete.h"
#include "gvfsjobmakedirectory.h"
#include "gvfsjobsetdisplayname.h"
#include "gvfsjobmove.h"
#include "gvfsjobcopy.h"
#include "gvfsutils.h"
#include "gvfsafpserver.h"
#include "gvfsafpvolume.h"
#include "gvfsbackendafp.h"
struct _GVfsBackendAfpClass
{
GVfsBackendClass parent_class;
};
struct _GVfsBackendAfp
{
GVfsBackend parent_instance;
GNetworkAddress *addr;
char *volume_name;
char *user;
GVfsAfpServer *server;
GVfsAfpVolume *volume;
guint32 user_id;
guint32 group_id;
};
G_DEFINE_TYPE (GVfsBackendAfp, g_vfs_backend_afp, G_VFS_TYPE_BACKEND);
/*
* Utility functions
*/
static void
copy_file_info_into (GFileInfo *src, GFileInfo *dest)
{
char **attrs;
gint i;
attrs = g_file_info_list_attributes (src, NULL);
for (i = 0; attrs[i]; i++)
{
GFileAttributeType type;
gpointer value;
g_file_info_get_attribute_data (src, attrs[i], &type, &value, NULL);
g_file_info_set_attribute (dest, attrs[i], type, value);
}
g_strfreev (attrs);
}
typedef enum
{
AFP_HANDLE_TYPE_READ_FILE,
AFP_HANDLE_TYPE_CREATE_FILE,
AFP_HANDLE_TYPE_REPLACE_FILE_TEMP,
AFP_HANDLE_TYPE_REPLACE_FILE_DIRECT,
AFP_HANDLE_TYPE_APPEND_TO_FILE
} AfpHandleType;
typedef struct
{
GVfsBackendAfp *backend;
AfpHandleType type;
gint16 fork_refnum;
gint64 offset;
/* Used if type == AFP_HANDLE_TYPE_REPLACE_FILE_DIRECT */
gint64 size;
/* Used if type == AFP_HANDLE_TYPE_REPLACE_FILE_TEMP */
char *filename;
char *tmp_filename;
gboolean make_backup;
} AfpHandle;
static AfpHandle *
afp_handle_new (GVfsBackendAfp *backend, gint16 fork_refnum)
{
AfpHandle *afp_handle;
afp_handle = g_slice_new0 (AfpHandle);
afp_handle->backend = backend;
afp_handle->fork_refnum = fork_refnum;
return afp_handle;
}
static void
afp_handle_free (AfpHandle *afp_handle)
{
g_free (afp_handle->filename);
g_free (afp_handle->tmp_filename);
g_slice_free (AfpHandle, afp_handle);
}
/*
* Backend code
*/
typedef struct
{
GVfsJobCopy *job;
GAsyncResult *source_parms_res;
GAsyncResult *dest_parms_res;
goffset size;
} CopyData;
static void
copy_data_free (CopyData *copy_data)
{
g_object_unref (copy_data->source_parms_res);
g_object_unref (copy_data->dest_parms_res);
g_slice_free (CopyData, copy_data);
}
static void
copy_copy_file_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
CopyData *copy_data = user_data;
GVfsJobCopy *job = copy_data->job;
goffset size;
GError *err = NULL;
size = copy_data->size;
copy_data_free (copy_data);
if (!g_vfs_afp_volume_copy_file_finish (volume, res, &err))
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
g_vfs_job_progress_callback (size, size, job);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
static void
copy_delete_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
CopyData *copy_data = user_data;
GVfsJobCopy *job = copy_data->job;
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
GError *err = NULL;
if (!g_vfs_afp_volume_delete_finish (volume, res, &err))
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
copy_data_free (copy_data);
return;
}
g_vfs_afp_volume_copy_file (afp_backend->volume, job->source, job->destination,
G_VFS_JOB (job)->cancellable, copy_copy_file_cb, copy_data);
}
static void
do_copy (CopyData *copy_data)
{
GVfsJobCopy *job = copy_data->job;
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
GFileInfo *info;
GError *err = NULL;
gboolean source_is_dir;
gboolean dest_exists;
gboolean dest_is_dir;
info = g_vfs_afp_volume_get_filedir_parms_finish (afp_backend->volume, copy_data->source_parms_res, &err);
if (!info)
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
goto error;
}
copy_data->size = g_file_info_get_size (info);
/* If 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.
*/
source_is_dir = g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY ? TRUE : FALSE;
g_object_unref (info);
info = g_vfs_afp_volume_get_filedir_parms_finish (afp_backend->volume, copy_data->dest_parms_res, &err);
if (!info)
{
if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
{
g_clear_error (&err);
dest_exists = FALSE;
}
else
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
goto error;
}
}
else
{
dest_exists = TRUE;
dest_is_dir = g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY ? TRUE : FALSE;
g_object_unref (info);
}
/* Check target errors */
if (dest_exists)
{
if ((job->flags & G_FILE_COPY_OVERWRITE))
{
/* Always fail on dirs, even with overwrite */
if (dest_is_dir)
{
if (source_is_dir)
g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_WOULD_MERGE,
_("Cant copy directory over directory"));
else
g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
_("File is directory"));
goto error;
}
}
else
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_EXISTS,
_("Target file already exists"));
goto error;
}
}
/* Now we fail if the source is a directory */
if (source_is_dir)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE,
_("Cant recursively copy directory"));
goto error;
}
if (dest_exists)
{
g_vfs_afp_volume_delete (afp_backend->volume, job->destination,
G_VFS_JOB (job)->cancellable, copy_delete_cb, copy_data);
}
else
{
g_vfs_afp_volume_copy_file (afp_backend->volume, job->source, job->destination,
G_VFS_JOB (job)->cancellable, copy_copy_file_cb, copy_data);
}
return;
error:
copy_data_free (copy_data);
return;
}
static void
copy_get_dest_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
CopyData *copy_data = (CopyData *)user_data;
copy_data->dest_parms_res = g_object_ref (res);
if (copy_data->source_parms_res)
do_copy (copy_data);
}
static void
copy_get_source_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
CopyData *copy_data = (CopyData *)user_data;
copy_data->source_parms_res = g_object_ref (res);
if (copy_data->dest_parms_res)
do_copy (copy_data);
}
static gboolean
try_copy (GVfsBackend *backend,
GVfsJobCopy *job,
const char *source,
const char *destination,
GFileCopyFlags flags,
GFileProgressCallback progress_callback,
gpointer progress_callback_data)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
CopyData *copy_data;
copy_data = g_slice_new0 (CopyData);
copy_data->job = job;
g_vfs_afp_volume_get_filedir_parms (afp_backend->volume, source,
AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT |
AFP_FILE_BITMAP_EXT_DATA_FORK_LEN_BIT,
AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
G_VFS_JOB (job)->cancellable, copy_get_source_parms_cb,
copy_data);
g_vfs_afp_volume_get_filedir_parms (afp_backend->volume, destination,
AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
G_VFS_JOB (job)->cancellable, copy_get_dest_parms_cb,
copy_data);
return TRUE;
}
static void
move_move_and_rename_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobMove *job = G_VFS_JOB_MOVE (user_data);
GError *err = NULL;
if (!g_vfs_afp_volume_move_and_rename_finish (volume, res, &err))
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
g_vfs_job_succeeded (G_VFS_JOB (job));
}
static void
move_delete_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobMove *job = G_VFS_JOB_MOVE (user_data);
GError *err = NULL;
if (!g_vfs_afp_volume_delete_finish (volume, res, &err))
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
g_vfs_afp_volume_move_and_rename (volume, job->source, job->destination,
G_VFS_JOB (job)->cancellable, move_move_and_rename_cb,
job);
}
typedef struct
{
GVfsJobMove *job;
GAsyncResult *source_parms_res;
GAsyncResult *dest_parms_res;
} MoveData;
static void
free_move_data (MoveData *move_data)
{
g_object_unref (move_data->source_parms_res);
g_object_unref (move_data->dest_parms_res);
g_slice_free (MoveData, move_data);
}
static void
do_move (MoveData *move_data)
{
GVfsJobMove *job = move_data->job;
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
GFileInfo *info;
GError *err = NULL;
gboolean source_is_dir;
gboolean dest_exists;
gboolean dest_is_dir;
info = g_vfs_afp_volume_get_filedir_parms_finish (afp_backend->volume,
move_data->source_parms_res, &err);
if (!info)
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
goto done;
}
source_is_dir = g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY ? TRUE : FALSE;
g_object_unref (info);
info = g_vfs_afp_volume_get_filedir_parms_finish (afp_backend->volume,
move_data->dest_parms_res, &err);
if (!info)
{
if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
{
g_clear_error (&err);
dest_exists = FALSE;
}
else
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
goto done;
}
}
else
{
dest_exists = TRUE;
dest_is_dir = g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY ? TRUE : FALSE;
g_object_unref (info);
}
if (dest_exists)
{
if ((job->flags & G_FILE_COPY_OVERWRITE))
{
/* Always fail on dirs, even with overwrite */
if (dest_is_dir)
{
if (source_is_dir)
g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_WOULD_MERGE,
_("Cant move directory over directory"));
else
g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
_("File is directory"));
goto done;
}
}
else
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_EXISTS,
_("Target file already exists"));
goto done;
}
g_vfs_afp_volume_delete (afp_backend->volume, job->destination,
G_VFS_JOB (job)->cancellable, move_delete_cb, job);
}
else
g_vfs_afp_volume_move_and_rename (afp_backend->volume, job->source, job->destination,
G_VFS_JOB (job)->cancellable, move_move_and_rename_cb,
job);
done:
free_move_data (move_data);
return;
}
static void
move_get_dest_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
MoveData *move_data = (MoveData *)user_data;
move_data->dest_parms_res = g_object_ref (res);
if (move_data->source_parms_res)
do_move (move_data);
}
static void
move_get_source_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
MoveData *move_data = (MoveData *)user_data;
move_data->source_parms_res = g_object_ref (res);
if (move_data->dest_parms_res)
do_move (move_data);
}
static gboolean
try_move (GVfsBackend *backend,
GVfsJobMove *job,
const char *source,
const char *destination,
GFileCopyFlags flags,
GFileProgressCallback progress_callback,
gpointer progress_callback_data)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
MoveData *move_data;
move_data = g_slice_new0 (MoveData);
move_data->job = job;
g_vfs_afp_volume_get_filedir_parms (afp_backend->volume, source,
AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
G_VFS_JOB (job)->cancellable, move_get_source_parms_cb,
move_data);
g_vfs_afp_volume_get_filedir_parms (afp_backend->volume, destination,
AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
G_VFS_JOB (job)->cancellable, move_get_dest_parms_cb,
move_data);
return TRUE;
}
static void
rename_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobSetDisplayName *job = G_VFS_JOB_SET_DISPLAY_NAME (user_data);
GError *err = NULL;
char *dirname, *newpath;
if (!g_vfs_afp_volume_rename_finish (volume, res, &err))
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
dirname = g_path_get_dirname (job->filename);
newpath = g_build_filename (dirname, job->display_name, NULL);
g_vfs_job_set_display_name_set_new_path (job, newpath);
g_free (dirname);
g_free (newpath);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
static gboolean
try_set_display_name (GVfsBackend *backend,
GVfsJobSetDisplayName *job,
const char *filename,
const char *display_name)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
if (is_root (filename))
{
g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_INVALID_FILENAME,
_("Cant rename volume"));
return TRUE;
}
g_vfs_afp_volume_rename (afp_backend->volume, filename, display_name,
G_VFS_JOB (job)->cancellable, rename_cb, job);
return TRUE;
}
static void
create_directory_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobMakeDirectory *job = G_VFS_JOB_MAKE_DIRECTORY (user_data);
GError *err = NULL;
if (!g_vfs_afp_volume_create_directory_finish (volume, res, &err))
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
g_vfs_job_succeeded (G_VFS_JOB (job));
}
static gboolean
try_make_directory (GVfsBackend *backend,
GVfsJobMakeDirectory *job,
const char *filename)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
g_vfs_afp_volume_create_directory (afp_backend->volume, job->filename,
G_VFS_JOB (job)->cancellable,
create_directory_cb, job);
return TRUE;
}
static void
delete_delete_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobDelete *job = G_VFS_JOB_DELETE (user_data);
GError *err = NULL;
if (!g_vfs_afp_volume_delete_finish (volume, res, &err))
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
g_vfs_job_succeeded (G_VFS_JOB (job));
}
static gboolean
try_delete (GVfsBackend *backend,
GVfsJobDelete *job,
const char *filename)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
g_vfs_afp_volume_delete (afp_backend->volume, filename,
G_VFS_JOB (job)->cancellable, delete_delete_cb, job);
return TRUE;
}
static void
write_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobWrite *job = G_VFS_JOB_WRITE (user_data);
AfpHandle *afp_handle = (AfpHandle *)job->handle;
GError *err = NULL;
gint64 last_written;
gsize written_size;
if (!g_vfs_afp_volume_write_to_fork_finish (volume, res, &last_written, &err))
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
written_size = last_written - afp_handle->offset;
afp_handle->offset = last_written;
if (afp_handle->type == AFP_HANDLE_TYPE_REPLACE_FILE_DIRECT)
afp_handle->size = MAX (last_written, afp_handle->size);
g_vfs_job_write_set_written_size (job, written_size);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
static gboolean
try_write (GVfsBackend *backend,
GVfsJobWrite *job,
GVfsBackendHandle handle,
char *buffer,
gsize buffer_size)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
AfpHandle *afp_handle = (AfpHandle *)handle;
g_vfs_afp_volume_write_to_fork (afp_backend->volume, afp_handle->fork_refnum,
buffer, buffer_size, afp_handle->offset,
G_VFS_JOB (job)->cancellable, write_cb, job);
return TRUE;
}
static void
seek_on_write_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobSeekWrite *job = G_VFS_JOB_SEEK_WRITE (user_data);
AfpHandle *afp_handle = (AfpHandle *)job->handle;
GError *err = NULL;
GFileInfo *info;
gsize size;
info = g_vfs_afp_volume_get_fork_parms_finish (volume, res, &err);
if (!info)
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
size = g_file_info_get_size (info);
g_object_unref (info);
afp_handle->offset = size + job->requested_offset;
if (afp_handle->offset < 0)
afp_handle->offset = 0;
g_vfs_job_seek_write_set_offset (job, afp_handle->offset);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
static gboolean
try_seek_on_write (GVfsBackend *backend,
GVfsJobSeekWrite *job,
GVfsBackendHandle handle,
goffset offset,
GSeekType type)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
AfpHandle *afp_handle = (AfpHandle *)handle;
if (afp_handle->type == AFP_HANDLE_TYPE_REPLACE_FILE_DIRECT ||
job->seek_type != G_SEEK_END)
{
switch (job->seek_type)
{
case G_SEEK_CUR:
afp_handle->offset += job->requested_offset;
break;
case G_SEEK_SET:
afp_handle->offset = job->requested_offset;
break;
case G_SEEK_END:
afp_handle->offset = afp_handle->size + job->requested_offset;
break;
}
if (afp_handle->offset < 0)
afp_handle->offset = 0;
g_vfs_job_seek_write_set_offset (job, afp_handle->offset);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
else
{
g_vfs_afp_volume_get_fork_parms (afp_backend->volume, afp_handle->fork_refnum,
AFP_FILE_BITMAP_EXT_DATA_FORK_LEN_BIT,
G_VFS_JOB (job)->cancellable, seek_on_write_cb, job);
}
return TRUE;
}
static void
truncate_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobTruncate *job = G_VFS_JOB_TRUNCATE (user_data);
GError *err = NULL;
if (g_vfs_afp_volume_set_fork_size_finish (volume, res, &err))
{
g_vfs_job_succeeded (G_VFS_JOB (job));
}
else
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
}
}
static gboolean
try_truncate (GVfsBackend *backend,
GVfsJobTruncate *job,
GVfsBackendHandle handle,
goffset size)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
AfpHandle *afp_handle = (AfpHandle *)handle;
g_vfs_afp_volume_set_fork_size (afp_backend->volume, afp_handle->fork_refnum,
size, G_VFS_JOB (job)->cancellable,
truncate_cb, job);
return TRUE;
}
static void
seek_on_read_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobSeekRead *job = G_VFS_JOB_SEEK_READ (user_data);
AfpHandle *afp_handle = (AfpHandle *)job->handle;
GError *err = NULL;
GFileInfo *info;
gsize size;
info = g_vfs_afp_volume_get_fork_parms_finish (volume, res, &err);
if (!info)
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
size = g_file_info_get_size (info);
g_object_unref (info);
afp_handle->offset = size + job->requested_offset;
if (afp_handle->offset < 0)
afp_handle->offset = 0;
g_vfs_job_seek_read_set_offset (job, afp_handle->offset);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
static gboolean
try_seek_on_read (GVfsBackend *backend,
GVfsJobSeekRead *job,
GVfsBackendHandle handle,
goffset offset,
GSeekType type)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
AfpHandle *afp_handle = (AfpHandle *)handle;
switch (job->seek_type)
{
case G_SEEK_CUR:
afp_handle->offset += job->requested_offset;
break;
case G_SEEK_SET:
afp_handle->offset = job->requested_offset;
break;
case G_SEEK_END:
g_vfs_afp_volume_get_fork_parms (afp_backend->volume, afp_handle->fork_refnum,
AFP_FILE_BITMAP_EXT_DATA_FORK_LEN_BIT,
G_VFS_JOB (job)->cancellable, seek_on_read_cb, job);
return TRUE;
}
if (afp_handle->offset < 0)
afp_handle->offset = 0;
g_vfs_job_seek_read_set_offset (job, afp_handle->offset);
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}
static void
read_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobRead *job = G_VFS_JOB_READ (user_data);
AfpHandle *afp_handle = (AfpHandle *)job->handle;
GError *err = NULL;
gsize bytes_read;
if (!g_vfs_afp_volume_read_from_fork_finish (volume, res, &bytes_read, &err))
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
afp_handle->offset += bytes_read;
g_vfs_job_read_set_size (job, bytes_read);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
static gboolean
try_read (GVfsBackend *backend,
GVfsJobRead *job,
GVfsBackendHandle handle,
char *buffer,
gsize bytes_requested)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
AfpHandle *afp_handle = (AfpHandle *)handle;
g_vfs_afp_volume_read_from_fork (afp_backend->volume, afp_handle->fork_refnum,
buffer, bytes_requested, afp_handle->offset,
G_VFS_JOB (job)->cancellable, read_cb, job);
return TRUE;
}
static void
close_replace_get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobCloseWrite *job = G_VFS_JOB_CLOSE_WRITE (user_data);
GFileInfo *info;
GError *err = NULL;
info = g_vfs_afp_volume_get_filedir_parms_finish (volume, res, &err);
if (!info)
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
g_vfs_job_close_write_set_etag (job, g_file_info_get_etag (info));
g_vfs_job_succeeded (G_VFS_JOB (job));
g_object_unref (info);
}
static void
close_replace_delete_backup_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
AfpHandle *afp_handle = (AfpHandle *)user_data;
char *backup_name;
/* We ignore all errors and just try to rename the temporary file anyway */
backup_name = g_strconcat (afp_handle->filename, "~", NULL);
g_vfs_afp_volume_move_and_rename (volume, afp_handle->tmp_filename, backup_name,
NULL, NULL, NULL);
afp_handle_free (afp_handle);
}
static void
close_replace_close_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
AfpHandle *afp_handle = (AfpHandle *)user_data;
if (afp_handle->make_backup)
{
char *backup_name = g_strconcat (afp_handle->filename, "~", NULL);
/* Delete old backup */
g_vfs_afp_volume_delete (volume, backup_name, NULL,
close_replace_delete_backup_cb, afp_handle);
g_free (backup_name);
}
else
{
/* Delete temporary file */
g_vfs_afp_volume_delete (volume, afp_handle->tmp_filename, NULL, NULL, NULL);
afp_handle_free (afp_handle);
}
}
static void
close_replace_exchange_files_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobCloseWrite *job = G_VFS_JOB_CLOSE_WRITE (user_data);
AfpHandle *afp_handle = (AfpHandle *)job->handle;
GError *err = NULL;
if (!g_vfs_afp_volume_exchange_files_finish (volume, res, &err))
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
afp_handle_free (afp_handle);
return;
}
/* Close fork and remove/rename the temporary file even if the exchange failed */
g_vfs_afp_volume_close_fork (volume, afp_handle->fork_refnum,
G_VFS_JOB (job)->cancellable,
close_replace_close_fork_cb, job->handle);
/* Get ETAG */
g_vfs_afp_volume_get_filedir_parms (volume, afp_handle->filename,
AFP_FILE_BITMAP_MOD_DATE_BIT, 0,
G_VFS_JOB (job)->cancellable,
close_replace_get_filedir_parms_cb, job);
}
static void
close_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJob *job = G_VFS_JOB (user_data);
GError *err = NULL;
if (!g_vfs_afp_volume_close_fork_finish (volume, res, &err))
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
g_vfs_job_succeeded (G_VFS_JOB (job));
}
static void
close_fork (GVfsAfpVolume *volume,
GVfsJob *job,
AfpHandle *afp_handle)
{
g_vfs_afp_volume_close_fork (volume, afp_handle->fork_refnum,
G_VFS_JOB (job)->cancellable,
close_fork_cb, job);
afp_handle_free (afp_handle);
}
static void
close_write_get_fork_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobCloseWrite *job = G_VFS_JOB_CLOSE_WRITE (user_data);
AfpHandle *afp_handle = (AfpHandle *)job->handle;
GFileInfo *info;
info = g_vfs_afp_volume_get_fork_parms_finish (volume, res, NULL);
if (info)
g_vfs_job_close_write_set_etag (job, g_file_info_get_etag (info));
close_fork (volume, G_VFS_JOB (job), afp_handle);
}
static void
close_replace_set_fork_size_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobCloseWrite *job = G_VFS_JOB_CLOSE_WRITE (user_data);
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
AfpHandle *afp_handle = (AfpHandle *)job->handle;
GError *err = NULL;
if (!g_vfs_afp_volume_set_fork_size_finish (volume, res, &err))
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
afp_handle_free (afp_handle);
return;
}
/* Get ETAG */
g_vfs_afp_volume_get_fork_parms (afp_backend->volume, afp_handle->fork_refnum,
AFP_FILE_BITMAP_MOD_DATE_BIT,
G_VFS_JOB (job)->cancellable,
close_write_get_fork_parms_cb, job);
}
static gboolean
try_close_write (GVfsBackend *backend,
GVfsJobCloseWrite *job,
GVfsBackendHandle handle)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
AfpHandle *afp_handle = (AfpHandle *)handle;
if (afp_handle->type == AFP_HANDLE_TYPE_REPLACE_FILE_TEMP)
{
g_vfs_afp_volume_exchange_files (afp_backend->volume, afp_handle->filename,
afp_handle->tmp_filename,
G_VFS_JOB (job)->cancellable,
close_replace_exchange_files_cb, job);
}
else if (afp_handle->type == AFP_HANDLE_TYPE_REPLACE_FILE_DIRECT)
{
g_vfs_afp_volume_set_fork_size (afp_backend->volume, afp_handle->fork_refnum,
afp_handle->size, G_VFS_JOB (job)->cancellable,
close_replace_set_fork_size_cb, job);
}
else
{
/* Get ETAG */
g_vfs_afp_volume_get_fork_parms (afp_backend->volume, afp_handle->fork_refnum,
AFP_FILE_BITMAP_MOD_DATE_BIT,
G_VFS_JOB (job)->cancellable,
close_write_get_fork_parms_cb, job);
}
return TRUE;
}
static gboolean
try_close_read (GVfsBackend *backend,
GVfsJobCloseRead *job,
GVfsBackendHandle handle)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
AfpHandle *afp_handle = (AfpHandle *)handle;
close_fork (afp_backend->volume, G_VFS_JOB (job), afp_handle);
return TRUE;
}
static void
create_open_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobOpenForWrite *job = G_VFS_JOB_OPEN_FOR_WRITE (user_data);
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
gint16 fork_refnum;
GError *err = NULL;
AfpHandle *afp_handle;
if (!g_vfs_afp_volume_open_fork_finish (volume, res, &fork_refnum, NULL, &err))
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
afp_handle = afp_handle_new (afp_backend, fork_refnum);
afp_handle->type = AFP_HANDLE_TYPE_CREATE_FILE;
g_vfs_job_open_for_write_set_handle (job, (GVfsBackendHandle) afp_handle);
g_vfs_job_open_for_write_set_can_seek (job, TRUE);
g_vfs_job_open_for_write_set_can_truncate (job, TRUE);
g_vfs_job_open_for_write_set_initial_offset (job, 0);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
static void
create_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobOpenForWrite *job = G_VFS_JOB_OPEN_FOR_WRITE (user_data);
GError *err = NULL;
if (!g_vfs_afp_volume_create_file_finish (volume, res, &err))
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
g_vfs_afp_volume_open_fork (volume, job->filename, AFP_ACCESS_MODE_WRITE_BIT, 0,
G_VFS_JOB (job)->cancellable, create_open_fork_cb, job);
}
static gboolean
try_create (GVfsBackend *backend,
GVfsJobOpenForWrite *job,
const char *filename,
GFileCreateFlags flags)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
g_vfs_afp_volume_create_file (afp_backend->volume, filename, FALSE,
G_VFS_JOB (job)->cancellable, create_cb, job);
return TRUE;
}
static void
replace_open_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobOpenForWrite *job = G_VFS_JOB_OPEN_FOR_WRITE (user_data);
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
gint16 fork_refnum;
GError *err = NULL;
AfpHandle *afp_handle;
char *tmp_filename;
if (!g_vfs_afp_volume_open_fork_finish (volume, res, &fork_refnum, NULL, &err))
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
afp_handle = afp_handle_new (afp_backend, fork_refnum);
tmp_filename = g_object_get_data (G_OBJECT (job), "TempFilename");
/* Replace using temporary file */
if (tmp_filename)
{
afp_handle->type = AFP_HANDLE_TYPE_REPLACE_FILE_TEMP;
afp_handle->filename = g_strdup (job->filename);
afp_handle->tmp_filename = g_strdup (tmp_filename);
afp_handle->make_backup = job->make_backup;
}
else
afp_handle->type = AFP_HANDLE_TYPE_REPLACE_FILE_DIRECT;
g_vfs_job_open_for_write_set_handle (job, (GVfsBackendHandle) afp_handle);
g_vfs_job_open_for_write_set_can_seek (job, TRUE);
g_vfs_job_open_for_write_set_can_truncate (job, TRUE);
g_vfs_job_open_for_write_set_initial_offset (job, 0);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
static void replace_create_tmp_file (GVfsAfpVolume *volume, GVfsJobOpenForWrite *job);
static void
replace_create_tmp_file_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobOpenForWrite *job = G_VFS_JOB_OPEN_FOR_WRITE (user_data);
GError *err = NULL;
char *tmp_filename;
if (!g_vfs_afp_volume_create_file_finish (volume, res, &err))
{
if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_EXISTS))
replace_create_tmp_file (volume, job);
/* We don't have the necessary permissions to create a temporary file
* so we try to write directly to the file */
else if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED))
{
/* FIXME: We don't support making backups when we can't use FPExchangeFiles */
if (job->make_backup)
{
g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_CANT_CREATE_BACKUP,
_("Backups not supported"));
}
else
{
g_object_set_data (G_OBJECT (job), "TempFilename", NULL);
g_vfs_afp_volume_open_fork (volume, job->filename,
AFP_ACCESS_MODE_WRITE_BIT, 0,
G_VFS_JOB (job)->cancellable, replace_open_fork_cb, job);
}
}
else
{
g_vfs_job_failed (G_VFS_JOB (job), err->domain, err->code,
_("Unable to create temporary file (%s)"), err->message);
}
g_error_free (err);
return;
}
tmp_filename = g_object_get_data (G_OBJECT (job), "TempFilename");
g_vfs_afp_volume_open_fork (volume, tmp_filename,
AFP_ACCESS_MODE_WRITE_BIT, 0,
G_VFS_JOB (job)->cancellable, replace_open_fork_cb, job);
}
static void
replace_create_tmp_file (GVfsAfpVolume *volume, GVfsJobOpenForWrite *job)
{
char basename[] = "~gvfXXXX.tmp";
char *dir, *tmp_filename;
gvfs_randomize_string (basename + 4, 4);
dir = g_path_get_dirname (job->filename);
tmp_filename = g_build_filename (dir, basename, NULL);
g_free (dir);
g_object_set_data_full (G_OBJECT (job), "TempFilename", tmp_filename, g_free);
g_vfs_afp_volume_create_file (volume, tmp_filename, FALSE,
G_VFS_JOB (job)->cancellable,
replace_create_tmp_file_cb, job);
}
static void
replace_get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobOpenForWrite *job = G_VFS_JOB_OPEN_FOR_WRITE (user_data);
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
GError *err = NULL;
GFileInfo *info;
info = g_vfs_afp_volume_get_filedir_parms_finish (volume, res, &err);
if (!info)
{
/* Create file if it doesn't exist */
if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
try_create (G_VFS_BACKEND (afp_backend), job, job->filename, job->flags);
else
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
{
g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
_("File is directory"));
}
else if (job->etag && g_strcmp0 (g_file_info_get_etag (info), job->etag) != 0)
{
g_vfs_job_failed_literal (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_WRONG_ETAG,
_("The file was externally modified"));
}
else
{
if (g_vfs_afp_volume_get_attributes (volume) & AFP_VOLUME_ATTRIBUTES_BITMAP_NO_EXCHANGE_FILES)
{
/* FIXME: We don't support making backups when we can't use FPExchangeFiles */
if (job->make_backup)
{
g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_CANT_CREATE_BACKUP,
_("Backups not supported"));
}
else
{
g_vfs_afp_volume_open_fork (volume, job->filename,
AFP_ACCESS_MODE_WRITE_BIT, 0,
G_VFS_JOB (job)->cancellable, replace_open_fork_cb, job);
}
}
else
replace_create_tmp_file (volume, job);
}
g_object_unref (info);
}
static gboolean
try_replace (GVfsBackend *backend,
GVfsJobOpenForWrite *job,
const char *filename,
const char *etag,
gboolean make_backup,
GFileCreateFlags flags)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
g_vfs_afp_volume_get_filedir_parms (afp_backend->volume, filename,
AFP_FILE_BITMAP_MOD_DATE_BIT, 0,
G_VFS_JOB (job)->cancellable,
replace_get_filedir_parms_cb, job);
return TRUE;
}
static void
append_to_get_fork_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobOpenForWrite *job = G_VFS_JOB_OPEN_FOR_WRITE (user_data);
AfpHandle *afp_handle = (AfpHandle *)job->backend_handle;
GFileInfo *info;
GError *err = NULL;
goffset size;
info = g_vfs_afp_volume_get_fork_parms_finish (volume, res, &err);
if (!info)
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
afp_handle_free (afp_handle);
return;
}
size = g_file_info_get_size (info);
g_object_unref (info);
afp_handle->offset = size;
g_vfs_job_open_for_write_set_initial_offset (job, size);
g_vfs_job_open_for_write_set_can_seek (job, TRUE);
g_vfs_job_open_for_write_set_can_truncate (job, TRUE);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
static void
append_to_open_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobOpenForWrite *job = G_VFS_JOB_OPEN_FOR_WRITE (user_data);
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
gint16 fork_refnum;
GError *err = NULL;
AfpHandle *afp_handle;
if (!g_vfs_afp_volume_open_fork_finish (volume, res, &fork_refnum, NULL, &err))
{
/* Create file if it doesn't exist */
if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
try_create (G_VFS_BACKEND (afp_backend), job, job->filename, job->flags);
else
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
afp_handle = afp_handle_new (afp_backend, fork_refnum);
afp_handle->type = AFP_HANDLE_TYPE_APPEND_TO_FILE;
g_vfs_job_open_for_write_set_handle (job, (GVfsBackendHandle) afp_handle);
g_vfs_afp_volume_get_fork_parms (afp_backend->volume, afp_handle->fork_refnum,
AFP_FILE_BITMAP_EXT_DATA_FORK_LEN_BIT,
G_VFS_JOB (job)->cancellable,
append_to_get_fork_parms_cb, job);
}
static gboolean
try_append_to (GVfsBackend *backend,
GVfsJobOpenForWrite *job,
const char *filename,
GFileCreateFlags flags)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
g_vfs_afp_volume_open_fork (afp_backend->volume, job->filename,
AFP_ACCESS_MODE_WRITE_BIT, 0,
G_VFS_JOB (job)->cancellable, append_to_open_fork_cb, job);
return TRUE;
}
static void
read_open_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobOpenForRead *job = G_VFS_JOB_OPEN_FOR_READ (user_data);
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
GError *err = NULL;
gint16 fork_refnum;
AfpHandle *afp_handle;
if (!g_vfs_afp_volume_open_fork_finish (volume, res, &fork_refnum, NULL, &err))
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
afp_handle = afp_handle_new (afp_backend, fork_refnum);
afp_handle->type = AFP_HANDLE_TYPE_READ_FILE;
g_vfs_job_open_for_read_set_handle (job, (GVfsBackendHandle) afp_handle);
g_vfs_job_open_for_read_set_can_seek (job, TRUE);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
static gboolean
try_open_for_read (GVfsBackend *backend,
GVfsJobOpenForRead *job,
const char *filename)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
g_vfs_afp_volume_open_fork (afp_backend->volume, filename,
AFP_ACCESS_MODE_READ_BIT, 0,
G_VFS_JOB (job)->cancellable, read_open_fork_cb, job);
return TRUE;
}
static guint16
create_filedir_bitmap (GVfsBackendAfp *afp_backend, GFileAttributeMatcher *matcher)
{
guint16 bitmap;
bitmap = AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT | AFP_FILEDIR_BITMAP_UTF8_NAME_BIT;
if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_TIME_CREATED))
bitmap |= AFP_FILEDIR_BITMAP_CREATE_DATE_BIT;
if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_TIME_MODIFIED) ||
g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_ETAG_VALUE))
bitmap |= AFP_FILEDIR_BITMAP_MOD_DATE_BIT;
if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_UNIX_MODE) ||
g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_UNIX_UID) ||
g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_UNIX_GID) ||
g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_READ) ||
g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE) ||
g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE)||
g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_OWNER_USER) ||
g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_OWNER_USER_REAL) ||
g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_OWNER_GROUP))
{
if (g_vfs_afp_volume_get_attributes (afp_backend->volume) & AFP_VOLUME_ATTRIBUTES_BITMAP_SUPPORTS_UNIX_PRIVS)
bitmap |= AFP_FILEDIR_BITMAP_UNIX_PRIVS_BIT;
}
return bitmap;
}
static guint16
create_file_bitmap (GVfsBackendAfp *afp_backend, GFileAttributeMatcher *matcher)
{
guint16 file_bitmap;
file_bitmap = create_filedir_bitmap (afp_backend, matcher);
if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_SIZE))
file_bitmap |= AFP_FILE_BITMAP_EXT_DATA_FORK_LEN_BIT;
return file_bitmap;
}
static guint16
create_dir_bitmap (GVfsBackendAfp *afp_backend, GFileAttributeMatcher *matcher)
{
guint16 dir_bitmap;
dir_bitmap = create_filedir_bitmap (afp_backend, matcher);
if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_AFP_CHILDREN_COUNT))
dir_bitmap |= AFP_DIR_BITMAP_OFFSPRING_COUNT_BIT;
return dir_bitmap;
}
static void
enumerate (GVfsBackendAfp *afp_backend,
GVfsJobEnumerate *job,
gint32 start_index);
static void
enumerate_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobEnumerate *job = G_VFS_JOB_ENUMERATE (user_data);
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
GPtrArray *infos;
GError *err = NULL;
guint i;
gint64 start_index;
if (!g_vfs_afp_volume_enumerate_finish (volume, res, &infos, &err))
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
/* No more files */
if (!infos)
{
g_vfs_job_succeeded (G_VFS_JOB (job));
g_vfs_job_enumerate_done (job);
return;
}
for (i = 0; i < infos->len; i++)
g_vfs_job_enumerate_add_info (job, g_ptr_array_index (infos, i));
start_index = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (job),
"start-index"));
start_index += infos->len;
g_ptr_array_unref (infos);
enumerate (afp_backend, job, start_index);
}
static void
enumerate (GVfsBackendAfp *afp_backend,
GVfsJobEnumerate *job,
gint32 start_index)
{
const char *filename = job->filename;
GFileAttributeMatcher *matcher = job->attribute_matcher;
guint16 file_bitmap, dir_bitmap;
g_object_set_data (G_OBJECT (job), "start-index",
GINT_TO_POINTER (start_index));
file_bitmap = create_file_bitmap (afp_backend, matcher);
dir_bitmap = create_dir_bitmap (afp_backend, matcher);
g_vfs_afp_volume_enumerate (afp_backend->volume, filename, start_index,
file_bitmap, dir_bitmap,
G_VFS_JOB (job)->cancellable, enumerate_cb, job);
}
static gboolean
try_enumerate (GVfsBackend *backend,
GVfsJobEnumerate *job,
const char *filename,
GFileAttributeMatcher *matcher,
GFileQueryInfoFlags flags)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
enumerate (afp_backend, job, 1);
return TRUE;
}
static gboolean
try_query_settable_attributes (GVfsBackend *backend,
GVfsJobQueryAttributes *job,
const char *filename)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
GFileAttributeInfoList *list;
list = g_file_attribute_info_list_new ();
if (g_vfs_afp_volume_get_attributes (afp_backend->volume) & AFP_VOLUME_ATTRIBUTES_BITMAP_SUPPORTS_UNIX_PRIVS)
{
g_file_attribute_info_list_add (list,
G_FILE_ATTRIBUTE_UNIX_MODE,
G_FILE_ATTRIBUTE_TYPE_UINT32,
G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE |
G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
g_file_attribute_info_list_add (list,
G_FILE_ATTRIBUTE_UNIX_UID,
G_FILE_ATTRIBUTE_TYPE_UINT32,
G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE |
G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
g_file_attribute_info_list_add (list,
G_FILE_ATTRIBUTE_UNIX_GID,
G_FILE_ATTRIBUTE_TYPE_UINT32,
G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE |
G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
}
g_vfs_job_query_attributes_set_list (job, list);
g_vfs_job_succeeded (G_VFS_JOB (job));
g_file_attribute_info_list_unref (list);
return TRUE;
}
static void
set_attribute_set_unix_privs_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobSetAttribute *job = G_VFS_JOB_SET_ATTRIBUTE (user_data);
GError *err = NULL;
if (!g_vfs_afp_volume_set_unix_privs_finish (volume, res, &err))
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
g_vfs_job_succeeded (G_VFS_JOB (job));
}
static void
set_attribute_get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobSetAttribute *job = G_VFS_JOB_SET_ATTRIBUTE (user_data);
GFileInfo *info;
GError *err = NULL;
guint32 uid, gid, permissions, ua_permissions;
info = g_vfs_afp_volume_get_filedir_parms_finish (volume, res, &err);
if (!info)
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
uid = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID);
gid = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID);
permissions = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE);
ua_permissions = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_AFP_UA_PERMISSIONS);
g_object_unref (info);
if (strcmp (job->attribute, G_FILE_ATTRIBUTE_UNIX_UID) == 0)
uid = job->value.uint32;
else if (strcmp (job->attribute, G_FILE_ATTRIBUTE_UNIX_GID) == 0)
gid = job->value.uint32;
else if (strcmp (job->attribute, G_FILE_ATTRIBUTE_UNIX_MODE) == 0)
permissions = job->value.uint32;
g_vfs_afp_volume_set_unix_privs (volume, job->filename,
uid, gid, permissions, ua_permissions,
G_VFS_JOB (job)->cancellable,
set_attribute_set_unix_privs_cb, job);
}
static gboolean
try_set_attribute (GVfsBackend *backend,
GVfsJobSetAttribute *job,
const char *filename,
const char *attribute,
GFileAttributeType type,
gpointer value_p,
GFileQueryInfoFlags flags)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
if ((strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) == 0 ||
strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_UID) == 0 ||
strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_GID) == 0)
&& g_vfs_afp_volume_get_attributes (afp_backend->volume) & AFP_VOLUME_ATTRIBUTES_BITMAP_SUPPORTS_UNIX_PRIVS)
{
if (type != G_FILE_ATTRIBUTE_TYPE_UINT32)
{
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
"%s",
_("Invalid attribute type (uint32 expected)"));
return TRUE;
}
g_vfs_afp_volume_get_filedir_parms (afp_backend->volume, filename,
AFP_FILEDIR_BITMAP_UNIX_PRIVS_BIT,
AFP_FILEDIR_BITMAP_UNIX_PRIVS_BIT,
G_VFS_JOB (job)->cancellable,
set_attribute_get_filedir_parms_cb,
job);
return TRUE;
}
else {
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Operation not supported"));
return TRUE;
}
}
static void
query_fs_info_get_vol_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobQueryFsInfo *job = G_VFS_JOB_QUERY_FS_INFO (user_data);
GError *err = NULL;
GFileInfo *info;
info = g_vfs_afp_volume_get_parms_finish (volume, res, &err);
if (!info)
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
copy_file_info_into (info, job->file_info);
g_object_unref (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 *matcher)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
guint16 vol_bitmap = 0;
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "afp");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS);
if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE))
vol_bitmap |= AFP_VOLUME_BITMAP_EXT_BYTES_TOTAL_BIT;
if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_FREE))
vol_bitmap |= AFP_VOLUME_BITMAP_EXT_BYTES_FREE_BIT;
if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_USED))
{
vol_bitmap |= AFP_VOLUME_BITMAP_EXT_BYTES_TOTAL_BIT;
vol_bitmap |= AFP_VOLUME_BITMAP_EXT_BYTES_FREE_BIT;
}
if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY))
vol_bitmap |= AFP_VOLUME_BITMAP_ATTRIBUTE_BIT;
if (vol_bitmap != 0)
{
g_vfs_afp_volume_get_parms (afp_backend->volume, vol_bitmap,
G_VFS_JOB (job)->cancellable,
query_fs_info_get_vol_parms_cb, job);
}
else
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}
static void
get_name_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpServer *server = G_VFS_AFP_SERVER (source_object);
GVfsJobQueryInfo *job = G_VFS_JOB_QUERY_INFO (user_data);
char *name;
GVfsAfpMapIDFunction map_function;
guint outstanding_requests;
name = g_vfs_afp_server_map_id_finish (server, res, &map_function, NULL);
if (name)
{
switch (map_function)
{
case GVFS_AFP_MAP_ID_FUNCTION_USER_ID_TO_NAME:
g_file_info_set_attribute_string (job->file_info, G_FILE_ATTRIBUTE_OWNER_USER,
name);
break;
case GVFS_AFP_MAP_ID_FUNCTION_USER_ID_TO_UTF8_NAME:
g_file_info_set_attribute_string (job->file_info, G_FILE_ATTRIBUTE_OWNER_USER_REAL,
name);
break;
case GVFS_AFP_MAP_ID_FUNCTION_GROUP_ID_TO_NAME:
g_file_info_set_attribute_string (job->file_info, G_FILE_ATTRIBUTE_OWNER_GROUP,
name);
break;
default:
g_assert_not_reached ();
}
g_free (name);
}
outstanding_requests = GPOINTER_TO_UINT (G_VFS_JOB (job)->backend_data);
if (--outstanding_requests == 0)
g_vfs_job_succeeded (G_VFS_JOB (job));
else
G_VFS_JOB (job)->backend_data = GUINT_TO_POINTER (outstanding_requests);
}
static void
set_root_info (GVfsBackendAfp *afp_backend, GFileInfo *info)
{
GIcon *icon;
g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
g_file_info_set_name (info, "/");
g_file_info_set_display_name (info,
g_vfs_backend_get_display_name (G_VFS_BACKEND (afp_backend)));
g_file_info_set_content_type (info, "inode/directory");
icon = g_vfs_backend_get_icon (G_VFS_BACKEND (afp_backend));
if (icon != NULL)
g_file_info_set_icon (info, icon);
icon = g_vfs_backend_get_symbolic_icon (G_VFS_BACKEND (afp_backend));
if (icon != NULL)
g_file_info_set_symbolic_icon (info, icon);
}
static void
query_info_get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
GVfsJobQueryInfo *job = G_VFS_JOB_QUERY_INFO (user_data);
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
GFileInfo *info;
GError *err = NULL;
GFileAttributeMatcher *matcher;
guint outstanding_requests;
info = g_vfs_afp_volume_get_filedir_parms_finish (volume, res, &err);
if (!info)
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
g_error_free (err);
return;
}
outstanding_requests = 0;
matcher = job->attribute_matcher;
if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_UNIX_UID))
{
guint32 uid;
uid = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID);
if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_OWNER_USER))
{
g_vfs_afp_server_map_id (afp_backend->server,
GVFS_AFP_MAP_ID_FUNCTION_USER_ID_TO_NAME, uid,
G_VFS_JOB (job)->cancellable, get_name_cb, job);
outstanding_requests++;
}
if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_OWNER_USER_REAL))
{
g_vfs_afp_server_map_id (afp_backend->server,
GVFS_AFP_MAP_ID_FUNCTION_USER_ID_TO_UTF8_NAME, uid,
G_VFS_JOB (job)->cancellable, get_name_cb, job);
outstanding_requests++;
}
}
if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_UNIX_GID) &&
g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_OWNER_GROUP))
{
guint32 gid;
gid = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID);
g_vfs_afp_server_map_id (afp_backend->server,
GVFS_AFP_MAP_ID_FUNCTION_GROUP_ID_TO_NAME, gid,
G_VFS_JOB (job)->cancellable, get_name_cb, job);
outstanding_requests++;
}
G_VFS_JOB (job)->backend_data = GUINT_TO_POINTER (outstanding_requests);
copy_file_info_into (info, job->file_info);
g_object_unref (info);
if (is_root (job->filename))
set_root_info (afp_backend, job->file_info);
if (outstanding_requests == 0)
g_vfs_job_succeeded (G_VFS_JOB (job));
}
static gboolean
try_query_info (GVfsBackend *backend,
GVfsJobQueryInfo *job,
const char *filename,
GFileQueryInfoFlags flags,
GFileInfo *info,
GFileAttributeMatcher *matcher)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
g_debug ("Filename: %s\n", filename);
if (is_root (filename))
{
guint16 dir_bitmap = 0;
dir_bitmap = create_dir_bitmap (afp_backend, matcher);
dir_bitmap &= ~AFP_DIR_BITMAP_UTF8_NAME_BIT;
if (dir_bitmap != 0)
{
g_vfs_afp_volume_get_filedir_parms (afp_backend->volume, filename,
0, dir_bitmap,
G_VFS_JOB (job)->cancellable,
query_info_get_filedir_parms_cb, job);
}
else
{
set_root_info (afp_backend, info);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
}
else {
guint16 file_bitmap, dir_bitmap;
file_bitmap = create_file_bitmap (afp_backend, matcher);
dir_bitmap = create_dir_bitmap (afp_backend, matcher);
g_vfs_afp_volume_get_filedir_parms (afp_backend->volume, filename,
file_bitmap, dir_bitmap,
G_VFS_JOB (job)->cancellable,
query_info_get_filedir_parms_cb, job);
}
return TRUE;
}
static void
do_unmount (GVfsBackend *backend,
GVfsJobUnmount *job,
GMountUnmountFlags flags,
GMountSource *mount_source)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
if (!(flags & G_MOUNT_UNMOUNT_FORCE))
{
g_vfs_afp_server_logout_sync (afp_backend->server, G_VFS_JOB (job)->cancellable,
NULL);
}
g_vfs_job_succeeded (G_VFS_JOB (job));
}
static void
do_mount (GVfsBackend *backend,
GVfsJobMount *job,
GMountSpec *mount_spec,
GMountSource *mount_source,
gboolean is_automount)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
gboolean res;
GError *err = NULL;
const GVfsAfpServerInfo *info;
GMountSpec *afp_mount_spec;
char *server_name;
char *display_name;
afp_backend->server = g_vfs_afp_server_new (afp_backend->addr);
res = g_vfs_afp_server_login (afp_backend->server, afp_backend->user, mount_source,
NULL, G_VFS_JOB (job)->cancellable, &err);
if (!res)
goto error;
afp_backend->volume =
g_vfs_afp_server_mount_volume_sync (afp_backend->server, afp_backend->volume_name,
G_VFS_JOB (job)->cancellable, &err);
if (!afp_backend->volume)
goto error;
/* set mount info */
afp_mount_spec = g_mount_spec_new ("afp-volume");
g_mount_spec_set (afp_mount_spec, "host",
g_network_address_get_hostname (G_NETWORK_ADDRESS (afp_backend->addr)));
g_mount_spec_set (afp_mount_spec, "volume", afp_backend->volume_name);
if (afp_backend->user)
g_mount_spec_set (afp_mount_spec, "user", afp_backend->user);
g_vfs_backend_set_mount_spec (backend, afp_mount_spec);
g_mount_spec_unref (afp_mount_spec);
info = g_vfs_afp_server_get_info (afp_backend->server);
if (info->utf8_server_name)
server_name = info->utf8_server_name;
else
server_name = info->server_name;
if (afp_backend->user)
/* Translators: first %s is volumename, second username and third servername */
display_name = g_strdup_printf (_("%s for %s on %s"),
afp_backend->volume_name, afp_backend->user,
server_name);
else
/* Translators: first %s is volumename and second servername */
display_name = g_strdup_printf (_("%s on %s"),
afp_backend->volume_name, server_name);
g_vfs_backend_set_display_name (backend, display_name);
g_free (display_name);
g_vfs_backend_set_icon_name (backend, "folder-remote-afp");
g_vfs_backend_set_symbolic_icon_name (backend, "folder-remote-symbolic");
g_vfs_backend_set_user_visible (backend, TRUE);
g_vfs_job_succeeded (G_VFS_JOB (job));
return;
error:
g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
return;
}
static gboolean
try_mount (GVfsBackend *backend,
GVfsJobMount *job,
GMountSpec *mount_spec,
GMountSource *mount_source,
gboolean is_automount)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
const char *host, *volume, *portstr, *user;
guint16 port = 548;
host = g_mount_spec_get (mount_spec, "host");
if (host == NULL)
{
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
_("No hostname specified"));
return TRUE;
}
volume = g_mount_spec_get (mount_spec, "volume");
if (volume == NULL)
{
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
_("No volume specified"));
return TRUE;
}
afp_backend->volume_name = g_strdup (volume);
portstr = g_mount_spec_get (mount_spec, "port");
if (portstr != NULL)
{
port = atoi (portstr);
}
afp_backend->addr = G_NETWORK_ADDRESS (g_network_address_new (host, port));
user = g_mount_spec_get (mount_spec, "user");
afp_backend->user = g_strdup (user);
return FALSE;
}
static void
g_vfs_backend_afp_init (GVfsBackendAfp *object)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (object);
afp_backend->volume_name = NULL;
afp_backend->user = NULL;
afp_backend->addr = NULL;
}
static void
g_vfs_backend_afp_finalize (GObject *object)
{
GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (object);
g_free (afp_backend->user);
if (afp_backend->volume_name)
g_free (afp_backend->volume_name);
if (afp_backend->volume)
g_object_unref (afp_backend->volume);
if (afp_backend->addr)
g_object_unref (afp_backend->addr);
G_OBJECT_CLASS (g_vfs_backend_afp_parent_class)->finalize (object);
}
static void
g_vfs_backend_afp_class_init (GVfsBackendAfpClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass);
object_class->finalize = g_vfs_backend_afp_finalize;
backend_class->try_mount = try_mount;
backend_class->mount = do_mount;
backend_class->unmount = do_unmount;
backend_class->try_query_info = try_query_info;
backend_class->try_query_fs_info = try_query_fs_info;
backend_class->try_set_attribute = try_set_attribute;
backend_class->try_query_settable_attributes = try_query_settable_attributes;
backend_class->try_enumerate = try_enumerate;
backend_class->try_open_for_read = try_open_for_read;
backend_class->try_close_read = try_close_read;
backend_class->try_read = try_read;
backend_class->try_seek_on_read = try_seek_on_read;
backend_class->try_append_to = try_append_to;
backend_class->try_create = try_create;
backend_class->try_replace = try_replace;
backend_class->try_write = try_write;
backend_class->try_seek_on_write = try_seek_on_write;
backend_class->try_truncate = try_truncate;
backend_class->try_close_write = try_close_write;
backend_class->try_delete = try_delete;
backend_class->try_make_directory = try_make_directory;
backend_class->try_set_display_name = try_set_display_name;
backend_class->try_move = try_move;
backend_class->try_copy = try_copy;
}
void
g_vfs_afp_daemon_init (void)
{
g_set_application_name (_("Apple Filing Protocol Service"));
#ifdef HAVE_GCRYPT
gcry_check_version (NULL);
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
#endif
}