[gvfs] MTP: Use android extensions to support in place write to files.
- From: Philip Langdale <philipl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gvfs] MTP: Use android extensions to support in place write to files.
- Date: Sat, 30 Mar 2013 23:26:40 +0000 (UTC)
commit e88aa8dad358a23c164a95267f7dd8415d377b21
Author: Philip Langdale <philipl overt org>
Date: Mon Feb 18 09:36:41 2013 -0800
MTP: Use android extensions to support in place write to files.
Allows for opening files directly on the device and writing to them.
https://bugzilla.gnome.org/show_bug.cgi?id=695984
daemon/gvfsbackendmtp.c | 350 ++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 348 insertions(+), 2 deletions(-)
---
diff --git a/daemon/gvfsbackendmtp.c b/daemon/gvfsbackendmtp.c
index 9b58f2b..b047e0b 100644
--- a/daemon/gvfsbackendmtp.c
+++ b/daemon/gvfsbackendmtp.c
@@ -1674,8 +1674,8 @@ do_read (GVfsBackend *backend,
static void
do_close_read (GVfsBackend *backend,
- GVfsJobCloseRead *job,
- GVfsBackendHandle opaque_handle)
+ GVfsJobCloseRead *job,
+ GVfsBackendHandle opaque_handle)
{
DEBUG ("(I) do_close_read");
RWHandle *handle = opaque_handle;
@@ -1688,6 +1688,344 @@ do_close_read (GVfsBackend *backend,
}
+#if HAVE_LIBMTP_1_1_6
+uint16_t zero_get_func (void* params,
+ void* priv,
+ uint32_t wantlen,
+ unsigned char *data,
+ uint32_t *gotlen)
+{
+ *gotlen = 0;
+ return LIBMTP_HANDLER_RETURN_OK;
+}
+
+
+static void
+do_create (GVfsBackend *backend,
+ GVfsJobOpenForWrite *job,
+ const char *filename,
+ GFileCreateFlags flags)
+{
+ if (!G_VFS_BACKEND_MTP (backend)->android_extension) {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Operation not supported."));
+ return;
+ }
+
+ DEBUG ("(I) do_create (%s)", filename);
+ g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
+
+ gchar **elements = g_strsplit_set (filename, "/", -1);
+ unsigned int ne = g_strv_length (elements);
+
+ if (ne < 3) {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Cannot create files in this location"));
+ goto exit;
+ }
+
+ int parent_id = 0;
+
+ if (ne > 3) {
+ parent_id = strtol (elements[ne-2], NULL, 10);
+ }
+
+ LIBMTP_mtpdevice_t *device;
+ device = G_VFS_BACKEND_MTP (backend)->device;
+
+ unsigned int id = strtol (elements[ne-1], NULL, 10);
+ char *existing_filename;
+ LIBMTP_file_t *existing_file = LIBMTP_Get_Filemetadata (device, id);
+ if (existing_file != NULL) {
+ existing_filename = strdup (existing_file->filename);
+ LIBMTP_destroy_file_t (existing_file);
+ } else {
+ existing_filename = strdup (elements[ne-1]);
+ }
+
+ LIBMTP_file_t *mtpfile = LIBMTP_new_file_t ();
+ mtpfile->filename = existing_filename;
+ mtpfile->parent_id = parent_id;
+ mtpfile->storage_id = strtol (elements[1], NULL, 10);
+ mtpfile->filetype = LIBMTP_FILETYPE_UNKNOWN;
+ mtpfile->filesize = 0;
+
+ int ret = LIBMTP_Send_File_From_Handler (device, zero_get_func, NULL,
+ mtpfile, NULL, NULL);
+ id = mtpfile->item_id;
+ LIBMTP_destroy_file_t (mtpfile);
+ if (ret != 0) {
+ fail_job (G_VFS_JOB (job), device);
+ DEBUG ("(I) Failed to create empty file.");
+ goto exit;
+ }
+
+ ret = LIBMTP_BeginEditObject (device, id);
+ if (ret != 0) {
+ fail_job (G_VFS_JOB (job), device);
+ DEBUG ("(I) Failed to begin edit.");
+ goto exit;
+ }
+
+ RWHandle *handle = g_new0(RWHandle, 1);
+ handle->handle_type = HANDLE_FILE;
+ handle->id = id;
+ handle->offset = 0;
+ handle->size = 0;
+
+ g_vfs_job_open_for_write_set_can_seek (G_VFS_JOB_OPEN_FOR_WRITE (job), TRUE);
+ g_vfs_job_open_for_write_set_handle (G_VFS_JOB_OPEN_FOR_WRITE (job), handle);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+
+ exit:
+ g_strfreev (elements);
+ g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
+
+ DEBUG ("(I) do_create done.");
+}
+
+
+static void
+do_append_to (GVfsBackend *backend,
+ GVfsJobOpenForWrite *job,
+ const char *filename,
+ GFileCreateFlags flags)
+{
+ if (!G_VFS_BACKEND_MTP (backend)->android_extension) {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Operation not supported."));
+ return;
+ }
+
+ DEBUG ("(I) do_append_to (%s)", filename);
+ g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
+
+ gchar **elements = g_strsplit_set (filename, "/", -1);
+ unsigned int ne = g_strv_length (elements);
+
+ if (ne < 3) {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Cannot open this entity"));
+ goto exit;
+ }
+
+ unsigned int id = strtol (elements[ne-1], NULL, 10);
+
+ LIBMTP_mtpdevice_t *device;
+ device = G_VFS_BACKEND_MTP (backend)->device;
+
+ LIBMTP_file_t *file = LIBMTP_Get_Filemetadata (device, id);
+ if (file == NULL) {
+ fail_job (G_VFS_JOB (job), device);
+ DEBUG ("(I) Failed to get metadata.");
+ goto exit;
+ }
+
+ int ret = LIBMTP_BeginEditObject (device, id);
+ if (ret != 0) {
+ fail_job (G_VFS_JOB (job), device);
+ DEBUG ("(I) Failed to begin edit.");
+ goto exit;
+ }
+
+ RWHandle *handle = g_new0(RWHandle, 1);
+ handle->handle_type = HANDLE_FILE;
+ handle->id = id;
+ handle->offset = file->filesize;
+ handle->size = file->filesize;
+
+ LIBMTP_destroy_file_t (file);
+
+ g_vfs_job_open_for_write_set_can_seek (G_VFS_JOB_OPEN_FOR_WRITE (job), TRUE);
+ g_vfs_job_open_for_write_set_handle (G_VFS_JOB_OPEN_FOR_WRITE (job), handle);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+
+ exit:
+ g_strfreev (elements);
+ g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
+
+ DEBUG ("(I) do_append_to done.");
+}
+
+
+static void
+do_replace (GVfsBackend *backend,
+ GVfsJobOpenForWrite *job,
+ const char *filename,
+ const char *etag,
+ gboolean make_backup,
+ GFileCreateFlags flags)
+{
+ if (!G_VFS_BACKEND_MTP (backend)->android_extension) {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Operation not supported."));
+ return;
+ }
+
+ DEBUG ("(I) do_replace (%s)", filename);
+ g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
+
+ gchar **elements = g_strsplit_set (filename, "/", -1);
+ unsigned int ne = g_strv_length (elements);
+
+ if (ne < 3) {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Cannot open this entity"));
+ goto exit;
+ }
+
+ unsigned int id = strtol (elements[ne-1], NULL, 10);
+
+ LIBMTP_mtpdevice_t *device;
+ device = G_VFS_BACKEND_MTP (backend)->device;
+
+ LIBMTP_file_t *file = LIBMTP_Get_Filemetadata (device, id);
+ if (file == NULL) {
+ g_strfreev (elements);
+ g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
+ return do_create(backend, job, filename, flags);
+
+ fail_job (G_VFS_JOB (job), device);
+ DEBUG ("(I) Failed to get metadata.");
+ goto exit;
+ }
+
+ int ret = LIBMTP_BeginEditObject (device, id);
+ if (ret != 0) {
+ fail_job (G_VFS_JOB (job), device);
+ DEBUG ("(I) Failed to begin edit.");
+ goto exit;
+ }
+
+ ret = LIBMTP_TruncateObject (device, id, 0);
+ if (ret != 0) {
+ fail_job (G_VFS_JOB (job), device);
+ DEBUG ("(I) Failed to truncate.");
+ goto exit;
+ }
+
+ RWHandle *handle = g_new0(RWHandle, 1);
+ handle->handle_type = HANDLE_FILE;
+ handle->id = id;
+ handle->offset = 0;
+ handle->size = file->filesize;
+
+ LIBMTP_destroy_file_t (file);
+
+ g_vfs_job_open_for_write_set_can_seek (G_VFS_JOB_OPEN_FOR_WRITE (job), TRUE);
+ g_vfs_job_open_for_write_set_handle (G_VFS_JOB_OPEN_FOR_WRITE (job), handle);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+
+ exit:
+ g_strfreev (elements);
+ g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
+
+ DEBUG ("(I) do_replace done.");
+}
+
+
+static void
+do_write (GVfsBackend *backend,
+ GVfsJobWrite *job,
+ GVfsBackendHandle opaque_handle,
+ char *buffer,
+ gsize buffer_size)
+{
+ RWHandle *handle = opaque_handle;
+ uint32_t id = handle->id;
+ goffset offset = handle->offset;
+ gsize size = handle->size;
+
+ DEBUG ("(I) do_write (%u %lu %lu)", id, offset, buffer_size);
+ g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
+
+ int ret = LIBMTP_SendPartialObject (G_VFS_BACKEND_MTP (backend)->device, id, offset,
+ buffer, buffer_size);
+ if (ret != 0) {
+ fail_job (G_VFS_JOB (job), G_VFS_BACKEND_MTP (backend)->device);
+ DEBUG ("(I) job failed.");
+ goto exit;
+ }
+
+ handle->offset = offset + buffer_size;
+ g_vfs_job_write_set_written_size (job, buffer_size);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+
+ exit:
+ g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
+ DEBUG ("(I) do_write done.");
+}
+
+
+static void
+do_seek_on_write (GVfsBackend *backend,
+ GVfsJobSeekWrite *job,
+ GVfsBackendHandle opaque_handle,
+ goffset offset,
+ GSeekType type)
+{
+ RWHandle *handle = opaque_handle;
+ uint32_t id = handle->id;
+ goffset old_offset = handle->offset;
+ gsize size = handle->size;
+
+ DEBUG ("(I) do_seek_on_write (%u %lu %ld %u)", id, old_offset, offset, type);
+ g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
+
+ if (type == G_SEEK_END) {
+ offset = size + offset;
+ } else if (type == G_SEEK_CUR) {
+ offset += old_offset;
+ }
+
+ if (offset < 0) {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ _("Can't seek outside file"));
+ goto exit;
+ }
+
+ handle->offset = offset;
+ g_vfs_job_seek_write_set_offset (job, offset);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+
+ exit:
+ g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
+ DEBUG ("(I) do_seek_on_write done. (%lu)", offset);
+}
+
+
+static void
+do_close_write (GVfsBackend *backend,
+ GVfsJobCloseWrite *job,
+ GVfsBackendHandle opaque_handle)
+{
+ DEBUG ("(I) do_close_write");
+ g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
+
+ RWHandle *handle = opaque_handle;
+
+ int ret = LIBMTP_EndEditObject (G_VFS_BACKEND_MTP (backend)->device, handle->id);
+ if (ret != 0) {
+ fail_job (G_VFS_JOB (job), G_VFS_BACKEND_MTP (backend)->device);
+ goto exit;
+ }
+
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+
+ exit:
+ g_free(handle);
+ g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
+ DEBUG ("(I) do_close_write done.");
+}
+#endif /* HAVE_LIBMTP_1_1_6 */
+
/************************************************
* Class init
*
@@ -1723,4 +2061,12 @@ g_vfs_backend_mtp_class_init (GVfsBackendMtpClass *klass)
backend_class->seek_on_read = do_seek_on_read;
backend_class->read = do_read;
backend_class->close_read = do_close_read;
+#if HAVE_LIBMTP_1_1_6
+ backend_class->create = do_create;
+ backend_class->append_to = do_append_to;
+ backend_class->replace = do_replace;
+ backend_class->write = do_write;
+ backend_class->seek_on_write = do_seek_on_write;
+ backend_class->close_write = do_close_write;
+#endif
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]