/* GVFS plugin for Tux Commander * Copyright (C) 2008 Tomas Bzatek * * This 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. * * 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include "vfs_types.h" #define VERSION "0.1.3" #define BUILD_DATE "2008-10-12" #define DEFAULT_BLOCK_SIZE 0x10000 /* 64kB */ #define CONST_DEFAULT_QUERY_INFO_ATTRIBUTES G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_NAME "," \ G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME "," G_FILE_ATTRIBUTE_STANDARD_SIZE "," \ G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET "," G_FILE_ATTRIBUTE_TIME_MODIFIED "," \ G_FILE_ATTRIBUTE_TIME_ACCESS "," G_FILE_ATTRIBUTE_TIME_CHANGED "," \ G_FILE_ATTRIBUTE_UNIX_MODE "," G_FILE_ATTRIBUTE_UNIX_UID "," \ G_FILE_ATTRIBUTE_UNIX_GID struct TVFSGlobs { TVFSLogFunc log_func; GFile *file; GFileEnumerator *enumerator; GMainLoop *mount_main_loop; TVFSResult mount_result; int mount_try; gboolean ftp_anonymous; gboolean break_get_dir_size; guint32 block_size; TVFSAskQuestionCallback callback_ask_question; TVFSAskPasswordCallback callback_ask_password; void *callbacks_data; }; static TVFSResult g_error_to_TVFSResult (GError *error) { g_print ("g_error_to_TVFSResult: code = %d\n", error->code); switch (error->code) { case G_IO_ERROR_FAILED: case G_IO_ERROR_NOT_FOUND: return cVFS_Failed; break; case G_IO_ERROR_PERMISSION_DENIED: return cVFS_PermissionDenied; break; case G_IO_ERROR_CANCELLED: return cVFS_Cancelled; break; case G_IO_ERROR_NOT_SUPPORTED: case G_IO_ERROR_FILENAME_TOO_LONG: return cVFS_Not_Supported; break; case G_IO_ERROR_NO_SPACE: return cVFS_NoSpaceLeft; break; case G_IO_ERROR_IS_DIRECTORY: case G_IO_ERROR_NOT_REGULAR_FILE: case G_IO_ERROR_NOT_SYMBOLIC_LINK: case G_IO_ERROR_NOT_MOUNTABLE_FILE: case G_IO_ERROR_INVALID_FILENAME: case G_IO_ERROR_TOO_MANY_LINKS: case G_IO_ERROR_INVALID_ARGUMENT: case G_IO_ERROR_NOT_DIRECTORY: case G_IO_ERROR_NOT_MOUNTED: case G_IO_ERROR_ALREADY_MOUNTED: case G_IO_ERROR_WRONG_ETAG: case G_IO_ERROR_TIMED_OUT: case G_IO_ERROR_WOULD_RECURSE: case G_IO_ERROR_HOST_NOT_FOUND: return cVFS_ReadErr; break; case G_IO_ERROR_EXISTS: case G_IO_ERROR_NOT_EMPTY: case G_IO_ERROR_CLOSED: case G_IO_ERROR_PENDING: case G_IO_ERROR_READ_ONLY: case G_IO_ERROR_CANT_CREATE_BACKUP: case G_IO_ERROR_BUSY: case G_IO_ERROR_WOULD_BLOCK: case G_IO_ERROR_WOULD_MERGE: return cVFS_WriteErr; break; case G_IO_ERROR_FAILED_HANDLED: default: return cVFS_Failed; } } static void ask_password_cb (GMountOperation *op, const char *message, const char *default_user, const char *default_domain, GAskPasswordFlags flags, gpointer user_data) { struct TVFSGlobs *globs; char *username; char *password; int anonymous; char *domain; TVFSPasswordSave password_save; int result; globs = (struct TVFSGlobs*) user_data; g_assert (globs != NULL); globs->mount_try++; /* First pass, look if we have a password to supply */ if (globs->mount_try == 1) { if ((flags & G_ASK_PASSWORD_ANONYMOUS_SUPPORTED) && globs->ftp_anonymous) { printf ("(WW) ask_password_cb: mount_try = %d, trying FTP anonymous login...\n", globs->mount_try); g_mount_operation_set_anonymous (op, TRUE); g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED); return; } } /* Ask user for password */ g_print ("(WW) ask_password_cb: mount_try = %d, message = '%s'\n", globs->mount_try, message); /* Handle abort message from certain backends properly */ /* - e.g. SMB backends use this to mask multiple auth callbacks from smbclient */ if (default_user && strcmp (default_user, "ABORT") == 0) { g_print ("(WW) default_user == \"ABORT\", aborting\n"); g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED); return; } username = NULL; domain = NULL; password = NULL; anonymous = FALSE; password_save = VFS_PASSWORD_SAVE_NEVER; if (globs->callback_ask_password) { fprintf (stderr, " (II) Spawning callback_ask_password (0x%.16llX)...\n", (unsigned long long) globs->callback_ask_password); result = globs->callback_ask_password (message, default_user, default_domain, flags, &username, &password, &anonymous, &domain, &password_save, globs->callbacks_data); fprintf (stderr, " (II) Received result = %d\n", result); if (result) { if (flags & G_ASK_PASSWORD_NEED_USERNAME) g_mount_operation_set_username (op, username); if (flags & G_ASK_PASSWORD_NEED_DOMAIN) g_mount_operation_set_domain (op, domain); if (flags & G_ASK_PASSWORD_NEED_PASSWORD) g_mount_operation_set_password (op, password); if (flags & G_ASK_PASSWORD_ANONYMOUS_SUPPORTED) g_mount_operation_set_anonymous (op, anonymous); if (flags & G_ASK_PASSWORD_SAVING_SUPPORTED) g_mount_operation_set_password_save (op, password_save); g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED); } else g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED); return; } /* Unhandled, abort */ g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED); } static void ask_question_cb (GMountOperation *op, const gchar *message, const gchar *choices[], gpointer user_data) { struct TVFSGlobs *globs; int len; int choice; globs = (struct TVFSGlobs*) user_data; g_assert (globs != NULL); g_print ("(WW) ask_question_cb: message = '%s'\n", message); len = 0; while (choices[len] != NULL) { g_print ("(WW) ask_question_cb: choice[%d] = '%s'\n", len, choices[len]); len++; } choice = -1; if (globs->callback_ask_question) { fprintf (stderr, " (II) Spawning callback_ask_question (0x%.16llX)...\n", (unsigned long long) globs->callback_ask_question); globs->callback_ask_question (message, choices, &choice, globs->callbacks_data); fprintf (stderr, " (II) Received choice = %d\n", choice); if (choice >= 0) { g_mount_operation_set_choice (op, choice); g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED); } else { g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED); } return; } g_mount_operation_reply (op, G_MOUNT_OPERATION_UNHANDLED); } static void mount_done_cb (GObject *object, GAsyncResult *res, gpointer user_data) { struct TVFSGlobs *globs; gboolean succeeded; GError *error = NULL; globs = (struct TVFSGlobs*) user_data; g_assert (globs != NULL); succeeded = g_file_mount_enclosing_volume_finish (G_FILE (object), res, &error); if (! succeeded) { g_print ("(EE) Error mounting location: %s\n", error->message); globs->mount_result = g_error_to_TVFSResult (error); g_error_free (error); } else { globs->mount_result = cVFS_OK; g_print ("(II) Mount successful.\n"); } g_main_loop_quit (globs->mount_main_loop); } static TVFSResult vfs_handle_mount (struct TVFSGlobs *globs, GFile *file) { GMountOperation *op; g_print ("(II) Mounting location...\n"); op = g_mount_operation_new (); g_signal_connect (op, "ask-password", (GCallback)ask_password_cb, globs); g_signal_connect (op, "ask-question", (GCallback)ask_question_cb, globs); globs->mount_result = cVFS_Failed; globs->mount_try = 0; /* Inspiration taken from Bastien Nocera's http://svn.gnome.org/viewvc/totem-pl-parser/trunk/plparse/totem-disc.c?view=markup */ globs->mount_main_loop = g_main_loop_new (NULL, FALSE); g_file_mount_enclosing_volume (file, G_MOUNT_MOUNT_NONE, op, NULL, mount_done_cb, globs); g_main_loop_run (globs->mount_main_loop); g_main_loop_unref (globs->mount_main_loop); globs->mount_main_loop = NULL; g_object_unref (op); return globs->mount_result; } struct TVFSGlobs * VFSNew (TVFSLogFunc log_func) { struct TVFSGlobs * globs; globs = (struct TVFSGlobs *) malloc (sizeof (struct TVFSGlobs)); memset (globs, 0, sizeof (struct TVFSGlobs)); globs->log_func = log_func; globs->log_func ("GVFS plugin: VFSInit"); globs->file = NULL; globs->enumerator = NULL; globs->break_get_dir_size = FALSE; globs->block_size = DEFAULT_BLOCK_SIZE; globs->callbacks_data = NULL; globs->callback_ask_question = NULL; globs->callback_ask_password = NULL; return globs; } void VFSSetCallbacks (struct TVFSGlobs *globs, TVFSAskQuestionCallback ask_question_callback, TVFSAskPasswordCallback ask_password_callback, void *data) { globs->callback_ask_question = ask_question_callback; globs->callback_ask_password = ask_password_callback; globs->callbacks_data = data; } void VFSFree (struct TVFSGlobs *globs) { globs->log_func ("GVFS plugin: VFSDestroy"); free (globs); } int VFSVersion () { return cVFSVersion; } struct TVFSInfo VFSGetInfo () { struct TVFSInfo module_info; module_info.Name = "GVFS plugin"; module_info.Description = "GVFS plugin"; module_info.About = g_strdup_printf ("version %s, build date: %s", VERSION, BUILD_DATE); module_info.Copyright = "Copyright (C) 2008 Tomáš Bžatek"; return module_info; } char * VFSGetExts () { return ""; } char * VFSGetServices () { /* FIXME: retrieve list of supported backends from gvfs subsystem */ return "http;https;ftp;sftp;smb;network"; } char * VFSGetPrefix (struct TVFSGlobs *globs) { GFile *f; char *s; if (globs->file) { f = g_file_resolve_relative_path (globs->file, "/"); s = g_file_get_uri (f); g_object_unref (f); return s; } else return NULL; } TVFSResult VFSOpen (struct TVFSGlobs *globs, char *sName) { GFile *f, *f2; GFileInfo *info; GError *error; TVFSResult res; globs->file = NULL; globs->ftp_anonymous = FALSE; if (strstr (sName, "@") == NULL) { /* test for FTP protocol (we only enable anonymous here) */ globs->ftp_anonymous = strcasestr (sName, "ftp://") == sName; } g_print ("(II) VFSOpen: opening URI '%s'\n", sName); f = g_file_new_for_commandline_arg (sName); while (1) { error = NULL; info = g_file_query_info (f, CONST_DEFAULT_QUERY_INFO_ATTRIBUTES, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error); /* Fallback to parent directory if specified path doesn't exist */ if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { f2 = g_file_get_parent (f); if (f2) { g_object_unref (f); f = f2; g_error_free (error); continue; } } /* Mount the target */ if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED)) { g_error_free (error); error = NULL; res = vfs_handle_mount (globs, f); if (res != cVFS_OK) return res; else continue; } /* Any other errors --> report */ if (error) { g_print ("(EE) VFSOpen: g_file_query_info() error: %s\n", error->message); res = g_error_to_TVFSResult (error); g_error_free (error); g_object_unref (f); return res; } /* everything ok? */ break; } globs->file = f; return cVFS_OK; } TVFSResult VFSClose (struct TVFSGlobs *globs) { g_print ("(II) VFSClose\n"); if (globs->file) g_object_unref (globs->file); globs->file = NULL; return cVFS_OK; } char * VFSGetPath (struct TVFSGlobs *globs) { GFile *root; char *path, *s; if (globs->file) { root = g_file_resolve_relative_path (globs->file, "/"); if (root == NULL) return NULL; path = g_file_get_relative_path (root, globs->file); if (path == NULL) return NULL; if (! g_path_is_absolute (path)) s = g_strdup_printf ("/%s", path); else s = g_strdup (path); g_print ("(II) VFSGetPath: '%s'\n", s); g_free (path); g_object_unref (root); return s; } else return NULL; } guint64 VFSGetFileSystemFree (struct TVFSGlobs *globs, char *APath) { GFileInfo *info; GError *error; guint64 res; if (globs->file == NULL) return 0; error = NULL; info = g_file_query_filesystem_info (globs->file, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, NULL, &error); if (error) { g_print ("(EE) VFSGetFileSystemFree: %s\n", error->message); g_error_free (error); return 0; } res = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE); g_object_unref (info); return res; } guint64 VFSGetFileSystemSize (struct TVFSGlobs *globs, char *APath) { GFileInfo *info; GError *error; guint64 res; if (globs->file == NULL) return 0; error = NULL; info = g_file_query_filesystem_info (globs->file, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, NULL, &error); if (error) { g_print ("(EE) VFSGetFileSystemSize: %s\n", error->message); g_error_free (error); return 0; } res = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE); g_object_unref (info); return res; } /**************************************************************************************************************************************/ /**************************************************************************************************************************************/ TVFSResult VFSChangeDir (struct TVFSGlobs *globs, char *NewPath) { GFile *f; GFileEnumerator *en; GError *error, *error_shortcut; TVFSResult res; GFileInfo *info; gchar *target_uri; if (globs->file == NULL) { g_print ("(EE) VFSChangeDir: globs->file == NULL !\n"); return cVFS_Failed; } g_print ("(II) VFSChangeDir: changing dir to '%s'\n", NewPath); f = g_file_resolve_relative_path (globs->file, NewPath); if (f == NULL) { g_print ("(EE) VFSChangeDir: g_file_resolve_relative_path() failed.\n"); return cVFS_Failed; } res = cVFS_OK; while (1) { error = NULL; en = g_file_enumerate_children (f, CONST_DEFAULT_QUERY_INFO_ATTRIBUTES, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error); /* if the target is shortcut, change the URI */ if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY)) { error_shortcut = NULL; info = g_file_query_info (f, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error_shortcut); if (info) { target_uri = g_strdup (g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI)); g_object_unref (info); if (target_uri) { g_print ("(WW) VFSChangeDir: following shortcut, changing URI to '%s'\n", target_uri); g_object_unref (f); f = g_file_new_for_uri (target_uri); g_free (target_uri); g_error_free (error); continue; } } if (error_shortcut) g_error_free (error_shortcut); } /* Mount the target */ if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED)) { g_error_free (error); res = vfs_handle_mount (globs, f); if (res != cVFS_OK) { g_object_unref (f); return res; } else continue; } /* Any other errors --> report */ if (error) { g_print ("(EE) VFSChangeDir: g_file_enumerate_children() error: %s\n", error->message); res = g_error_to_TVFSResult (error); g_error_free (error); g_object_unref (f); return res; } /* everything ok? */ break; } globs->enumerator = en; g_object_unref (globs->file); globs->file = f; return res; } int VFSLogin (struct TVFSGlobs *globs, char *user, char *pass) { g_print ("(II) VFSLogin: logging in with '%s'/'%s'\n", user, pass); /* FIXME: add auth code */ return cVFS_Not_Supported; } /**************************************************************************************************************************************/ /**************************************************************************************************************************************/ static void g_file_info_to_TVFSItem (GFileInfo *info, struct TVFSItem *Item) { g_assert (info != NULL); g_assert (Item != NULL); Item->FName = g_strdup (g_file_info_get_name (info)); Item->FDisplayName = g_strdup (g_file_info_get_display_name (info)); Item->sLinkTo = g_file_info_get_symlink_target (info) == NULL ? NULL : g_strdup (g_file_info_get_symlink_target (info)); Item->iSize = g_file_info_get_size (info); Item->iMode = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE); Item->m_time = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED); Item->a_time = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS); Item->c_time = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CHANGED); Item->iUID = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID); Item->iGID = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID); // g_print ("(II) g_file_info_to_TVFSItem: type = %d\n", g_file_info_get_file_type (info)); // g_print ("(II) g_file_info_to_TVFSItem: UNIX_MODE = %d\n", Item->iMode); if (g_file_info_get_is_symlink (info)) { Item->ItemType = vSymlink; } else { switch (g_file_info_get_file_type (info)) { case G_FILE_TYPE_REGULAR: Item->ItemType = vRegular; break; case G_FILE_TYPE_DIRECTORY: case G_FILE_TYPE_SHORTCUT: /* Used in network:/// */ case G_FILE_TYPE_MOUNTABLE: Item->ItemType = vDirectory; break; case G_FILE_TYPE_SYMBOLIC_LINK: Item->ItemType = vSymlink; break; case G_FILE_TYPE_SPECIAL: /* socket, fifo, blockdev, chardev */ Item->ItemType = vBlockdev; break; case G_FILE_TYPE_UNKNOWN: default: Item->ItemType = vRegular; } } /* fallback to default file mode if read fails */ if (Item->iMode == 0) { if (Item->ItemType == vDirectory) Item->iMode = S_IFDIR + S_IRWXU + S_IRGRP + S_IXGRP + S_IROTH + S_IXOTH; else Item->iMode = S_IRUSR + S_IWUSR + S_IRGRP + S_IROTH; } } TVFSResult VFSListNext (struct TVFSGlobs *globs, char *sDir, struct TVFSItem *Item) { GError *error; GFileInfo *info; TVFSResult res; if (globs->file == NULL) { g_print ("(EE) VFSListNext: globs->file == NULL !\n"); return cVFS_Failed; } if (globs->enumerator == NULL) { g_print ("(EE) VFSListNext: globs->enumerator == NULL !\n"); return cVFS_Failed; } error = NULL; info = g_file_enumerator_next_file (globs->enumerator, NULL, &error); if (error) { g_print ("(EE) VFSListNext: g_file_enumerator_next_file() error: %s\n", error->message); res = g_error_to_TVFSResult (error); g_error_free (error); return res; } if (! error && ! info) return cVFS_No_More_Files; g_file_info_to_TVFSItem (info, Item); g_object_unref (info); return cVFS_OK; } TVFSResult VFSListFirst (struct TVFSGlobs *globs, char *sDir, struct TVFSItem *Item) { return VFSListNext (globs, sDir, Item); } TVFSResult VFSListClose (struct TVFSGlobs *globs) { GError *error; TVFSResult res; if (globs->file == NULL) { g_print ("(EE) VFSListClose: globs->file == NULL !\n"); return cVFS_Failed; } if (globs->enumerator == NULL) { g_print ("(EE) VFSListClose: globs->enumerator == NULL !\n"); return cVFS_Failed; } g_print ("(II) VFSListClose\n"); error = NULL; g_file_enumerator_close (globs->enumerator, NULL, &error); g_object_unref (globs->enumerator); globs->enumerator = NULL; if (error) { g_print ("(EE) VFSListClose: g_file_enumerator_close() error: %s\n", error->message); res = g_error_to_TVFSResult (error); g_error_free (error); return res; } return cVFS_OK; } /**************************************************************************************************************************************/ /**************************************************************************************************************************************/ long VFSFileExists (struct TVFSGlobs *globs, const char *FileName, const long Use_lstat) { GFile *f; GError *error; GFileInfo *info; if (globs->file == NULL) { g_print ("(EE) VFSFileExists: globs->file == NULL !\n"); return cVFS_Failed; } f = g_file_resolve_relative_path (globs->file, FileName); if (f == NULL) { g_print ("(EE) VFSMkDir: g_file_resolve_relative_path() failed.\n"); return cVFS_Failed; } error = NULL; info = g_file_query_info (f, G_FILE_ATTRIBUTE_STANDARD_NAME, Use_lstat ? G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS : G_FILE_QUERY_INFO_NONE, NULL, &error); g_object_unref (f); if (error) { // g_print ("(EE) VFSFileExists: g_file_query_info() error: %s\n", error->message); g_error_free (error); return FALSE; } g_object_unref (info); return TRUE; } TVFSResult VFSFileInfo (struct TVFSGlobs *globs, char *AFileName, struct TVFSItem *Item) { GFile *f; GError *error; GFileInfo *info; TVFSResult res; if (globs->file == NULL) { g_print ("(EE) VFSFileInfo: globs->file == NULL !\n"); return cVFS_Failed; } f = g_file_resolve_relative_path (globs->file, AFileName); if (f == NULL) { g_print ("(EE) VFSMkDir: g_file_resolve_relative_path() failed.\n"); return cVFS_Failed; } error = NULL; info = g_file_query_info (f, CONST_DEFAULT_QUERY_INFO_ATTRIBUTES, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error); g_object_unref (f); if (error) { g_print ("(EE) VFSFileInfo: g_file_query_info() error: %s\n", error->message); res = g_error_to_TVFSResult (error); g_error_free (error); return res; } g_file_info_to_TVFSItem (info, Item); g_object_unref (info); return cVFS_OK; } TVFSResult VFSMkDir (struct TVFSGlobs *globs, const char *sDirName) { GFile *f; GError *error; TVFSResult res; if (globs->file == NULL) { g_print ("(EE) VFSMkDir: globs->file == NULL !\n"); return cVFS_Failed; } f = g_file_resolve_relative_path (globs->file, sDirName); if (f == NULL) { g_print ("(EE) VFSMkDir: g_file_resolve_relative_path() failed.\n"); return cVFS_Failed; } error = NULL; g_file_make_directory (f, NULL, &error); g_object_unref (f); if (error) { g_print ("(EE) VFSMkDir: g_file_make_directory() error: %s\n", error->message); res = g_error_to_TVFSResult (error); g_error_free (error); return res; } return cVFS_OK; } TVFSResult VFSRemove (struct TVFSGlobs *globs, const char *APath) { GFile *f; GError *error; TVFSResult res; if (globs->file == NULL) { g_print ("(EE) VFSRemove: globs->file == NULL !\n"); return cVFS_Failed; } f = g_file_resolve_relative_path (globs->file, APath); if (f == NULL) { g_print ("(EE) VFSRemove: g_file_resolve_relative_path() failed.\n"); return cVFS_Failed; } error = NULL; g_file_delete (f, NULL, &error); g_object_unref (f); if (error) { g_print ("(EE) VFSRemove: g_file_delete() error: %s\n", error->message); res = g_error_to_TVFSResult (error); g_error_free (error); return res; } return cVFS_OK; } TVFSResult VFSRename (struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName) { GFile *src, *dst; GError *error; TVFSResult res; if (globs->file == NULL) { g_print ("(EE) VFSRename: globs->file == NULL !\n"); return cVFS_Failed; } src = g_file_resolve_relative_path (globs->file, sSrcName); if (src == NULL) { g_print ("(EE) VFSRename: g_file_resolve_relative_path() failed.\n"); return cVFS_Failed; } g_print ("VFSRename: '%s' --> '%s'\n", sSrcName, sDstName); error = NULL; g_file_set_display_name (src, sDstName, NULL, &error); if (error) { g_print ("(WW) VFSRename: g_file_set_display_name() failed (\"%s\"), using fallback g_file_move()\n", error->message); g_error_free (error); dst = g_file_resolve_relative_path (src, sDstName); if (dst == NULL) { g_print ("(EE) VFSRename: g_file_resolve_relative_path() failed.\n"); g_object_unref (src); return cVFS_Failed; } error = NULL; g_file_move (src, dst, G_FILE_COPY_NONE | G_FILE_COPY_NO_FALLBACK_FOR_MOVE, NULL, NULL, NULL, &error); if (error) { g_print ("(EE) VFSRename: g_file_move() error: %s\n", error->message); res = g_error_to_TVFSResult (error); g_error_free (error); g_object_unref (src); g_object_unref (dst); return res; } g_object_unref (dst); } g_object_unref (src); return cVFS_OK; } TVFSResult VFSMakeSymLink (struct TVFSGlobs *globs, const char *NewFileName, const char *PointTo) { GFile *f; GError *error; TVFSResult res; if (globs->file == NULL) { g_print ("(EE) VFSMakeSymLink: globs->file == NULL !\n"); return cVFS_Failed; } f = g_file_resolve_relative_path (globs->file, NewFileName); if (f == NULL) { g_print ("(EE) VFSMakeSymLink: g_file_resolve_relative_path() failed.\n"); return cVFS_Failed; } error = NULL; g_file_make_symbolic_link (f, PointTo, NULL, &error); g_object_unref (f); if (error) { g_print ("(EE) VFSMakeSymLink: g_file_make_symbolic_link() error: %s\n", error->message); res = g_error_to_TVFSResult (error); g_error_free (error); return res; } return cVFS_OK; } TVFSResult VFSChmod (struct TVFSGlobs *globs, const char *FileName, const uint Mode) { GFile *f; GError *error; TVFSResult res; if (globs->file == NULL) { g_print ("(EE) VFSChmod: globs->file == NULL !\n"); return cVFS_Failed; } f = g_file_resolve_relative_path (globs->file, FileName); if (f == NULL) { g_print ("(EE) VFSChmod: g_file_resolve_relative_path() failed.\n"); return cVFS_Failed; } // g_print ("(II) VFSChmod (%s, %d): Going to set permissions on '%s'\n", FileName, Mode, g_file_get_uri (f)); error = NULL; g_file_set_attribute_uint32 (f, G_FILE_ATTRIBUTE_UNIX_MODE, Mode, G_FILE_QUERY_INFO_NONE, NULL, &error); g_object_unref (f); if (error) { g_print ("(EE) VFSChmod: g_file_set_attribute_uint32() error: %s\n", error->message); res = g_error_to_TVFSResult (error); g_error_free (error); return res; } return cVFS_OK; } TVFSResult VFSChown (struct TVFSGlobs *globs, const char *FileName, const uint UID, const uint GID) { GFile *f; GError *error; TVFSResult res; if (globs->file == NULL) { g_print ("(EE) VFSChown: globs->file == NULL !\n"); return cVFS_Failed; } f = g_file_resolve_relative_path (globs->file, FileName); if (f == NULL) { g_print ("(EE) VFSChown: g_file_resolve_relative_path() failed.\n"); return cVFS_Failed; } error = NULL; g_file_set_attribute_uint32 (f, G_FILE_ATTRIBUTE_UNIX_UID, UID, G_FILE_QUERY_INFO_NONE, NULL, &error); if (error) { g_print ("(EE) VFSChown: g_file_set_attribute_uint32() error: %s\n", error->message); res = g_error_to_TVFSResult (error); g_error_free (error); g_object_unref (f); return res; } error = NULL; g_file_set_attribute_uint32 (f, G_FILE_ATTRIBUTE_UNIX_GID, GID, G_FILE_QUERY_INFO_NONE, NULL, &error); if (error) { g_print ("(EE) VFSChown: g_file_set_attribute_uint32() error: %s\n", error->message); res = g_error_to_TVFSResult (error); g_error_free (error); g_object_unref (f); return res; } g_object_unref (f); return cVFS_OK; } TVFSResult VFSChangeTimes (struct TVFSGlobs *globs, char *APath, long mtime, long atime) { GFile *f; GError *error; TVFSResult res; if (globs->file == NULL) { g_print ("(EE) VFSChangeTimes: globs->file == NULL !\n"); return cVFS_Failed; } f = g_file_resolve_relative_path (globs->file, APath); if (f == NULL) { g_print ("(EE) VFSChangeTimes: g_file_resolve_relative_path() failed.\n"); return cVFS_Failed; } error = NULL; g_file_set_attribute_uint64 (f, G_FILE_ATTRIBUTE_TIME_MODIFIED, mtime, G_FILE_QUERY_INFO_NONE, NULL, &error); if (error) { g_print ("(EE) VFSChangeTimes: g_file_set_attribute_uint64() error: %s\n", error->message); res = g_error_to_TVFSResult (error); g_error_free (error); g_object_unref (f); return res; } error = NULL; g_file_set_attribute_uint64 (f, G_FILE_ATTRIBUTE_TIME_ACCESS, atime, G_FILE_QUERY_INFO_NONE, NULL, &error); if (error) { g_print ("(EE) VFSChangeTimes: g_file_set_attribute_uint64() error: %s\n", error->message); g_error_free (error); /* Silently drop the error, atime is not commonly supported on most systems */ } g_object_unref (f); return cVFS_OK; } /**************************************************************************************************************************************/ /**************************************************************************************************************************************/ static void VFSGetDirSize_recurse (struct TVFSGlobs *globs, GFile *file, guint64 *size) { GFile *f; GFileEnumerator *en; GError *error; GFileInfo *info; if (globs->break_get_dir_size) return; error = NULL; en = g_file_enumerate_children (file, G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error); if (error) { g_print ("(EE) VFSGetDirSize_recurse: g_file_enumerate_children() error: %s\n", error->message); g_error_free (error); return; } error = NULL; while ((info = g_file_enumerator_next_file (en, NULL, &error))) { if (globs->break_get_dir_size) break; if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) { f = g_file_resolve_relative_path (file, g_file_info_get_name (info)); if (f == NULL) { g_print ("(EE) VFSGetDirSize_recurse: g_file_resolve_relative_path() failed.\n"); return; } VFSGetDirSize_recurse (globs, f, size); g_object_unref (f); } else *size += g_file_info_get_size (info); g_object_unref (info); } if (error) { g_print ("(EE) VFSGetDirSize_recurse: g_file_enumerator_next_file() error: %s\n", error->message); g_error_free (error); } g_file_enumerator_close (en, NULL, &error); g_object_unref (en); if (error) { g_print ("(EE) VFSGetDirSize_recurse: g_file_enumerator_close() error: %s\n", error->message); g_error_free (error); } } guint64 VFSGetDirSize (struct TVFSGlobs *globs, char *APath) { GFile *f; guint64 size; if (globs == NULL) return 0; if (globs->file == NULL) { g_print ("(EE) VFSGetDirSize: globs->file == NULL !\n"); return 0; } f = g_file_resolve_relative_path (globs->file, APath); if (f == NULL) { g_print ("(EE) VFSGetDirSize: g_file_resolve_relative_path() failed.\n"); return 0; } size = 0; globs->break_get_dir_size = FALSE; VFSGetDirSize_recurse (globs, f, &size); globs->break_get_dir_size = FALSE; g_object_unref (f); return size; } void VFSBreakGetDirSize (struct TVFSGlobs *globs) { g_print ("(WW) VFSBreakGetDirSize: ################################### calling Break\n"); if (globs) globs->break_get_dir_size = TRUE; } /**************************************************************************************************************************************/ /**************************************************************************************************************************************/ TVFSFileDes VFSOpenFile (struct TVFSGlobs *globs, const char *APath, int Mode, int *Error) { printf("(WW) VFSOpenFile: Not supported in GVFS plugin.\n"); return NULL; } TVFSResult VFSCloseFile (struct TVFSGlobs *globs, TVFSFileDes FileDescriptor) { printf("(WW) VFSCloseFile: Not supported in GVFS plugin.\n"); return cVFS_Not_Supported; } guint64 VFSFileSeek (struct TVFSGlobs *globs, TVFSFileDes FileDescriptor, guint64 AbsoluteOffset, int *Error) { printf("(WW) VFSFileSeek: Not supported in GVFS plugin.\n"); return -1; } int VFSReadFile (struct TVFSGlobs *globs, TVFSFileDes FileDescriptor, gpointer Buffer, int ABlockSize, int *Error) { printf("(WW) VFSReadFile: Not supported in GVFS plugin.\n"); return cVFS_Not_Supported; } int VFSWriteFile (struct TVFSGlobs *globs, TVFSFileDes FileDescriptor, gpointer Buffer, int BytesCount, int *Error) { printf("(WW) VFSWriteFile: Not supported in GVFS plugin.\n"); return cVFS_Not_Supported; } void VFSSetBlockSize (struct TVFSGlobs *globs, int Value) { if (globs) globs->block_size = Value; } gboolean VFSIsOnSameFS (struct TVFSGlobs *globs, const char *Path1, const char *Path2) { GFile *file1, *file2; GFileInfo *info1, *info2; GError *error; gboolean res; if (globs->file == NULL) { g_print ("(EE) VFSIsOnSameFS: globs->file == NULL !\n"); return FALSE; } file1 = g_file_resolve_relative_path (globs->file, Path1); file2 = g_file_resolve_relative_path (globs->file, Path2); if (file1 == NULL) { g_print ("(EE) VFSIsOnSameFS: g_file_resolve_relative_path() failed.\n"); return FALSE; } if (file2 == NULL) { g_print ("(EE) VFSIsOnSameFS: g_file_resolve_relative_path() failed.\n"); return FALSE; } error = NULL; info1 = g_file_query_info (file1, G_FILE_ATTRIBUTE_ID_FILESYSTEM, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error); if (error) { g_print ("(EE) VFSIsOnSameFS: g_file_query_info() error: %s\n", error->message); g_error_free (error); g_object_unref (file1); g_object_unref (file2); return FALSE; } info2 = g_file_query_info (file2, G_FILE_ATTRIBUTE_ID_FILESYSTEM, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error); if (error) { g_print ("(EE) VFSIsOnSameFS: g_file_query_info() error: %s\n", error->message); g_error_free (error); g_object_unref (info1); g_object_unref (file1); g_object_unref (file2); return FALSE; } g_print ("(II) VFSIsOnSameFS: '%s' vs. '%s'\n", g_file_info_get_attribute_string (info1, G_FILE_ATTRIBUTE_ID_FILESYSTEM), g_file_info_get_attribute_string (info2, G_FILE_ATTRIBUTE_ID_FILESYSTEM)); res = strcmp (g_file_info_get_attribute_string (info1, G_FILE_ATTRIBUTE_ID_FILESYSTEM), g_file_info_get_attribute_string (info2, G_FILE_ATTRIBUTE_ID_FILESYSTEM)) == 0; g_object_unref (file1); g_object_unref (file2); g_object_unref (info1); g_object_unref (info2); return res; } gboolean VFSTwoSameFiles (struct TVFSGlobs *globs, const char *Path1, const char *Path2) { GFile *file1, *file2; gboolean res; if (globs->file == NULL) { g_print ("(EE) VFSTwoSameFiles: globs->file == NULL !\n"); return FALSE; } file1 = g_file_resolve_relative_path (globs->file, Path1); file2 = g_file_resolve_relative_path (globs->file, Path2); if (file1 == NULL) { g_print ("(EE) VFSTwoSameFiles: g_file_resolve_relative_path() failed.\n"); return FALSE; } if (file2 == NULL) { g_print ("(EE) VFSTwoSameFiles: g_file_resolve_relative_path() failed.\n"); return FALSE; } /* FIXME: we should do some I/O test, we're esentially comparing strings at the moment */ res = g_file_equal (file1, file2); g_object_unref (file1); g_object_unref (file2); return res; } /**************************************************************************************************************************************/ /**************************************************************************************************************************************/ struct CopyJobRef { struct TVFSGlobs *globs; TVFSCopyCallBackFunc callback_func; void *callback_data; GCancellable *cancellable; }; static void vfs_copy_progress_callback (goffset current_num_bytes, goffset total_num_bytes, gpointer user_data) { struct CopyJobRef *ref; // g_print ("(II) vfs_copy_progress_callback spawned: current_num_bytes = %lu, total_num_bytes = %lu\n", current_num_bytes, total_num_bytes); if (! user_data) return; ref = (struct CopyJobRef*) user_data; if (ref->callback_func) { if (! ref->callback_func (current_num_bytes, total_num_bytes, ref->callback_data)) g_cancellable_cancel (ref->cancellable); } } TVFSResult VFSCopyOut (struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName, TVFSCopyCallBackFunc pCallBackProgress, void *data, gboolean Append) { GFile *src, *dst; GError *error; struct CopyJobRef *ref; TVFSResult res; if (globs->file == NULL) { g_print ("(EE) VFSCopyOut: globs->file == NULL !\n"); return cVFS_Failed; } g_print ("(II) VFSCopyOut: '%s' --> '%s'\n", sSrcName, sDstName); src = g_file_resolve_relative_path (globs->file, sSrcName); if (src == NULL) { g_print ("(EE) VFSCopyOut: g_file_resolve_relative_path() failed.\n"); return cVFS_Failed; } dst = g_file_new_for_path (sDstName); if (dst == NULL) { g_print ("(EE) VFSCopyOut: g_file_resolve_relative_path() failed.\n"); return cVFS_Failed; } ref = g_slice_new0 (struct CopyJobRef); ref->globs = globs; ref->callback_func = pCallBackProgress; ref->callback_data = data; ref->cancellable = g_cancellable_new (); res = cVFS_OK; error = NULL; g_file_copy (src, dst, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS, ref->cancellable, vfs_copy_progress_callback, ref, &error); if (error) { g_print ("(EE) VFSCopyOut: g_file_copy() error: %s\n", error->message); // res = g_error_to_TVFSResult (error); if (error->code == G_IO_ERROR_CANCELLED) res = cVFS_Cancelled; else res = cVFS_ReadErr; g_error_free (error); } g_object_unref (ref->cancellable); g_slice_free (struct CopyJobRef, ref); g_object_unref (src); g_object_unref (dst); return res; } TVFSResult VFSCopyIn (struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName, TVFSCopyCallBackFunc pCallBackProgress, void *data, gboolean Append) { GFile *src, *dst; GError *error; struct CopyJobRef *ref; TVFSResult res; if (globs->file == NULL) { g_print ("(EE) VFSCopyIn: globs->file == NULL !\n"); return cVFS_Failed; } g_print ("(II) VFSCopyIn: '%s' --> '%s'\n", sSrcName, sDstName); src = g_file_new_for_path (sSrcName); if (src == NULL) { g_print ("(EE) VFSCopyIn: g_file_resolve_relative_path() failed.\n"); return cVFS_Failed; } dst = g_file_resolve_relative_path (globs->file, sDstName); if (dst == NULL) { g_print ("(EE) VFSCopyIn: g_file_resolve_relative_path() failed.\n"); return cVFS_Failed; } ref = g_slice_new0 (struct CopyJobRef); ref->globs = globs; ref->callback_func = pCallBackProgress; ref->callback_data = data; ref->cancellable = g_cancellable_new (); res = cVFS_OK; error = NULL; /* FIXME: Appending not supported */ g_file_copy (src, dst, G_FILE_COPY_NOFOLLOW_SYMLINKS, ref->cancellable, vfs_copy_progress_callback, ref, &error); if (error) { g_print ("(EE) VFSCopyIn: g_file_copy() error: %s\n", error->message); // res = g_error_to_TVFSResult (error); if (error->code == G_IO_ERROR_CANCELLED) res = cVFS_Cancelled; else res = cVFS_WriteErr; g_error_free (error); } g_object_unref (ref->cancellable); g_slice_free (struct CopyJobRef, ref); g_object_unref (src); g_object_unref (dst); return res; } /********** * TODO: * - block size settings for GIO subsystem * - variable block size for different protocols? * - support for appending in VFSCopyIn * DONE- authentication improvements (needs new VFS API) * ***/