Imported Upstream version 3.2.0
[debian/amanda] / device-src / xfer-dest-device.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 2009, 2010 Zmanda, Inc.  All Rights Reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 as published
7  * by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17  *
18  * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
19  * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
20  */
21
22 #include "amanda.h"
23 #include "amxfer.h"
24 #include "device.h"
25 #include "property.h"
26 #include "xfer-device.h"
27
28 /*
29  * Class declaration
30  *
31  * This declaration is entirely private; nothing but xfer_dest_device() references
32  * it directly.
33  */
34
35 GType xfer_dest_device_get_type(void);
36 #define XFER_DEST_DEVICE_TYPE (xfer_dest_device_get_type())
37 #define XFER_DEST_DEVICE(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), xfer_dest_device_get_type(), XferDestDevice)
38 #define XFER_DEST_DEVICE_CONST(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), xfer_dest_device_get_type(), XferDestDevice const)
39 #define XFER_DEST_DEVICE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST((klass), xfer_dest_device_get_type(), XferDestDeviceClass)
40 #define IS_XFER_DEST_DEVICE(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), xfer_dest_device_get_type ())
41 #define XFER_DEST_DEVICE_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS((obj), xfer_dest_device_get_type(), XferDestDeviceClass)
42
43 static GObjectClass *parent_class = NULL;
44
45 /*
46  * Main object structure
47  */
48
49 typedef struct XferDestDevice {
50     XferElement __parent__;
51
52     Device *device;
53
54     gboolean cancel_at_leom;
55
56     gpointer partial;
57     gsize block_size;
58     gsize partial_length;
59 } XferDestDevice;
60
61 /*
62  * Class definition
63  */
64
65 typedef struct {
66     XferElementClass __parent__;
67 } XferDestDeviceClass;
68
69 /*
70  * Implementation
71  */
72
73 static gboolean
74 do_block(
75     XferDestDevice *self,
76     guint size,
77     gpointer data)
78 {
79     XferElement *elt = XFER_ELEMENT(self);
80
81     if (!device_write_block(self->device, size, data)) {
82         xfer_cancel_with_error(elt, "%s: %s",
83                 self->device->device_name, device_error_or_status(self->device));
84         wait_until_xfer_cancelled(elt->xfer);
85         return FALSE;
86     }
87
88     /* check for LEOM */
89     if (self->cancel_at_leom && self->device->is_eom) {
90         xfer_cancel_with_error(elt, "%s: LEOM detected", self->device->device_name);
91         wait_until_xfer_cancelled(elt->xfer);
92         return FALSE;
93     }
94
95     return TRUE;
96 }
97
98 static void
99 push_buffer_impl(
100     XferElement *elt,
101     gpointer buf,
102     size_t len)
103 {
104     XferDestDevice *self = XFER_DEST_DEVICE(elt);
105     gpointer to_free = buf;
106
107     /* Handle EOF */
108     if (!buf) {
109         /* write out the partial buffer, if there's anything in it */
110         if (self->partial_length) {
111             if (!do_block(self, self->block_size, self->partial)) {
112                 return;
113             }
114             self->partial_length = 0;
115         }
116
117         device_finish_file(self->device);
118         return;
119     }
120
121     /* set up the block buffer, now that we can depend on having a blocksize
122      * from the device */
123     if (!self->partial) {
124         self->partial = g_malloc(self->device->block_size);
125         self->block_size = self->device->block_size;
126         self->partial_length = 0;
127     }
128
129     /* if we already have data in the buffer, add the new data to it */
130     if (self->partial_length != 0) {
131         gsize to_copy = min(self->block_size - self->partial_length, len);
132         memmove(self->partial + self->partial_length, buf, to_copy);
133         buf = (gpointer)(to_copy + (char *)buf);
134         len -= to_copy;
135         self->partial_length += to_copy;
136     }
137
138     /* and if the buffer is now full, write the block */
139     if (self->partial_length == self->block_size) {
140         if (!do_block(self, self->block_size, self->partial)) {
141             g_free(to_free);
142             return;
143         }
144         self->partial_length = 0;
145     }
146
147     /* write any whole blocks directly from the push buffer */
148     while (len >= self->block_size) {
149         if (!do_block(self, self->block_size, buf)) {
150             g_free(to_free);
151             return;
152         }
153
154         buf = (gpointer)(self->block_size + (char *)buf);
155         len -= self->block_size;
156     }
157
158     /* and finally store any leftover data in the partial buffer */
159     if (len) {
160         memmove(self->partial, buf, len);
161         self->partial_length = len;
162     }
163
164     g_free(to_free);
165 }
166
167 static void
168 instance_init(
169     XferElement *elt)
170 {
171     XferDestDevice *self = XFER_DEST_DEVICE(elt);
172     self->partial = NULL;
173 }
174
175 static void
176 finalize_impl(
177     GObject * obj_self)
178 {
179     XferDestDevice *self = XFER_DEST_DEVICE(obj_self);
180
181     if (self->partial) {
182         g_free(self->partial);
183     }
184 }
185
186 static void
187 class_init(
188     XferDestDeviceClass * selfc)
189 {
190     XferElementClass *klass = XFER_ELEMENT_CLASS(selfc);
191     GObjectClass *goc = (GObjectClass*) klass;
192     static xfer_element_mech_pair_t mech_pairs[] = {
193         { XFER_MECH_PUSH_BUFFER, XFER_MECH_NONE, 0, 0},
194         { XFER_MECH_NONE, XFER_MECH_NONE, 0, 0},
195     };
196
197     klass->push_buffer = push_buffer_impl;
198
199     klass->perl_class = "Amanda::Xfer::Dest::Device";
200     klass->mech_pairs = mech_pairs;
201
202     goc->finalize = finalize_impl;
203
204     parent_class = g_type_class_peek_parent(selfc);
205 }
206
207 GType
208 xfer_dest_device_get_type (void)
209 {
210     static GType type = 0;
211
212     if G_UNLIKELY(type == 0) {
213         static const GTypeInfo info = {
214             sizeof (XferDestDeviceClass),
215             (GBaseInitFunc) NULL,
216             (GBaseFinalizeFunc) NULL,
217             (GClassInitFunc) class_init,
218             (GClassFinalizeFunc) NULL,
219             NULL /* class_data */,
220             sizeof (XferDestDevice),
221             0 /* n_preallocs */,
222             (GInstanceInitFunc) instance_init,
223             NULL
224         };
225
226         type = g_type_register_static (XFER_ELEMENT_TYPE, "XferDestDevice", &info, 0);
227     }
228
229     return type;
230 }
231
232 /* create an element of this class; prototype is in xfer-device.h */
233 XferElement *
234 xfer_dest_device(
235     Device *device,
236     gboolean cancel_at_leom)
237 {
238     XferDestDevice *self = (XferDestDevice *)g_object_new(XFER_DEST_DEVICE_TYPE, NULL);
239     XferElement *elt = XFER_ELEMENT(self);
240
241     g_assert(device != NULL);
242
243     self->device = device;
244     self->cancel_at_leom = cancel_at_leom;
245
246     return elt;
247 }