~ubuntu-branches/ubuntu/vivid/brasero/vivid

« back to all changes in this revision

Viewing changes to .pc/git_burn_libisofs.patch/plugins/libburnia/burn-libisofs.c

  • Committer: Package Import Robot
  • Author(s): Ayan George, on-the-fly mode
  • Date: 2012-10-12 10:15:07 UTC
  • Revision ID: package-import@ubuntu.com-20121012101507-xm4tibzuauit7bre
Tags: 3.4.1-0ubuntu3
* debian/patches/git_burn_libisofs.patch:
  - Fix for "Brasero finishes without error but unusable media
    [on-the-fly mode] (Ubuntu 11.04 ->12.04)" (LP: #780117)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
 
2
/*
 
3
 * Libbrasero-burn
 
4
 * Copyright (C) Philippe Rouquier 2005-2009 <bonfire-app@wanadoo.fr>
 
5
 *
 
6
 * Libbrasero-burn is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * The Libbrasero-burn authors hereby grant permission for non-GPL compatible
 
12
 * GStreamer plugins to be used and distributed together with GStreamer
 
13
 * and Libbrasero-burn. This permission is above and beyond the permissions granted
 
14
 * by the GPL license by which Libbrasero-burn is covered. If you modify this code
 
15
 * you may extend this exception to your version of the code, but you are not
 
16
 * obligated to do so. If you do not wish to do so, delete this exception
 
17
 * statement from your version.
 
18
 * 
 
19
 * Libbrasero-burn is distributed in the hope that it will be useful,
 
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
22
 * GNU Library General Public License for more details.
 
23
 * 
 
24
 * You should have received a copy of the GNU General Public License
 
25
 * along with this program; if not, write to:
 
26
 *      The Free Software Foundation, Inc.,
 
27
 *      51 Franklin Street, Fifth Floor
 
28
 *      Boston, MA  02110-1301, USA.
 
29
 */
 
30
 
 
31
#ifdef HAVE_CONFIG_H
 
32
#  include <config.h>
 
33
#endif
 
34
 
 
35
#include <string.h>
 
36
#include <stdio.h>
 
37
#include <errno.h>
 
38
#include <stdlib.h>
 
39
#include <unistd.h>
 
40
 
 
41
#include <glib.h>
 
42
#include <glib-object.h>
 
43
#include <glib/gstdio.h>
 
44
#include <glib/gi18n-lib.h>
 
45
#include <gmodule.h>
 
46
 
 
47
#include <libisofs/libisofs.h>
 
48
#include <libburn/libburn.h>
 
49
 
 
50
#include "burn-libburnia.h"
 
51
#include "burn-job.h"
 
52
#include "brasero-units.h"
 
53
#include "brasero-plugin-registration.h"
 
54
#include "burn-libburn-common.h"
 
55
#include "brasero-track-data.h"
 
56
#include "brasero-track-image.h"
 
57
 
 
58
 
 
59
#define BRASERO_TYPE_LIBISOFS         (brasero_libisofs_get_type ())
 
60
#define BRASERO_LIBISOFS(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), BRASERO_TYPE_LIBISOFS, BraseroLibisofs))
 
61
#define BRASERO_LIBISOFS_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), BRASERO_TYPE_LIBISOFS, BraseroLibisofsClass))
 
62
#define BRASERO_IS_LIBISOFS(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), BRASERO_TYPE_LIBISOFS))
 
63
#define BRASERO_IS_LIBISOFS_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), BRASERO_TYPE_LIBISOFS))
 
64
#define BRASERO_LIBISOFS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), BRASERO_TYPE_LIBISOFS, BraseroLibisofsClass))
 
65
 
 
66
BRASERO_PLUGIN_BOILERPLATE (BraseroLibisofs, brasero_libisofs, BRASERO_TYPE_JOB, BraseroJob);
 
67
 
 
68
struct _BraseroLibisofsPrivate {
 
69
        struct burn_source *libburn_src;
 
70
 
 
71
        /* that's for multisession */
 
72
        BraseroLibburnCtx *ctx;
 
73
 
 
74
        GError *error;
 
75
        GThread *thread;
 
76
        GMutex *mutex;
 
77
        GCond *cond;
 
78
        guint thread_id;
 
79
 
 
80
        guint cancel:1;
 
81
};
 
82
typedef struct _BraseroLibisofsPrivate BraseroLibisofsPrivate;
 
83
 
 
84
#define BRASERO_LIBISOFS_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), BRASERO_TYPE_LIBISOFS, BraseroLibisofsPrivate))
 
85
 
 
86
static GObjectClass *parent_class = NULL;
 
87
 
 
88
static gboolean
 
89
brasero_libisofs_thread_finished (gpointer data)
 
90
{
 
91
        BraseroLibisofs *self = data;
 
92
        BraseroLibisofsPrivate *priv;
 
93
 
 
94
        priv = BRASERO_LIBISOFS_PRIVATE (self);
 
95
 
 
96
        priv->thread_id = 0;
 
97
        if (priv->error) {
 
98
                GError *error;
 
99
 
 
100
                error = priv->error;
 
101
                priv->error = NULL;
 
102
                brasero_job_error (BRASERO_JOB (self), error);
 
103
                return FALSE;
 
104
        }
 
105
 
 
106
        if (brasero_job_get_fd_out (BRASERO_JOB (self), NULL) != BRASERO_BURN_OK) {
 
107
                BraseroTrackImage *track = NULL;
 
108
                gchar *output = NULL;
 
109
                goffset blocks = 0;
 
110
 
 
111
                /* Let's make a track */
 
112
                track = brasero_track_image_new ();
 
113
                brasero_job_get_image_output (BRASERO_JOB (self),
 
114
                                              &output,
 
115
                                              NULL);
 
116
                brasero_track_image_set_source (track,
 
117
                                                output,
 
118
                                                NULL,
 
119
                                                BRASERO_IMAGE_FORMAT_BIN);
 
120
 
 
121
                brasero_job_get_session_output_size (BRASERO_JOB (self), &blocks, NULL);
 
122
                brasero_track_image_set_block_num (track, blocks);
 
123
 
 
124
                brasero_job_add_track (BRASERO_JOB (self), BRASERO_TRACK (track));
 
125
                g_object_unref (track);
 
126
        }
 
127
 
 
128
        brasero_job_finished_track (BRASERO_JOB (self));
 
129
        return FALSE;
 
130
}
 
