Logo Search packages:      
Sourcecode: nautilus-cd-burner version File versions  Download package

mapping-method.c

/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
 *
 * mapping-method.c - VFS modules for handling remapped files
 *
 * Copyright (C) 2002 Red Hat Inc,
 * Copyright (C) 2005-2006 William Jon McCann <mccann@jhu.edu>
 *
 * The Gnome Library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * The Gnome 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with the Gnome Library; see the file COPYING.LIB.  If not,
 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Authors: Alexander Larsson <alexl@redhat.com>
 *          William Jon McCann <mccann@jhu.edu>
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/poll.h>

#include <glib/ghash.h>
#include <glib/glist.h>

#include <libgnomevfs/gnome-vfs.h>
#include <libgnomevfs/gnome-vfs-mime.h>
#include <libgnomevfs/gnome-vfs-mime-utils.h>
#include <libgnomevfs/gnome-vfs-ops.h>
#include <libgnomevfs/gnome-vfs-cancellable-ops.h>

#include <libgnomevfs/gnome-vfs-method.h>
#include <libgnomevfs/gnome-vfs-module.h>
#include <libgnomevfs/gnome-vfs-module-shared.h>

#include "mapping-protocol.h"
#include "mapping-method.h"

MappingProtocolChannel *daemon_ioc = NULL;

#define LAUNCH_DAEMON_TIMEOUT 2*1000

#undef DEBUG_ENABLE

typedef struct {
      char                   *root;
      int                     pos;
      char                  **listing;
      int                     n_items;
      char                   *dirname;
      GnomeVFSFileInfoOptions options;
} VirtualDirHandle;

typedef struct {
      GnomeVFSHandle *handle;
      char           *backing_file;
} VirtualFileHandle;

typedef struct {
      GnomeVFSURI *uri;
      gboolean     cancelled;
} VirtualFileMonitorHandle;


#ifdef DEBUG_ENABLE
#ifdef G_HAVE_ISO_VARARGS
#  define DEBUG_PRINT(...) profile_log (G_STRFUNC, NULL, __VA_ARGS__);
#elif defined(G_HAVE_GNUC_VARARGS)
#  define DEBUG_PRINT(args...) profile_log (G_STRFUNC, NULL, args);
#endif
#else
#ifdef G_HAVE_ISO_VARARGS
#  define DEBUG_PRINT(...)
#elif defined(G_HAVE_GNUC_VARARGS)
#  define DEBUG_PRINT(args...)
#endif
#endif

#ifdef DEBUG_ENABLE
static void
profile_log (const char *func,
           const char *note,
           const char *format,
           ...)
{
        va_list args;
        char   *str;
        char   *formatted;

        if (format == NULL) {
                formatted = g_strdup ("");
        } else {
            va_start (args, format);
            formatted = g_strdup_vprintf (format, args);
            va_end (args);
        }

        if (func != NULL) {
                str = g_strdup_printf ("MARK: %s %s: %s %s", g_get_prgname(), func, note ? note : "", formatted);
        } else {
                str = g_strdup_printf ("MARK: %s: %s %s", g_get_prgname(), note ? note : "", formatted);
        }

        g_free (formatted);

        g_access (str, F_OK);
        g_free (str);
}
#endif

static char *
get_path_from_uri (const GnomeVFSURI *uri)
{
      char *path;

      g_return_val_if_fail (uri != NULL, NULL);

      path = gnome_vfs_unescape_string (uri->text,
                                G_DIR_SEPARATOR_S);

      if (path == NULL) {
            return NULL;
      }

      if (! g_path_is_absolute (path)) {
            g_free (path);
            return NULL;
      }

      return path;
}

static GnomeVFSURI *
get_uri (char *path)
{
      char        *text_uri;
      GnomeVFSURI *uri;

      g_return_val_if_fail (path != NULL, NULL);

      /*g_assert (path != NULL);*/

      text_uri = gnome_vfs_get_uri_from_local_path (path);
      uri = gnome_vfs_uri_new (text_uri);
      g_free (text_uri);
      return uri;
}

static void
process_event (MappingProtocolMonitorEvent *event)
{
      VirtualFileMonitorHandle *handle;
      gboolean                  cancelled;
      GnomeVFSMonitorEventType  event_type;

      if (!event->userdata) {
            DEBUG_PRINT ("Received event with no userdata, skipping");
            return;
      }

      handle = (VirtualFileMonitorHandle *)event->userdata;
      cancelled = handle->cancelled;
      event_type = event->type;

      if (!cancelled) {
            GnomeVFSURI *info_uri;

            /*
             * can send events with either a absolute or
             * relative (from the monitored URI) path, so check if
             * the filename starts with G_DIR_SEPARATOR.
             */
            if (g_path_is_absolute (event->path)) {
                  char        *info_str;
                  GnomeVFSURI *tmp_uri;

                  info_str = g_strdup_printf ("%s://", handle->uri->method_string);
                  /*info_str = g_strdup ("burn://");*/
                  tmp_uri = gnome_vfs_uri_new (info_str);
                  info_uri = gnome_vfs_uri_append_path (tmp_uri, event->path);
                  gnome_vfs_uri_unref (tmp_uri);
                  g_free (info_str);
            } else {
                  info_uri = gnome_vfs_uri_append_file_name (handle->uri, event->path);
            }

            DEBUG_PRINT ("Returning event %d path %s handle %p",
                       event_type,
                       event->path,
                       handle);

            /* This queues an idle, so there are no reentrancy issues */
            gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
                                  info_uri,
                                  event_type);
            gnome_vfs_uri_unref (info_uri);
      } else {
            DEBUG_PRINT ("Cancelled event %d path %s handle %p",
                       event_type,
                       event->path,
                       handle);
      }
}

