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