131
 
 
132
static BraseroBurnResult
 
133
brasero_libisofs_write_sector_to_fd (BraseroLibisofs *self,
 
134
                                     int fd,
 
135
                                     gpointer buffer,
 
136
                                     gint bytes_remaining)
 
137
{
 
138
        gint bytes_written = 0;
 
139
        BraseroLibisofsPrivate *priv;
 
140
 
 
141
        priv = BRASERO_LIBISOFS_PRIVATE (self);
 
142
 
 
143
        while (bytes_remaining) {
 
144
                gint written;
 
145
 
 
146
                written = write (fd,
 
147
                                 ((gchar *) buffer) + bytes_written,
 
148
                                 bytes_remaining);
 
149
 
 
150
                if (priv->cancel)
 
151
                        break;
 
152
 
 
153
                if (written != bytes_remaining) {
 
154
                        if (errno != EINTR && errno != EAGAIN) {
 
155
                                int errsv = errno;
 
156
 
 
157
                                /* unrecoverable error */
 
158
                                priv->error = g_error_new (BRASERO_BURN_ERROR,
 
159
                                                           BRASERO_BURN_ERROR_GENERAL,
 
160
                                                           _("Data could not be written (%s)"),
 
161
                                                           g_strerror (errsv));
 
162
                                return BRASERO_BURN_ERR;
 
163
                        }
 
164
 
 
165
                        g_thread_yield ();
 
166
                }
 
167
 
 
168
                if (written > 0) {
 
169
                        bytes_remaining -= written;
 
170
                        bytes_written += written;
 
171
                }
 
172
        }
 
173
 
 
174
        return BRASERO_BURN_OK;
 
175
}
 
176
 
 
177
static void
 
178
brasero_libisofs_write_image_to_fd_thread (BraseroLibisofs *self)
 
179
{
 
180
        const gint sector_size = 2048;
 
181
        BraseroLibisofsPrivate *priv;
 
182
        gint64 written_sectors = 0;
 
183
        BraseroBurnResult result;
 
184
        guchar buf [sector_size];
 
185
        int read_bytes;
 
186
        int fd = -1;
 
187
 
 
188
        priv = BRASERO_LIBISOFS_PRIVATE (self);
 
189
 
 
190
        brasero_job_set_nonblocking (BRASERO_JOB (self), NULL);
 
191
 
 
192
        brasero_job_set_current_action (BRASERO_JOB (self),
 
193
                                        BRASERO_BURN_ACTION_CREATING_IMAGE,
 
194
                                        NULL,
 
195
                                        FALSE);
 
196
 
 
197
        brasero_job_start_progress (BRASERO_JOB (self), FALSE);
 
198
        brasero_job_get_fd_out (BRASERO_JOB (self), &fd);
 
199
 
 
200
        BRASERO_JOB_LOG (self, "Writing to pipe");
 
201
        read_bytes = priv->libburn_src->read_xt (priv->libburn_src, buf, sector_size);
 
202
        while (priv->libburn_src->read_xt (priv->libburn_src, buf, sector_size) == sector_size) {
 
203
                if (priv->cancel)
 
204
                        break;
 
205
 
 
206
                result = brasero_libisofs_write_sector_to_fd (self,
 
207
                                                              fd,
 
208
                                                              buf,
 
209
                                                              sector_size);
 
210
                if (result != BRASERO_BURN_OK)
 
211
                        break;
 
212
 
 
213
                written_sectors ++;
 
214
                brasero_job_set_written_track (BRASERO_JOB (self), written_sectors << 11);
 
215
 
 
216
                read_bytes = priv->libburn_src->read_xt (priv->libburn_src, buf, sector_size);
 
217
        }
 
218
 
 
219
        if (read_bytes == -1 && !priv->error)
 
220
                priv->error = g_error_new (BRASERO_BURN_ERROR,
 
221
                                           BRASERO_BURN_ERROR_GENERAL,
 
222
                                           "%s", _("Volume could not be created"));
 
223
}
 
224
 
 
225
static void
 
226
brasero_libisofs_write_image_to_file_thread (BraseroLibisofs *self)
 
227
{
 
228
        const gint sector_size = 2048;
 
229
        BraseroLibisofsPrivate *priv;
 
230
        gint64 written_sectors = 0;
 
231
        guchar buf [sector_size];
 
232
        int read_bytes;
 
233
        gchar *output;
 
234
        FILE *file;
 
235
 
 
236
        priv = BRASERO_LIBISOFS_PRIVATE (self);
 
237
 
 
238
        brasero_job_get_image_output (BRASERO_JOB (self), &output, NULL);
 
239
        file = fopen (output, "w");
 
240
        if (!file) {
 
241
                int errnum = errno;
 
242
 
 
243
                if (errno == EACCES)
 
244
                        priv->error = g_error_new_literal (BRASERO_BURN_ERROR,
 
245
                                                           BRASERO_BURN_ERROR_PERMISSION,
 
246
                                                           _("You do not have the required permission to write at this location"));
 
247
                else
 
248
                        priv->error = g_error_new_literal (BRASERO_BURN_ERROR,
 
249
                                                           BRASERO_BURN_ERROR_GENERAL,
 
250
                                                           g_strerror (errnum));
 
251
                return;
 
252
        }
 
253
 
 
254
        BRASERO_JOB_LOG (self, "writing to file %s", output);
 
255
 
 
256
        brasero_job_set_current_action (BRASERO_JOB (self),
 
257
                                        BRASERO_BURN_ACTION_CREATING_IMAGE,
 
258
                                        NULL,
 
259
                                        FALSE);
 
260
 
 
261
        priv = BRASERO_LIBISOFS_PRIVATE (self);
 
262
        brasero_job_start_progress (BRASERO_JOB (self), FALSE);
 
263
 
 
264
        read_bytes = priv->libburn_src->read_xt (priv->libburn_src, buf, sector_size);
 
265
        while (read_bytes == sector_size) {
 
266
                if (priv->cancel)
 
267
                        break;
 
268
 
 
269
                if (fwrite (buf, 1, sector_size, file) != sector_size) {
 
270
                        int errsv = errno;
 
271
 
 
272
                        priv->error = g_error_new (BRASERO_BURN_ERROR,
 
273
                                                   BRASERO_BURN_ERROR_GENERAL,
 
274
                                                   _("Data could not be written (%s)"),
 
275
                                                   g_strerror (errsv));
 
276
                        break;
 
277
                }
 
278
 
 
279
                if (priv->cancel)
 
280
                        break;
 
281
 
 
282
                written_sectors ++;
 
283
                brasero_job_set_written_track (BRASERO_JOB (self), written_sectors << 11);
 
284
 
 
285
                read_bytes = priv->libburn_src->read_xt (priv->libburn_src, buf, sector_size);
 
286
        }
 
287
 
 
288
        if (read_bytes == -1 && !priv->error)
 
289
                priv->error = g_error_new (BRASERO_BURN_ERROR,
 
290
                                           BRASERO_BURN_ERROR_GENERAL,
 
291
                                           _("Volume could not be created"));
 
292
 
 
293
        fclose (file);
 
294
        file = NULL;
 
295
}
 