static gboolean
request_op (gint32                   operation,
          char                    *root,
          char                    *path1,
          char                    *path2,
          gboolean                 option,
          void                    *userdata,
          MappingProtocolMessage **reply)
{
      gboolean                res;
      MappingProtocolMessage *message;
      MappingProtocolRequest *request;

      DEBUG_PRINT ("request_op: %d", operation);

      message = mapping_protocol_message_new_request ();
      request = MAPPING_PROTOCOL_REQUEST (message);

      request->operation = operation;
      request->root      = g_strdup (root);
      request->path1     = g_strdup (path1);
      request->path2     = g_strdup (path2);
      request->option    = option;
      request->userdata  = userdata;

      res = mapping_protocol_channel_send_with_reply (daemon_ioc, message, reply);

      if (! res) {
            DEBUG_PRINT ("request_op: send ERROR");
      } else {
            DEBUG_PRINT ("request_op: send OK");
      }

      return res;
}

static GnomeVFSResult
remove_file_helper (char *method,
                char *path)
{
      MappingProtocolMessage *reply;
      GnomeVFSResult          ret;
      gboolean                res;

      reply = NULL;
      res = request_op (MAPPING_PROTOCOL_OP_REMOVE_FILE, method,
                    path, NULL, FALSE, NULL, &reply);


      if (! res) {
            return GNOME_VFS_ERROR_IO;
      }

      ret = MAPPING_PROTOCOL_REPLY (reply)->result;
      mapping_protocol_message_unref (reply);

      return ret;
}

static GnomeVFSResult
do_open (GnomeVFSMethod        *method,
       GnomeVFSMethodHandle **method_handle,
       GnomeVFSURI           *uri,
       GnomeVFSOpenMode       mode,
       GnomeVFSContext       *context)
{
      GnomeVFSResult          ret;
      gboolean                res;
      GnomeVFSURI            *file_uri = NULL;
      GnomeVFSHandle         *file_handle;
      VirtualFileHandle      *handle;
      char                   *path;
      MappingProtocolMessage *reply;

      DEBUG_PRINT ("do_open: %s", uri->text);

      *method_handle = NULL;
      path = get_path_from_uri (uri);
      if (path == NULL) {
            return GNOME_VFS_ERROR_INVALID_URI;
      }

      reply = NULL;
      res = request_op (MAPPING_PROTOCOL_OP_GET_BACKING_FILE,
                    uri->method_string,
                    path,
                    NULL,
                    mode & GNOME_VFS_OPEN_WRITE,
                    NULL,
                    &reply);
      g_free (path);

      if (! res) {
            return GNOME_VFS_ERROR_IO;
      }

      ret = MAPPING_PROTOCOL_REPLY (reply)->result;

      if (ret == GNOME_VFS_OK) {
            file_uri = get_uri (MAPPING_PROTOCOL_REPLY (reply)->path);
            ret = gnome_vfs_open_uri_cancellable (&file_handle,
                                          file_uri, mode, context);

            if (ret == GNOME_VFS_OK) {
                  handle = g_new (VirtualFileHandle, 1);
                  handle->handle = file_handle;
                  handle->backing_file = g_strdup (MAPPING_PROTOCOL_REPLY (reply)->path);
                  *method_handle = (GnomeVFSMethodHandle *)handle;
            }

            gnome_vfs_uri_unref (file_uri);
      }

      if (reply) {
            mapping_protocol_message_unref (reply);
      }

      return ret;
}

