/* 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.1" #define BUILD_DATE "2008-10-06" #define DEFAULT_BLOCK_SIZE 0x10000 /* 64kB */ #define ANON_FTP_PASS "ftpuser@tuxcmd.net" #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; gchar *mount_password; gboolean ftp_anonymous; gboolean break_get_dir_size; guint32 block_size; }; 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; globs = (struct TVFSGlobs*) user_data; g_assert (globs != NULL); g_print ("(WW) ask_password_cb: message = '%s'\n", message); if (globs->ftp_anonymous) g_print ("(II) Trying anonymous FTP login...\n"); if (flags & G_ASK_PASSWORD_NEED_USERNAME) { g_print (" need username...\n"); if (globs->ftp_anonymous) g_mount_operation_set_username (op, "anonymous"); /* FIXME: need proper API to spawn a callback g_mount_operation_set_username (op, s); */ } if (flags & G_ASK_PASSWORD_NEED_DOMAIN) { g_print (" need domain...\n"); /* FIXME: need proper API to spawn a callback g_mount_operation_set_domain (op, s); */ } if (flags & G_ASK_PASSWORD_NEED_PASSWORD) { g_print (" need password...\n"); if (globs->mount_password) g_mount_operation_set_password (op, globs->mount_password); else if (globs->ftp_anonymous) g_mount_operation_set_password (op, ANON_FTP_PASS); } g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED); } 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); globs->mount_result = cVFS_Failed; /* 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; return globs; } 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; gboolean try_again; gchar **uri_matched; char *uri_schema, *uri_username, *uri_password, *uri_service; gchar *uri; int i, seg; globs->file = NULL; globs->mount_password = NULL; globs->ftp_anonymous = FALSE; uri = NULL; /* Rip out password as GVFS URIs should contain only username (and domain). */ /* Password will be used in GMountOperation later */ /* FIXME: this just temporary solution until we got new VFS API */ /* (this is really ugly code) */ if (strstr (sName, "@") != NULL) { uri_schema = NULL; uri_username = NULL; uri_password = NULL; uri_service = NULL; uri_matched = g_regex_split_simple ("^(.*)://(.*):(.*)@(.*)", sName, G_REGEX_CASELESS | G_REGEX_UNGREEDY | G_REGEX_ANCHORED, G_REGEX_MATCH_ANCHORED); if (uri_matched) { seg = 0; for (i = 0; uri_matched[i] != NULL; i++) if (strlen (uri_matched[i]) > 0) { seg++; switch (seg) { case 1: uri_schema = g_strdup (uri_matched[i]); break; case 2: uri_username = g_strdup (uri_matched[i]); break; case 3: uri_password = g_strdup (uri_matched[i]); break; case 4: uri_service = g_strdup (uri_matched[i]); break; } } g_strfreev (uri_matched); globs->mount_password = g_strdup (uri_password); if (seg == 4 && uri_schema && uri_username && uri_password && uri_service) uri = g_strdup_printf ("%s://%s@%s", uri_schema, uri_username, uri_service); } // g_print ("uri_schema = '%s', uri_username = '%s', uri_password = '%s', uri_service = '%s'\n", uri_schema, uri_username, uri_password, uri_service); // g_print ("uri = '%s'\n", uri); if (uri_schema) free (uri_schema); if (uri_username) free (uri_username); if (uri_password) free (uri_password); if (uri_service) free (uri_service); } else { /* should be anonymous */ globs->ftp_anonymous = strcasestr (sName, "ftp://") == sName; } g_print ("(II) VFSOpen: opening URI '%s'\n", uri ? uri : sName); f = g_file_new_for_commandline_arg (uri ? uri : sName); if (uri) g_free (uri); try_again = FALSE; 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; try_again = TRUE; g_error_free (error); continue; } } 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; } 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; } error = NULL; res = cVFS_OK; 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); error = NULL; en = g_file_enumerate_children (f, CONST_DEFAULT_QUERY_INFO_ATTRIBUTES, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error); } } if (error_shortcut) g_error_free (error_shortcut); } 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; } 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 * - authentication improvements (needs new VFS API) * ***/