296
 
 
297
static gpointer
 
298
brasero_libisofs_thread_started (gpointer data)
 
299
{
 
300
        BraseroLibisofsPrivate *priv;
 
301
        BraseroLibisofs *self;
 
302
 
 
303
        self = BRASERO_LIBISOFS (data);
 
304
        priv = BRASERO_LIBISOFS_PRIVATE (self);
 
305
 
 
306
        BRASERO_JOB_LOG (self, "Entering thread");
 
307
        if (brasero_job_get_fd_out (BRASERO_JOB (self), NULL) == BRASERO_BURN_OK)
 
308
                brasero_libisofs_write_image_to_fd_thread (self);
 
309
        else
 
310
                brasero_libisofs_write_image_to_file_thread (self);
 
311
 
 
312
        BRASERO_JOB_LOG (self, "Getting out thread");
 
313
 
 
314
        /* End thread */
 
315
        g_mutex_lock (priv->mutex);
 
316
 
 
317
        if (!priv->cancel)
 
318
                priv->thread_id = g_idle_add (brasero_libisofs_thread_finished, self);
 
319
 
 
320
        priv->thread = NULL;
 
321
        g_cond_signal (priv->cond);
 
322
        g_mutex_unlock (priv->mutex);
 
323
 
 
324
        g_thread_exit (NULL);
 
325
 
 
326
        return NULL;
 
327
}
 
328
 
 
329
static BraseroBurnResult
 
330
brasero_libisofs_create_image (BraseroLibisofs *self,
 
331
                               GError **error)
 
332
{
 
333
        BraseroLibisofsPrivate *priv;
 
334
        GError *thread_error = NULL;
 
335
 
 
336
        priv = BRASERO_LIBISOFS_PRIVATE (self);
 
337
 
 
338
        if (priv->thread)
 
339
                return BRASERO_BURN_RUNNING;
 
340
 
 
341
        if (iso_init () < 0) {
 
342
                g_set_error (error,
 
343
                             BRASERO_BURN_ERROR,
 
344
                             BRASERO_BURN_ERROR_GENERAL,
 
345
                             _("libisofs could not be initialized."));
 
346
                return BRASERO_BURN_ERR;
 
347
        }
 
348
 
 
349
        iso_set_msgs_severities ("NEVER", "ALL", "brasero (libisofs)");
 
350
 
 
351
        g_mutex_lock (priv->mutex);
 
352
        priv->thread = g_thread_create (brasero_libisofs_thread_started,
 
353
                                        self,
 
354
                                        FALSE,
 
355
                                        &thread_error);
 
356
        g_mutex_unlock (priv->mutex);
 
357
 
 
358
        /* Reminder: this is not necessarily an error as the thread may have finished */
 
359
        //if (!priv->thread)
 
360
        //      return BRASERO_BURN_ERR;
 
361
 
 
362
        if (thread_error) {
 
363
                g_propagate_error (error, thread_error);
 
364
                return BRASERO_BURN_ERR;
 
365
        }
 
366
 
 
367
        return BRASERO_BURN_OK;
 
368
}
 
369
 
 
370
static gboolean
 
371
brasero_libisofs_create_volume_thread_finished (gpointer data)
 
372
{
 
373
        BraseroLibisofs *self = data;
 
374
        BraseroLibisofsPrivate *priv;
 
375
        BraseroJobAction action;
 
376
 
 
377
        priv = BRASERO_LIBISOFS_PRIVATE (self);
 
378
 
 
379
        priv->thread_id = 0;
 
380
        if (priv->error) {
 
381
                GError *error;
 
382
 
 
383
                error = priv->error;
 
384
                priv->error = NULL;
 
385
                brasero_job_error (BRASERO_JOB (self), error);
 
386
                return FALSE;
 
387
        }
 
388
 
 
389
        brasero_job_get_action (BRASERO_JOB (self), &action);
 
390
        if (action == BRASERO_JOB_ACTION_IMAGE) {
 
391
                BraseroBurnResult result;
 
392
                GError *error = NULL;
 
393
 
 
394
                result = brasero_libisofs_create_image (self, &error);
 
395
                if (error)
 
396
                brasero_job_error (BRASERO_JOB (self), error);
 
397
                else
 
398
                        return FALSE;
 
399
        }
 
400
 
 
401
        brasero_job_finished_track (BRASERO_JOB (self));
 
402
        return FALSE;
 
403
}
 
404
 
 
405
static gint
 
406
brasero_libisofs_sort_graft_points (gconstpointer a, gconstpointer b)
 
407
{
 
408
        const BraseroGraftPt *graft_a, *graft_b;
 
409
        gint len_a, len_b;
 
410
 
 
411
        graft_a = a;
 
412
        graft_b = b;
 
413
 
 
414
        /* we only want to know if:
 
415
         * - a is a parent of b (a > b, retval < 0) 
 
416
         * - b is a parent of a (b > a, retval > 0). */
 
417
        len_a = strlen (graft_a->path);
 
418
        len_b = strlen (graft_b->path);
 
419
 
 
420
        return len_a - len_b;
 
421
}
 
422
 
 
423
static int 
 
424
brasero_libisofs_import_read (IsoDataSource *src, uint32_t lba, uint8_t *buffer)
 