static GnomeVFSResult
do_create (GnomeVFSMethod        *method,
         GnomeVFSMethodHandle **method_handle,
         GnomeVFSURI           *uri,
         GnomeVFSOpenMode       mode,
         gboolean               exclusive,
         guint                  perm,
         GnomeVFSContext       *context)
{
      GnomeVFSResult          ret;
      gboolean                res;
      GnomeVFSURI            *file_uri;
      char                   *path;
      MappingProtocolMessage *reply;
      gboolean                newly_created;
      GnomeVFSHandle         *file_handle;
      VirtualFileHandle      *handle;

      DEBUG_PRINT ("do_create: %s", uri->text);

      *method_handle = NULL;
      path = get_path_from_uri (uri);
      if (path == NULL) {
            return GNOME_VFS_ERROR_INVALID_URI;
      }

      reply = NULL;
      res = request_op (MAPPING_PROTOCOL_OP_CREATE_FILE,
                    uri->method_string,
                    path, NULL,
                    exclusive,
                    NULL,
                    &reply);

      if (! res) {
            return GNOME_VFS_ERROR_IO;
      }

      ret = MAPPING_PROTOCOL_REPLY (reply)->result;
      newly_created = MAPPING_PROTOCOL_REPLY (reply)->option;

      if (ret == GNOME_VFS_OK) {
            file_uri = get_uri (MAPPING_PROTOCOL_REPLY (reply)->path);
            res = gnome_vfs_create_uri_cancellable
                  (&file_handle,
                   file_uri, mode, exclusive,
                   perm, context);
            gnome_vfs_uri_unref (file_uri);
            if (ret == GNOME_VFS_OK) {
                  handle = g_new (VirtualFileHandle, 1);
                  handle->handle = file_handle;
                  handle->backing_file = g_strdup (MAPPING_PROTOCOL_REPLY (reply)->path);
                  *method_handle = (GnomeVFSMethodHandle *)handle;
            }
      }

      mapping_protocol_message_unref (reply);

      if (ret != GNOME_VFS_OK && newly_created) {
                  /* TODO: Remove the file that was created, since we failed */
                  /* virtual_unlink (dir, file);
                   * virtual_node_free (file, FALSE);
                   */
      }

      return ret;
}

static GnomeVFSResult
do_close (GnomeVFSMethod       *method,
        GnomeVFSMethodHandle *method_handle,
        GnomeVFSContext      *context)

{
      VirtualFileHandle *vfh;
      GnomeVFSHandle    *handle;

      DEBUG_PRINT ("do_close: %p", method_handle);

      vfh    = (VirtualFileHandle *)method_handle;
      handle = vfh->handle;

      g_free (vfh->backing_file);
      g_free (vfh);

      return gnome_vfs_close_cancellable (handle, context);
}

static GnomeVFSResult
do_read (GnomeVFSMethod       *method,
       GnomeVFSMethodHandle *method_handle,
       gpointer             buffer,
       GnomeVFSFileSize     num_bytes,
       GnomeVFSFileSize    *bytes_read,
       GnomeVFSContext     *context)
{
      DEBUG_PRINT ("do_read: %p", method_handle);
      return gnome_vfs_read_cancellable (((VirtualFileHandle *)method_handle)->handle,
                                 buffer,
                                 num_bytes,
                                 bytes_read,
                                 context);
}

static GnomeVFSResult
do_write (GnomeVFSMethod       *method,
        GnomeVFSMethodHandle *method_handle,
        gconstpointer         buffer,
        GnomeVFSFileSize      num_bytes,
        GnomeVFSFileSize     *bytes_written,
        GnomeVFSContext      *context)
{
      DEBUG_PRINT ("do_write: %p", method_handle);
      return gnome_vfs_write_cancellable (((VirtualFileHandle *)method_handle)->handle,
                                  buffer,
                                  num_bytes,
                                  bytes_written,
                                  context);

}

static GnomeVFSResult
do_seek (GnomeVFSMethod       *method,
       GnomeVFSMethodHandle *method_handle,
       GnomeVFSSeekPosition  whence,
       GnomeVFSFileOffset    offset,
       GnomeVFSContext      *context)
{
      DEBUG_PRINT ("do_seek: %p", method_handle);
      return gnome_vfs_seek_cancellable (((VirtualFileHandle *)method_handle)->handle,
                                 whence,
                                 offset,
                                 context);
}

static GnomeVFSResult
do_tell (GnomeVFSMethod       *method,
       GnomeVFSMethodHandle *method_handle,
       GnomeVFSFileSize     *offset_return)
{
      DEBUG_PRINT ("do_tell: %p", method_handle);
      return gnome_vfs_tell (((VirtualFileHandle *)method_handle)->handle,
                         offset_return);
}

static GnomeVFSResult
do_truncate_handle (GnomeVFSMethod       *method,
                GnomeVFSMethodHandle *method_handle,
                GnomeVFSFileSize      where,
                GnomeVFSContext      *context)
{
      DEBUG_PRINT ("do_truncate_handle: %p", method_handle);
      return gnome_vfs_truncate_handle_cancellable (((VirtualFileHandle *)method_handle)->handle,
                                          where,
                                          context);
}

