Imported Upstream version 3.2.0
[debian/amanda] / device-src / ndmp-device.c
1 /*
2  * Copyright (c) 2009, 2010 Zmanda, Inc.  All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 2 as published
6  * by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16  *
17  * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
18  * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
19  */
20
21 #include "amanda.h"
22 #include "util.h"
23 #include "device.h"
24 #include "directtcp.h"
25 #include "ndmlib.h"
26 #include "ndmpconnobj.h"
27
28 /*
29  * Type checking and casting macros
30  */
31 #define TYPE_NDMP_DEVICE        (ndmp_device_get_type())
32 #define NDMP_DEVICE(obj)        G_TYPE_CHECK_INSTANCE_CAST((obj), ndmp_device_get_type(), NdmpDevice)
33 #define NDMP_DEVICE_CONST(obj)  G_TYPE_CHECK_INSTANCE_CAST((obj), ndmp_device_get_type(), NdmpDevice const)
34 #define NDMP_DEVICE_CLASS(klass)        G_TYPE_CHECK_CLASS_CAST((klass), ndmp_device_get_type(), NdmpDeviceClass)
35 #define IS_NDMP_DEVICE(obj)     G_TYPE_CHECK_INSTANCE_TYPE((obj), ndmp_device_get_type ())
36 #define NDMP_DEVICE_GET_CLASS(obj)      G_TYPE_INSTANCE_GET_CLASS((obj), ndmp_device_get_type(), NdmpDeviceClass)
37 static GType    ndmp_device_get_type    (void);
38
39 /*
40  * Main object structure
41  */
42
43 typedef struct NdmpDevice_ NdmpDevice;
44 struct NdmpDevice_ {
45     Device __parent__;
46
47     NDMPConnection *ndmp;
48
49     /* true if tape service is open on the NDMP server */
50     gboolean tape_open;
51
52     /* addresses the object is listening on, and how the connection
53      * was opened */
54     DirectTCPAddr *listen_addrs;
55     gboolean for_writing;
56
57     /* Current DirectTCPConnectionNDMP */
58     struct DirectTCPConnectionNDMP_ *directtcp_conn;
59
60     /* constructor parameters and properties */
61     gchar        *ndmp_hostname;
62     gint         ndmp_port;
63     gchar        *ndmp_device_name;
64     gchar        *ndmp_username;
65     gchar        *ndmp_password;
66     gchar        *ndmp_auth;
67     gboolean     verbose;
68     gsize        read_block_size;
69 };
70
71 /*
72  * Class definition
73  */
74
75 typedef struct NdmpDeviceClass_ NdmpDeviceClass;
76 struct NdmpDeviceClass_ {
77     DeviceClass __parent__;
78 };
79
80 /*
81  * A directtcp connection subclass representing a running mover on the other end of
82  * the given NDMP connection
83  */
84
85 #define TYPE_DIRECTTCP_CONNECTION_NDMP  (directtcp_connection_ndmp_get_type())
86 #define DIRECTTCP_CONNECTION_NDMP(obj)  G_TYPE_CHECK_INSTANCE_CAST((obj), directtcp_connection_ndmp_get_type(), DirectTCPConnectionNDMP)
87 #define DIRECTTCP_CONNECTION_NDMP_CONST(obj)    G_TYPE_CHECK_INSTANCE_CAST((obj), directtcp_connection_ndmp_get_type(), DirectTCPConnectionNDMP const)
88 #define DIRECTTCP_CONNECTION_NDMP_CLASS(klass)  G_TYPE_CHECK_CLASS_CAST((klass), directtcp_connection_ndmp_get_type(), DirectTCPConnectionNDMPClass)
89 #define IS_DIRECTTCP_CONNECTION_NDMP(obj)       G_TYPE_CHECK_INSTANCE_TYPE((obj), directtcp_connection_ndmp_get_type ())
90 #define DIRECTTCP_CONNECTION_NDMP_GET_CLASS(obj)        G_TYPE_INSTANCE_GET_CLASS((obj), directtcp_connection_ndmp_get_type(), DirectTCPConnectionNDMPClass)
91 GType directtcp_connection_ndmp_get_type(void);
92
93 typedef struct DirectTCPConnectionNDMP_ {
94     DirectTCPConnection __parent__;
95
96     /* NDMP connection controlling the mover */
97     NDMPConnection *ndmp;
98
99     /* mode for this operation */
100     ndmp9_mover_mode mode;
101
102     /* last reported mover position in the datastream */
103     guint64 offset;
104 } DirectTCPConnectionNDMP;
105
106 typedef struct DirectTCPConnectionNDMPClass_ {
107     DirectTCPConnectionClass __parent__;
108 } DirectTCPConnectionNDMPClass;
109
110 static DirectTCPConnectionNDMP *directtcp_connection_ndmp_new(
111         NDMPConnection *ndmp,
112         ndmp9_mover_mode mode);
113
114 /*
115  * Constants and static data
116  */
117
118 #define NDMP_DEVICE_NAME "ndmp"
119
120 /* pointer to the class of our parent */
121 static DeviceClass *parent_class = NULL;
122
123 /* robust_write results */
124 typedef enum {
125     ROBUST_WRITE_OK,
126     ROBUST_WRITE_OK_LEOM,
127     ROBUST_WRITE_ERROR, /* device error already set */
128     ROBUST_WRITE_NO_SPACE
129 } robust_write_result;
130
131 /*
132  * device-specific properties
133  */
134
135 /* Authentication information for NDMP agent. Both of these are strings. */
136 static DevicePropertyBase device_property_ndmp_username;
137 static DevicePropertyBase device_property_ndmp_password;
138 static DevicePropertyBase device_property_ndmp_auth;
139 #define PROPERTY_NDMP_USERNAME (device_property_ndmp_username.ID)
140 #define PROPERTY_NDMP_PASSWORD (device_property_ndmp_password.ID)
141 #define PROPERTY_NDMP_AUTH (device_property_ndmp_auth.ID)
142
143
144 /*
145  * prototypes
146  */
147
148 void ndmp_device_register(void);
149 static void set_error_from_ndmp(NdmpDevice *self);
150
151 #define ndmp_device_read_size(self) \
152     (((NdmpDevice *)(self))->read_block_size? \
153         ((NdmpDevice *)(self))->read_block_size : ((Device *)(self))->block_size)
154
155 /*
156  * Utility functions
157  */
158
159 static gboolean
160 open_connection(
161         NdmpDevice *self)
162 {
163     if (!self->ndmp) {
164         self->ndmp = ndmp_connection_new(
165             self->ndmp_hostname,
166             self->ndmp_port,
167             self->ndmp_username,
168             self->ndmp_password,
169             self->ndmp_auth);
170
171         if (ndmp_connection_err_code(self->ndmp)) {
172             char *errmsg = ndmp_connection_err_msg(self->ndmp);
173             device_set_error(DEVICE(self),
174                 g_strdup_printf("could not connect to ndmp-server '%s:%d': %s",
175                     self->ndmp_hostname, self->ndmp_port, errmsg),
176                 DEVICE_STATUS_DEVICE_ERROR);
177             g_object_unref(self->ndmp);
178             self->ndmp = NULL;
179             return FALSE;
180         }
181
182         if (self->verbose)
183             ndmp_connection_set_verbose(self->ndmp, TRUE);
184
185         self->tape_open = FALSE;
186     }
187
188     return TRUE;
189 }
190
191 static void
192 close_connection(
193         NdmpDevice *self)
194 {
195     /* note that this does not send NDMP_TAPE_CLOSE, as it's used in error
196      * situations too */
197     if (self->ndmp) {
198         g_object_unref(self->ndmp);
199         self->ndmp = NULL;
200         self->tape_open = FALSE;
201     }
202 }
203
204 static gboolean
205 open_tape_agent(
206     NdmpDevice *self)
207 {
208     guint64 file_num, blockno, blocksize;
209
210     /* if already open, stop now */
211     if (self->tape_open) {
212         return TRUE;
213     }
214
215     if (!open_connection(self)) {
216         /* error message set by open_connection */
217         return FALSE;
218     }
219
220     g_debug("opening tape device '%s' on NDMP server '%s:%d'",
221         self->ndmp_device_name, self->ndmp_hostname, self->ndmp_port);
222
223     /* send NDMP_TAPE_OPEN, using RAW mode so that it will open even with no tape */
224     if (!ndmp_connection_tape_open(self->ndmp,
225                 self->ndmp_device_name, NDMP9_TAPE_RAW_MODE)) {
226         set_error_from_ndmp(self);
227         return FALSE;
228     }
229
230     /* check that the block sizes match */
231     if (!ndmp_connection_tape_get_state(self->ndmp,
232         &blocksize, &file_num, &blockno)) {
233         set_error_from_ndmp(self);
234         return FALSE;
235     }
236     if (blocksize != 0 && blocksize != DEVICE(self)->block_size) {
237         device_set_error(DEVICE(self),
238             g_strdup_printf("NDMP device has fixed block size %ju, but Amanda "
239                     "device is configured with blocksize %ju", (uintmax_t)blocksize,
240                     (uintmax_t)(DEVICE(self)->block_size)),
241             DEVICE_STATUS_DEVICE_ERROR);
242     }
243
244     self->tape_open = TRUE;
245
246     return TRUE;
247 }
248
249 static gboolean
250 close_tape_agent(
251         NdmpDevice *self)
252 {
253     if (self->tape_open) {
254         g_debug("closing tape device '%s' on NDMP server '%s:%d'",
255             self->ndmp_device_name, self->ndmp_hostname, self->ndmp_port);
256         self->tape_open = FALSE; /* count it as closed even if there is an error */
257         if (!ndmp_connection_tape_close(self->ndmp)) {
258             set_error_from_ndmp(self);
259             return FALSE;
260         }
261     }
262
263     return TRUE;
264 }
265
266 static gboolean
267 single_ndmp_mtio(
268     NdmpDevice *self,
269     ndmp9_tape_mtio_op tape_op)
270 {
271     guint resid;
272
273     if (!ndmp_connection_tape_mtio(self->ndmp, tape_op, 1, &resid)) {
274         set_error_from_ndmp(self);
275         return FALSE;
276     }
277
278     if (resid != 0) {
279         device_set_error(DEVICE(self),
280                 g_strdup_printf("NDMP MTIO operation %d did not complete", tape_op),
281                 DEVICE_STATUS_DEVICE_ERROR);
282     }
283
284     return TRUE;
285 }
286
287 /* get the tape state straight from the device; we try to track these things
288  * accurately in the device, but sometimes it's good to check. */
289 static gboolean
290 ndmp_get_state(
291     NdmpDevice *self)
292 {
293     Device *dself = DEVICE(self);
294     guint64 file_num, blockno, blocksize;
295
296     if (!ndmp_connection_tape_get_state(self->ndmp,
297         &blocksize, &file_num, &blockno)) {
298         set_error_from_ndmp(self);
299         return FALSE;
300     }
301
302     g_assert(file_num < INT_MAX);
303     dself->file = (int)file_num;
304     dself->block = blockno;
305
306     return TRUE;
307 }
308
309 static robust_write_result
310 robust_write(
311     NdmpDevice *self,
312     char *buf,
313     guint64 count)
314 {
315     guint64 actual;
316     robust_write_result subresult;
317
318     if (!ndmp_connection_tape_write(self->ndmp, buf, count, &actual)) {
319         switch (ndmp_connection_err_code(self->ndmp)) {
320             case NDMP9_IO_ERR:
321                 /* We encountered PEOM; this only happens when the caller ignores
322                  * LEOM */
323                 return ROBUST_WRITE_NO_SPACE;
324
325             case NDMP9_EOM_ERR:
326                 /* We encountered LEOM; retry the write (which should succeed) */
327                 subresult = robust_write(self, buf, count);
328                 if (subresult != ROBUST_WRITE_OK)
329                     return subresult;
330                 g_debug("ndmp device hit logical EOM");
331                 return ROBUST_WRITE_OK_LEOM;
332
333             default:
334                 set_error_from_ndmp(self);
335                 return ROBUST_WRITE_ERROR;
336         }
337     }
338
339     g_assert(count == actual);
340     return ROBUST_WRITE_OK;
341 }
342
343 static void
344 set_error_from_ndmp(
345     NdmpDevice *self)
346 {
347     /* translate some error codes to the corresponding Device API status */
348     switch (ndmp_connection_err_code(self->ndmp)) {
349         case NDMP9_NO_TAPE_LOADED_ERR:
350             device_set_error(DEVICE(self),
351                     g_strdup(_("no tape loaded")),
352                             DEVICE_STATUS_VOLUME_MISSING);
353             break;
354
355         case NDMP9_DEVICE_BUSY_ERR:
356             device_set_error(DEVICE(self),
357                     g_strdup(_("device busy")),
358                             DEVICE_STATUS_DEVICE_BUSY);
359             break;
360
361         case NDMP9_IO_ERR:
362             device_set_error(DEVICE(self),
363                     g_strdup(_("IO error")),
364                             DEVICE_STATUS_VOLUME_UNLABELED |
365                             DEVICE_STATUS_VOLUME_ERROR |
366                             DEVICE_STATUS_DEVICE_ERROR);
367             break;
368
369         default:
370             device_set_error(DEVICE(self),
371                     ndmp_connection_err_msg(self->ndmp),
372                     DEVICE_STATUS_DEVICE_ERROR);
373             break;
374         }
375     close_connection(self);
376 }
377
378 /*
379  * Virtual function overrides
380  */
381
382 static void
383 ndmp_device_open_device(
384     Device *dself,
385     char   *device_name,
386     char   *device_type,
387     char   *device_node)
388 {
389     NdmpDevice          *self = NDMP_DEVICE(dself);
390     char *colon, *at;
391
392     /* first, extract the various parts of the device_node:
393      * HOST[:PORT]@DEVICE */
394     colon = strchr(device_node, ':');
395     at = strchr(device_node, '@');
396     if (colon > at)
397         colon = NULL; /* :PORT only counts if it's before the device name */
398     if (!at) {
399         device_set_error(dself,
400                          g_strdup_printf("invalid ndmp device name '%s'", device_name),
401                          DEVICE_STATUS_DEVICE_ERROR);
402         return;
403     }
404
405     if (colon) {
406         char *p = NULL;
407         long port = strtol(colon+1, &p, 10);
408
409         if (port < 0 || port >= 65536 || p != at || (!port && EINVAL == errno)) {
410             device_set_error(dself,
411                             g_strdup_printf("invalid ndmp port in device name '%s'",
412                                             device_name),
413                             DEVICE_STATUS_DEVICE_ERROR);
414             return;
415         }
416         self->ndmp_port = (gint)port;
417         self->ndmp_hostname = g_strndup(device_node, colon-device_node);
418     } else {
419         self->ndmp_port = 0; /* (use ndmjob's default, 10000) */
420         self->ndmp_hostname = g_strndup(device_node, at-device_node);
421     }
422     self->ndmp_device_name = g_strdup(at+1);
423
424     if (parent_class->open_device) {
425         parent_class->open_device(dself, device_name, device_type, device_node);
426     }
427 }
428
429 static void ndmp_device_finalize(GObject * obj_self)
430 {
431     NdmpDevice       *self = NDMP_DEVICE (obj_self);
432
433     if(G_OBJECT_CLASS(parent_class)->finalize)
434         (* G_OBJECT_CLASS(parent_class)->finalize)(obj_self);
435
436     (void)close_tape_agent(self); /* ignore any error */
437
438     if (self->directtcp_conn)
439         g_object_unref(self->directtcp_conn);
440
441     if (self->listen_addrs)
442         g_free(self->listen_addrs);
443
444     close_connection(self);
445
446     if (self->ndmp_hostname)
447         g_free(self->ndmp_hostname);
448     if (self->ndmp_device_name)
449         g_free(self->ndmp_device_name);
450     if (self->ndmp_username)
451         g_free(self->ndmp_username);
452     if (self->ndmp_password)
453         g_free(self->ndmp_password);
454     if (self->ndmp_auth)
455         g_free(self->ndmp_auth);
456 }
457
458 static DeviceStatusFlags
459 ndmp_device_read_label(
460     Device *dself)
461 {
462     NdmpDevice       *self = NDMP_DEVICE(dself);
463     dumpfile_t       *header = NULL;
464     gpointer buf = NULL;
465     guint64 buf_size = 0;
466     gsize read_block_size = 0;
467
468     amfree(dself->volume_label);
469     amfree(dself->volume_time);
470     dumpfile_free(dself->volume_header);
471     dself->volume_header = NULL;
472
473     if (device_in_error(self)) return dself->status;
474
475     if (!open_tape_agent(self)) {
476         /* error status was set by open_tape_agent */
477         return dself->status;
478     }
479
480     if (!single_ndmp_mtio(self, NDMP9_MTIO_REW)) {
481         /* error message, if any, is set by single_ndmp_mtio */
482         return dself->status;
483     }
484
485     /* read the tape header from the NDMP server */
486     dself->status = 0;
487     read_block_size = ndmp_device_read_size(self);
488     buf = g_malloc(read_block_size);
489     if (!ndmp_connection_tape_read(self->ndmp,
490         buf,
491         read_block_size,
492         &buf_size)) {
493
494         /* handle known errors */
495         switch (ndmp_connection_err_code(self->ndmp)) {
496             case NDMP9_NO_TAPE_LOADED_ERR:
497                 device_set_error(dself,
498                         g_strdup(_("no tape loaded")),
499                                 DEVICE_STATUS_VOLUME_MISSING);
500                 goto read_err;
501
502             case NDMP9_IO_ERR:
503                 device_set_error(dself,
504                         g_strdup(_("IO error reading tape label")),
505                                 DEVICE_STATUS_VOLUME_UNLABELED |
506                                 DEVICE_STATUS_VOLUME_ERROR |
507                                 DEVICE_STATUS_DEVICE_ERROR);
508                 goto read_err;
509
510             case NDMP9_EOM_ERR:
511             case NDMP9_EOF_ERR:
512                 device_set_error(dself,
513                         g_strdup(_("no tape label found")),
514                                 DEVICE_STATUS_VOLUME_UNLABELED);
515                 header = dself->volume_header = g_new(dumpfile_t, 1);
516                 fh_init(header);
517                 goto read_err;
518
519             default:
520                 set_error_from_ndmp(self);
521                 goto read_err;
522             }
523         }
524
525         header = dself->volume_header = g_new(dumpfile_t, 1);
526         fh_init(header);
527         parse_file_header(buf, header, buf_size);
528
529 read_err:
530     g_free(buf);
531
532     if (dself->status != 0) {
533         /* error already set above */
534         return dself->status;
535     }
536
537     /* handle a "weird" label */
538     if (header->type != F_TAPESTART) {
539         device_set_error(dself,
540                 stralloc(_("No tapestart header -- unlabeled device?")),
541                          DEVICE_STATUS_VOLUME_UNLABELED);
542         return dself->status;
543     }
544     dself->volume_label = g_strdup(header->name);
545     dself->volume_time = g_strdup(header->datestamp);
546     /* dself->volume_header is already set */
547
548     /* note: connection is left open, as well as the tape device */
549
550     device_set_error(dself, NULL, DEVICE_STATUS_SUCCESS);
551
552     return dself->status;
553 }
554
555
556 static gboolean
557 ndmp_device_start(
558     Device           *dself,
559     DeviceAccessMode  mode,
560     char             *label,
561     char             *timestamp)
562 {
563     NdmpDevice *self = NDMP_DEVICE(dself);
564     dumpfile_t *header;
565     char       *header_buf;
566
567     self = NDMP_DEVICE(dself);
568
569     if (device_in_error(self)) return FALSE;
570
571     if (!open_tape_agent(self)) {
572         /* error status was set by open_tape_agent */
573         return FALSE;
574     }
575
576     if (mode != ACCESS_WRITE && dself->volume_label == NULL) {
577         if (ndmp_device_read_label(dself) != DEVICE_STATUS_SUCCESS)
578             /* the error was set by ndmp_device_read_label */
579             return FALSE;
580     }
581
582     dself->access_mode = mode;
583     dself->in_file = FALSE;
584
585     if (!single_ndmp_mtio(self, NDMP9_MTIO_REW)) {
586         /* single_ndmp_mtio already set our error message */
587         return FALSE;
588     }
589
590     /* Position the tape */
591     switch (mode) {
592     case ACCESS_APPEND:
593         device_set_error(dself,
594             g_strdup("operation not supported"),
595             DEVICE_STATUS_DEVICE_ERROR);
596         return FALSE;
597         break;
598
599     case ACCESS_READ:
600         dself->file = 0;
601         break;
602
603     case ACCESS_WRITE:
604         header = make_tapestart_header(dself, label, timestamp);
605         g_assert(header != NULL);
606
607         header_buf = device_build_amanda_header(dself, header, NULL);
608         if (header_buf == NULL) {
609             device_set_error(dself,
610                 stralloc(_("Tapestart header won't fit in a single block!")),
611                 DEVICE_STATUS_DEVICE_ERROR);
612             dumpfile_free(header);
613             return FALSE;
614         }
615
616         switch (robust_write(self, header_buf, dself->block_size)) {
617             case ROBUST_WRITE_OK_LEOM:
618                 dself->is_eom = TRUE;
619                 /* fall through */
620             case ROBUST_WRITE_OK:
621                 break;
622
623             case ROBUST_WRITE_NO_SPACE:
624                 /* this would be an odd error to see writing the tape label, but
625                  * oh well */
626                 device_set_error(dself,
627                     stralloc(_("No space left on device")),
628                     DEVICE_STATUS_VOLUME_ERROR);
629                 dself->is_eom = TRUE;
630                 /* fall through */
631
632             case ROBUST_WRITE_ERROR:
633                 /* error was set by robust_write or above */
634                 dumpfile_free(header);
635                 amfree(header_buf);
636                 return FALSE;
637
638         }
639         amfree(header_buf);
640
641         if (!single_ndmp_mtio(self, NDMP9_MTIO_EOF)) {
642             /* error was set by single_ndmp_mtio */
643             dumpfile_free(header);
644             return FALSE;
645         }
646
647         dself->volume_label = newstralloc(dself->volume_label, label);
648         dself->volume_time = newstralloc(dself->volume_time, timestamp);
649         dumpfile_free(dself->volume_header);
650         dself->volume_header = header;
651
652         /* unset the VOLUME_UNLABELED flag, if it was set */
653         device_set_error(dself, NULL, DEVICE_STATUS_SUCCESS);
654         dself->file = 0;
655         break;
656
657     default:
658         g_assert_not_reached();
659     }
660
661     return TRUE;
662 }
663
664 static gboolean
665 ndmp_device_finish(
666     Device *dself)
667 {
668     gboolean rval;
669
670     NdmpDevice *self = NDMP_DEVICE(dself);
671     rval = !device_in_error(dself);
672
673     /* we're not in a file anymore */
674     dself->access_mode = ACCESS_NULL;
675
676     if (!close_tape_agent(self)) {
677         /* error is set by close_tape_agent */
678         rval = FALSE;
679     }
680
681     if (self->ndmp)
682         close_connection(self);
683
684     return rval;
685 }
686
687 static gboolean
688 ndmp_device_eject(
689     Device *dself)
690 {
691     NdmpDevice *self = NDMP_DEVICE(dself);
692     if (device_in_error(dself)) return FALSE;
693
694     if (!single_ndmp_mtio(self, NDMP9_MTIO_OFF)) {
695         /* error was set by single_ndmp_mtio */
696         return FALSE;
697     }
698
699     return TRUE;
700 }
701
702
703 /* functions for writing */
704
705 static gboolean
706 ndmp_device_start_file(
707     Device     *dself,
708     dumpfile_t *header)
709 {
710     NdmpDevice *self = NDMP_DEVICE(dself);
711     char *header_buf;
712
713     if (device_in_error(self)) return FALSE;
714
715     dself->is_eof = FALSE;
716     dself->is_eom = FALSE;
717
718     /* set the blocksize in the header properly */
719     header->blocksize = dself->block_size;
720
721     header_buf = device_build_amanda_header(dself, header, NULL);
722     if (header_buf == NULL) {
723         device_set_error(dself,
724             stralloc(_("Amanda file header won't fit in a single block!")),
725             DEVICE_STATUS_DEVICE_ERROR);
726         return FALSE;
727     }
728
729     switch (robust_write(self, header_buf, dself->block_size)) {
730         case ROBUST_WRITE_OK_LEOM:
731             dself->is_eom = TRUE;
732             /* fall through */
733
734         case ROBUST_WRITE_OK:
735             break;
736
737         case ROBUST_WRITE_NO_SPACE:
738             /* this would be an odd error to see writing the tape label, but
739              * oh well */
740             device_set_error(dself,
741                 stralloc(_("No space left on device")),
742                 DEVICE_STATUS_VOLUME_ERROR);
743             dself->is_eom = TRUE;
744             /* fall through */
745
746         case ROBUST_WRITE_ERROR:
747             /* error was set by robust_write or above */
748             amfree(header_buf);
749             return FALSE;
750     }
751     amfree(header_buf);
752
753     /* arrange the file numbers correctly */
754     dself->in_file = TRUE;
755     if (!ndmp_get_state(self)) {
756         /* error already set by ndmp_get_state */
757         return FALSE;
758     }
759
760     /* double-check that the tape agent gave us a non-bogus file number */
761     g_assert(dself->file > 0);
762
763     return TRUE;
764 }
765
766 static gboolean
767 ndmp_device_write_block(
768     Device   *dself,
769     guint     size,
770     gpointer  data)
771 {
772     NdmpDevice *self = NDMP_DEVICE(dself);
773     gpointer replacement_buffer = NULL;
774
775     if (device_in_error(self)) return FALSE;
776
777     /* zero out to the end of a short block -- tape devices only write
778      * whole blocks. */
779     if (size < dself->block_size) {
780         replacement_buffer = malloc(dself->block_size);
781         memcpy(replacement_buffer, data, size);
782         bzero(replacement_buffer+size, dself->block_size-size);
783
784         data = replacement_buffer;
785         size = dself->block_size;
786     }
787
788     switch (robust_write(self, data, size)) {
789         case ROBUST_WRITE_OK_LEOM:
790             dself->is_eom = TRUE;
791             /* fall through */
792
793         case ROBUST_WRITE_OK:
794             break;
795
796         case ROBUST_WRITE_NO_SPACE:
797             /* this would be an odd error to see writing the tape label, but
798              * oh well */
799             device_set_error(dself,
800                 stralloc(_("No space left on device")),
801                 DEVICE_STATUS_VOLUME_ERROR);
802             dself->is_eom = TRUE;
803             /* fall through */
804
805         case ROBUST_WRITE_ERROR:
806             /* error was set by robust_write or above */
807             if (replacement_buffer) g_free(replacement_buffer);
808             return FALSE;
809     }
810
811     dself->block++;
812
813     if (replacement_buffer) g_free(replacement_buffer);
814     return TRUE;
815 }
816
817 static gboolean
818 ndmp_device_finish_file(
819     Device *dself)
820 {
821     NdmpDevice *self = NDMP_DEVICE(dself);
822
823     if (device_in_error(dself)) return FALSE;
824
825     /* we're not in a file anymore */
826     dself->in_file = FALSE;
827
828     if (!single_ndmp_mtio(self, NDMP9_MTIO_EOF)) {
829         /* error was set by single_ndmp_mtio */
830         dself->is_eom = TRUE;
831         return FALSE;
832     }
833
834     return TRUE;
835 }
836
837 /* functions for reading */
838
839 static dumpfile_t*
840 ndmp_device_seek_file(
841     Device *dself,
842     guint   file)
843 {
844     NdmpDevice *self = NDMP_DEVICE(dself);
845     gint delta;
846     guint resid;
847     gpointer buf;
848     guint64 buf_size;
849     dumpfile_t *header;
850     gsize read_block_size = 0;
851
852     if (device_in_error(dself)) return FALSE;
853
854     /* file 0 is the tape header, and isn't seekable as a distinct
855      * Device-API-level file */
856     if (file == 0) {
857         device_set_error(dself,
858             g_strdup("cannot seek to file 0"),
859             DEVICE_STATUS_DEVICE_ERROR);
860         return NULL;
861     }
862
863     /* first, make sure the file and block numbers are correct */
864     if (!ndmp_get_state(self)) {
865         /* error already set by ndmp_get_state */
866         return FALSE;
867     }
868
869     /* now calculate the file delta */
870     delta = file - dself->file;
871
872     if (delta <= 0) {
873         /* Note that this algorithm will rewind to the beginning of
874          * the current part, too */
875
876         /* BSF *past* the filemark we want to seek to */
877         if (!ndmp_connection_tape_mtio(self->ndmp, NDMP9_MTIO_BSF, -delta + 1, &resid)) {
878             set_error_from_ndmp(self);
879             return NULL;
880         }
881         if (resid != 0)
882             goto incomplete_bsf;
883
884         /* now we are on the BOT side of the filemark, but we want to be
885          * on the EOT side of it.  An FSF will get us there.. */
886         if (!ndmp_connection_tape_mtio(self->ndmp, NDMP9_MTIO_FSF, 1, &resid)) {
887             set_error_from_ndmp(self);
888             return NULL;
889         }
890
891         if (resid != 0) {
892 incomplete_bsf:
893             device_set_error(dself,
894                 g_strdup_printf("BSF operation failed to seek by %d files", resid),
895                 DEVICE_STATUS_DEVICE_ERROR);
896             return NULL;
897         }
898     } else /* (delta > 0) */ {
899         if (!ndmp_connection_tape_mtio(self->ndmp, NDMP9_MTIO_FSF, delta, &resid)) {
900             set_error_from_ndmp(self);
901             return FALSE;
902         }
903
904         /* if we didn't seek all the way there, then we're past the tapeend */
905         if (resid > 0) {
906             device_set_error(dself,
907                 vstrallocf(_("Could not seek forward to file %d"), file),
908                 DEVICE_STATUS_VOLUME_ERROR);
909             return NULL;
910         }
911     }
912
913     /* fix up status */
914     dself->in_file = TRUE;
915     dself->file = file;
916     dself->block = 0;
917
918     /* now read the header */
919     read_block_size = ndmp_device_read_size(self);
920     buf = g_malloc(read_block_size);
921     if (!ndmp_connection_tape_read(self->ndmp,
922                 buf, read_block_size, &buf_size)) {
923         switch (ndmp_connection_err_code(self->ndmp)) {
924             case NDMP9_EOF_ERR:
925             case NDMP9_EOM_ERR:
926                 return make_tapeend_header();
927
928             default:
929                 set_error_from_ndmp(self);
930                 g_free(buf);
931                 return NULL;
932         }
933     }
934
935     header = g_new(dumpfile_t, 1);
936     fh_init(header);
937     parse_file_header(buf, header, buf_size);
938     g_free(buf);
939
940     return header;
941 }
942
943 static gboolean
944 ndmp_device_seek_block(
945     Device  *dself,
946     guint64  block)
947 {
948     if (device_in_error(dself)) return FALSE;
949
950     dself->block = block;
951
952     device_set_error(dself, g_strdup("operation not supported"), DEVICE_STATUS_DEVICE_ERROR);
953     return FALSE;
954 }
955
956 static int
957 ndmp_device_read_block (Device * dself, gpointer data, int *size_req) {
958     NdmpDevice *self = NDMP_DEVICE(dself);
959     guint64 requested, actual;
960     gsize read_block_size = ndmp_device_read_size(self);
961
962     /* We checked the NDMP device's blocksize when the device was opened, which should
963      * catch any misalignent of server block size and Amanda block size */
964
965     g_assert(read_block_size < INT_MAX); /* check data type mismatch */
966     if (!data || *size_req < (int)(read_block_size)) {
967         *size_req = (int)(read_block_size);
968         return 0;
969     }
970
971     requested = *size_req;
972     if (!ndmp_connection_tape_read(self->ndmp,
973         data,
974         requested,
975         &actual)) {
976
977         /* handle known errors */
978         switch (ndmp_connection_err_code(self->ndmp)) {
979             case NDMP9_EOM_ERR:
980             case NDMP9_EOF_ERR:
981                 dself->is_eof = TRUE;
982                 return -1;
983
984             default:
985                 set_error_from_ndmp(self);
986                 return -1;
987         }
988     }
989
990     *size_req = (int)actual; /* cast is OK - requested size was < INT_MAX too */
991
992     return *size_req;
993 }
994
995 static gboolean
996 listen_impl(
997     Device *dself,
998     gboolean for_writing,
999     DirectTCPAddr **addrs)
1000 {
1001     NdmpDevice *self = NDMP_DEVICE(dself);
1002
1003     if (device_in_error(self)) return FALSE;
1004
1005     /* check status */
1006     g_assert(!self->listen_addrs);
1007
1008     if (!open_tape_agent(self)) {
1009         /* error message was set by open_tape_agent */
1010         return FALSE;
1011     }
1012
1013     /* first, set the window to an empty span so that the mover doesn't start
1014      * reading or writing data immediately.  NDMJOB tends to reset the record
1015      * size periodically (in direct contradiction to the spec), so we reset it
1016      * here as well. */
1017     if (!ndmp_connection_mover_set_record_size(self->ndmp,
1018                 DEVICE(self)->block_size)) {
1019         set_error_from_ndmp(self);
1020         return FALSE;
1021     }
1022
1023     if (!ndmp_connection_mover_set_window(self->ndmp, 0, 0)) {
1024         set_error_from_ndmp(self);
1025         return FALSE;
1026     }
1027
1028     /* then tell it to start listening */
1029     if (!ndmp_connection_mover_listen(self->ndmp,
1030                 for_writing? NDMP4_MOVER_MODE_READ : NDMP4_MOVER_MODE_WRITE,
1031                 NDMP4_ADDR_TCP,
1032                 addrs)) {
1033         set_error_from_ndmp(self);
1034         return FALSE;
1035     }
1036     self->listen_addrs = *addrs;
1037     self->for_writing = for_writing;
1038
1039     return TRUE;
1040 }
1041
1042 static gboolean
1043 accept_impl(
1044     Device *dself,
1045     DirectTCPConnection **dtcpconn,
1046     ProlongProc prolong,
1047     gpointer prolong_data)
1048 {
1049     NdmpDevice *self = NDMP_DEVICE(dself);
1050     ndmp9_mover_state state;
1051     guint64 bytes_moved;
1052     ndmp9_mover_mode mode;
1053     ndmp9_mover_pause_reason reason;
1054     guint64 seek_position;
1055
1056     if (device_in_error(self)) return FALSE;
1057
1058     g_assert(self->listen_addrs);
1059
1060     *dtcpconn = NULL;
1061
1062     /* TODO: support aborting this operation - maybe just always poll? */
1063     prolong = prolong;
1064     prolong_data = prolong_data;
1065
1066     if (!self->for_writing) {
1067         /* when reading, we don't get any kind of notification that the
1068          * connection has been established, but we can't call NDMP_MOVER_READ
1069          * until the mover is active.  So we have to poll, waiting for ACTIVE.
1070          * This is ugly. */
1071         gulong backoff = G_USEC_PER_SEC/20; /* 5 msec */
1072         while (1) {
1073             if (!ndmp_connection_mover_get_state(self->ndmp,
1074                     &state, &bytes_moved, NULL, NULL)) {
1075                 set_error_from_ndmp(self);
1076                 return FALSE;
1077             }
1078
1079             if (state != NDMP4_MOVER_STATE_LISTEN)
1080                 break;
1081
1082             /* back off a little bit to give the other side time to breathe,
1083              * but not more than one second */
1084             g_usleep(backoff);
1085             backoff *= 2;
1086             if (backoff > G_USEC_PER_SEC)
1087                 backoff = G_USEC_PER_SEC;
1088         }
1089
1090         /* double-check state */
1091         if (state != NDMP4_MOVER_STATE_ACTIVE) {
1092             device_set_error(DEVICE(self),
1093                 g_strdup("mover did not enter the ACTIVE state as expected"),
1094                 DEVICE_STATUS_DEVICE_ERROR);
1095             return FALSE;
1096         }
1097
1098         /* now, we need to get this into the PAUSED state, since right now we
1099          * aren't allowed to perform any tape movement commands.  So we issue a
1100          * MOVER_READ request for the whole darn image stream after setting the
1101          * usual empty window. Note that this means the whole dump will be read
1102          * in one MOVER_READ operation, even if it does not begin at the
1103          * beginning of a part. */
1104         if (!ndmp_connection_mover_read(self->ndmp, 0, G_MAXUINT64)) {
1105             set_error_from_ndmp(self);
1106             return FALSE;
1107         }
1108
1109         /* now we should expect a notice that the mover has paused */
1110     } else {
1111         /* when writing, the mover will pause as soon as the first byte comes
1112          * in, so there's no need to do anything to trigger the pause. */
1113     }
1114
1115     /* NDMJOB sends NDMP9_MOVER_PAUSE_SEEK to indicate that it wants to write
1116      * outside the window, while the standard specifies .._EOW, instead.  When
1117      * reading to a connection, we get the appropriate .._SEEK.  It's easy
1118      * enough to handle both. */
1119
1120     if (!ndmp_connection_wait_for_notify(self->ndmp,
1121             NULL,
1122             NULL,
1123             &reason, &seek_position)) {
1124         set_error_from_ndmp(self);
1125         return FALSE;
1126     }
1127
1128     if (reason != NDMP9_MOVER_PAUSE_SEEK && reason != NDMP9_MOVER_PAUSE_EOW) {
1129         device_set_error(DEVICE(self),
1130             g_strdup_printf("got NOTIFY_MOVER_PAUSED, but not because of EOW or SEEK"),
1131             DEVICE_STATUS_DEVICE_ERROR);
1132         return FALSE;
1133     }
1134
1135     g_free(self->listen_addrs);
1136     self->listen_addrs = NULL;
1137
1138     if (self->for_writing)
1139         mode = NDMP4_MOVER_MODE_READ;
1140     else
1141         mode = NDMP4_MOVER_MODE_WRITE;
1142
1143     /* set up the new directtcp connection */
1144     if (self->directtcp_conn)
1145         g_object_unref(self->directtcp_conn);
1146     self->directtcp_conn =
1147         directtcp_connection_ndmp_new(self->ndmp, mode);
1148     *dtcpconn = DIRECTTCP_CONNECTION(self->directtcp_conn);
1149
1150     /* reference it for the caller */
1151     g_object_ref(*dtcpconn);
1152
1153     return TRUE;
1154 }
1155
1156 static gboolean
1157 connect_impl(
1158     Device *dself,
1159     gboolean for_writing,
1160     DirectTCPAddr *addrs,
1161     DirectTCPConnection **dtcpconn,
1162     ProlongProc prolong,
1163     gpointer prolong_data)
1164 {
1165     NdmpDevice *self = NDMP_DEVICE(dself);
1166     ndmp9_mover_mode mode;
1167     ndmp9_mover_pause_reason reason;
1168     guint64 seek_position;
1169
1170     g_assert(!self->listen_addrs);
1171
1172     *dtcpconn = NULL;
1173     self->for_writing = for_writing;
1174
1175     /* TODO: support aborting this operation - maybe just always poll? */
1176     prolong = prolong;
1177     prolong_data = prolong_data;
1178
1179     if (!open_tape_agent(self)) {
1180         /* error message was set by open_tape_agent */
1181         return FALSE;
1182     }
1183
1184     /* first, set the window to an empty span so that the mover doesn't start
1185      * reading or writing data immediately.  NDMJOB tends to reset the record
1186      * size periodically (in direct contradiction to the spec), so we reset it
1187      * here as well. */
1188     if (!ndmp_connection_mover_set_record_size(self->ndmp,
1189                 DEVICE(self)->block_size)) {
1190         set_error_from_ndmp(self);
1191         return FALSE;
1192     }
1193
1194     if (!ndmp_connection_mover_set_window(self->ndmp, 0, 0)) {
1195         set_error_from_ndmp(self);
1196         return FALSE;
1197     }
1198
1199     if (self->for_writing)
1200         mode = NDMP4_MOVER_MODE_READ;
1201     else
1202         mode = NDMP4_MOVER_MODE_WRITE;
1203
1204     if (!ndmp_connection_mover_connect(self->ndmp, mode, addrs)) {
1205         set_error_from_ndmp(self);
1206         return FALSE;
1207     }
1208
1209     if (!self->for_writing) {
1210         /* The agent is in the ACTIVE state, and will remain so until we tell
1211          * it to do something else.  The thing we want to is for it to start
1212          * reading data from the tape, which will immediately trigger an EOW or
1213          * SEEK pause. */
1214         if (!ndmp_connection_mover_read(self->ndmp, 0, G_MAXUINT64)) {
1215             set_error_from_ndmp(self);
1216             return FALSE;
1217         }
1218
1219         /* now we should expect a notice that the mover has paused */
1220     } else {
1221         /* when writing, the mover will pause as soon as the first byte comes
1222          * in, so there's no need to do anything to trigger the pause. */
1223     }
1224
1225     /* NDMJOB sends NDMP9_MOVER_PAUSE_SEEK to indicate that it wants to write
1226      * outside the window, while the standard specifies .._EOW, instead.  When
1227      * reading to a connection, we get the appropriate .._SEEK.  It's easy
1228      * enough to handle both. */
1229
1230     if (!ndmp_connection_wait_for_notify(self->ndmp,
1231             NULL,
1232             NULL,
1233             &reason, &seek_position)) {
1234         set_error_from_ndmp(self);
1235         return FALSE;
1236     }
1237
1238     if (reason != NDMP9_MOVER_PAUSE_SEEK && reason != NDMP9_MOVER_PAUSE_EOW) {
1239         device_set_error(DEVICE(self),
1240             g_strdup_printf("got NOTIFY_MOVER_PAUSED, but not because of EOW or SEEK"),
1241             DEVICE_STATUS_DEVICE_ERROR);
1242         return FALSE;
1243     }
1244
1245     if (self->listen_addrs) {
1246         g_free(self->listen_addrs);
1247         self->listen_addrs = NULL;
1248     }
1249
1250     /* set up the new directtcp connection */
1251     if (self->directtcp_conn)
1252         g_object_unref(self->directtcp_conn);
1253     self->directtcp_conn =
1254         directtcp_connection_ndmp_new(self->ndmp, mode);
1255     *dtcpconn = DIRECTTCP_CONNECTION(self->directtcp_conn);
1256
1257     /* reference it for the caller */
1258     g_object_ref(*dtcpconn);
1259
1260     return TRUE;
1261 }
1262
1263 static gboolean
1264 write_from_connection_impl(
1265     Device *dself,
1266     guint64 size,
1267     guint64 *actual_size)
1268 {
1269     NdmpDevice *self = NDMP_DEVICE(dself);
1270     DirectTCPConnectionNDMP *nconn = self->directtcp_conn;
1271     gboolean eom = FALSE, eof = FALSE, eow = FALSE;
1272     ndmp9_mover_state mover_state;
1273     ndmp9_mover_halt_reason halt_reason;
1274     ndmp9_mover_pause_reason pause_reason;
1275     guint64 bytes_moved_before, bytes_moved_after;
1276     gchar *err;
1277
1278     if (device_in_error(self)) return FALSE;
1279
1280     if (actual_size)
1281         *actual_size = 0;
1282
1283     /* if this is false, then the caller did not use use_connection correctly */
1284     g_assert(self->directtcp_conn != NULL);
1285     g_assert(self->ndmp == nconn->ndmp);
1286     g_assert(nconn->mode == NDMP4_MOVER_MODE_READ);
1287
1288     if (!ndmp_connection_mover_get_state(self->ndmp,
1289                 &mover_state, &bytes_moved_before, NULL, NULL)) {
1290         set_error_from_ndmp(self);
1291         return FALSE;
1292     }
1293
1294     /* the mover had best be PAUSED right now */
1295     g_assert(mover_state == NDMP4_MOVER_STATE_PAUSED);
1296
1297     if (!ndmp_connection_mover_set_window(self->ndmp,
1298                 nconn->offset,
1299                 size? size : G_MAXUINT64 - nconn->offset)) {
1300         set_error_from_ndmp(self);
1301         return FALSE;
1302     }
1303
1304     if (!ndmp_connection_mover_continue(self->ndmp)) {
1305         set_error_from_ndmp(self);
1306         return FALSE;
1307     }
1308
1309     /* now wait for the mover to pause itself again, or halt on EOF or an error */
1310     if (!ndmp_connection_wait_for_notify(self->ndmp,
1311             NULL,
1312             &halt_reason,
1313             &pause_reason, NULL)) {
1314         set_error_from_ndmp(self);
1315         return FALSE;
1316     }
1317
1318     err = NULL;
1319     if (pause_reason) {
1320         switch (pause_reason) {
1321             case NDMP9_MOVER_PAUSE_EOM:
1322                 eom = TRUE;
1323                 break;
1324
1325             /* ndmjob sends .._SEEK when it should send .._EOW, so deal with
1326                 * both equivalently */
1327             case NDMP9_MOVER_PAUSE_EOW:
1328             case NDMP9_MOVER_PAUSE_SEEK:
1329                 eow = TRUE;
1330                 break;
1331
1332             default:
1333                 err = "got NOTIFY_MOVER_PAUSED, but not because of EOW or SEEK";
1334                 break;
1335         }
1336     } else if (halt_reason) {
1337         switch (halt_reason) {
1338             case NDMP9_MOVER_HALT_CONNECT_CLOSED:
1339                 eof = TRUE;
1340                 break;
1341
1342             default:
1343             case NDMP9_MOVER_HALT_ABORTED:
1344             /* case NDMP9_MOVER_HALT_MEDIA_ERROR: <-- not in ndmjob */
1345             case NDMP9_MOVER_HALT_INTERNAL_ERROR:
1346             case NDMP9_MOVER_HALT_CONNECT_ERROR:
1347                 err = "unexpected NDMP_NOTIFY_MOVER_HALTED";
1348                 break;
1349         }
1350     }
1351
1352     if (err) {
1353         device_set_error(DEVICE(self),
1354             g_strdup_printf("waiting for accept: %s", err),
1355             DEVICE_STATUS_DEVICE_ERROR);
1356         return FALSE;
1357     }
1358
1359     /* no error, so the mover stopped due to one of EOM (volume out of space),
1360      * EOF (data connection is done), or EOW (maximum part size was written).
1361      * In any case, we want to know how many bytes were written. */
1362
1363     if (!ndmp_connection_mover_get_state(self->ndmp,
1364                 &mover_state, &bytes_moved_after, NULL, NULL)) {
1365         set_error_from_ndmp(self);
1366         return FALSE;
1367     }
1368     size = bytes_moved_after - bytes_moved_before;
1369     nconn->offset += size;
1370
1371     if (actual_size) {
1372         *actual_size = bytes_moved_after - bytes_moved_before;
1373     }
1374
1375     if (eow) {
1376         ; /* mover finished the whole part -- nothing to report! */
1377     } else if (eof) {
1378         DEVICE(self)->is_eof = TRUE;
1379     } else if (eom) {
1380         /* this is a *lossless* EOM, so no need to set error, but
1381          * we do need to figure out the actual size */
1382         DEVICE(self)->is_eom = TRUE;
1383     } else {
1384         error("not reached");
1385     }
1386
1387     return TRUE;
1388 }
1389
1390 static gboolean
1391 read_to_connection_impl(
1392     Device *dself,
1393     guint64 size,
1394     guint64 *actual_size)
1395 {
1396     NdmpDevice *self = NDMP_DEVICE(dself);
1397     DirectTCPConnectionNDMP *nconn = self->directtcp_conn;
1398     gboolean eom = FALSE, eof = FALSE, eow = FALSE;
1399     ndmp9_mover_state mover_state;
1400     ndmp9_mover_halt_reason halt_reason;
1401     ndmp9_mover_pause_reason pause_reason;
1402     guint64 bytes_moved_before, bytes_moved_after;
1403     gchar *err;
1404
1405     if (actual_size)
1406         *actual_size = 0;
1407
1408     if (device_in_error(self)) return FALSE;
1409
1410     /* if this is false, then the caller did not use use_connection correctly */
1411     g_assert(nconn != NULL);
1412     g_assert(self->ndmp == nconn->ndmp);
1413     g_assert(nconn->mode == NDMP4_MOVER_MODE_WRITE);
1414
1415     if (!ndmp_connection_mover_get_state(self->ndmp,
1416                 &mover_state, &bytes_moved_before, NULL, NULL)) {
1417         set_error_from_ndmp(self);
1418         return FALSE;
1419     }
1420
1421     /* the mover had best be PAUSED right now */
1422     g_assert(mover_state == NDMP4_MOVER_STATE_PAUSED);
1423
1424     if (!ndmp_connection_mover_set_window(self->ndmp,
1425                 nconn->offset,
1426                 size? size : G_MAXUINT64 - nconn->offset)) {
1427         set_error_from_ndmp(self);
1428         return FALSE;
1429     }
1430
1431     if (!ndmp_connection_mover_continue(self->ndmp)) {
1432         set_error_from_ndmp(self);
1433         return FALSE;
1434     }
1435
1436     /* now wait for the mover to pause itself again, or halt on EOF or an error */
1437     if (!ndmp_connection_wait_for_notify(self->ndmp,
1438             NULL,
1439             &halt_reason,
1440             &pause_reason, NULL)) {
1441         set_error_from_ndmp(self);
1442         return FALSE;
1443     }
1444
1445     err = NULL;
1446     if (pause_reason) {
1447         switch (pause_reason) {
1448             case NDMP9_MOVER_PAUSE_EOF:
1449                 eof = TRUE;
1450                 break;
1451
1452             /* ndmjob sends .._SEEK when it should send .._EOW, so deal with
1453                 * both equivalently */
1454             case NDMP9_MOVER_PAUSE_EOW:
1455             case NDMP9_MOVER_PAUSE_SEEK:
1456                 eow = TRUE;
1457                 break;
1458
1459             default:
1460                 err = "got NOTIFY_MOVER_PAUSED, but not because of EOW or SEEK";
1461                 break;
1462         }
1463     } else if (halt_reason) {
1464         switch (halt_reason) {
1465             case NDMP9_MOVER_HALT_CONNECT_CLOSED:
1466                 eof = TRUE;
1467                 break;
1468
1469             default:
1470             case NDMP9_MOVER_HALT_ABORTED:
1471             /* case NDMP9_MOVER_HALT_MEDIA_ERROR: <-- not in ndmjob */
1472             case NDMP9_MOVER_HALT_INTERNAL_ERROR:
1473             case NDMP9_MOVER_HALT_CONNECT_ERROR:
1474                 err = "unexpected NDMP_NOTIFY_MOVER_HALTED";
1475                 break;
1476         }
1477     }
1478
1479     if (err) {
1480         device_set_error(DEVICE(self),
1481             g_strdup_printf("waiting for accept: %s", err),
1482             DEVICE_STATUS_DEVICE_ERROR);
1483         return FALSE;
1484     }
1485
1486     /* no error, so the mover stopped due to one of EOM (volume out of space),
1487      * EOF (data connection is done), or EOW (maximum part size was written).
1488      * In any case, we want to know how many bytes were written. */
1489
1490     if (!ndmp_connection_mover_get_state(self->ndmp,
1491                 &mover_state, &bytes_moved_after, NULL, NULL)) {
1492         set_error_from_ndmp(self);
1493         return FALSE;
1494     }
1495     size = bytes_moved_after - bytes_moved_before;
1496     nconn->offset += size;
1497
1498     if (actual_size) {
1499         *actual_size = bytes_moved_after - bytes_moved_before;
1500     }
1501
1502     if (eow) {
1503         ; /* mover finished the whole part -- nothing to report! */
1504     } else if (eof) {
1505         DEVICE(self)->is_eof = TRUE;
1506     } else if (eom) {
1507         /* this is a *lossless* EOM, so no need to set error, but
1508          * we do need to figure out the actual size */
1509         DEVICE(self)->is_eom = TRUE;
1510     } else {
1511         error("not reached");
1512     }
1513
1514     return TRUE;
1515 }
1516
1517 static gboolean
1518 use_connection_impl(
1519     Device *dself,
1520     DirectTCPConnection *conn)
1521 {
1522     NdmpDevice *self = NDMP_DEVICE(dself);
1523     DirectTCPConnectionNDMP *nconn;
1524
1525     /* the device_use_connection_impl wrapper already made sure we're in
1526      * ACCESS_NULL, but we may have opened the tape service already to read
1527      * a label - so close it to be sure */
1528     if (!close_tape_agent(self)) {
1529         /* error was already set by close_tape_agent */
1530         return FALSE;
1531     }
1532
1533     /* we had best not be listening when this is called */
1534     g_assert(!self->listen_addrs);
1535
1536     if (!IS_DIRECTTCP_CONNECTION_NDMP(conn)) {
1537         device_set_error(DEVICE(self),
1538             g_strdup("existing DirectTCPConnection is not compatible with this device"),
1539             DEVICE_STATUS_DEVICE_ERROR);
1540         return FALSE;
1541     }
1542
1543     if (self->directtcp_conn)
1544         g_object_unref(self->directtcp_conn);
1545     self->directtcp_conn = nconn = DIRECTTCP_CONNECTION_NDMP(conn);
1546     g_object_ref(self->directtcp_conn);
1547
1548     /* if this is a different connection, use it */
1549     if (nconn->ndmp != self->ndmp) {
1550         if (self->ndmp)
1551             close_connection(self);
1552         self->ndmp = nconn->ndmp;
1553         g_object_ref(self->ndmp);
1554     }
1555
1556     return TRUE;
1557 }
1558
1559 /*
1560  * Class mechanics
1561  */
1562
1563 static gboolean
1564 ndmp_device_set_username_fn(Device *dself,
1565     DevicePropertyBase *base, GValue *val,
1566     PropertySurety surety, PropertySource source)
1567 {
1568     NdmpDevice *self = NDMP_DEVICE(dself);
1569
1570     amfree(self->ndmp_username);
1571     self->ndmp_username = g_value_dup_string(val);
1572     device_clear_volume_details(dself);
1573
1574     return device_simple_property_set_fn(dself, base, val, surety, source);
1575 }
1576
1577 static gboolean
1578 ndmp_device_set_password_fn(Device *dself,
1579     DevicePropertyBase *base, GValue *val,
1580     PropertySurety surety, PropertySource source)
1581 {
1582     NdmpDevice *self = NDMP_DEVICE(dself);
1583
1584     amfree(self->ndmp_password);
1585     self->ndmp_password = g_value_dup_string(val);
1586     device_clear_volume_details(dself);
1587
1588     return device_simple_property_set_fn(dself, base, val, surety, source);
1589 }
1590
1591 static gboolean
1592 ndmp_device_set_auth_fn(Device *dself,
1593     DevicePropertyBase *base, GValue *val,
1594     PropertySurety surety, PropertySource source)
1595 {
1596     NdmpDevice *self = NDMP_DEVICE(dself);
1597
1598     amfree(self->ndmp_auth);
1599     self->ndmp_auth = g_value_dup_string(val);
1600     device_clear_volume_details(dself);
1601
1602     return device_simple_property_set_fn(dself, base, val, surety, source);
1603 }
1604
1605 static gboolean
1606 ndmp_device_set_verbose_fn(Device *p_self, DevicePropertyBase *base,
1607     GValue *val, PropertySurety surety, PropertySource source)
1608 {
1609     NdmpDevice *self = NDMP_DEVICE(p_self);
1610
1611     self->verbose = g_value_get_boolean(val);
1612
1613     /* if the connection is active, set up verbose logging or turn it off */
1614     if (self->ndmp) {
1615         ndmp_connection_set_verbose(self->ndmp, self->verbose);
1616     }
1617
1618
1619     return device_simple_property_set_fn(p_self, base, val, surety, source);
1620 }
1621
1622 static gboolean
1623 ndmp_device_set_read_block_size_fn(Device *p_self, DevicePropertyBase *base G_GNUC_UNUSED,
1624     GValue *val, PropertySurety surety, PropertySource source)
1625 {
1626     NdmpDevice *self = NDMP_DEVICE(p_self);
1627     gsize read_block_size = g_value_get_uint(val);
1628
1629     if (read_block_size != 0 &&
1630             ((gsize)read_block_size < p_self->block_size ||
1631              (gsize)read_block_size > p_self->max_block_size))
1632         return FALSE;
1633
1634     self->read_block_size = read_block_size;
1635
1636     /* use the READ_BLOCK_SIZE, even if we're invoked to get the old READ_BUFFER_SIZE */
1637     return device_simple_property_set_fn(p_self, base,
1638                                         val, surety, source);
1639 }
1640
1641 static void
1642 ndmp_device_class_init(NdmpDeviceClass * c G_GNUC_UNUSED)
1643 {
1644     GObjectClass *g_object_class = (GObjectClass*) c;
1645     DeviceClass *device_class = (DeviceClass *)c;
1646
1647     parent_class = g_type_class_ref (TYPE_DEVICE);
1648
1649     device_class->open_device = ndmp_device_open_device;
1650     device_class->read_label = ndmp_device_read_label;
1651     device_class->start = ndmp_device_start;
1652     device_class->finish = ndmp_device_finish;
1653     device_class->eject = ndmp_device_eject;
1654
1655     device_class->start_file = ndmp_device_start_file;
1656     device_class->write_block = ndmp_device_write_block;
1657     device_class->finish_file = ndmp_device_finish_file;
1658
1659     device_class->seek_file = ndmp_device_seek_file;
1660     device_class->seek_block = ndmp_device_seek_block;
1661     device_class->read_block = ndmp_device_read_block;
1662
1663     device_class->directtcp_supported = TRUE;
1664     device_class->listen = listen_impl;
1665     device_class->accept = accept_impl;
1666     device_class->connect = connect_impl;
1667     device_class->write_from_connection = write_from_connection_impl;
1668     device_class->read_to_connection = read_to_connection_impl;
1669     device_class->use_connection = use_connection_impl;
1670
1671     g_object_class->finalize = ndmp_device_finalize;
1672
1673     device_class_register_property(device_class, PROPERTY_NDMP_USERNAME,
1674             PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
1675             device_simple_property_get_fn,
1676             ndmp_device_set_username_fn);
1677
1678     device_class_register_property(device_class, PROPERTY_NDMP_PASSWORD,
1679             PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
1680             device_simple_property_get_fn,
1681             ndmp_device_set_password_fn);
1682
1683     device_class_register_property(device_class, PROPERTY_NDMP_AUTH,
1684             PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
1685             device_simple_property_get_fn,
1686             ndmp_device_set_auth_fn);
1687
1688     device_class_register_property(device_class, PROPERTY_VERBOSE,
1689             PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_MASK,
1690             device_simple_property_get_fn,
1691             ndmp_device_set_verbose_fn);
1692     device_class_register_property(device_class, PROPERTY_READ_BLOCK_SIZE,
1693             PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
1694             device_simple_property_get_fn,
1695             ndmp_device_set_read_block_size_fn);
1696 }
1697
1698 static void
1699 ndmp_device_init(NdmpDevice *self)
1700 {
1701     Device *dself = DEVICE(self);
1702     GValue response;
1703
1704     /* begin unconnected */
1705     self->ndmp = NULL;
1706
1707     /* decent defaults */
1708     dself->block_size = 32768;
1709     dself->min_block_size = 32768;
1710     dself->max_block_size = SIZE_MAX;
1711
1712     bzero(&response, sizeof(response));
1713
1714     g_value_init(&response, CONCURRENCY_PARADIGM_TYPE);
1715     g_value_set_enum(&response, CONCURRENCY_PARADIGM_EXCLUSIVE);
1716     device_set_simple_property(dself, PROPERTY_CONCURRENCY,
1717             &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
1718     g_value_unset(&response);
1719
1720     g_value_init(&response, STREAMING_REQUIREMENT_TYPE);
1721     g_value_set_enum(&response, STREAMING_REQUIREMENT_DESIRED);
1722     device_set_simple_property(dself, PROPERTY_STREAMING,
1723             &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
1724     g_value_unset(&response);
1725
1726     g_value_init(&response, G_TYPE_BOOLEAN);
1727     g_value_set_boolean(&response, FALSE);
1728     device_set_simple_property(dself, PROPERTY_APPENDABLE,
1729             &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
1730     g_value_unset(&response);
1731
1732     g_value_init(&response, G_TYPE_BOOLEAN);
1733     g_value_set_boolean(&response, FALSE);
1734     device_set_simple_property(dself, PROPERTY_PARTIAL_DELETION,
1735             &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
1736     g_value_unset(&response);
1737
1738     g_value_init(&response, G_TYPE_BOOLEAN);
1739     g_value_set_boolean(&response, FALSE);
1740     device_set_simple_property(dself, PROPERTY_FULL_DELETION,
1741             &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
1742     g_value_unset(&response);
1743
1744     g_value_init(&response, G_TYPE_BOOLEAN);
1745     g_value_set_boolean(&response, TRUE);
1746     device_set_simple_property(dself, PROPERTY_LEOM,
1747             &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
1748     g_value_unset(&response);
1749
1750     g_value_init(&response, MEDIA_ACCESS_MODE_TYPE);
1751     g_value_set_enum(&response, MEDIA_ACCESS_MODE_READ_WRITE);
1752     device_set_simple_property(dself, PROPERTY_MEDIUM_ACCESS_TYPE,
1753             &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
1754     g_value_unset(&response);
1755
1756     self->read_block_size = 0;
1757     g_value_init(&response, G_TYPE_UINT);
1758     g_value_set_uint(&response, self->read_block_size);
1759     device_set_simple_property(dself, PROPERTY_READ_BLOCK_SIZE,
1760             &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DEFAULT);
1761     g_value_unset(&response);
1762
1763     g_value_init(&response, G_TYPE_STRING);
1764     g_value_set_string(&response, "ndmp");
1765     device_set_simple_property(dself, PROPERTY_NDMP_USERNAME,
1766             &response, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
1767     g_value_unset(&response);
1768     self->ndmp_username = g_strdup("ndmp");
1769
1770     g_value_init(&response, G_TYPE_STRING);
1771     g_value_set_string(&response, "ndmp");
1772     device_set_simple_property(dself, PROPERTY_NDMP_PASSWORD,
1773             &response, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
1774     g_value_unset(&response);
1775     self->ndmp_password = g_strdup("ndmp");
1776
1777     g_value_init(&response, G_TYPE_STRING);
1778     g_value_set_string(&response, "md5");
1779     device_set_simple_property(dself, PROPERTY_NDMP_AUTH,
1780             &response, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
1781     g_value_unset(&response);
1782     self->ndmp_auth = g_strdup("md5");
1783
1784 }
1785
1786 static GType
1787 ndmp_device_get_type(void)
1788 {
1789     static GType type = 0;
1790
1791     if G_UNLIKELY(type == 0) {
1792         static const GTypeInfo info = {
1793             sizeof (NdmpDeviceClass),
1794             (GBaseInitFunc) NULL,
1795             (GBaseFinalizeFunc) NULL,
1796             (GClassInitFunc) ndmp_device_class_init,
1797             (GClassFinalizeFunc) NULL,
1798             NULL /* class_data */,
1799             sizeof (NdmpDevice),
1800             0 /* n_preallocs */,
1801             (GInstanceInitFunc) ndmp_device_init,
1802             NULL
1803         };
1804
1805         type = g_type_register_static (TYPE_DEVICE, "NdmpDevice", &info,
1806                                        (GTypeFlags)0);
1807     }
1808
1809     return type;
1810 }
1811
1812 static Device*
1813 ndmp_device_factory(
1814     char *device_name,
1815     char *device_type,
1816     char *device_node)
1817 {
1818     Device *rval;
1819     NdmpDevice * ndmp_rval;
1820     g_assert(0 == strcmp(device_type, NDMP_DEVICE_NAME));
1821     rval = DEVICE(g_object_new(TYPE_NDMP_DEVICE, NULL));
1822     ndmp_rval = (NdmpDevice *)rval;
1823
1824     device_open_device(rval, device_name, device_type, device_node);
1825     return rval;
1826 }
1827
1828 void
1829 ndmp_device_register(void)
1830 {
1831     static const char * device_prefix_list[] = { NDMP_DEVICE_NAME, NULL };
1832
1833     /* register the device itself */
1834     register_device(ndmp_device_factory, device_prefix_list);
1835
1836     device_property_fill_and_register(&device_property_ndmp_username,
1837                                       G_TYPE_STRING, "ndmp_username",
1838        "Username for access to the NDMP agent");
1839     device_property_fill_and_register(&device_property_ndmp_password,
1840                                       G_TYPE_STRING, "ndmp_password",
1841        "Password for access to the NDMP agent");
1842     device_property_fill_and_register(&device_property_ndmp_auth,
1843                                       G_TYPE_STRING, "ndmp_auth",
1844        "Authentication method for the NDMP agent - md5 (default), text, none, or void");
1845 }
1846
1847 /*
1848  * DirectTCPConnectionNDMP implementation
1849  */
1850
1851 static char *
1852 directtcp_connection_ndmp_close(DirectTCPConnection *dself)
1853 {
1854     DirectTCPConnectionNDMP *self = DIRECTTCP_CONNECTION_NDMP(dself);
1855     char *rv = NULL;
1856     ndmp9_mover_state state;
1857     guint64 bytes_moved;
1858     ndmp9_mover_halt_reason reason;
1859     gboolean expect_notif = FALSE;
1860
1861     /* based on the current state, we may need to abort or stop the
1862      * mover before closing it */
1863     if (!ndmp_connection_mover_get_state(self->ndmp, &state,
1864                                     &bytes_moved, NULL, NULL)) {
1865         rv = ndmp_connection_err_msg(self->ndmp);
1866         goto error;
1867     }
1868
1869     switch (state) {
1870         case NDMP9_MOVER_STATE_HALTED:
1871             break; /* nothing to do but ndmp_mover_close, below */
1872
1873         case NDMP9_MOVER_STATE_PAUSED:
1874             if (!ndmp_connection_mover_close(self->ndmp)) {
1875                 rv = ndmp_connection_err_msg(self->ndmp);
1876                 goto error;
1877             }
1878             expect_notif = TRUE;
1879             break;
1880
1881         case NDMP9_MOVER_STATE_ACTIVE:
1882         default:
1883             if (!ndmp_connection_mover_abort(self->ndmp)) {
1884                 rv = ndmp_connection_err_msg(self->ndmp);
1885                 goto error;
1886             }
1887             expect_notif = TRUE;
1888             break;
1889     }
1890
1891     /* the spec isn't entirely clear that mover_close and mover_abort should
1892      * generate a NOTIF_MOVER_HALTED, but ndmjob does it */
1893     if (expect_notif) {
1894         if (!ndmp_connection_wait_for_notify(self->ndmp,
1895                 NULL,
1896                 &reason, /* value is ignored.. */
1897                 NULL, NULL)) {
1898             goto error;
1899         }
1900     }
1901
1902     if (!ndmp_connection_mover_stop(self->ndmp)) {
1903         rv = ndmp_connection_err_msg(self->ndmp);
1904         goto error;
1905     }
1906
1907 error:
1908     if (self->ndmp) {
1909         g_object_unref(self->ndmp);
1910         self->ndmp = NULL;
1911     }
1912
1913     return rv;
1914 }
1915
1916 static void
1917 directtcp_connection_ndmp_class_init(DirectTCPConnectionNDMPClass * c)
1918 {
1919     DirectTCPConnectionClass *connc = (DirectTCPConnectionClass *)c;
1920
1921     connc->close = directtcp_connection_ndmp_close;
1922 }
1923
1924 GType
1925 directtcp_connection_ndmp_get_type (void)
1926 {
1927     static GType type = 0;
1928
1929     if G_UNLIKELY(type == 0) {
1930         static const GTypeInfo info = {
1931             sizeof (DirectTCPConnectionNDMPClass),
1932             (GBaseInitFunc) NULL,
1933             (GBaseFinalizeFunc) NULL,
1934             (GClassInitFunc) directtcp_connection_ndmp_class_init,
1935             (GClassFinalizeFunc) NULL,
1936             NULL /* class_data */,
1937             sizeof (DirectTCPConnectionNDMP),
1938             0 /* n_preallocs */,
1939             (GInstanceInitFunc) NULL,
1940             NULL
1941         };
1942
1943         type = g_type_register_static(TYPE_DIRECTTCP_CONNECTION,
1944                                 "DirectTCPConnectionNDMP", &info, (GTypeFlags)0);
1945     }
1946
1947     return type;
1948 }
1949
1950 static DirectTCPConnectionNDMP *
1951 directtcp_connection_ndmp_new(
1952     NDMPConnection *ndmp,
1953     ndmp9_mover_mode mode)
1954 {
1955     DirectTCPConnectionNDMP *dcn = DIRECTTCP_CONNECTION_NDMP(
1956             g_object_new(TYPE_DIRECTTCP_CONNECTION_NDMP, NULL));
1957
1958     /* hang onto a copy of this NDMP connection */
1959     g_object_ref(ndmp);
1960     dcn->ndmp = ndmp;
1961     dcn->mode = mode;
1962     dcn->offset = 0;
1963
1964     return dcn;
1965 }