1 /* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2008 Benjamin Otte <otte@gnome.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 *
20 * Author: Benjmain Otte <otte@gnome.org>
21 */
22
23
24 #include <config.h>
25
26 #include <glib/gi18n.h>
27 #include <string.h>
28 #include <archive.h>
29 #include <archive_entry.h>
30
31 #include "gvfsbackendarchive.h"
32 #include "gvfsjobopenforread.h"
33 #include "gvfsjobread.h"
34 #include "gvfsjobseekread.h"
35 #include "gvfsjobopenforwrite.h"
36 #include "gvfsjobwrite.h"
37 #include "gvfsjobseekwrite.h"
38 #include "gvfsjobsetdisplayname.h"
39 #include "gvfsjobqueryinfo.h"
40 #include "gvfsjobqueryfsinfo.h"
41 #include "gvfsjobqueryattributes.h"
42 #include "gvfsjobenumerate.h"
43 #include "gvfsdaemonprotocol.h"
44 #include "gvfsdaemonutils.h"
45 #include "gvfskeyring.h"
46
47 #define MOUNT_ICON_NAME "drive-removable-media"
48 #define MOUNT_SYMBOLIC_ICON_NAME "drive-removable-media-symbolic"
49
50 /*** TYPE DEFINITIONS ***/
51
52 typedef struct _ArchiveFile ArchiveFile;
53 struct _ArchiveFile {
54 char * name; /* name of the file inside the archive */
55 GFileInfo * info; /* file info created from archive_entry */
56 GSList * children; /* (unordered) list of child files */
57 };
58
59 struct _GVfsBackendArchive
60 {
61 GVfsBackend backend;
62
63 GFile * file;
64 ArchiveFile * files; /* the tree of files */
65 gsize size;
66 };
67
68 G_DEFINE_TYPE (GVfsBackendArchive, g_vfs_backend_archive, G_VFS_TYPE_BACKEND)
69
70 static void backend_unmount (GVfsBackendArchive *ba);
71
72 /*** AN ARCHIVE WE CAN OPERATE ON ***/
73
74 typedef struct {
75 struct archive * archive;
76 GFile * file;
77 GFileInputStream *stream;
78 GVfsJob * job;
79 GVfsBackendArchive *backend;
80 GError * error;
81 guchar data[4096];
82 } GVfsArchive;
83
84 #define gvfs_archive_return(d) ((d)->error ? ARCHIVE_FATAL : ARCHIVE_OK)
85
86 static int
gvfs_archive_open(struct archive * archive,void * data)87 gvfs_archive_open (struct archive *archive,
88 void *data)
89 {
90 GVfsArchive *d = data;
91
92 g_debug ("OPEN\n");
93 g_assert (d->stream == NULL);
94 d->stream = g_file_read (d->file,
95 d->job->cancellable,
96 &d->error);
97 return gvfs_archive_return (d);
98 }
99
100 static ssize_t
gvfs_archive_read(struct archive * archive,void * data,const void ** buffer)101 gvfs_archive_read (struct archive *archive,
102 void *data,
103 const void **buffer)
104 {
105 GVfsArchive *d = data;
106 gssize read_bytes;
107
108 *buffer = d->data;
109 read_bytes = g_input_stream_read (G_INPUT_STREAM (d->stream),
110 d->data,
111 sizeof (d->data),
112 d->job->cancellable,
113 &d->error);
114
115 g_debug ("READ %d\n", (int) read_bytes);
116 return read_bytes;
117 }
118
119 static int64_t
gvfs_archive_skip(struct archive * archive,void * data,int64_t request)120 gvfs_archive_skip (struct archive *archive,
121 void *data,
122 int64_t request)
123 {
124 GVfsArchive *d = data;
125
126 if (g_seekable_can_seek (G_SEEKABLE (d->stream)))
127 g_seekable_seek (G_SEEKABLE (d->stream),
128 request,
129 G_SEEK_CUR,
130 d->job->cancellable,
131 &d->error);
132 else
133 return 0;
134
135 if (d->error)
136 {
137 g_clear_error (&d->error);
138 request = 0;
139 }
140 g_debug ("SEEK %d (%d)\n", (int) request,
141 (int) g_seekable_tell (G_SEEKABLE (d->stream)));
142
143 return request;
144 }
145
146 static int
gvfs_archive_close(struct archive * archive,void * data)147 gvfs_archive_close (struct archive *archive,
148 void *data)
149 {
150 GVfsArchive *d = data;
151
152 g_debug ("CLOSE\n");
153 if (!d->stream)
154 g_vfs_backend_force_unmount (G_VFS_BACKEND (d->backend));
155 g_clear_object (&d->stream);
156 return ARCHIVE_OK;
157 }
158
159 #define gvfs_archive_in_error(archive) ((archive)->error != NULL)
160
161 static void
gvfs_archive_set_error_from_errno(GVfsArchive * archive)162 gvfs_archive_set_error_from_errno (GVfsArchive *archive)
163 {
164 if (gvfs_archive_in_error (archive))
165 return;
166
167 g_set_error_literal (&archive->error,
168 G_IO_ERROR,
169 g_io_error_from_errno (archive_errno (archive->archive)),
170 archive_error_string (archive->archive));
171 }
172
173 static void
gvfs_archive_push_job(GVfsArchive * archive,GVfsJob * job)174 gvfs_archive_push_job (GVfsArchive *archive, GVfsJob *job)
175 {
176 archive->job = job;
177 }
178
179 static void
gvfs_archive_pop_job(GVfsArchive * archive)180 gvfs_archive_pop_job (GVfsArchive *archive)
181 {
182 if (archive->job == NULL)
183 return;
184
185 g_debug ("popping job %s\n", G_OBJECT_TYPE_NAME (archive->job));
186 if (archive->error)
187 {
188 g_vfs_job_failed_from_error (archive->job, archive->error);
189 g_clear_error (&archive->error);
190 }
191 else
192 g_vfs_job_succeeded (archive->job);
193
194
195 archive->job = NULL;
196 }
197
198 static void
gvfs_archive_finish(GVfsArchive * archive)199 gvfs_archive_finish (GVfsArchive *archive)
200 {
201 gvfs_archive_pop_job (archive);
202
203 g_object_unref (archive->backend);
204 archive_read_free (archive->archive);
205 g_slice_free (GVfsArchive, archive);
206 }
207
208 /* NB: assumes an GVfsArchive initialized with ARCHIVE_DATA_INIT */
209 static GVfsArchive *
gvfs_archive_new(GVfsBackendArchive * ba,GVfsJob * job)210 gvfs_archive_new (GVfsBackendArchive *ba, GVfsJob *job)
211 {
212 GVfsArchive *d;
213
214 d = g_slice_new0 (GVfsArchive);
215
216 d->backend = g_object_ref (ba);
217 d->file = ba->file;
218 gvfs_archive_push_job (d, job);
219
220 d->archive = archive_read_new ();
221 archive_read_support_filter_all (d->archive);
222 archive_read_support_format_all (d->archive);
223 archive_read_open2 (d->archive,
224 d,
225 gvfs_archive_open,
226 gvfs_archive_read,
227 gvfs_archive_skip,
228 gvfs_archive_close);
229
230 return d;
231 }
232
233 /*** BACKEND ***/
234
235 static void
g_vfs_backend_archive_finalize(GObject * object)236 g_vfs_backend_archive_finalize (GObject *object)
237 {
238 GVfsBackendArchive *archive = G_VFS_BACKEND_ARCHIVE (object);
239
240 backend_unmount (archive);
241
242 if (G_OBJECT_CLASS (g_vfs_backend_archive_parent_class)->finalize)
243 (*G_OBJECT_CLASS (g_vfs_backend_archive_parent_class)->finalize) (object);
244 }
245
246 static void
g_vfs_backend_archive_init(GVfsBackendArchive * archive)247 g_vfs_backend_archive_init (GVfsBackendArchive *archive)
248 {
249 }
250
251 /*** FILE TREE HANDLING ***/
252 static char *
fixup_path(const char * path)253 fixup_path (const char *path)
254 {
255 char *str, *ptr;
256 int len;
257
258 /* skip leading garbage if present */
259 if (g_str_has_prefix (path, "./"))
260 str = g_strdup (path + 2);
261 else
262 str = g_strdup (path);
263
264 /* strip '/./' from the path */
265 ptr = str;
266 while ((ptr = strstr (ptr, "/./")))
267 {
268 char *dst = ptr + 2;
269 while (*dst)
270 *++ptr = *++dst;
271 }
272
273 /* strip '//' from the path */
274 ptr = str;
275 while ((ptr = strstr (ptr, "//")))
276 {
277 char *dst = ptr + 1;
278 while (*dst)
279 *++ptr = *++dst;
280 }
281
282 /* strip trailing slash from the path */
283 len = strlen (str);
284 if (len > 0 && str[len - 1] == '/')
285 str[len - 1] = '\0';
286
287 return str;
288 }
289
290 /* Filename must be a clean path containing no '.' entries, no empty entries
291 * and must not start with a '/'. */
292 static ArchiveFile *
archive_file_get_from_path(ArchiveFile * file,const char * filename,gboolean add)293 archive_file_get_from_path (ArchiveFile *file, const char *filename, gboolean add)
294 {
295 char **names;
296 ArchiveFile *cur;
297 GSList *walk;
298 guint i;
299
300 names = g_strsplit (filename, "/", -1);
301
302 g_debug ("%s %s\n", add ? "add" : "find", filename);
303 for (i = 0; file && names[i] != NULL; i++)
304 {
305 cur = NULL;
306 for (walk = file->children; walk; walk = walk->next)
307 {
308 cur = walk->data;
309 if (g_str_equal (cur->name, names[i]))
310 break;
311 cur = NULL;
312 }
313 if (cur == NULL && add != FALSE)
314 {
315 g_debug ("adding node %s to %s\n", names[i], file->name);
316 cur = g_slice_new0 (ArchiveFile);
317 cur->name = g_strdup (names[i]);
318 file->children = g_slist_prepend (file->children, cur);
319 }
320 file = cur;
321 }
322 g_strfreev (names);
323 return file;
324 }
325 #define archive_file_find(ba, filename) archive_file_get_from_path((ba)->files, (filename) + 1, FALSE)
326
327 static void
create_root_file(GVfsBackendArchive * ba)328 create_root_file (GVfsBackendArchive *ba)
329 {
330 ArchiveFile *root;
331 GFileInfo *info;
332 const char *content_type = "inode/directory";
333 char *s, *display_name;
334 GIcon *icon;
335
336 root = g_slice_new0 (ArchiveFile);
337 root->name = g_strdup ("/");
338 ba->files = root;
339
340 info = g_file_info_new ();
341 root->info = info;
342
343 g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
344
345 g_file_info_set_name (info, "/");
346 s = g_file_get_basename (ba->file);
347
348 /* Translators: This is the name of the root in a mounted archive file,
349 e.g. "/ in archive.tar.gz" for a file with the name "archive.tar.gz" */
350 display_name = g_strdup_printf (_("/ in %s"), s);
351 g_free (s);
352 g_file_info_set_display_name (info, display_name);
353 g_free (display_name);
354 g_file_info_set_edit_name (info, "/");
355
356 g_file_info_set_content_type (info, content_type);
357 g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE, content_type);
358
359 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, TRUE);
360 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, FALSE);
361 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, FALSE);
362 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, TRUE);
363 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
364 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, FALSE);
365
366 icon = g_content_type_get_icon (content_type);
367 g_file_info_set_icon (info, icon);
368 g_object_unref (icon);
369 icon = g_content_type_get_symbolic_icon (content_type);
370 g_file_info_set_symbolic_icon (info, icon);
371 g_object_unref (icon);
372 }
373
374 /* Read archive entry data blocks to determine file size */
375 static int64_t
archive_entry_determine_size(GVfsArchive * archive,struct archive_entry * entry)376 archive_entry_determine_size (GVfsArchive *archive,
377 struct archive_entry *entry)
378 {
379 size_t read;
380 int result;
381 const void *block;
382 int64_t offset, size = 0;
383
384 do
385 {
386 result = archive_read_data_block (archive->archive, &block, &read, &offset);
387 if (result >= ARCHIVE_FAILED && result <= ARCHIVE_OK)
388 {
389 if (result < ARCHIVE_OK) {
390 g_debug ("archive_read_data_block: result = %d, error = '%s'\n", result, archive_error_string (archive->archive));
391 archive_set_error (archive->archive, ARCHIVE_OK, "No error");
392 archive_clear_error (archive->archive);
393 if (result == ARCHIVE_RETRY)
394 continue;
395
396 /* We don't want to fail the mount job, just because of unknown file
397 * size (e.g. caused by unsupported archive encryption). */
398 if (result < ARCHIVE_WARN)
399 {
400 size = -1;
401 break;
402 }
403 }
404
405 size += read;
406 }
407 }
408 while (result >= ARCHIVE_FAILED && result != ARCHIVE_EOF);
409
410 if (result == ARCHIVE_FATAL)
411 gvfs_archive_set_error_from_errno (archive);
412
413 return size;
414 }
415
416 static void
archive_file_set_info_from_entry(GVfsArchive * archive,ArchiveFile * file,struct archive_entry * entry,guint64 entry_index)417 archive_file_set_info_from_entry (GVfsArchive * archive,
418 ArchiveFile * file,
419 struct archive_entry *entry,
420 guint64 entry_index)
421 {
422 GFileInfo *info = g_file_info_new ();
423 GFileType type;
424 mode_t mode;
425 int64_t size;
426 file->info = info;
427
428 g_debug ("setting up %s (%s)\n", archive_entry_pathname (entry), file->name);
429
430 g_file_info_set_attribute_uint64 (info,
431 G_FILE_ATTRIBUTE_TIME_ACCESS,
432 archive_entry_atime (entry));
433 g_file_info_set_attribute_uint32 (info,
434 G_FILE_ATTRIBUTE_TIME_ACCESS_USEC,
435 archive_entry_atime_nsec (entry) / 1000);
436 g_file_info_set_attribute_uint64 (info,
437 G_FILE_ATTRIBUTE_TIME_CHANGED,
438 archive_entry_ctime (entry));
439 g_file_info_set_attribute_uint32 (info,
440 G_FILE_ATTRIBUTE_TIME_CHANGED_USEC,
441 archive_entry_ctime_nsec (entry) / 1000);
442 g_file_info_set_attribute_uint64 (info,
443 G_FILE_ATTRIBUTE_TIME_MODIFIED,
444 archive_entry_mtime (entry));
445 g_file_info_set_attribute_uint32 (info,
446 G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC,
447 archive_entry_mtime_nsec (entry) / 1000);
448
449 switch (archive_entry_filetype (entry))
450 {
451 case AE_IFREG:
452 type = G_FILE_TYPE_REGULAR;
453 break;
454 case AE_IFLNK:
455 g_file_info_set_symlink_target (info,
456 archive_entry_symlink (entry));
457 type = G_FILE_TYPE_SYMBOLIC_LINK;
458 break;
459 case AE_IFDIR:
460 type = G_FILE_TYPE_DIRECTORY;
461 break;
462 case AE_IFCHR:
463 case AE_IFBLK:
464 case AE_IFIFO:
465 type = G_FILE_TYPE_SPECIAL;
466 break;
467 default:
468 g_warning ("unknown file type %u", archive_entry_filetype (entry));
469 type = G_FILE_TYPE_SPECIAL;
470 break;
471 }
472 g_file_info_set_name (info, file->name);
473 gvfs_file_info_populate_default (info,
474 file->name,
475 type);
476
477 if (archive_entry_size_is_set (entry))
478 {
479 size = archive_entry_size (entry);
480 }
481 else
482 {
483 size = archive_entry_determine_size (archive, entry);
484 }
485
486 if (size >= 0)
487 g_file_info_set_size (info, size);
488
489 if (file->name[0] == '.')
490 g_file_info_set_is_hidden (info, TRUE);
491
492 mode = archive_entry_perm (entry);
493 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, TRUE);
494 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, FALSE);
495 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, FALSE);
496 g_file_info_set_attribute_boolean (info,
497 G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE,
498 type == G_FILE_TYPE_DIRECTORY || mode & S_IXUSR);
499 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
500 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, FALSE);
501
502 /* Set inode number to reflect absolute position in the archive. */
503 g_file_info_set_attribute_uint64 (info,
504 G_FILE_ATTRIBUTE_UNIX_INODE,
505 entry_index);
506
507
508 /* FIXME: add info for these
509 dev_t archive_entry_dev(struct archive_entry *);
510 dev_t archive_entry_devmajor(struct archive_entry *);
511 dev_t archive_entry_devminor(struct archive_entry *);
512 void archive_entry_fflags(struct archive_entry *,
513 unsigned long *set, unsigned long *clear);
514 const char *archive_entry_fflags_text(struct archive_entry *);
515 gid_t archive_entry_gid(struct archive_entry *);
516 const char *archive_entry_gname(struct archive_entry *);
517 const char *archive_entry_hardlink(struct archive_entry *);
518 unsigned int archive_entry_nlink(struct archive_entry *);
519 dev_t archive_entry_rdev(struct archive_entry *);
520 dev_t archive_entry_rdevmajor(struct archive_entry *);
521 dev_t archive_entry_rdevminor(struct archive_entry *);
522 uid_t archive_entry_uid(struct archive_entry *);
523 const char *archive_entry_uname(struct archive_entry *);
524 */
525
526 /* FIXME: do ACLs */
527 }
528
529 static void
fixup_dirs(ArchiveFile * file)530 fixup_dirs (ArchiveFile *file)
531 {
532 GSList *l;
533
534 if (file->info == NULL)
535 {
536 GFileInfo *info = g_file_info_new ();
537
538 file->info = info;
539 g_file_info_set_name (info, file->name);
540 gvfs_file_info_populate_default (info,
541 file->name,
542 G_FILE_TYPE_DIRECTORY);
543 }
544
545 for (l = file->children; l != NULL; l = l->next)
546 fixup_dirs (l->data);
547 }
548
549 static void
create_file_tree(GVfsBackendArchive * ba,GVfsJob * job)550 create_file_tree (GVfsBackendArchive *ba, GVfsJob *job)
551 {
552 GVfsArchive *archive;
553 struct archive_entry *entry;
554 int result;
555 guint64 entry_index = 0;
556
557 archive = gvfs_archive_new (ba, job);
558
559 g_assert (ba->files != NULL);
560
561 do
562 {
563 result = archive_read_next_header (archive->archive, &entry);
564 if (result >= ARCHIVE_WARN && result <= ARCHIVE_OK)
565 {
566 ArchiveFile *file;
567 char *path;
568
569 if (result < ARCHIVE_OK) {
570 g_debug ("archive_read_next_header: result = %d, error = '%s'\n", result, archive_error_string (archive->archive));
571 archive_set_error (archive->archive, ARCHIVE_OK, "No error");
572 archive_clear_error (archive->archive);
573 if (result == ARCHIVE_RETRY)
574 continue;
575 }
576
577 path = fixup_path (archive_entry_pathname (entry));
578 file = archive_file_get_from_path (ba->files, path, TRUE);
579 g_free (path);
580 /* Don't set info for root */
581 if (file != ba->files)
582 {
583 archive_file_set_info_from_entry (archive, file, entry, entry_index);
584 ba->size += g_file_info_get_size (file->info);
585 }
586 archive_read_data_skip (archive->archive);
587 entry_index++;
588 }
589 }
590 while (result >= ARCHIVE_WARN && result != ARCHIVE_EOF && !gvfs_archive_in_error (archive));
591
592 if (result < ARCHIVE_WARN)
593 gvfs_archive_set_error_from_errno (archive);
594 fixup_dirs (ba->files);
595
596 gvfs_archive_finish (archive);
597 }
598
599 static void
archive_file_free(ArchiveFile * file)600 archive_file_free (ArchiveFile *file)
601 {
602 g_slist_free_full (file->children, (GDestroyNotify) archive_file_free);
603 if (file->info)
604 g_object_unref (file->info);
605 g_free (file->name);
606 }
607
608 static void
do_mount(GVfsBackend * backend,GVfsJobMount * job,GMountSpec * mount_spec,GMountSource * mount_source,gboolean is_automount)609 do_mount (GVfsBackend *backend,
610 GVfsJobMount *job,
611 GMountSpec *mount_spec,
612 GMountSource *mount_source,
613 gboolean is_automount)
614 {
615 GVfsBackendArchive *archive = G_VFS_BACKEND_ARCHIVE (backend);
616 const char *host, *file;
617 GFileInfo *info;
618 char *filename, *s;
619 GError *error = NULL;
620
621 host = g_mount_spec_get (mount_spec, "host");
622 file = g_mount_spec_get (mount_spec, "file");
623 if (host == NULL &&
624 file == NULL)
625 {
626 g_vfs_job_failed (G_VFS_JOB (job),
627 G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
628 _("No hostname specified"));
629 return;
630 }
631
632 if (host != NULL)
633 {
634 filename = g_uri_unescape_string (host, NULL);
635 if (filename == NULL)
636 {
637 g_vfs_job_failed (G_VFS_JOB (job),
638 G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
639 _("Invalid mount spec"));
640 return;
641 }
642
643 archive->file = g_file_new_for_commandline_arg (filename);
644 g_free (filename);
645 }
646 else
647 archive->file = g_file_new_for_commandline_arg (file);
648
649 g_debug ("Trying to mount %s\n", g_file_get_uri (archive->file));
650
651 info = g_file_query_info (archive->file,
652 "*",
653 G_FILE_QUERY_INFO_NONE,
654 G_VFS_JOB (job)->cancellable,
655 &error);
656 if (info == NULL)
657 {
658 g_vfs_job_failed_from_error (G_VFS_JOB (job),
659 error);
660 g_error_free (error);
661 return;
662 }
663
664 if (g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR)
665 {
666 g_vfs_job_failed (G_VFS_JOB (job),
667 G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
668 _("Invalid mount spec"));
669 return;
670 }
671
672 /* FIXME: check if this file is an archive */
673
674 filename = g_file_get_uri (archive->file);
675 g_debug ("mounted %s\n", filename);
676 s = g_uri_escape_string (filename, NULL, FALSE);
677 g_free (filename);
678 mount_spec = g_mount_spec_new ("archive");
679 g_mount_spec_set (mount_spec, "host", s);
680 g_free (s);
681 g_vfs_backend_set_mount_spec (backend, mount_spec);
682 g_mount_spec_unref (mount_spec);
683
684 g_vfs_backend_set_display_name (backend, g_file_info_get_display_name (info));
685
686 g_vfs_backend_set_icon_name (backend, MOUNT_ICON_NAME);
687 g_vfs_backend_set_symbolic_icon_name (backend, MOUNT_SYMBOLIC_ICON_NAME);
688
689 create_root_file (archive);
690 create_file_tree (archive, G_VFS_JOB (job));
691 g_object_unref (info);
692 }
693
694 static void
backend_unmount(GVfsBackendArchive * ba)695 backend_unmount (GVfsBackendArchive *ba)
696 {
697 if (ba->file)
698 {
699 g_object_unref (ba->file);
700 ba->file = NULL;
701 }
702 if (ba->files)
703 {
704 archive_file_free (ba->files);
705 ba->files = NULL;
706 }
707 }
708
709 static void
do_unmount(GVfsBackend * backend,GVfsJobUnmount * job,GMountUnmountFlags flags,GMountSource * mount_source)710 do_unmount (GVfsBackend *backend,
711 GVfsJobUnmount *job,
712 GMountUnmountFlags flags,
713 GMountSource *mount_source)
714 {
715 backend_unmount (G_VFS_BACKEND_ARCHIVE (backend));
716
717 g_vfs_job_succeeded (G_VFS_JOB (job));
718 }
719
720 static void
do_open_for_read(GVfsBackend * backend,GVfsJobOpenForRead * job,const char * filename)721 do_open_for_read (GVfsBackend * backend,
722 GVfsJobOpenForRead *job,
723 const char * filename)
724 {
725 GVfsBackendArchive *ba = G_VFS_BACKEND_ARCHIVE (backend);
726 GVfsArchive *archive;
727 struct archive_entry *entry;
728 int result;
729 ArchiveFile *file;
730 char *entry_pathname;
731
732 file = archive_file_find (ba, filename);
733 if (file == NULL)
734 {
735 g_vfs_job_failed (G_VFS_JOB (job),
736 G_IO_ERROR,
737 G_IO_ERROR_NOT_FOUND,
738 _("File doesn’t exist"));
739 return;
740 }
741
742 if (g_file_info_get_file_type (file->info) == G_FILE_TYPE_DIRECTORY)
743 {
744 g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
745 G_IO_ERROR_IS_DIRECTORY,
746 _("Can’t open directory"));
747 return;
748 }
749
750 archive = gvfs_archive_new (ba, G_VFS_JOB (job));
751
752 do
753 {
754 result = archive_read_next_header (archive->archive, &entry);
755 if (result >= ARCHIVE_WARN && result <= ARCHIVE_OK)
756 {
757 if (result < ARCHIVE_OK) {
758 g_debug ("do_open_for_read: result = %d, error = '%s'\n", result, archive_error_string (archive->archive));
759 archive_set_error (archive->archive, ARCHIVE_OK, "No error");
760 archive_clear_error (archive->archive);
761 if (result == ARCHIVE_RETRY)
762 continue;
763 }
764
765 entry_pathname = fixup_path (archive_entry_pathname (entry));
766
767 if (g_str_equal (entry_pathname, filename + 1))
768 {
769 g_free (entry_pathname);
770
771 /* SUCCESS */
772 g_vfs_job_open_for_read_set_handle (job, archive);
773 g_vfs_job_open_for_read_set_can_seek (job, FALSE);
774 gvfs_archive_pop_job (archive);
775 return;
776 }
777 else
778 archive_read_data_skip (archive->archive);
779
780 g_free (entry_pathname);
781 }
782 }
783 while (result >= ARCHIVE_WARN && result != ARCHIVE_EOF);
784
785 if (result < ARCHIVE_WARN)
786 gvfs_archive_set_error_from_errno (archive);
787
788 if (!gvfs_archive_in_error (archive))
789 {
790 g_set_error_literal (&archive->error,
791 G_IO_ERROR,
792 G_IO_ERROR_NOT_FOUND,
793 _("File doesn’t exist"));
794 }
795 gvfs_archive_finish (archive);
796 }
797
798 static void
do_close_read(GVfsBackend * backend,GVfsJobCloseRead * job,GVfsBackendHandle handle)799 do_close_read (GVfsBackend *backend,
800 GVfsJobCloseRead *job,
801 GVfsBackendHandle handle)
802 {
803 GVfsArchive *archive = handle;
804
805 gvfs_archive_push_job (archive, G_VFS_JOB (job));
806 gvfs_archive_finish (archive);
807 }
808
809 static void
do_read(GVfsBackend * backend,GVfsJobRead * job,GVfsBackendHandle handle,char * buffer,gsize bytes_requested)810 do_read (GVfsBackend *backend,
811 GVfsJobRead *job,
812 GVfsBackendHandle handle,
813 char *buffer,
814 gsize bytes_requested)
815 {
816 GVfsArchive *archive = handle;
817 gssize bytes_read;
818
819 gvfs_archive_push_job (archive, G_VFS_JOB (job));
820 bytes_read = archive_read_data (archive->archive, buffer, bytes_requested);
821 if (bytes_read >= 0)
822 g_vfs_job_read_set_size (job, bytes_read);
823 else
824 gvfs_archive_set_error_from_errno (archive);
825 gvfs_archive_pop_job (archive);
826 }
827
828 static void
do_query_info(GVfsBackend * backend,GVfsJobQueryInfo * job,const char * filename,GFileQueryInfoFlags flags,GFileInfo * info,GFileAttributeMatcher * attribute_matcher)829 do_query_info (GVfsBackend *backend,
830 GVfsJobQueryInfo *job,
831 const char *filename,
832 GFileQueryInfoFlags flags,
833 GFileInfo *info,
834 GFileAttributeMatcher *attribute_matcher)
835 {
836 GVfsBackendArchive *ba = G_VFS_BACKEND_ARCHIVE (backend);
837 ArchiveFile *file;
838
839 file = archive_file_find (ba, filename);
840 if (file == NULL)
841 {
842 g_vfs_job_failed (G_VFS_JOB (job),
843 G_IO_ERROR,
844 G_IO_ERROR_NOT_FOUND,
845 _("File doesn’t exist"));
846 return;
847 }
848
849 if (!(flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS))
850 g_warning ("FIXME: follow symlinks");
851
852 g_file_info_copy_into (file->info, info);
853
854 g_vfs_job_succeeded (G_VFS_JOB (job));
855 }
856
857 static void
do_enumerate(GVfsBackend * backend,GVfsJobEnumerate * job,const char * filename,GFileAttributeMatcher * attribute_matcher,GFileQueryInfoFlags flags)858 do_enumerate (GVfsBackend *backend,
859 GVfsJobEnumerate *job,
860 const char *filename,
861 GFileAttributeMatcher *attribute_matcher,
862 GFileQueryInfoFlags flags)
863 {
864 GVfsBackendArchive *ba = G_VFS_BACKEND_ARCHIVE (backend);
865 ArchiveFile *file;
866 GSList *walk;
867
868 file = archive_file_find (ba, filename);
869 if (file == NULL)
870 {
871 g_vfs_job_failed (G_VFS_JOB (job),
872 G_IO_ERROR,
873 G_IO_ERROR_NOT_FOUND,
874 _("File doesn’t exist"));
875 return;
876 }
877
878 if (g_file_info_get_file_type (file->info) != G_FILE_TYPE_DIRECTORY)
879 {
880 g_vfs_job_failed (G_VFS_JOB (job),
881 G_IO_ERROR,
882 G_IO_ERROR_NOT_DIRECTORY,
883 _("The file is not a directory"));
884 return;
885 }
886
887 if (!(flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS))
888 g_warning ("FIXME: follow symlinks");
889
890 for (walk = file->children; walk; walk = walk->next)
891 {
892 GFileInfo *info = g_file_info_dup (((ArchiveFile *) walk->data)->info);
893 g_vfs_job_enumerate_add_info (job, info);
894 g_object_unref (info);
895 }
896 g_vfs_job_enumerate_done (job);
897
898 g_vfs_job_succeeded (G_VFS_JOB (job));
899 }
900
901 static gboolean
try_query_fs_info(GVfsBackend * backend,GVfsJobQueryFsInfo * job,const char * filename,GFileInfo * info,GFileAttributeMatcher * attribute_matcher)902 try_query_fs_info (GVfsBackend *backend,
903 GVfsJobQueryFsInfo *job,
904 const char *filename,
905 GFileInfo *info,
906 GFileAttributeMatcher *attribute_matcher)
907 {
908 GVfsBackendArchive *ba = G_VFS_BACKEND_ARCHIVE (backend);
909
910 g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "archive");
911 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, FALSE);
912 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, TRUE);
913 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL);
914 g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, ba->size);
915 g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, 0);
916 g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USED, ba->size);
917 g_vfs_job_succeeded (G_VFS_JOB (job));
918 return TRUE;
919 }
920
921 static void
g_vfs_backend_archive_class_init(GVfsBackendArchiveClass * klass)922 g_vfs_backend_archive_class_init (GVfsBackendArchiveClass *klass)
923 {
924 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
925 GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass);
926
927 gobject_class->finalize = g_vfs_backend_archive_finalize;
928
929 backend_class->mount = do_mount;
930 backend_class->unmount = do_unmount;
931 backend_class->open_for_read = do_open_for_read;
932 backend_class->close_read = do_close_read;
933 backend_class->read = do_read;
934 backend_class->enumerate = do_enumerate;
935 backend_class->query_info = do_query_info;
936 backend_class->try_query_fs_info = try_query_fs_info;
937 }
938