static GnomeVFSResult
do_open_directory (GnomeVFSMethod         *method,
               GnomeVFSMethodHandle  **method_handle,
               GnomeVFSURI            *uri,
               GnomeVFSFileInfoOptions options,
               GnomeVFSContext        *context)
{
      char                   *path;
      VirtualDirHandle       *handle;
      MappingProtocolMessage *reply;
      GnomeVFSResult          ret;
      gboolean                res;

      DEBUG_PRINT ("do_open_directory: %s", uri->text);

      g_return_val_if_fail (uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);

      path = get_path_from_uri (uri);
      if (path == NULL) {
            return GNOME_VFS_ERROR_INVALID_URI;
      }

      reply = NULL;
      res = request_op (MAPPING_PROTOCOL_OP_LIST_DIR,
                    uri->method_string,
                    path, NULL,
                    FALSE,
                    NULL,
                    &reply);

      if (! res) {
            return GNOME_VFS_ERROR_IO;
      }

      ret = MAPPING_PROTOCOL_REPLY (reply)->result;

      if (ret == GNOME_VFS_OK) {
            int i;

            handle = g_new (VirtualDirHandle, 1);

            handle->pos = 0;
            handle->dirname = path;

            g_assert ((MAPPING_PROTOCOL_REPLY (reply)->n_strings % 2) == 0);
            handle->n_items = MAPPING_PROTOCOL_REPLY (reply)->n_strings / 2;
            handle->listing = g_new0 (char *, MAPPING_PROTOCOL_REPLY (reply)->n_strings);
            for (i = 0; i < MAPPING_PROTOCOL_REPLY (reply)->n_strings; i++) {
                  handle->listing[i] = g_strdup (MAPPING_PROTOCOL_REPLY (reply)->strings[i]);
            }

            handle->root = g_strdup (uri->method_string);
            handle->options = options;

            *method_handle = (GnomeVFSMethodHandle *)handle;
      } else {
            g_free (path);
      }

      mapping_protocol_message_unref (reply);

      return ret;
}

static GnomeVFSResult
do_close_directory (GnomeVFSMethod       *method,
                GnomeVFSMethodHandle *method_handle,
                GnomeVFSContext      *context)
{
      VirtualDirHandle *handle = (VirtualDirHandle *)method_handle;
      int i;

      DEBUG_PRINT ("do_close_directory: %p", method_handle);
      for (i = 0; i < handle->n_items * 2; i++) {
            g_free (handle->listing [i]);
      }
      g_free (handle->listing);
      g_free (handle->root);
      g_free (handle->dirname);
      g_free (handle);
      return GNOME_VFS_OK;
}

static void
fill_in_directory_info (GnomeVFSFileInfo *file_info)
{
      file_info->valid_fields |=
            GNOME_VFS_FILE_INFO_FIELDS_TYPE |
            GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS |
            GNOME_VFS_FILE_INFO_FIELDS_FLAGS |
            GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;

      file_info->uid = getuid ();
      file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
      file_info->permissions = GNOME_VFS_PERM_USER_ALL | GNOME_VFS_PERM_OTHER_ALL;
      file_info->flags = GNOME_VFS_FILE_FLAGS_LOCAL;
      file_info->mime_type = g_strdup ("x-directory/normal");
}

static GnomeVFSResult
do_read_directory (GnomeVFSMethod       *method,
               GnomeVFSMethodHandle *method_handle,
               GnomeVFSFileInfo     *file_info,
               GnomeVFSContext      *context)
{
      VirtualDirHandle *handle = (VirtualDirHandle *)method_handle;
      GnomeVFSResult    res;
      char             *name;
      char             *backingfile;
      char             *path;
      GnomeVFSURI      *file_uri;

      DEBUG_PRINT ("do_read_directory: %p", method_handle);

      while (TRUE) {
            if (handle->pos >= handle->n_items) {
                  return GNOME_VFS_ERROR_EOF;
            }

            name = handle->listing [handle->pos * 2];
            backingfile = handle->listing [handle->pos * 2 + 1];
            ++handle->pos;

            if (backingfile == NULL) {
                  file_info->name = g_strdup (name);
                  fill_in_directory_info (file_info);
                  break;
            }

            file_uri = get_uri (backingfile);
            res = gnome_vfs_get_file_info_uri_cancellable
                  (file_uri, file_info, handle->options, context);
            gnome_vfs_uri_unref (file_uri);

            if (res == GNOME_VFS_ERROR_NOT_FOUND) {
                  path = g_build_filename (handle->dirname, name, NULL);
                  remove_file_helper (handle->root, path);
                  g_free (path);
                  continue;
            }

            g_free (file_info->name);
            file_info->name = g_strdup (name);
            break;
      }

      return GNOME_VFS_OK;
}

static GnomeVFSResult
do_get_file_info (GnomeVFSMethod         *method,
              GnomeVFSURI            *uri,
              GnomeVFSFileInfo       *file_info,
              GnomeVFSFileInfoOptions options,
              GnomeVFSContext        *context)
{
      GnomeVFSResult          ret;
      gboolean                res;
      GnomeVFSURI            *file_uri = NULL;
      char                   *path;
      MappingProtocolMessage *reply;

      DEBUG_PRINT ("do_get_file_info: %s", uri->text);

      path = get_path_from_uri (uri);
      if (path == NULL) {
            return GNOME_VFS_ERROR_INVALID_URI;
      }

      reply = NULL;
      res = request_op (MAPPING_PROTOCOL_OP_GET_BACKING_FILE,
                    uri->method_string,
                    path,
                    NULL,
                    FALSE,
                    NULL,
                    &reply);

      if (! res) {
            ret = GNOME_VFS_ERROR_IO;
            DEBUG_PRINT ("do_get_file_info: file %s IO ERROR", path);
            goto out;
      }

      ret = MAPPING_PROTOCOL_REPLY (reply)->result;
      DEBUG_PRINT ("do_get_file_info: file %s result %s", path, gnome_vfs_result_to_string (ret));

      if (ret == GNOME_VFS_ERROR_IS_DIRECTORY) {
            file_info->name = g_path_get_basename (path);
            fill_in_directory_info (file_info);
            ret = GNOME_VFS_OK;
      } else if (ret == GNOME_VFS_OK) {
            file_uri = get_uri (MAPPING_PROTOCOL_REPLY (reply)->path);
            ret = gnome_vfs_get_file_info_uri_cancellable (file_uri, file_info, options, context);
            gnome_vfs_uri_unref (file_uri);

            g_free (file_info->name);
            file_info->name = g_path_get_basename (path);
      }

      mapping_protocol_message_unref (reply);

 out:
      g_free (path);

      return ret;
}