425
{
 
426
        struct burn_drive *d;
 
427
        off_t data_count;
 
428
        gint result;
 
429
 
 
430
        d = (struct burn_drive*)src->data;
 
431
 
 
432
        result = burn_read_data(d,
 
433
                                (off_t) lba * (off_t) 2048,
 
434
                                (char*)buffer, 
 
435
                                2048,
 
436
                                &data_count,
 
437
                                0);
 
438
        if (result < 0 )
 
439
                return -1; /* error */
 
440
 
 
441
        return 1;
 
442
}
 
443
 
 
444
static int
 
445
brasero_libisofs_import_open (IsoDataSource *src)
 
446
{
 
447
        return 1;
 
448
}
 
449
 
 
450
static int
 
451
brasero_libisofs_import_close (IsoDataSource *src)
 
452
{
 
453
        return 1;
 
454
}
 
455
    
 
456
static void 
 
457
brasero_libisofs_import_free (IsoDataSource *src)
 
458
{ }
 
459
 
 
460
static BraseroBurnResult
 
461
brasero_libisofs_import_last_session (BraseroLibisofs *self,
 
462
                                      IsoImage *image,
 
463
                                      IsoWriteOpts *wopts,
 
464
                                      GError **error)
 
465
{
 
466
        int result;
 
467
        IsoReadOpts *opts;
 
468
        BraseroMedia media;
 
469
        IsoDataSource *src;
 
470
        goffset start_block;
 
471
        goffset session_block;
 
472
        BraseroLibisofsPrivate *priv;
 
473
 
 
474
        priv = BRASERO_LIBISOFS_PRIVATE (self);
 
475
 
 
476
        priv->ctx = brasero_libburn_common_ctx_new (BRASERO_JOB (self), FALSE, error);
 
477
        if (!priv->ctx)
 
478
                return BRASERO_BURN_ERR;
 
479
 
 
480
        result = iso_read_opts_new (&opts, 0);
 
481
        if (result < 0) {
 
482
                g_set_error (error,
 
483
                             BRASERO_BURN_ERROR,
 
484
                             BRASERO_BURN_ERROR_GENERAL,
 
485
                             _("Read options could not be created"));
 
486
                return BRASERO_BURN_ERR;
 
487
        }
 
488
 
 
489
        src = g_new0 (IsoDataSource, 1);
 
490
        src->version = 0;
 
491
        src->refcount = 1;
 
492
        src->read_block = brasero_libisofs_import_read;
 
493
        src->open = brasero_libisofs_import_open;
 
494
        src->close = brasero_libisofs_import_close;
 
495
        src->free_data = brasero_libisofs_import_free;
 
496
        src->data = priv->ctx->drive;
 
497
 
 
498
        brasero_job_get_last_session_address (BRASERO_JOB (self), &session_block);
 
499
        iso_read_opts_set_start_block (opts, session_block);
 
500
 
 
501
        /* import image */
 
502
        result = iso_image_import (image, src, opts, NULL);
 
503
        iso_data_source_unref (src);
 
504
        iso_read_opts_free (opts);
 
505
 
 
506
        /* release the drive */
 
507
        if (priv->ctx) {
 
508
                /* This may not be a good idea ...*/
 
509
                brasero_libburn_common_ctx_free (priv->ctx);
 
510
                priv->ctx = NULL;
 
511
        }
 
512
 
 
513
        if (result < 0) {
 
514
                BRASERO_JOB_LOG (self, "Import failed 0x%x", result);
 
515
                g_set_error (error,
 
516
                             BRASERO_BURN_ERROR,
 
517
                             BRASERO_BURN_ERROR_IMAGE_LAST_SESSION,
 
518
                             _("Last session import failed"));  
 
519
                return BRASERO_BURN_ERR;
 
520
        }
 
521
 
 
522
        /* check is this is a DVD+RW */
 
523
        brasero_job_get_next_writable_address (BRASERO_JOB (self), &start_block);
 
524
 
 
525
        brasero_job_get_media (BRASERO_JOB (self), &media);
 
526
        if (BRASERO_MEDIUM_IS (media, BRASERO_MEDIUM_DVDRW_PLUS)
 
527
        ||  BRASERO_MEDIUM_IS (media, BRASERO_MEDIUM_DVDRW_RESTRICTED)
 
528
        ||  BRASERO_MEDIUM_IS (media, BRASERO_MEDIUM_DVDRW_PLUS_DL)) {
 
529
                /* This is specific to overwrite media; the start address is the
 
530
                 * size of all the previous data written */
 
531
                BRASERO_JOB_LOG (self, "Growing image (start %i)", start_block);
 
532
        }
 
533
 
 
534
        /* set the start block for the multisession image */
 
535
        iso_write_opts_set_ms_block (wopts, start_block);
 
536
        iso_write_opts_set_appendable (wopts, 1);
 
537
 
 
538
        iso_tree_set_replace_mode (image, ISO_REPLACE_ALWAYS);
 
539
        return BRASERO_BURN_OK;
 
540
}
 
541
 
 
542
static gpointer
 
543
brasero_libisofs_create_volume_thread (gpointer data)
 
