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