static GnomeVFSResult
do_get_file_info_from_handle (GnomeVFSMethod         *method,
                        GnomeVFSMethodHandle   *method_handle,
                        GnomeVFSFileInfo       *file_info,
                        GnomeVFSFileInfoOptions options,
                        GnomeVFSContext        *context)
{
      GnomeVFSResult res;

      DEBUG_PRINT ("do_get_file_info_from_handle: %p", method_handle);
      res = gnome_vfs_get_file_info_from_handle_cancellable (((VirtualFileHandle *)method_handle)->handle,
                                                 file_info, options, context);
      /* TODO: Need to fill out the real name here. Need to wrap method_handle */
      return res;
}

static GnomeVFSResult
do_make_directory (GnomeVFSMethod  *method,
               GnomeVFSURI     *uri,
               guint            perm,
               GnomeVFSContext *context)
{
      GnomeVFSResult          ret;
      gboolean                res;
      char                   *path;
      MappingProtocolMessage *reply;

      DEBUG_PRINT ("do_make_directory: %s", uri->text);

      path = get_path_from_uri (uri);
      if (path == NULL) {
            return GNOME_VFS_ERROR_INVALID_URI;
      }

      reply = NULL;
      res = request_op (MAPPING_PROTOCOL_OP_CREATE_DIR,
                    uri->method_string,
                    path,
                    NULL, FALSE,
                    NULL,
                    &reply);

      if (! res) {
            ret = GNOME_VFS_ERROR_IO;
      } else {
            ret = MAPPING_PROTOCOL_REPLY (reply)->result;
            mapping_protocol_message_unref (reply);
      }

      g_free (path);

      return ret;
}

static GnomeVFSResult
do_remove_directory (GnomeVFSMethod  *method,
                 GnomeVFSURI     *uri,
                 GnomeVFSContext *context)
{
      GnomeVFSResult          ret;
      gboolean                res;
      char                   *path;
      MappingProtocolMessage *reply;

      DEBUG_PRINT ("do_remove_directory: %s", uri->text);

      path = get_path_from_uri (uri);
      if (path == NULL) {
            return GNOME_VFS_ERROR_INVALID_URI;
      }

      reply = NULL;
      res = request_op (MAPPING_PROTOCOL_OP_REMOVE_DIR,
                    uri->method_string,
                    path,
                    NULL, FALSE,
                    NULL,
                    &reply);

      if (! res) {
            ret = GNOME_VFS_ERROR_IO;
            goto out;
      }

      ret = MAPPING_PROTOCOL_REPLY (reply)->result;

      mapping_protocol_message_unref (reply);
 out:
      g_free (path);

      return ret;
}

static GnomeVFSResult
do_unlink (GnomeVFSMethod  *method,
         GnomeVFSURI     *uri,
         GnomeVFSContext *context)
{
      GnomeVFSResult res;
      char          *path;

      DEBUG_PRINT ("do_remove_directory: %s", uri->text);

      path = get_path_from_uri (uri);
      if (path == NULL) {
            return GNOME_VFS_ERROR_INVALID_URI;
      }

      res = remove_file_helper (uri->method_string, path);
      g_free (path);

      return res;
}

static GnomeVFSResult
do_move (GnomeVFSMethod  *method,
       GnomeVFSURI     *old_uri,
       GnomeVFSURI     *new_uri,
       gboolean         force_replace,
       GnomeVFSContext *context)
{
      GnomeVFSResult          ret;
      gboolean                res;
      MappingProtocolMessage *reply;
      char                   *old_path;
      char                   *new_path;

      DEBUG_PRINT ("do_move (%s, %s)", old_uri->text, new_uri->text);

      if (strcmp (new_uri->method_string,
                old_uri->method_string) != 0) {
            return GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM;
      }

      old_path = get_path_from_uri (old_uri);
      if (old_path == NULL) {
            return GNOME_VFS_ERROR_INVALID_URI;
      }

      new_path = get_path_from_uri (new_uri);
      if (new_path == NULL) {
            g_free (old_path);
            return GNOME_VFS_ERROR_INVALID_URI;
      }

      reply = NULL;
      res = request_op (MAPPING_PROTOCOL_OP_MOVE_FILE,
                    old_uri->method_string,
                    old_path,
                    new_path,
                    FALSE,
                    NULL,
                    &reply);

      if (! res) {
            ret = GNOME_VFS_ERROR_IO;
            goto out;
      }

      ret = MAPPING_PROTOCOL_REPLY (reply)->result;

      mapping_protocol_message_unref (reply);

 out:
      g_free (old_path);
      g_free (new_path);

      return ret;
}