544
{
 
545
        BraseroLibisofs *self = BRASERO_LIBISOFS (data);
 
546
        BraseroLibisofsPrivate *priv;
 
547
        BraseroTrack *track = NULL;
 
548
        IsoWriteOpts *opts = NULL;
 
549
        IsoImage *image = NULL;
 
550
        BraseroBurnFlag flags;
 
551
        GSList *grafts = NULL;
 
552
        gchar *label = NULL;
 
553
        gchar *publisher;
 
554
        GSList *excluded;
 
555
        GSList *iter;
 
556
 
 
557
        priv = BRASERO_LIBISOFS_PRIVATE (self);
 
558
 
 
559
        if (priv->libburn_src) {
 
560
                burn_source_free (priv->libburn_src);
 
561
                priv->libburn_src = NULL;
 
562
        }
 
563
 
 
564
        BRASERO_JOB_LOG (self, "creating volume");
 
565
 
 
566
        /* create volume */
 
567
        brasero_job_get_data_label (BRASERO_JOB (self), &label);
 
568
        if (!iso_image_new (label, &image)) {
 
569
                priv->error = g_error_new (BRASERO_BURN_ERROR,
 
570
                                           BRASERO_BURN_ERROR_GENERAL, "%s",
 
571
                                           _("Volume could not be created"));
 
572
                g_free (label);
 
573
                goto end;
 
574
        }
 
575
 
 
576
        iso_write_opts_new (&opts, 2);
 
577
        iso_write_opts_set_relaxed_vol_atts(opts, 1);
 
578
 
 
579
        brasero_job_get_flags (BRASERO_JOB (self), &flags);
 
580
        if (flags & BRASERO_BURN_FLAG_MERGE) {
 
581
                BraseroBurnResult result;
 
582
 
 
583
                result = brasero_libisofs_import_last_session (self,
 
584
                                                               image,
 
585
                                                               opts,
 
586
                                                               &priv->error);
 
587
                if (result != BRASERO_BURN_OK) {
 
588
                        g_free (label);
 
589
                        goto end;
 
590
                }
 
591
        }
 
592
        else if (flags & BRASERO_BURN_FLAG_APPEND) {
 
593
                goffset start_block;
 
594
 
 
595
                brasero_job_get_next_writable_address (BRASERO_JOB (self), &start_block);
 
596
                iso_write_opts_set_ms_block (opts, start_block);
 
597
        }
 
598
 
 
599
        /* set label but set it after merging so the
 
600
         * new does not get replaced by the former */
 
601
        publisher = g_strdup_printf ("Brasero-%i.%i.%i",
 
602
                                     BRASERO_MAJOR_VERSION,
 
603
                                     BRASERO_MINOR_VERSION,
 
604
                                     BRASERO_SUB);
 
605
 
 
606
        if (label)
 
607
                iso_image_set_volume_id (image, label);
 
608
 
 
609
        iso_image_set_publisher_id (image, publisher);
 
610
        iso_image_set_data_preparer_id (image, g_get_real_name ());
 
611
 
 
612
        g_free (publisher);
 
613
        g_free (label);
 
614
 
 
615
        brasero_job_start_progress (BRASERO_JOB (self), FALSE);
 
616
 
 
617
        /* copy the list as we're going to reorder it */
 
618
        brasero_job_get_current_track (BRASERO_JOB (self), &track);
 
619
        grafts = brasero_track_data_get_grafts (BRASERO_TRACK_DATA (track));
 
620
        grafts = g_slist_copy (grafts);
 
621
        grafts = g_slist_sort (grafts, brasero_libisofs_sort_graft_points);
 
622
 
 
623
        /* add global exclusions */
 
624
        for (excluded = brasero_track_data_get_excluded_list (BRASERO_TRACK_DATA (track));
 
625
             excluded; excluded = excluded->next) {
 
626
                gchar *uri, *local;
 
627
 
 
628
                uri = excluded->data;
 
629
                local = g_filename_from_uri (uri, NULL, NULL);
 
630
                iso_tree_add_exclude (image, local);
 
631
                g_free (local);
 
632
        }
 
633
 
 
634
        for (iter = grafts; iter; iter = iter->next) {
 
635
                BraseroGraftPt *graft;
 
636
                gboolean is_directory;
 
637
                gchar *path_parent;
 
638
                gchar *path_name;
 
639
                IsoNode *parent;
 
640
 
 
641
                if (priv->cancel)
 
642
                        goto end;
 
643
 
 
644
                graft = iter->data;
 
645
 
 
646
                BRASERO_JOB_LOG (self,
 
647
                                 "Adding graft disc path = %s, URI = %s",
 
648
                                 graft->path,
 
649
                                 graft->uri);
 
650
 
 
651
                /* search for parent node.
 
652
                 * NOTE: because of mkisofs/genisoimage, we add a "/" at the end
 
653
                 * directories. So make sure there isn't one when getting the 
 
654
                 * parent path or g_path_get_dirname () will return the same
 
655
                 * exact name */
 
656
                if (g_str_has_suffix (graft->path, G_DIR_SEPARATOR_S)) {
 
657
                        gchar *tmp;
 
658
 
 
659
                        /* remove trailing "/" */
 
660
                        tmp = g_strdup (graft->path);
 
661
                        tmp [strlen (tmp) - 1] = '\0';
 
662
                        path_parent = g_path_get_dirname (tmp);
 
663
                        path_name = g_path_get_basename (tmp);
 
664
                        g_free (tmp);
 
665
 
 
666
                        is_directory = TRUE;
 
667
                }
 
668
                else {
 
669
                        path_parent = g_path_get_dirname (graft->path);
 
670
                        path_name = g_path_get_basename (graft->path);
 
671
                        is_directory = FALSE;
 
672
                }
 
673
 
 
674
                iso_tree_path_to_node (image, path_parent, &parent);
 
675
                g_free (path_parent);
 
676
 
 
677
                if (!parent) {
 
678
                        /* an error has occurred, possibly libisofs hasn't been
 
679
                         * able to find a parent for this node */
 
680
                        g_free (path_name);
 
681
                        priv->error = g_error_new (BRASERO_BURN_ERROR,
 
682
                                                   BRASERO_BURN_ERROR_GENERAL,
 
683
                                                   /* Translators: %s is the path */
 
684
                                                   _("No parent could be found in the tree for the path \"%s\""),
 
685
                                                   graft->path);
 
686
                        goto end;
 
687
                }
 
688
 
 
689
                BRASERO_JOB_LOG (self, "Found parent");
 
690
 
 
691
                /* add the file/directory to the volume */
 
692
                if (graft->uri) {
 
693
                        gchar *local_path;
 
694
                        IsoDirIter *sibling;
 
695
 
 
696
                        /* graft->uri can be a path or a URI */
 
697
                        if (graft->uri [0] == '/')
 
698
                                local_path = g_strdup (graft->uri);
 
699
                        else if (g_str_has_prefix (graft->uri, "file://"))
 
700
                                local_path = g_filename_from_uri (graft->uri, NULL, NULL);
 
701
                        else
 
702
                                local_path = NULL;
 
703
 
 
704
                        if (!local_path){
 
705
                                priv->error = g_error_new (BRASERO_BURN_ERROR,
 
706
                                                           BRASERO_BURN_ERROR_FILE_NOT_LOCAL,
 
707
                                                           _("The file is not stored locally"));
 
708
                                g_free (path_name);
 
709
                                goto end;
 
710
                        }
 
711
 
 
712
                        /* see if the node exists with the same name among the 
 
713
                         * children of the parent directory. If there is a
 
714
                         * sibling destroy it. */
 
715
                        sibling = NULL;
 
716
                        iso_dir_get_children (ISO_DIR (parent), &sibling);
 
717
 
 
718
                        IsoNode *node;
 
719
                        while (iso_dir_iter_next (sibling, &node) == 1) {
 
720
                                const gchar *iso_name;
 
721
 
 
722
                                /* check if it has the same name */
 
723
                                iso_name = iso_node_get_name (node);
 
724
                                if (iso_name && !strcmp (iso_name, path_name))
 
725
                                        BRASERO_JOB_LOG (self,
 
726
                                                         "Found sibling for %s: removing %x",
 
727
                                                         path_name,
 
728
                                                         iso_dir_iter_remove (sibling));
 
729
                        }
 
730
 
 
731
                        if  (is_directory) {
 
732
                                int result;
 
733
                                IsoDir *directory;
 
734
 
 
735
                                /* add directory node */
 
736
                                result = iso_tree_add_new_dir (ISO_DIR (parent), path_name, &directory);
 
737
                                if (result < 0) {
 
738
                                        BRASERO_JOB_LOG (self,
 
739
                                                         "ERROR %s %x",
 
740
                                                         path_name,
 
741
                                                         result);
 
742
                                        priv->error = g_error_new (BRASERO_BURN_ERROR,
 
743
                                                                   BRASERO_BURN_ERROR_GENERAL,
 
744
                                                                   _("libisofs reported an error while creating directory \"%s\""),
 
745
                                                                   graft->path);
 
746
                                        g_free (path_name);
 
747
                                        goto end;
 
748
                                }
 
749
 
 
750
                                /* add contents */
 
751
                                result = iso_tree_add_dir_rec (image, directory, local_path);
 
752
                                if (result < 0) {
 
753
                                        BRASERO_JOB_LOG (self,
 
754
                                                         "ERROR %s %x",
 
755
                                                         path_name,
 
756
                                                         result);
 
757
                                        priv->error = g_error_new (BRASERO_BURN_ERROR,
 
758
                                                                   BRASERO_BURN_ERROR_GENERAL,
 
759
                                                                   _("libisofs reported an error while adding contents to directory \"%s\" (%x)"),
 
760
                                                                   graft->path,
 
761
                                                                   result);
 
762
                                        g_free (path_name);
 
763
                                        goto end;
 
764
                                }
 
765
                        }
 
766
                        else {
 
767
                                IsoNode *node;
 
768
                                int err;
 
769
 
 
770
                                err = iso_tree_add_new_node (image,
 
771
                                                         ISO_DIR (parent),
 
772
                                                         path_name,
 
773
                                                         local_path,
 
774
                                                         &node);
 
775
                                if (err < 0) {
 
776
                                        BRASERO_JOB_LOG (self,
 
777
                                                         "ERROR %s %x",
 
778
                                                         path_name,
 
779
                                                         err);
 
780
                                        priv->error = g_error_new (BRASERO_BURN_ERROR,
 
781
                                                                   BRASERO_BURN_ERROR_GENERAL,
 
782
                                                                   _("libisofs reported an error while adding file at path \"%s\""),
 
783
                                                                   graft->path);
 
784
                                        g_free (path_name);
 
785
                                        goto end;
 
786
                                }
 
787
 
 
788
                                if (iso_node_get_name (node)
 
789
                                &&  strcmp (iso_node_get_name (node), path_name)) {
 
790
                                        err = iso_node_set_name (node, path_name);
 
791
                                        if (err < 0) {
 
792
                                                BRASERO_JOB_LOG (self,
 
793
                                                                 "ERROR %s %x",
 
794
                                                                 path_name,
 
795
                                                                 err);
 
796
                                                priv->error = g_error_new (BRASERO_BURN_ERROR,
 
797
                                                                           BRASERO_BURN_ERROR_GENERAL,
 
798
                                                                           _("libisofs reported an error while adding file at path \"%s\""),
 
799
                                                                           graft->path);
 
800
                                                g_free (path_name);
 
801
                                                goto end;
 
802
                                        }
 
803
                                }
 
804
                        }
 
805
 
 
806
                        g_free (local_path);
 
807
                }
 
808
                else if (iso_tree_add_new_dir (ISO_DIR (parent), path_name, NULL) < 0) {
 
809
                        priv->error = g_error_new (BRASERO_BURN_ERROR,
 
810
                                                   BRASERO_BURN_ERROR_GENERAL,
 
811
                                                   _("libisofs reported an error while creating directory \"%s\""),
 
812
                                                   graft->path);
 
813
                        g_free (path_name);
 
814
                        goto end;
 
815
 
 
816
                }
 
817
 
 
818
                g_free (path_name);
 
819
        }
 
820
 
 
821
 
 
822
end:
 
823
 
 
824
        if (grafts)
 
825
                g_slist_free (grafts);
 
826
 
 
827
        if (!priv->error && !priv->cancel) {
 
828
                gint64 size;
 
829
                BraseroImageFS image_fs;
 
830
 
 
831
                image_fs = brasero_track_data_get_fs (BRASERO_TRACK_DATA (track));
 
832
 
 
833
                if ((image_fs & BRASERO_IMAGE_FS_ISO)
 
834
                &&  (image_fs & BRASERO_IMAGE_ISO_FS_LEVEL_3))
 
835
                        iso_write_opts_set_iso_level (opts, 3);
 
836
                else
 
837
                        iso_write_opts_set_iso_level (opts, 2);
 
838
 
 
839
                iso_write_opts_set_rockridge (opts, 1);
 
840
                iso_write_opts_set_joliet (opts, (image_fs & BRASERO_IMAGE_FS_JOLIET) != 0);
 
841
                iso_write_opts_set_allow_deep_paths (opts, (image_fs & BRASERO_IMAGE_ISO_FS_DEEP_DIRECTORY) != 0);
 
842
 
 
843
                if (iso_image_create_burn_source (image, opts, &priv->libburn_src) >= 0) {
 
844
                        size = priv->libburn_src->get_size (priv->libburn_src);
 
845
                        brasero_job_set_output_size_for_current_track (BRASERO_JOB (self),
 
846
                                                                       BRASERO_BYTES_TO_SECTORS (size, 2048),
 
847
                                                                       size);
 
848
                }
 
849
        }
 
850
 
 
851
        if (opts)
 
852
                iso_write_opts_free (opts);
 
853
 
 
854
        if (image)
 
855
                iso_image_unref (image);
 
856
 
 
857
        /* End thread */
 
858
        g_mutex_lock (priv->mutex);
 
859
 
 
860
        /* It is important that the following is done inside the lock; indeed,
 
861
         * if the main loop is idle then that brasero_libisofs_stop_real () can
 
862
         * be called immediatly to stop the plugin while priv->thread is not
 
863
         * NULL.
 
864
         * As in this callback we check whether the thread is running (which
 
865
         * means that we were cancelled) in some cases it would mean that we
 
866
         * would cancel the libburn_src object and create crippled images. */
 
867
        if (!priv->cancel)
 
868
                priv->thread_id = g_idle_add (brasero_libisofs_create_volume_thread_finished, self);
 
869
 
 
870
        priv->thread = NULL;
 
871
        g_cond_signal (priv->cond);
 
872
        g_mutex_unlock (priv->mutex);
 
873
 
 
874
        g_thread_exit (NULL);
 
875
 
 
876
        return NULL;
 
877
}
 
