X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=device-src%2Fndmp-device.c;h=865f7407b47196eb625c57ad7819fb9c6897c0b1;hb=949b8910a5e23c4285d0b1aedacfc82a14dc97a5;hp=c832270208ff5698502ba52e56692b52db04530b;hpb=fd48f3e498442f0cbff5f3606c7c403d0566150e;p=debian%2Famanda diff --git a/device-src/ndmp-device.c b/device-src/ndmp-device.c index c832270..865f740 100644 --- a/device-src/ndmp-device.c +++ b/device-src/ndmp-device.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010 Zmanda, Inc. All Rights Reserved. + * Copyright (c) 2009-2012 Zmanda, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -25,6 +25,7 @@ #include "stream.h" #include "ndmlib.h" #include "ndmpconnobj.h" +#include "sockaddr-util.h" /* * Type checking and casting macros @@ -57,7 +58,7 @@ struct NdmpDevice_ { /* support for IndirectTCP */ int indirecttcp_sock; /* -1 if not in use */ - int force_indirecttcp; + int indirect; /* Current DirectTCPConnectionNDMP */ struct DirectTCPConnectionNDMP_ *directtcp_conn; @@ -70,6 +71,7 @@ struct NdmpDevice_ { gchar *ndmp_password; gchar *ndmp_auth; gboolean verbose; + gsize read_block_size; }; /* @@ -140,11 +142,12 @@ typedef enum { static DevicePropertyBase device_property_ndmp_username; static DevicePropertyBase device_property_ndmp_password; static DevicePropertyBase device_property_ndmp_auth; -static DevicePropertyBase device_property__force_indirecttcp; +static DevicePropertyBase device_property_indirect; #define PROPERTY_NDMP_USERNAME (device_property_ndmp_username.ID) #define PROPERTY_NDMP_PASSWORD (device_property_ndmp_password.ID) #define PROPERTY_NDMP_AUTH (device_property_ndmp_auth.ID) -#define PROPERTY__FORCE_INDIRECTTCP (device_property__force_indirecttcp.ID) +#define PROPERTY_INDIRECT (device_property_indirect.ID) + /* * prototypes @@ -153,6 +156,10 @@ static DevicePropertyBase device_property__force_indirecttcp; void ndmp_device_register(void); static void set_error_from_ndmp(NdmpDevice *self); +#define ndmp_device_read_size(self) \ + (((NdmpDevice *)(self))->read_block_size? \ + ((NdmpDevice *)(self))->read_block_size : ((Device *)(self))->block_size) + /* * Utility functions */ @@ -463,9 +470,10 @@ ndmp_device_read_label( Device *dself) { NdmpDevice *self = NDMP_DEVICE(dself); - dumpfile_t *header; + dumpfile_t *header = NULL; gpointer buf = NULL; guint64 buf_size = 0; + gsize read_block_size = 0; amfree(dself->volume_label); amfree(dself->volume_time); @@ -474,9 +482,6 @@ ndmp_device_read_label( if (device_in_error(self)) return dself->status; - header = dself->volume_header = g_new(dumpfile_t, 1); - fh_init(header); - if (!open_tape_agent(self)) { /* error status was set by open_tape_agent */ return dself->status; @@ -489,10 +494,11 @@ ndmp_device_read_label( /* read the tape header from the NDMP server */ dself->status = 0; - buf = g_malloc(dself->block_size); + read_block_size = ndmp_device_read_size(self); + buf = g_malloc(read_block_size); if (!ndmp_connection_tape_read(self->ndmp, buf, - dself->block_size, + read_block_size, &buf_size)) { /* handle known errors */ @@ -516,6 +522,8 @@ ndmp_device_read_label( device_set_error(dself, g_strdup(_("no tape label found")), DEVICE_STATUS_VOLUME_UNLABELED); + header = dself->volume_header = g_new(dumpfile_t, 1); + fh_init(header); goto read_err; default: @@ -524,6 +532,8 @@ ndmp_device_read_label( } } + header = dself->volume_header = g_new(dumpfile_t, 1); + fh_init(header); parse_file_header(buf, header, buf_size); read_err: @@ -580,7 +590,9 @@ ndmp_device_start( } dself->access_mode = mode; + g_mutex_lock(dself->device_mutex); dself->in_file = FALSE; + g_mutex_unlock(dself->device_mutex); if (!single_ndmp_mtio(self, NDMP9_MTIO_REW)) { /* single_ndmp_mtio already set our error message */ @@ -665,21 +677,23 @@ static gboolean ndmp_device_finish( Device *dself) { + gboolean rval; + NdmpDevice *self = NDMP_DEVICE(dself); - if (device_in_error(dself)) return FALSE; + rval = !device_in_error(dself); /* we're not in a file anymore */ dself->access_mode = ACCESS_NULL; if (!close_tape_agent(self)) { /* error is set by close_tape_agent */ - return FALSE; + rval = FALSE; } if (self->ndmp) close_connection(self); - return TRUE; + return rval; } static gboolean @@ -712,6 +726,9 @@ ndmp_device_start_file( dself->is_eof = FALSE; dself->is_eom = FALSE; + g_mutex_lock(dself->device_mutex); + dself->bytes_written = 0; + g_mutex_unlock(dself->device_mutex); /* set the blocksize in the header properly */ header->blocksize = dself->block_size; @@ -749,7 +766,9 @@ ndmp_device_start_file( amfree(header_buf); /* arrange the file numbers correctly */ + g_mutex_lock(dself->device_mutex); dself->in_file = TRUE; + g_mutex_unlock(dself->device_mutex); if (!ndmp_get_state(self)) { /* error already set by ndmp_get_state */ return FALSE; @@ -807,6 +826,9 @@ ndmp_device_write_block( } dself->block++; + g_mutex_lock(dself->device_mutex); + dself->bytes_written += size; + g_mutex_unlock(dself->device_mutex); if (replacement_buffer) g_free(replacement_buffer); return TRUE; @@ -821,7 +843,9 @@ ndmp_device_finish_file( if (device_in_error(dself)) return FALSE; /* we're not in a file anymore */ + g_mutex_lock(dself->device_mutex); dself->in_file = FALSE; + g_mutex_unlock(dself->device_mutex); if (!single_ndmp_mtio(self, NDMP9_MTIO_EOF)) { /* error was set by single_ndmp_mtio */ @@ -845,6 +869,7 @@ ndmp_device_seek_file( gpointer buf; guint64 buf_size; dumpfile_t *header; + gsize read_block_size = 0; if (device_in_error(dself)) return FALSE; @@ -908,14 +933,20 @@ incomplete_bsf: } /* fix up status */ + g_mutex_lock(dself->device_mutex); dself->in_file = TRUE; + g_mutex_unlock(dself->device_mutex); dself->file = file; dself->block = 0; + g_mutex_lock(dself->device_mutex); + dself->bytes_read = 0; + g_mutex_unlock(dself->device_mutex); /* now read the header */ - buf = g_malloc(dself->block_size); + read_block_size = ndmp_device_read_size(self); + buf = g_malloc(read_block_size); if (!ndmp_connection_tape_read(self->ndmp, - buf, dself->block_size, &buf_size)) { + buf, read_block_size, &buf_size)) { switch (ndmp_connection_err_code(self->ndmp)) { case NDMP9_EOF_ERR: case NDMP9_EOM_ERR: @@ -953,13 +984,14 @@ static int ndmp_device_read_block (Device * dself, gpointer data, int *size_req) { NdmpDevice *self = NDMP_DEVICE(dself); guint64 requested, actual; + gsize read_block_size = ndmp_device_read_size(self); /* We checked the NDMP device's blocksize when the device was opened, which should * catch any misalignent of server block size and Amanda block size */ - g_assert(dself->block_size < INT_MAX); /* check data type mismatch */ - if (!data || *size_req < (int)(dself->block_size)) { - *size_req = (int)(dself->block_size); + g_assert(read_block_size < INT_MAX); /* check data type mismatch */ + if (!data || *size_req < (int)(read_block_size)) { + *size_req = (int)(read_block_size); return 0; } @@ -983,6 +1015,9 @@ ndmp_device_read_block (Device * dself, gpointer data, int *size_req) { } *size_req = (int)actual; /* cast is OK - requested size was < INT_MAX too */ + g_mutex_lock(dself->device_mutex); + dself->bytes_read += actual; + g_mutex_unlock(dself->device_mutex); return *size_req; } @@ -1004,8 +1039,9 @@ indirecttcp_listen( /* An IndirectTCP address is 255.255.255.255:$port */ self->listen_addrs = *addrs = g_new0(DirectTCPAddr, 2); - (*addrs)->ipv4 = 0xffffffff; - (*addrs)->port = port; + addrs[0]->sin.sin_family = AF_INET; + addrs[0]->sin.sin_addr.s_addr = htonl(0xffffffff); + SU_SET_PORT(addrs[0], port); return TRUE; } @@ -1042,13 +1078,13 @@ listen_impl( if (for_writing) { /* if we're forcing indirecttcp, just do it */ - if (self->force_indirecttcp) { + if (self->indirect) { return indirecttcp_listen(self, addrs); } if (!ndmp_connection_mover_set_window(self->ndmp, 0, 0)) { - /* NDMP9_ILLEGAL_ARGS_ERR means the NDMP server doesn't like a zero-byte + /* NDMP4_ILLEGAL_ARGS_ERR means the NDMP server doesn't like a zero-byte * mover window, so we'll ignore it */ - if (ndmp_connection_err_code(self->ndmp) != NDMP9_ILLEGAL_ARGS_ERR) { + if (ndmp_connection_err_code(self->ndmp) != NDMP4_ILLEGAL_ARGS_ERR) { set_error_from_ndmp(self); return FALSE; } @@ -1063,8 +1099,8 @@ listen_impl( * mover will pause immediately when it wants to read the first mover * record. */ if (!ndmp_connection_mover_set_window(self->ndmp, - DEVICE(self)->block_size, - DEVICE(self)->block_size)) { + dself->block_size, + dself->block_size)) { set_error_from_ndmp(self); return FALSE; } @@ -1072,8 +1108,8 @@ listen_impl( /* then tell it to start listening */ if (!ndmp_connection_mover_listen(self->ndmp, - for_writing? NDMP4_MOVER_MODE_READ : NDMP4_MOVER_MODE_WRITE, - NDMP4_ADDR_TCP, + for_writing? NDMP9_MOVER_MODE_READ : NDMP9_MOVER_MODE_WRITE, + NDMP9_ADDR_TCP, addrs)) { set_error_from_ndmp(self); return FALSE; @@ -1120,7 +1156,7 @@ accept_impl( return FALSE; } - if (state != NDMP4_MOVER_STATE_LISTEN) + if (state != NDMP9_MOVER_STATE_LISTEN) break; /* back off a little bit to give the other side time to breathe, @@ -1132,7 +1168,7 @@ accept_impl( } /* double-check state */ - if (state != NDMP4_MOVER_STATE_ACTIVE) { + if (state != NDMP9_MOVER_STATE_ACTIVE) { device_set_error(DEVICE(self), g_strdup("mover did not enter the ACTIVE state as expected"), DEVICE_STATUS_DEVICE_ERROR); @@ -1153,11 +1189,11 @@ accept_impl( /* now we should expect a notice that the mover has paused */ } else { /* when writing, the mover will pause as soon as the first byte comes - * in, so there's no need to do anything to trigger the pause. - * - * Well, sometimes it won't - specifically, when it does not allow a - * zero-byte mover window, which means we've set up IndirectTCP. But in - * that case, there's nothing interesting to do here.*/ + * in, so there's no need to do anything to trigger the pause. + * + * Well, sometimes it won't - specifically, when it does not allow a + * zero-byte mover window, which means we've set up IndirectTCP. But in + * that case, there's nothing interesting to do here.*/ } if (self->indirecttcp_sock == -1) { @@ -1185,7 +1221,8 @@ accept_impl( /* at this point, if we're doing directtcp, the mover is paused and ready * to go, and the listen addrs are no longer required; if we're doing * indirecttcp, then the other end may not even know of our listen_addrs - * yet, so we can't free them. */ + * yet, so we can't free them. + */ if (self->indirecttcp_sock == -1) { g_free(self->listen_addrs); @@ -1193,9 +1230,9 @@ accept_impl( } if (self->for_writing) - mode = NDMP4_MOVER_MODE_WRITE; + mode = NDMP9_MOVER_MODE_READ; else - mode = NDMP4_MOVER_MODE_READ; + mode = NDMP9_MOVER_MODE_WRITE; /* set up the new directtcp connection */ if (self->directtcp_conn) @@ -1210,6 +1247,348 @@ accept_impl( return TRUE; } +static int +accept_with_cond_impl( + Device *dself, + DirectTCPConnection **dtcpconn, + GMutex *abort_mutex, + GCond *abort_cond) +{ + NdmpDevice *self = NDMP_DEVICE(dself); + ndmp9_mover_state state; + guint64 bytes_moved; + ndmp9_mover_mode mode; + ndmp9_mover_pause_reason reason; + guint64 seek_position; + int result; + + if (device_in_error(self)) return 1; + + g_assert(self->listen_addrs); + + *dtcpconn = NULL; + + if (!self->for_writing) { + /* when reading, we don't get any kind of notification that the + * connection has been established, but we can't call NDMP_MOVER_READ + * until the mover is active. So we have to poll, waiting for ACTIVE. + * This is ugly. */ + gulong backoff = G_USEC_PER_SEC/20; /* 5 msec */ + while (1) { + if (!ndmp_connection_mover_get_state(self->ndmp, + &state, &bytes_moved, NULL, NULL)) { + set_error_from_ndmp(self); + return 1; + } + + if (state != NDMP9_MOVER_STATE_LISTEN) + break; + + /* back off a little bit to give the other side time to breathe, + * but not more than one second */ + g_usleep(backoff); + backoff *= 2; + if (backoff > G_USEC_PER_SEC) + backoff = G_USEC_PER_SEC; + } + + /* double-check state */ + if (state != NDMP9_MOVER_STATE_ACTIVE) { + device_set_error(DEVICE(self), + g_strdup("mover did not enter the ACTIVE state as expected"), + DEVICE_STATUS_DEVICE_ERROR); + return 1; + } + + /* now, we need to get this into the PAUSED state, since right now we + * aren't allowed to perform any tape movement commands. So we issue a + * MOVER_READ request for the whole darn image stream after setting the + * usual empty window. Note that this means the whole dump will be read + * in one MOVER_READ operation, even if it does not begin at the + * beginning of a part. */ + if (!ndmp_connection_mover_read(self->ndmp, 0, G_MAXUINT64)) { + set_error_from_ndmp(self); + return 1; + } + + /* now we should expect a notice that the mover has paused */ + } else { + /* when writing, the mover will pause as soon as the first byte comes + * in, so there's no need to do anything to trigger the pause. */ + } + + if (self->indirecttcp_sock == -1) { + /* NDMJOB sends NDMP9_MOVER_PAUSE_SEEK to indicate that it wants to + * write outside the window, while the standard specifies .._EOW, + * instead. When reading to a connection, we get the appropriate + * .._SEEK. It's easy enough to handle both. */ + result = ndmp_connection_wait_for_notify_with_cond(self->ndmp, + NULL, + NULL, + &reason, &seek_position, + abort_mutex, abort_cond); + + if (result == 1) { + set_error_from_ndmp(self); + return 1; + } else if (result == 2) { + return 2; + } + + if (reason != NDMP9_MOVER_PAUSE_SEEK && + reason != NDMP9_MOVER_PAUSE_EOW) { + device_set_error(DEVICE(self), + g_strdup_printf( + "got NOTIFY_MOVER_PAUSED, but not because of EOW or SEEK"), + DEVICE_STATUS_DEVICE_ERROR); + return FALSE; + } + } + + /* NDMJOB sends NDMP9_MOVER_PAUSE_SEEK to indicate that it wants to write + * outside the window, while the standard specifies .._EOW, instead. When + * reading to a connection, we get the appropriate .._SEEK. It's easy + * enough to handle both. */ + + if (self->indirecttcp_sock == -1) { + g_free(self->listen_addrs); + self->listen_addrs = NULL; + } + + if (self->for_writing) + mode = NDMP9_MOVER_MODE_READ; + else + mode = NDMP9_MOVER_MODE_WRITE; + + /* set up the new directtcp connection */ + if (self->directtcp_conn) + g_object_unref(self->directtcp_conn); + self->directtcp_conn = + directtcp_connection_ndmp_new(self->ndmp, mode); + *dtcpconn = DIRECTTCP_CONNECTION(self->directtcp_conn); + + /* reference it for the caller */ + g_object_ref(*dtcpconn); + + return 0; +} + +static gboolean +connect_impl( + Device *dself, + gboolean for_writing, + DirectTCPAddr *addrs, + DirectTCPConnection **dtcpconn, + ProlongProc prolong, + gpointer prolong_data) +{ + NdmpDevice *self = NDMP_DEVICE(dself); + ndmp9_mover_mode mode; + ndmp9_mover_pause_reason reason; + guint64 seek_position; + + g_assert(!self->listen_addrs); + + *dtcpconn = NULL; + self->for_writing = for_writing; + + /* TODO: support aborting this operation - maybe just always poll? */ + prolong = prolong; + prolong_data = prolong_data; + + if (!open_tape_agent(self)) { + /* error message was set by open_tape_agent */ + return FALSE; + } + + /* first, set the window to an empty span so that the mover doesn't start + * reading or writing data immediately. NDMJOB tends to reset the record + * size periodically (in direct contradiction to the spec), so we reset it + * here as well. */ + if (!ndmp_connection_mover_set_record_size(self->ndmp, + DEVICE(self)->block_size)) { + set_error_from_ndmp(self); + return FALSE; + } + + if (!ndmp_connection_mover_set_window(self->ndmp, 0, 0)) { + set_error_from_ndmp(self); + return FALSE; + } + + if (self->for_writing) + mode = NDMP9_MOVER_MODE_READ; + else + mode = NDMP9_MOVER_MODE_WRITE; + + if (!ndmp_connection_mover_connect(self->ndmp, mode, addrs)) { + set_error_from_ndmp(self); + return FALSE; + } + + if (!self->for_writing) { + /* The agent is in the ACTIVE state, and will remain so until we tell + * it to do something else. The thing we want to is for it to start + * reading data from the tape, which will immediately trigger an EOW or + * SEEK pause. */ + if (!ndmp_connection_mover_read(self->ndmp, 0, G_MAXUINT64)) { + set_error_from_ndmp(self); + return FALSE; + } + + /* now we should expect a notice that the mover has paused */ + } else { + /* when writing, the mover will pause as soon as the first byte comes + * in, so there's no need to do anything to trigger the pause. */ + } + + /* NDMJOB sends NDMP9_MOVER_PAUSE_SEEK to indicate that it wants to write + * outside the window, while the standard specifies .._EOW, instead. When + * reading to a connection, we get the appropriate .._SEEK. It's easy + * enough to handle both. */ + + if (!ndmp_connection_wait_for_notify(self->ndmp, + NULL, + NULL, + &reason, &seek_position)) { + set_error_from_ndmp(self); + return FALSE; + } + + if (reason != NDMP9_MOVER_PAUSE_SEEK && reason != NDMP9_MOVER_PAUSE_EOW) { + device_set_error(DEVICE(self), + g_strdup_printf("got NOTIFY_MOVER_PAUSED, but not because of EOW or SEEK"), + DEVICE_STATUS_DEVICE_ERROR); + return FALSE; + } + + if (self->listen_addrs) { + g_free(self->listen_addrs); + self->listen_addrs = NULL; + } + + /* set up the new directtcp connection */ + if (self->directtcp_conn) + g_object_unref(self->directtcp_conn); + self->directtcp_conn = + directtcp_connection_ndmp_new(self->ndmp, mode); + *dtcpconn = DIRECTTCP_CONNECTION(self->directtcp_conn); + + /* reference it for the caller */ + g_object_ref(*dtcpconn); + + return TRUE; +} + +static gboolean +connect_with_cond_impl( + Device *dself, + gboolean for_writing, + DirectTCPAddr *addrs, + DirectTCPConnection **dtcpconn, + GMutex *abort_mutex, + GCond *abort_cond) +{ + NdmpDevice *self = NDMP_DEVICE(dself); + ndmp9_mover_mode mode; + ndmp9_mover_pause_reason reason; + guint64 seek_position; + int result; + + g_assert(!self->listen_addrs); + + *dtcpconn = NULL; + self->for_writing = for_writing; + + if (!open_tape_agent(self)) { + /* error message was set by open_tape_agent */ + return 1; + } + + /* first, set the window to an empty span so that the mover doesn't start + * reading or writing data immediately. NDMJOB tends to reset the record + * size periodically (in direct contradiction to the spec), so we reset it + * here as well. */ + if (!ndmp_connection_mover_set_record_size(self->ndmp, + DEVICE(self)->block_size)) { + set_error_from_ndmp(self); + return 1; + } + + if (!ndmp_connection_mover_set_window(self->ndmp, 0, 0)) { + set_error_from_ndmp(self); + return 1; + } + + if (self->for_writing) + mode = NDMP9_MOVER_MODE_READ; + else + mode = NDMP9_MOVER_MODE_WRITE; + + if (!ndmp_connection_mover_connect(self->ndmp, mode, addrs)) { + set_error_from_ndmp(self); + return 1; + } + + if (!self->for_writing) { + /* The agent is in the ACTIVE state, and will remain so until we tell + * it to do something else. The thing we want to is for it to start + * reading data from the tape, which will immediately trigger an EOW or + * SEEK pause. */ + if (!ndmp_connection_mover_read(self->ndmp, 0, G_MAXUINT64)) { + set_error_from_ndmp(self); + return 1; + } + + /* now we should expect a notice that the mover has paused */ + } else { + /* when writing, the mover will pause as soon as the first byte comes + * in, so there's no need to do anything to trigger the pause. */ + } + + /* NDMJOB sends NDMP9_MOVER_PAUSE_SEEK to indicate that it wants to write + * outside the window, while the standard specifies .._EOW, instead. When + * reading to a connection, we get the appropriate .._SEEK. It's easy + * enough to handle both. */ + + result = ndmp_connection_wait_for_notify_with_cond(self->ndmp, + NULL, + NULL, + &reason, &seek_position, + abort_mutex, abort_cond); + + if (result == 1) { + set_error_from_ndmp(self); + return 1; + } else if (result == 2) { + return 2; + } + + if (reason != NDMP9_MOVER_PAUSE_SEEK && reason != NDMP9_MOVER_PAUSE_EOW) { + device_set_error(DEVICE(self), + g_strdup_printf("got NOTIFY_MOVER_PAUSED, but not because of EOW or SEEK"), + DEVICE_STATUS_DEVICE_ERROR); + return 1; + } + + if (self->listen_addrs) { + g_free(self->listen_addrs); + self->listen_addrs = NULL; + } + + /* set up the new directtcp connection */ + if (self->directtcp_conn) + g_object_unref(self->directtcp_conn); + self->directtcp_conn = + directtcp_connection_ndmp_new(self->ndmp, mode); + *dtcpconn = DIRECTTCP_CONNECTION(self->directtcp_conn); + + /* reference it for the caller */ + g_object_ref(*dtcpconn); + + return 0; +} + static gboolean indirecttcp_start_writing( NdmpDevice *self) @@ -1221,6 +1600,7 @@ indirecttcp_start_writing( * indirecttcp_sock. The mover remains IDLE, although its window is set * correctly for the part we are about to write. */ + g_debug("indirecttcp_start_writing, ready to accept"); conn_sock = accept(self->indirecttcp_sock, NULL, NULL); if (conn_sock < 0) { device_set_error(DEVICE(self), @@ -1228,6 +1608,7 @@ indirecttcp_start_writing( DEVICE_STATUS_DEVICE_ERROR); return FALSE; } + g_debug("indirecttcp_start_writing, accepted"); close(self->indirecttcp_sock); self->indirecttcp_sock = -1; @@ -1243,15 +1624,16 @@ indirecttcp_start_writing( } /* format the addresses and send them down the socket */ - for (iter = real_addrs; iter && iter->ipv4; iter++) { - struct in_addr in; - char *addr, *addrspec; + for (iter = real_addrs; iter && SU_GET_FAMILY(iter) != 0; iter++) { + char inet[INET_ADDRSTRLEN]; + const char *addr; + char *addrspec; - in.s_addr = htonl(iter->ipv4); - addr = inet_ntoa(in); + addr = inet_ntop(AF_INET, &iter->sin.sin_addr.s_addr, inet, 40); - addrspec = g_strdup_printf("%s:%d%s", addr, iter->port, - (iter+1)->ipv4? " ":""); + addrspec = g_strdup_printf("%s:%d%s", addr, SU_GET_PORT(iter), + SU_GET_FAMILY(iter+1) !=0? " ":""); + g_debug("indirecttcp_start_writing, send %s", addrspec); if (full_write(conn_sock, addrspec, strlen(addrspec)) < strlen(addrspec)) { device_set_error(DEVICE(self), g_strdup_printf("writing to indirecttcp socket: %s", strerror(errno)), @@ -1300,12 +1682,14 @@ write_from_connection_impl( if (device_in_error(self)) return FALSE; + g_debug("write_from_connection_impl"); if (actual_size) *actual_size = 0; /* if this is false, then the caller did not use use_connection correctly */ g_assert(self->directtcp_conn != NULL); g_assert(self->ndmp == nconn->ndmp); + g_assert(nconn->mode == NDMP9_MOVER_MODE_READ); if (!ndmp_connection_mover_get_state(self->ndmp, &mover_state, &bytes_moved_before, NULL, NULL)) { @@ -1314,16 +1698,19 @@ write_from_connection_impl( } if (self->indirecttcp_sock != -1) { - /* If we're doing IndirectTCP, then we've deferred the whole mover_set_window - * / mover_listen process.. until now. So the mover should be IDLE. */ - g_assert(mover_state == NDMP4_MOVER_STATE_IDLE); + /* If we're doing IndirectTCP, then we've deferred the whole + * mover_set_window mover_listen process.. until now. + * So the mover should be IDLE. + */ + g_assert(mover_state == NDMP9_MOVER_STATE_IDLE); } else { /* the mover had best be PAUSED right now */ - g_assert(mover_state == NDMP4_MOVER_STATE_PAUSED); + g_assert(mover_state == NDMP9_MOVER_STATE_PAUSED); } /* we want to set the window regardless of whether this is directtcp or - * indirecttcp */ + * indirecttcp + */ if (!ndmp_connection_mover_set_window(self->ndmp, nconn->offset, size? size : G_MAXUINT64 - nconn->offset)) { @@ -1450,6 +1837,7 @@ read_to_connection_impl( /* if this is false, then the caller did not use use_connection correctly */ g_assert(nconn != NULL); g_assert(self->ndmp == nconn->ndmp); + g_assert(nconn->mode == NDMP9_MOVER_MODE_WRITE); if (!ndmp_connection_mover_get_state(self->ndmp, &mover_state, &bytes_moved_before, NULL, NULL)) { @@ -1458,7 +1846,7 @@ read_to_connection_impl( } /* the mover had best be PAUSED right now */ - g_assert(mover_state == NDMP4_MOVER_STATE_PAUSED); + g_assert(mover_state == NDMP9_MOVER_STATE_PAUSED); if (!ndmp_connection_mover_set_window(self->ndmp, nconn->offset, @@ -1659,13 +2047,36 @@ ndmp_device_set_verbose_fn(Device *p_self, DevicePropertyBase *base, } static gboolean -ndmp_device_set__force_indirecttcp_fn(Device *dself, +ndmp_device_set_read_block_size_fn(Device *p_self, DevicePropertyBase *base G_GNUC_UNUSED, + GValue *val, PropertySurety surety, PropertySource source) +{ + NdmpDevice *self = NDMP_DEVICE(p_self); + gsize read_block_size = g_value_get_uint(val); + + if (read_block_size != 0 && + ((gsize)read_block_size < p_self->block_size || + (gsize)read_block_size > p_self->max_block_size)) { + device_set_error(p_self, + 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), + DEVICE_STATUS_DEVICE_ERROR); + return FALSE; + } + + self->read_block_size = read_block_size; + + /* use the READ_BLOCK_SIZE, even if we're invoked to get the old READ_BUFFER_SIZE */ + return device_simple_property_set_fn(p_self, base, + val, surety, source); +} + +static gboolean +ndmp_device_set_indirect_fn(Device *dself, DevicePropertyBase *base, GValue *val, PropertySurety surety, PropertySource source) { NdmpDevice *self = NDMP_DEVICE(dself); - self->force_indirecttcp = g_value_get_boolean(val); + self->indirect = g_value_get_boolean(val); return device_simple_property_set_fn(dself, base, val, surety, source); } @@ -1695,6 +2106,9 @@ ndmp_device_class_init(NdmpDeviceClass * c G_GNUC_UNUSED) device_class->directtcp_supported = TRUE; device_class->listen = listen_impl; device_class->accept = accept_impl; + device_class->accept_with_cond = accept_with_cond_impl; + device_class->connect = connect_impl; + device_class->connect_with_cond = connect_with_cond_impl; device_class->write_from_connection = write_from_connection_impl; device_class->read_to_connection = read_to_connection_impl; device_class->use_connection = use_connection_impl; @@ -1721,10 +2135,15 @@ ndmp_device_class_init(NdmpDeviceClass * c G_GNUC_UNUSED) device_simple_property_get_fn, ndmp_device_set_verbose_fn); - device_class_register_property(device_class, PROPERTY__FORCE_INDIRECTTCP, + device_class_register_property(device_class, PROPERTY_INDIRECT, PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_MASK, device_simple_property_get_fn, - ndmp_device_set__force_indirecttcp_fn); + ndmp_device_set_indirect_fn); + + device_class_register_property(device_class, PROPERTY_READ_BLOCK_SIZE, + PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START, + device_simple_property_get_fn, + ndmp_device_set_read_block_size_fn); } static void @@ -1773,12 +2192,25 @@ ndmp_device_init(NdmpDevice *self) &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED); g_value_unset(&response); + g_value_init(&response, G_TYPE_BOOLEAN); + g_value_set_boolean(&response, TRUE); + device_set_simple_property(dself, PROPERTY_LEOM, + &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED); + g_value_unset(&response); + g_value_init(&response, MEDIA_ACCESS_MODE_TYPE); g_value_set_enum(&response, MEDIA_ACCESS_MODE_READ_WRITE); device_set_simple_property(dself, PROPERTY_MEDIUM_ACCESS_TYPE, &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED); g_value_unset(&response); + self->read_block_size = 0; + g_value_init(&response, G_TYPE_UINT); + g_value_set_uint(&response, self->read_block_size); + device_set_simple_property(dself, PROPERTY_READ_BLOCK_SIZE, + &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DEFAULT); + g_value_unset(&response); + g_value_init(&response, G_TYPE_STRING); g_value_set_string(&response, "ndmp"); device_set_simple_property(dself, PROPERTY_NDMP_USERNAME, @@ -1802,10 +2234,10 @@ ndmp_device_init(NdmpDevice *self) g_value_init(&response, G_TYPE_BOOLEAN); g_value_set_boolean(&response, FALSE); - device_set_simple_property(dself, PROPERTY__FORCE_INDIRECTTCP, + device_set_simple_property(dself, PROPERTY_INDIRECT, &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DEFAULT); g_value_unset(&response); - self->force_indirecttcp = FALSE; + self->indirect = FALSE; self->indirecttcp_sock = -1; } @@ -1843,10 +2275,8 @@ ndmp_device_factory( char *device_node) { Device *rval; - NdmpDevice * ndmp_rval; g_assert(0 == strcmp(device_type, NDMP_DEVICE_NAME)); rval = DEVICE(g_object_new(TYPE_NDMP_DEVICE, NULL)); - ndmp_rval = (NdmpDevice *)rval; device_open_device(rval, device_name, device_type, device_node); return rval; @@ -1869,9 +2299,9 @@ ndmp_device_register(void) device_property_fill_and_register(&device_property_ndmp_auth, G_TYPE_STRING, "ndmp_auth", "Authentication method for the NDMP agent - md5 (default), text, none, or void"); - device_property_fill_and_register(&device_property__force_indirecttcp, - G_TYPE_BOOLEAN, "_force_indirecttcp", - "For testing only - force IndirectTCP mode, even if the NDMP server supports " + device_property_fill_and_register(&device_property_indirect, + G_TYPE_BOOLEAN, "indirect", + "Use Indirect TCP mode, even if the NDMP server supports " "window length 0"); }