static gboolean
do_is_local (GnomeVFSMethod    *method,
           const GnomeVFSURI *uri)
{
      return TRUE;
}

/* When checking whether two locations are on the same file system, we are
   doing this to determine whether we can recursively move or do other
   sorts of transfers.  When a symbolic link is the "source", its
   location is the location of the link file, because we want to
   know about transferring the link, whereas for symbolic links that
   are "targets", we use the location of the object being pointed to,
   because that is where we will be moving/copying to. */
static GnomeVFSResult
do_check_same_fs (GnomeVFSMethod  *method,
              GnomeVFSURI     *source_uri,
              GnomeVFSURI     *target_uri,
              gboolean        *same_fs_return,
              GnomeVFSContext *context)
{
      *same_fs_return = strcmp (source_uri->method_string,  target_uri->method_string) == 0;
      return GNOME_VFS_OK;
}

static GnomeVFSResult
do_set_file_info (GnomeVFSMethod         *method,
              GnomeVFSURI            *uri,
              const GnomeVFSFileInfo *info,
              GnomeVFSSetFileInfoMask mask,
              GnomeVFSContext        *context)
{
      char                   *full_name;
      GnomeVFSResult          ret;
      gboolean                res;
      MappingProtocolMessage *reply;
      GnomeVFSURI            *file_uri = NULL;

      DEBUG_PRINT ("do_set_file_info: %s", uri->text);

      full_name = get_path_from_uri (uri);
      if (full_name == NULL) {
            return GNOME_VFS_ERROR_INVALID_URI;
      }

      if (mask & GNOME_VFS_SET_FILE_INFO_NAME) {
            char *dir, *encoded_dir;
            char *new_name;

            encoded_dir = gnome_vfs_uri_extract_dirname (uri);
            dir = gnome_vfs_unescape_string (encoded_dir, G_DIR_SEPARATOR_S);
            g_free (encoded_dir);
            g_assert (dir != NULL);

            /* FIXME bugzilla.eazel.com 645: This needs to return
             * an error for incoming names with "/" characters in
             * them, instead of moving the file.
             */

            if (dir [strlen (dir) - 1] != G_DIR_SEPARATOR) {
                  new_name = g_strconcat (dir, G_DIR_SEPARATOR_S, info->name, NULL);
            } else {
                  new_name = g_strconcat (dir, info->name, NULL);
            }

            reply = NULL;
            res = request_op (MAPPING_PROTOCOL_OP_MOVE_FILE,
                          uri->method_string,
                          full_name,
                          new_name,
                          FALSE,
                          NULL,
                          &reply);

            if (! res) {
                  ret = GNOME_VFS_ERROR_IO;
            } else {
                  ret = MAPPING_PROTOCOL_REPLY (reply)->result;
                  mapping_protocol_message_unref (reply);
            }

            g_free (dir);
            g_free (full_name);
            full_name = new_name;

            if (ret != GNOME_VFS_OK) {
                  g_free (full_name);
                  return ret;
            }
            mask = mask & ~GNOME_VFS_SET_FILE_INFO_NAME;
      }

      if (mask != 0) {
            reply = NULL;
            res = request_op (MAPPING_PROTOCOL_OP_GET_BACKING_FILE,
                          uri->method_string,
                          full_name,
                          NULL,
                          TRUE,
                          NULL,
                          &reply);

            g_free (full_name);

            if (! res) {
                  return GNOME_VFS_ERROR_IO;
            }

            ret = MAPPING_PROTOCOL_REPLY (reply)->result;

            if (ret != GNOME_VFS_OK) {
                  mapping_protocol_message_unref (reply);
                  return ret;
            }

            file_uri = get_uri (MAPPING_PROTOCOL_REPLY (reply)->path);

            mapping_protocol_message_unref (reply);

            ret = gnome_vfs_set_file_info_cancellable (file_uri,
                                             info, mask, context);
            gnome_vfs_uri_unref (file_uri);
      }

      return GNOME_VFS_OK;
}