878
 
 
879
static BraseroBurnResult
 
880
brasero_libisofs_create_volume (BraseroLibisofs *self, GError **error)
 
881
{
 
882
        BraseroLibisofsPrivate *priv;
 
883
        GError *thread_error = NULL;
 
884
 
 
885
        priv = BRASERO_LIBISOFS_PRIVATE (self);
 
886
        if (priv->thread)
 
887
                return BRASERO_BURN_RUNNING;
 
888
 
 
889
        if (iso_init () < 0) {
 
890
                g_set_error (error,
 
891
                             BRASERO_BURN_ERROR,
 
892
                             BRASERO_BURN_ERROR_GENERAL,
 
893
                             _("libisofs could not be initialized."));
 
894
                return BRASERO_BURN_ERR;
 
895
        }
 
896
 
 
897
        iso_set_msgs_severities ("NEVER", "ALL", "brasero (libisofs)");
 
898
        g_mutex_lock (priv->mutex);
 
899
        priv->thread = g_thread_create (brasero_libisofs_create_volume_thread,
 
900
                                        self,
 
901
                                        FALSE,
 
902
                                        &thread_error);
 
903
        g_mutex_unlock (priv->mutex);
 
904
 
 
905
        /* Reminder: this is not necessarily an error as the thread may have finished */
 
906
        //if (!priv->thread)
 
907
        //      return BRASERO_BURN_ERR;
 
908
        if (thread_error) {
 
909
                g_propagate_error (error, thread_error);
 
910
                return BRASERO_BURN_ERR;
 
911
        }
 
912
 
 
913
        return BRASERO_BURN_OK;
 
914
}
 
915
 
 
916
static void
 
917
brasero_libisofs_clean_output (BraseroLibisofs *self)
 
918
{
 
919
        BraseroLibisofsPrivate *priv;
 
920
 
 
921
        priv = BRASERO_LIBISOFS_PRIVATE (self);
 
922
 
 
923
        if (priv->libburn_src) {
 
924
                burn_source_free (priv->libburn_src);
 
925
                priv->libburn_src = NULL;
 
926
        }
 
927
 
 
928
        if (priv->error) {
 
929
                g_error_free (priv->error);
 
930
                priv->error = NULL;
 
931
        }
 
932
}
 
933
 
 
934
static BraseroBurnResult
 
935
brasero_libisofs_start (BraseroJob *job,
 
936
                        GError **error)
 
937
{
 
938
        BraseroLibisofs *self;
 
939
        BraseroJobAction action;
 
940
        BraseroLibisofsPrivate *priv;
 
941
 
 
942
        self = BRASERO_LIBISOFS (job);
 
943
        priv = BRASERO_LIBISOFS_PRIVATE (self);
 
944
 
 
945
        brasero_job_get_action (job, &action);
 
946
        if (action == BRASERO_JOB_ACTION_SIZE) {
 
947
                /* do this to avoid a problem when using
 
948
                 * DUMMY flag. libisofs would not generate
 
949
                 * a second time. */
 
950
                brasero_libisofs_clean_output (BRASERO_LIBISOFS (job));
 
951
                brasero_job_set_current_action (BRASERO_JOB (self),
 
952
                                                BRASERO_BURN_ACTION_GETTING_SIZE,
 
953
                                                NULL,
 
954
                                                FALSE);
 
955
                return brasero_libisofs_create_volume (self, error);
 
956
        }
 
957
 
 
958
        if (priv->error) {
 
959
                g_error_free (priv->error);
 
960
                priv->error = NULL;
 
961
        }
 
962
 
 
963
        /* we need the source before starting anything */
 
964
        if (!priv->libburn_src)
 
965
                return brasero_libisofs_create_volume (self, error);
 
966
 
 
967
        return brasero_libisofs_create_image (self, error);
 
968
}
 
969
 
 
970
static void
 
971
brasero_libisofs_stop_real (BraseroLibisofs *self)
 
972
{
 
973
        BraseroLibisofsPrivate *priv;
 
974
 
 
975
        priv = BRASERO_LIBISOFS_PRIVATE (self);
 
976
 
 
977
        /* Check whether we properly shut down or if we were cancelled */
 
978
        g_mutex_lock (priv->mutex);
 
979
        if (priv->thread) {
 
980
                /* NOTE: this can only happen when we're preparing the volumes
 
981
                 * for a multi session disc. At this point we're only running
 
982
                 * to get the size of the future volume and we can't race with
 
983
                 * libburn plugin that isn't operating at this stage. */
 
984
                if (priv->ctx) {
 
985
                        brasero_libburn_common_ctx_free (priv->ctx);
 
986
                        priv->ctx = NULL;
 
987
                }
 
988
 
 
989
                /* A thread is running. In this context we are probably cancelling */
 
990
                if (priv->libburn_src)
 
991
                        priv->libburn_src->cancel (priv->libburn_src);
 
992
 
 
993
                priv->cancel = 1;
 
994
                g_cond_wait (priv->cond, priv->mutex);
 
995
                priv->cancel = 0;
 
996
        }
 
997
        g_mutex_unlock (priv->mutex);
 
998
 
 
999
        if (priv->thread_id) {
 
1000
                g_source_remove (priv->thread_id);
 
1001
                priv->thread_id = 0;
 
1002
        }
 
1003
}
 