static GnomeVFSResult
do_truncate (GnomeVFSMethod   *method,
           GnomeVFSURI      *uri,
           GnomeVFSFileSize  where,
           GnomeVFSContext  *context)
{
      GnomeVFSResult          ret;
      gboolean                res;
      GnomeVFSURI            *file_uri = NULL;
      char                   *path;
      MappingProtocolMessage *reply;

      DEBUG_PRINT ("do_truncate: %s", uri->text);

      path = get_path_from_uri (uri);
      if (path == NULL) {
            return GNOME_VFS_ERROR_INVALID_URI;
      }

      reply = NULL;
      res = request_op (MAPPING_PROTOCOL_OP_GET_BACKING_FILE,
                    uri->method_string,
                    path,
                    NULL,
                    TRUE,
                    NULL,
                    &reply);

      if (! res) {
            return GNOME_VFS_ERROR_IO;
      }

      ret = MAPPING_PROTOCOL_REPLY (reply)->result;
      if (ret == GNOME_VFS_OK) {
            file_uri = get_uri (MAPPING_PROTOCOL_REPLY (reply)->path);
            res = gnome_vfs_truncate_uri_cancellable (file_uri, where, context);
            gnome_vfs_uri_unref (file_uri);
      }

      mapping_protocol_message_unref (reply);

      return ret;
}

static GnomeVFSResult
do_create_symbolic_link (GnomeVFSMethod  *method,
                   GnomeVFSURI     *uri,
                   const char      *target_reference,
                   GnomeVFSContext *context)
{
      GnomeVFSResult          ret;
      gboolean                res;
      MappingProtocolMessage *reply;
      char                   *path;
      char                   *target_path;

      DEBUG_PRINT ("do_create_symbolic_link: %s -> %s", uri->text, target_reference);

      path = get_path_from_uri (uri);
      if (path == NULL) {
            return GNOME_VFS_ERROR_INVALID_URI;
      }

      target_path = gnome_vfs_get_local_path_from_uri (target_reference);

      if (target_path == NULL) {
            g_free (path);
            return GNOME_VFS_ERROR_NOT_SUPPORTED;
      }

      reply = NULL;
      res = request_op (MAPPING_PROTOCOL_OP_CREATE_LINK,
                    uri->method_string,
                    path,
                    target_path,
                    FALSE,
                    NULL,
                    &reply);

      if (! res) {
            ret = GNOME_VFS_ERROR_IO;
      } else {
            ret = MAPPING_PROTOCOL_REPLY (reply)->result;
            mapping_protocol_message_unref (reply);
      }

      g_free (target_path);
      g_free (path);

      return ret;
}

static GnomeVFSResult
do_monitor_add (GnomeVFSMethod        *method,
            GnomeVFSMethodHandle **method_handle_return,
            GnomeVFSURI           *uri,
            GnomeVFSMonitorType    monitor_type)
{
      VirtualFileMonitorHandle *monitor;
      MappingProtocolMessage   *reply;
      char                     *path;
      GnomeVFSResult            ret;
      gboolean                  res;

      path = get_path_from_uri (uri);
      if (path == NULL) {
            return GNOME_VFS_ERROR_INVALID_URI;
      }

      monitor = g_new0 (VirtualFileMonitorHandle, 1);
      monitor->uri = uri;
      gnome_vfs_uri_ref (uri);

      DEBUG_PRINT ("do_monitor_add: %p path: %s", monitor, path);

      reply = NULL;
      res = request_op (MAPPING_PROTOCOL_OP_MONITOR_ADD,
                    monitor->uri->method_string,
                    path,
                    NULL,
                    FALSE,
                    monitor,
                    &reply);

      if (! res) {
            ret = GNOME_VFS_ERROR_IO;
      } else {
            ret = MAPPING_PROTOCOL_REPLY (reply)->result;
            mapping_protocol_message_unref (reply);
      }

      *method_handle_return = (GnomeVFSMethodHandle *)monitor;

      g_free (path);

      DEBUG_PRINT ("do_monitor_add: DONE %p", monitor);

      return GNOME_VFS_OK;
}

static GnomeVFSResult
do_monitor_cancel (GnomeVFSMethod       *method,
               GnomeVFSMethodHandle *method_handle)
{
      MappingProtocolMessage   *reply;
      GnomeVFSResult            ret;
      gboolean                  res;
      char                     *path;
      VirtualFileMonitorHandle *monitor;

      monitor = (VirtualFileMonitorHandle *) method_handle;

      DEBUG_PRINT ("do_monitor_cancel: %p", monitor);

      if (monitor->cancelled)
            return GNOME_VFS_OK;

      path = get_path_from_uri (monitor->uri);
      if (path == NULL) {
            return GNOME_VFS_ERROR_INVALID_URI;
      }

      monitor->cancelled = TRUE;

      reply = NULL;
      res = request_op (MAPPING_PROTOCOL_OP_MONITOR_CANCEL,
                    monitor->uri->method_string,
                    path,
                    NULL,
                    FALSE,
                    monitor,
                    &reply);

      if (! res) {
            ret = GNOME_VFS_ERROR_IO;
      } else {
            ret = MAPPING_PROTOCOL_REPLY (reply)->result;
            mapping_protocol_message_unref (reply);
      }

      gnome_vfs_uri_unref (monitor->uri);

      g_free (monitor);
      monitor = NULL;

      return GNOME_VFS_OK;
}

static GnomeVFSResult
do_file_control (GnomeVFSMethod       *method,
             GnomeVFSMethodHandle *method_handle,
             const char           *operation,
             gpointer              operation_data,
             GnomeVFSContext      *context)
{
      VirtualFileHandle *handle;

      handle = (VirtualFileHandle *)method_handle;

      if (strcmp (operation, "mapping:get_mapping") == 0) {
            *(char **)operation_data = g_strdup (handle->backing_file);
            return GNOME_VFS_OK;
      }
      return GNOME_VFS_ERROR_NOT_SUPPORTED;
}