1004
 
 
1005
static BraseroBurnResult
 
1006
brasero_libisofs_stop (BraseroJob *job,
 
1007
                       GError **error)
 
1008
{
 
1009
        BraseroLibisofs *self;
 
1010
 
 
1011
        self = BRASERO_LIBISOFS (job);
 
1012
        brasero_libisofs_stop_real (self);
 
1013
        return BRASERO_BURN_OK;
 
1014
}
 
1015
 
 
1016
static void
 
1017
brasero_libisofs_class_init (BraseroLibisofsClass *klass)
 
1018
{
 
1019
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
1020
        BraseroJobClass *job_class = BRASERO_JOB_CLASS (klass);
 
1021
 
 
1022
        g_type_class_add_private (klass, sizeof (BraseroLibisofsPrivate));
 
1023
 
 
1024
        parent_class = g_type_class_peek_parent(klass);
 
1025
        object_class->finalize = brasero_libisofs_finalize;
 
1026
 
 
1027
        job_class->start = brasero_libisofs_start;
 
1028
        job_class->stop = brasero_libisofs_stop;
 
1029
}
 
1030
 
 
1031
static void
 
1032
brasero_libisofs_init (BraseroLibisofs *obj)
 
1033
{
 
1034
        BraseroLibisofsPrivate *priv;
 
1035
 
 
1036
        priv = BRASERO_LIBISOFS_PRIVATE (obj);
 
1037
        priv->mutex = g_mutex_new ();
 
1038
        priv->cond = g_cond_new ();
 
1039
}
 
1040
 
 
1041
static void
 
1042
brasero_libisofs_finalize (GObject *object)
 
1043
{
 
1044
        BraseroLibisofs *cobj;
 
1045
        BraseroLibisofsPrivate *priv;
 
1046
 
 
1047
        cobj = BRASERO_LIBISOFS (object);
 
1048
        priv = BRASERO_LIBISOFS_PRIVATE (object);
 
1049
 
 
1050
        brasero_libisofs_stop_real (cobj);
 
1051
        brasero_libisofs_clean_output (cobj);
 
1052
 
 
1053
        if (priv->mutex) {
 
1054
                g_mutex_free (priv->mutex);
 
1055
                priv->mutex = NULL;
 
1056
        }
 
1057
 
 
1058
        if (priv->cond) {
 
1059
                g_cond_free (priv->cond);
 
1060
                priv->cond = NULL;
 
1061
        }
 
1062
 
 
1063
        /* close libisofs library */
 
1064
        iso_finish ();
 
1065
 
 
1066
        G_OBJECT_CLASS (parent_class)->finalize (object);
 
1067
}
 
1068
 
 
1069
static void
 
1070
brasero_libisofs_export_caps (BraseroPlugin *plugin)
 
1071
{
 
1072
        GSList *output;
 
1073
        GSList *input;
 
1074
 
 
1075
        brasero_plugin_define (plugin,
 
1076
                               "libisofs",
 
1077
                               NULL,
 
1078
                               _("Creates disc images from a file selection"),
 
1079
                               "Philippe Rouquier",
 
1080
                               3);
 
1081
 
 
1082
        brasero_plugin_set_flags (plugin,
 
1083
                                  BRASERO_MEDIUM_CDR|
 
1084
                                  BRASERO_MEDIUM_CDRW|
 
1085
                                  BRASERO_MEDIUM_DVDR|
 
1086
                                  BRASERO_MEDIUM_DVDRW|
 
1087
                                  BRASERO_MEDIUM_DUAL_L|
 
1088
                                  BRASERO_MEDIUM_APPENDABLE|
 
1089
                                  BRASERO_MEDIUM_HAS_AUDIO|
 
1090
                                  BRASERO_MEDIUM_HAS_DATA,
 
1091
                                  BRASERO_BURN_FLAG_APPEND|
 
1092
                                  BRASERO_BURN_FLAG_MERGE,
 
1093
                                  BRASERO_BURN_FLAG_NONE);
 
1094
 
 
1095
        brasero_plugin_set_flags (plugin,
 
1096
                                  BRASERO_MEDIUM_DVDRW_PLUS|
 
1097
                                  BRASERO_MEDIUM_RESTRICTED|
 
1098
                                  BRASERO_MEDIUM_DUAL_L|
 
1099
                                  BRASERO_MEDIUM_APPENDABLE|
 
1100
                                  BRASERO_MEDIUM_CLOSED|
 
1101
                                  BRASERO_MEDIUM_HAS_DATA,
 
1102
                                  BRASERO_BURN_FLAG_APPEND|
 
1103
                                  BRASERO_BURN_FLAG_MERGE,
 
1104
                                  BRASERO_BURN_FLAG_NONE);
 
1105
 
 
1106
        output = brasero_caps_image_new (BRASERO_PLUGIN_IO_ACCEPT_FILE|
 
1107
                                         BRASERO_PLUGIN_IO_ACCEPT_PIPE,
 
1108
                                         BRASERO_IMAGE_FORMAT_BIN);
 
1109
 
 
1110
        input = brasero_caps_data_new (BRASERO_IMAGE_FS_ISO|
 
1111
                                       BRASERO_IMAGE_ISO_FS_DEEP_DIRECTORY|
 
1112
                                       BRASERO_IMAGE_ISO_FS_LEVEL_3|
 
1113
                                       BRASERO_IMAGE_FS_JOLIET);
 
1114
        brasero_plugin_link_caps (plugin, output, input);
 
1115
        g_slist_free (input);
 
1116
 
 
1117
        input = brasero_caps_data_new (BRASERO_IMAGE_FS_ISO|
 
1118
                                       BRASERO_IMAGE_ISO_FS_DEEP_DIRECTORY|
 
1119
                                       BRASERO_IMAGE_ISO_FS_LEVEL_3|
 
1120
                                       BRASERO_IMAGE_FS_SYMLINK);
 
1121
        brasero_plugin_link_caps (plugin, output, input);
 
1122
        g_slist_free (input);
 
1123
 
 
1124
        g_slist_free (output);
 
1125
 
 
1126
        brasero_plugin_register_group (plugin, _(LIBBURNIA_DESCRIPTION));
 
1127
}