static GnomeVFSResult
do_forget_cache   (GnomeVFSMethod       *method,
             GnomeVFSMethodHandle *method_handle,
             GnomeVFSFileOffset    offset,
             GnomeVFSFileSize      size)
{
      VirtualFileHandle *handle;

      g_return_val_if_fail (method_handle != NULL, GNOME_VFS_ERROR_INTERNAL);

      handle = (VirtualFileHandle *) method_handle;

      return GNOME_VFS_ERROR_NOT_SUPPORTED;
}

static GnomeVFSMethod method = {
      sizeof (GnomeVFSMethod),
      do_open,
      do_create,
      do_close,
      do_read,
      do_write,
      do_seek,
      do_tell,
      do_truncate_handle,
      do_open_directory,
      do_close_directory,
      do_read_directory,
      do_get_file_info,
      do_get_file_info_from_handle,
      do_is_local,
      do_make_directory,
      do_remove_directory,
      do_move,
      do_unlink,
      do_check_same_fs,
      do_set_file_info,
      do_truncate,
      NULL, /* do_find_directory */
      do_create_symbolic_link,
      do_monitor_add,
      do_monitor_cancel,
      do_file_control,
      do_forget_cache
};

static void
daemon_child_setup (gpointer user_data)
{
      gint open_max;
      gint i;
      int *pipes = user_data;

      close (pipes [0]);
      dup2 (pipes [1], 3);

      open_max = sysconf (_SC_OPEN_MAX);
      for (i = 4; i < open_max; i++) {
            fcntl (i, F_SETFD, FD_CLOEXEC);
      }
}

static gboolean
launch_daemon (void)
{
      GError       *error;
      gint          pipes [2];
      struct pollfd pollfd;
      char          c;
      char         *argv [] = {
            LIBEXECDIR G_DIR_SEPARATOR_S "mapping-daemon",
            NULL
      };

      if (pipe (pipes) != 0) {
            g_warning ("pipe failure");
            return FALSE;
      }

      error = NULL;
      if (!g_spawn_async (NULL,
                      argv, NULL,
                      G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
                      &daemon_child_setup, pipes,
                      NULL,
                      &error)) {
            g_warning ("Couldn't launch mapping-daemon: %s\n",
                     error->message);
            g_error_free (error);
            return FALSE;
      }

      close (pipes [1]);

      pollfd.fd = pipes [0];
      pollfd.events = POLLIN;
      pollfd.revents = 0;

      if (poll (&pollfd, 1, LAUNCH_DAEMON_TIMEOUT) != 1) {
            g_warning ("Didn't get any signs from mapping-daemon\n");
            return FALSE;
      }
      read (pipes [0], &c, 1);
      close (pipes [0]);

      return TRUE;
}

static void
handle_message (MappingProtocolChannel *channel,
            MappingProtocolMessage *message,
            gpointer                data)
{

      if (message == NULL) {
            /* connection error */
            return;
      }

      switch (message->type) {
      case MAPPING_PROTOCOL_MESSAGE_MONITOR_EVENT:
            process_event (MAPPING_PROTOCOL_MONITOR_EVENT (message));
            break;
      case MAPPING_PROTOCOL_MESSAGE_REPLY:
            break;
      case MAPPING_PROTOCOL_MESSAGE_REQUEST:
            break;
      default:
            break;
      }
}

GnomeVFSMethod *
vfs_module_init (const char *method_name,
             const char *args)
{
        struct sockaddr_un sin;
      int                daemon_fd;
      char              *path;

      path = mapping_protocol_get_unix_name ();

      sin.sun_family = AF_UNIX;
      g_snprintf (sin.sun_path, sizeof (sin.sun_path), "%s", path);
      g_free (path);

        if ((daemon_fd = socket (AF_UNIX, SOCK_STREAM, 0)) == -1) {
                perror ("mapping method init - socket");
                return NULL;
        }

        if (connect (daemon_fd, (const struct sockaddr *) &sin, sizeof (sin)) == -1) {
            if (errno == ECONNREFUSED || errno == ENOENT) {
                  if (launch_daemon ()) {
                        if (connect (daemon_fd, (const struct sockaddr *) &sin, sizeof (sin)) == -1) {
                              perror ("mapping method init - connect2");
                              return NULL;
                        }
                  } else {
                        return NULL;
                  }
            } else {
                  perror ("mapping method init - connect");
                  return NULL;
            }
        }

      daemon_ioc = mapping_protocol_channel_new (daemon_fd);
      mapping_protocol_channel_set_message_handler (daemon_ioc, handle_message, NULL);

      return &method;
}

void
vfs_module_shutdown (GnomeVFSMethod *method)
{
      mapping_protocol_channel_unref (daemon_ioc);
}

Generated by  Doxygen 1.6.0   Back to index