Merge branch 'master' into squeeze
[debian/amanda] / ndmp-src / ndma_data.c
diff --git a/ndmp-src/ndma_data.c b/ndmp-src/ndma_data.c
new file mode 100644 (file)
index 0000000..81667f4
--- /dev/null
@@ -0,0 +1,1010 @@
+/*
+ * Copyright (c) 1998,1999,2000
+ *     Traakan, Inc., Los Altos, CA
+ *     All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Project:  NDMJOB
+ * Ident:    $Id: $
+ *
+ * Description:
+ *
+ */
+
+
+#include "ndmagents.h"
+#include "wraplib.h"
+
+#ifndef NDMOS_OPTION_NO_DATA_AGENT
+
+
+
+/*
+ * Initialization and Cleanup
+ ****************************************************************
+ */
+
+/* Initialize -- Set data structure to know value, ignore current value */
+int
+ndmda_initialize (struct ndm_session *sess)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+
+       NDMOS_MACRO_ZEROFILL (da);
+       da->data_state.state = NDMP9_DATA_STATE_IDLE;
+       ndmchan_initialize (&da->formatter_error, "dfp-error");
+       ndmchan_initialize (&da->formatter_wrap, "dfp-wrap");
+       ndmchan_initialize (&da->formatter_image, "dfp-image");
+       ndmda_fh_initialize (sess);
+
+       return 0;
+}
+
+/* Commission -- Get agent ready. Entire session has been initialize()d */
+int
+ndmda_commission (struct ndm_session *sess)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+
+       da->data_state.state = NDMP9_DATA_STATE_IDLE;
+       ndmda_fh_commission (sess);
+
+       return 0;
+}
+
+/* Decommission -- Discard agent */
+int
+ndmda_decommission (struct ndm_session *sess)
+{
+       ndmis_data_close (sess);
+       ndmda_purge_environment (sess);
+       ndmda_purge_nlist (sess);
+       ndmda_fh_decommission (sess);
+       NDMOS_API_BZERO (sess->data_acb.bu_type,sizeof sess->data_acb.bu_type);
+
+       ndmda_commission (sess);
+
+       return 0;
+}
+
+/* Belay -- Cancel partially issued activation/start */
+int
+ndmda_belay (struct ndm_session *sess)
+{
+       ndmda_fh_belay (sess);
+       return ndmda_decommission (sess);
+}
+
+
+
+
+/*
+ * Semantic actions -- called from ndma_dispatch()
+ ****************************************************************
+ */
+
+static int
+add_env (struct ndm_env_table *envtab, char *cmd)
+{
+       char            buf[1024];
+       int             i;
+
+       for (i = 0; i < envtab->n_env; i++) {
+               strcpy (buf, envtab->env[i].name);
+               strcat (buf, "=");
+               strcat (buf, envtab->env[i].value);
+               ndmda_add_to_cmd (cmd, "-E");
+               ndmda_add_to_cmd (cmd, buf);
+       }
+
+       return 0;
+}
+
+static int
+add_nlist (struct ndm_nlist_table *nlisttab, char *cmd)
+{
+       char            buf[32];
+       int             i;
+
+       for (i = 0; i < nlisttab->n_nlist; i++) {
+               ndmp9_name *    nl = &nlisttab->nlist[i];
+
+               ndmda_add_to_cmd (cmd, nl->original_path);
+               if (nl->fh_info.valid == NDMP9_VALIDITY_VALID) {
+                       sprintf (buf, "@%llu", nl->fh_info.value);
+                       ndmda_add_to_cmd (cmd, buf);
+               } else {
+                       ndmda_add_to_cmd (cmd, "@-");
+               }
+               ndmda_add_to_cmd (cmd, nl->destination_path);
+       }
+
+       return 0;
+}
+
+
+ndmp9_error
+ndmda_data_start_backup (struct ndm_session *sess)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+       ndmp9_error             error = NDMP9_NO_ERR;
+       char                    cmd[NDMDA_MAX_CMD];
+
+       strcpy (cmd, "wrap_");
+       strcat (cmd, da->bu_type);
+
+       if (sess->param.log_level > 0) {
+           char tmpbuf[40];
+           sprintf(tmpbuf, "-d%d", sess->param.log_level);
+           ndmda_add_to_cmd (cmd, tmpbuf);
+       }
+
+       ndmda_add_to_cmd (cmd, "-c");
+       ndmda_add_to_cmd (cmd, "-I#3");
+       add_env (&da->env_tab, cmd);
+
+       ndma_send_logmsg (sess, NDMP9_LOG_DEBUG, sess->plumb.data,
+               "CMD: %s", cmd);
+
+       if (ndmda_pipe_fork_exec (sess, cmd, 1) < 0) {
+               return NDMP9_UNDEFINED_ERR;
+       }
+
+       ndmis_data_start (sess, NDMCHAN_MODE_WRITE);
+
+       da->data_state.state = NDMP9_DATA_STATE_ACTIVE;
+       da->data_state.operation = NDMP9_DATA_OP_BACKUP;
+
+       return error;
+}
+
+ndmp9_error
+ndmda_data_start_recover (struct ndm_session *sess)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+       ndmp9_error             error = NDMP9_NO_ERR;
+       char                    cmd[NDMDA_MAX_CMD];
+
+       strcpy (cmd, "wrap_");
+       strcat (cmd, da->bu_type);
+
+       if (sess->param.log_level > 0) {
+           char tmpbuf[40];
+           sprintf(tmpbuf, "-d%d", sess->param.log_level);
+           ndmda_add_to_cmd (cmd, tmpbuf);
+       }
+
+       ndmda_add_to_cmd (cmd, "-x");
+       ndmda_add_to_cmd (cmd, "-I#3");
+       add_env (&da->env_tab, cmd);
+       add_nlist (&da->nlist_tab, cmd);
+
+       ndma_send_logmsg (sess, NDMP9_LOG_DEBUG, sess->plumb.data,
+               "CMD: %s", cmd);
+
+       if (ndmda_pipe_fork_exec (sess, cmd, 0) < 0) {
+               return NDMP9_UNDEFINED_ERR;
+       }
+
+       ndmis_data_start (sess, NDMCHAN_MODE_READ);
+
+       da->data_state.state = NDMP9_DATA_STATE_ACTIVE;
+       da->data_state.operation = NDMP9_DATA_OP_RECOVER;
+
+       return error;
+}
+
+ndmp9_error
+ndmda_data_start_recover_fh (struct ndm_session *sess)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+       ndmp9_error             error = NDMP9_NO_ERR;
+       char                    cmd[NDMDA_MAX_CMD];
+
+       strcpy (cmd, "wrap_");
+       strcat (cmd, da->bu_type);
+       ndmda_add_to_cmd (cmd, "-t");
+       ndmda_add_to_cmd (cmd, "-I#3");
+       add_env (&da->env_tab, cmd);
+       add_nlist (&da->nlist_tab, cmd);
+
+       ndma_send_logmsg (sess, NDMP9_LOG_DEBUG,  sess->plumb.data,
+               "CMD: %s", cmd);
+
+       if (ndmda_pipe_fork_exec (sess, cmd, 0) < 0) {
+               return NDMP9_UNDEFINED_ERR;
+       }
+
+       ndmis_data_start (sess, NDMCHAN_MODE_READ);
+
+       da->data_state.state = NDMP9_DATA_STATE_ACTIVE;
+       da->data_state.operation = NDMP9_DATA_OP_RECOVER_FILEHIST;
+
+       return error;
+}
+
+void
+ndmda_sync_state (struct ndm_session *sess)
+{
+       /* no-op, always accurate */
+}
+
+void
+ndmda_data_abort (struct ndm_session *sess)
+{
+       ndmda_data_halt (sess, NDMP9_DATA_HALT_ABORTED);
+}
+
+void
+ndmda_sync_environment (struct ndm_session *sess)
+{
+       /* no-op, always accurate */
+}
+
+ndmp9_error
+ndmda_data_listen (struct ndm_session *sess)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+
+       da->data_state.state = NDMP9_DATA_STATE_LISTEN;
+       da->data_state.halt_reason = NDMP9_DATA_HALT_NA;
+
+       return NDMP9_NO_ERR;
+}
+
+ndmp9_error
+ndmda_data_connect (struct ndm_session *sess)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+
+       da->data_state.state = NDMP9_DATA_STATE_CONNECTED;
+       da->data_state.halt_reason = NDMP9_DATA_HALT_NA;
+
+       return NDMP9_NO_ERR;
+}
+
+void
+ndmda_data_halt (struct ndm_session *sess, ndmp9_data_halt_reason reason)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+
+       da->data_state.state = NDMP9_DATA_STATE_HALTED;
+       da->data_state.halt_reason = reason;
+       da->data_notify_pending = 1;
+
+       ndmda_fh_flush (sess);
+
+       ndmis_data_close (sess);
+
+       ndmchan_cleanup (&da->formatter_image);
+       ndmchan_cleanup (&da->formatter_error);
+       ndmchan_cleanup (&da->formatter_wrap);
+
+       /* this needs to be better */
+       if (da->formatter_pid) {
+               sleep (1);      /* give gtar a chance to stop by itself */
+               kill (da->formatter_pid, SIGTERM);
+       }
+}
+
+void
+ndmda_data_stop (struct ndm_session *sess)
+{
+       ndmda_decommission (sess);
+}
+
+
+
+
+/*
+ * Quantum -- get a bit of work done
+ ****************************************************************
+ */
+
+int
+ndmda_quantum (struct ndm_session *sess)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+       int                     did_something = 0;      /* did nothing */
+
+
+       switch (da->data_state.state) {
+       default:
+               ndmalogf (sess, 0, 0, "BOTCH data state");
+               return -1;
+
+       case NDMP9_DATA_STATE_IDLE:
+       case NDMP9_DATA_STATE_HALTED:
+       case NDMP9_DATA_STATE_CONNECTED:
+               break;
+
+       case NDMP9_DATA_STATE_LISTEN:
+               switch (sess->plumb.image_stream.data_ep.connect_status) {
+               case NDMIS_CONN_LISTEN:         /* no connection yet */
+                       break;
+
+               case NDMIS_CONN_ACCEPTED:       /* we're in business */
+                       /* drum roll please... */
+                       da->data_state.state = NDMP9_DATA_STATE_CONNECTED;
+                       /* tah-dah */
+                       did_something++;        /* did something */
+                       break;
+
+               case NDMIS_CONN_BOTCHED:        /* accept() went south */
+               default:                        /* ain't suppose to happen */
+                       ndmda_data_halt (sess, NDMP9_DATA_HALT_CONNECT_ERROR);
+                       did_something++;        /* did something */
+                       break;
+               }
+               break;
+
+       case NDMP9_DATA_STATE_ACTIVE:
+               did_something |= ndmda_quantum_stderr (sess);
+               did_something |= ndmda_quantum_wrap (sess);
+               did_something |= ndmda_quantum_image (sess);
+               break;
+       }
+
+       ndmda_send_notice (sess);
+
+       return did_something;
+}
+
+int
+ndmda_quantum_stderr (struct ndm_session *sess)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+       struct ndmchan *        ch = &da->formatter_error;
+       int                     did_something = 0;
+       char *                  p;
+       char *                  data;
+       char *                  pend;
+       unsigned                n_ready;
+
+  again:
+       n_ready = ndmchan_n_ready (ch);
+       if (n_ready == 0)
+               return did_something;
+
+       data = p = &ch->data[ch->beg_ix];
+       pend = p + n_ready;
+
+       while (p < pend && *p != '\n') p++;
+
+
+       if (p < pend && *p == '\n') {
+               *p++ = 0;
+               ndma_send_logmsg (sess, NDMP9_LOG_NORMAL,  sess->plumb.data,
+                       "%s", data);
+               ch->beg_ix += p - data;
+               did_something++;
+               goto again;
+       }
+
+       if (!ch->eof)
+               return did_something;
+
+       /* content w/o newline, and EOF */
+       /* p == pend */
+       if (ch->end_ix >= ch->data_size) {
+               if (data != ch->data) {
+                       ndmchan_compress (ch);
+                       goto again;
+               }
+               /* that's one huge message */
+               p--;    /* lose last byte */
+       }
+
+       ch->data[ch->end_ix++] = '\n';
+       did_something++;
+       goto again;
+}
+
+int
+ndmda_quantum_wrap (struct ndm_session *sess)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+       struct ndmchan *        ch = &da->formatter_wrap;
+       int                     did_something = 0;
+       char *                  p;
+       char *                  data;
+       char *                  pend;
+       unsigned                n_ready;
+       int                     is_recover = 0;
+
+       switch (da->data_state.operation) {
+       default:
+               assert (0);
+               break;
+
+       case NDMP9_DATA_OP_BACKUP:
+               break;
+
+       case NDMP9_DATA_OP_RECOVER:
+       case NDMP9_DATA_OP_RECOVER_FILEHIST:
+               is_recover = 1;
+               break;
+       }
+
+  again:
+       n_ready = ndmchan_n_ready (ch);
+       if (n_ready == 0) {
+               if (ch->eof && is_recover) {
+                       ndmda_data_halt (sess, NDMP9_DATA_HALT_SUCCESSFUL);
+               }
+               return did_something;
+       }
+       data = p = &ch->data[ch->beg_ix];
+       pend = p + n_ready;
+
+       while (p < pend && *p != '\n') p++;
+
+
+       if (p < pend && *p == '\n') {
+               *p++ = 0;
+               ndmda_wrap_in (sess, data);
+               ch->beg_ix += p - data;
+               did_something++;
+               goto again;
+       }
+
+       if (!ch->eof)
+               return did_something;
+
+       /* content w/o newline, and EOF */
+       /* p == pend */
+       if (ch->end_ix >= ch->data_size) {
+               if (data != ch->data) {
+                       ndmchan_compress (ch);
+                       goto again;
+               }
+               /* that's one huge message */
+               p--;    /* lose last byte */
+       }
+
+       ch->data[ch->end_ix++] = '\n';
+       did_something++;
+       goto again;
+}
+
+
+
+int
+ndmda_quantum_image (struct ndm_session *sess)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+       struct ndmchan *        from_chan;
+       struct ndmchan *        to_chan;
+       unsigned                n_ready, n_avail, n_copy;
+       int                     is_backup = 0;
+
+       switch (da->data_state.operation) {
+       default:
+               assert (0);
+               from_chan = 0;
+               to_chan = 0;
+               break;
+
+       case NDMP9_DATA_OP_BACKUP:
+               from_chan = &da->formatter_image;
+               to_chan = &sess->plumb.image_stream.chan;
+               is_backup = 1;
+               break;
+
+       case NDMP9_DATA_OP_RECOVER:
+       case NDMP9_DATA_OP_RECOVER_FILEHIST:
+               from_chan = &sess->plumb.image_stream.chan;
+               to_chan = &da->formatter_image;
+               break;
+       }
+
+  again:
+       n_copy = n_ready = ndmchan_n_ready (from_chan);
+       if (n_ready == 0) {
+               if (from_chan->eof) {
+                       to_chan->eof = 1;
+                       if (ndmchan_n_ready (to_chan) == 0) {
+                               if (is_backup) {
+                                       ndmda_data_halt (sess,
+                                               NDMP9_DATA_HALT_SUCCESSFUL);
+                               }
+                       }
+               }
+               return 0;       /* data blocked */
+       }
+
+       n_avail = ndmchan_n_avail (to_chan);
+       if (n_copy > n_avail)
+               n_copy = n_avail;
+
+       if (da->enable_hist) {
+               if (n_copy > da->pass_resid)
+                       n_copy = da->pass_resid;
+       }
+
+       if (n_copy > 0) {
+               bcopy (&from_chan->data[from_chan->beg_ix],
+                       &to_chan->data[to_chan->end_ix],
+                       n_copy);
+               from_chan->beg_ix += n_copy;
+               to_chan->end_ix += n_copy;
+               da->data_state.bytes_processed += n_copy;
+               da->pass_resid -= n_copy;
+               goto again;     /* do as much as possible */
+       }
+
+       return 0;
+
+}
+
+
+
+
+/*
+ * Process WRAP messages from the formatter. Called from
+ * ndmda_quantum_wrap(). The formatter sends one line text
+ * messages via the WRAP pipe (fd=3 on formatter).
+ * The WRAP message contain log messages, file history,
+ * status updates, etc, etc, etc.
+ *
+ * Here the messages are parsed and directed to the
+ * right NDMP interface toward the Control Agent.
+ */
+
+void           ndmp9_fstat_from_wrap_fstat (ndmp9_file_stat *fstat9,
+                               struct wrap_fstat *fstatw);
+
+int
+ndmda_wrap_in (struct ndm_session *sess, char *wrap_line)
+{
+       struct wrap_msg_buf     _wmsg, *wmsg = &_wmsg;
+       int                     rc;
+       ndmp9_file_stat         fstat9;
+
+       NDMOS_MACRO_ZEROFILL (wmsg);
+
+       rc = wrap_parse_msg (wrap_line, wmsg);
+       if (rc != 0) {
+               ndmalogf (sess, 0, 2, "Malformed wrap: %s", wrap_line);
+               return -1;
+       }
+
+       switch (wmsg->msg_type) {
+       case WRAP_MSGTYPE_LOG_MESSAGE:
+               ndmalogf (sess, "WRAP", 2, "%s",
+                       wmsg->body.log_message.message);
+               ndma_send_logmsg (sess, NDMP9_LOG_NORMAL, sess->plumb.data,
+                       "WRAP: %s", wmsg->body.log_message.message);
+               break;
+
+       case WRAP_MSGTYPE_ADD_FILE:
+               ndmp9_fstat_from_wrap_fstat (&fstat9,
+                               &wmsg->body.add_file.fstat);
+               fstat9.fh_info.valid = NDMP9_VALIDITY_VALID;
+               fstat9.fh_info.value = wmsg->body.add_file.fhinfo;
+               ndmda_fh_add_file (sess, &fstat9, wmsg->body.add_file.path);
+               break;
+
+       case WRAP_MSGTYPE_ADD_DIRENT:
+               ndmda_fh_add_dir (sess,
+                       wmsg->body.add_dirent.dir_fileno,
+                       wmsg->body.add_dirent.name,
+                       wmsg->body.add_dirent.fileno);
+               break;
+
+       case WRAP_MSGTYPE_ADD_NODE:
+               ndmp9_fstat_from_wrap_fstat (&fstat9,
+                               &wmsg->body.add_node.fstat);
+               fstat9.fh_info.valid = NDMP9_VALIDITY_VALID;
+               fstat9.fh_info.value = wmsg->body.add_node.fhinfo;
+               ndmda_fh_add_node (sess, &fstat9);
+               break;
+
+       case WRAP_MSGTYPE_DATA_READ:
+               ndmda_send_data_read (sess,
+                       wmsg->body.data_read.offset,
+                       wmsg->body.data_read.length);
+               break;
+
+       case WRAP_MSGTYPE_ADD_ENV:
+       case WRAP_MSGTYPE_DATA_STATS:
+       case WRAP_MSGTYPE_RECOVERY_RESULT:
+               ndmalogf (sess, 0, 2, "Unimplemented wrap: %s", wrap_line);
+               break;
+       }
+
+       return 0;
+}
+
+void
+ndmp9_fstat_from_wrap_fstat (ndmp9_file_stat *fstat9,
+  struct wrap_fstat *fstatw)
+{
+       NDMOS_MACRO_ZEROFILL (fstat9);
+
+       switch (fstatw->ftype) {
+       default:
+       case WRAP_FTYPE_INVALID:fstat9->ftype = NDMP9_FILE_OTHER;       break;
+       case WRAP_FTYPE_DIR:    fstat9->ftype = NDMP9_FILE_DIR;         break;
+       case WRAP_FTYPE_FIFO:   fstat9->ftype = NDMP9_FILE_FIFO;        break;
+       case WRAP_FTYPE_CSPEC:  fstat9->ftype = NDMP9_FILE_CSPEC;       break;
+       case WRAP_FTYPE_BSPEC:  fstat9->ftype = NDMP9_FILE_BSPEC;       break;
+       case WRAP_FTYPE_REG:    fstat9->ftype = NDMP9_FILE_REG;         break;
+       case WRAP_FTYPE_SLINK:  fstat9->ftype = NDMP9_FILE_SLINK;       break;
+       case WRAP_FTYPE_SOCK:   fstat9->ftype = NDMP9_FILE_SOCK;        break;
+       case WRAP_FTYPE_REGISTRY:fstat9->ftype = NDMP9_FILE_REGISTRY;   break;
+       case WRAP_FTYPE_OTHER:  fstat9->ftype = NDMP9_FILE_OTHER;       break;
+       }
+
+       if (fstatw->valid & WRAP_FSTAT_VALID_FTYPE) {
+       }
+
+       if (fstatw->valid & WRAP_FSTAT_VALID_MODE) {
+               fstat9->mode.valid = NDMP9_VALIDITY_VALID;
+               fstat9->mode.value = fstatw->mode;
+       }
+
+       if (fstatw->valid & WRAP_FSTAT_VALID_SIZE) {
+               fstat9->size.valid = NDMP9_VALIDITY_VALID;
+               fstat9->size.value = fstatw->size;
+       }
+
+       if (fstatw->valid & WRAP_FSTAT_VALID_LINKS) {
+               fstat9->links.valid = NDMP9_VALIDITY_VALID;
+               fstat9->links.value = fstatw->size;
+       }
+
+       if (fstatw->valid & WRAP_FSTAT_VALID_UID) {
+               fstat9->uid.valid = NDMP9_VALIDITY_VALID;
+               fstat9->uid.value = fstatw->uid;
+       }
+
+       if (fstatw->valid & WRAP_FSTAT_VALID_GID) {
+               fstat9->gid.valid = NDMP9_VALIDITY_VALID;
+               fstat9->gid.value = fstatw->gid;
+       }
+
+       if (fstatw->valid & WRAP_FSTAT_VALID_ATIME) {
+               fstat9->atime.valid = NDMP9_VALIDITY_VALID;
+               fstat9->atime.value = fstatw->atime;
+       }
+
+       if (fstatw->valid & WRAP_FSTAT_VALID_MTIME) {
+               fstat9->mtime.valid = NDMP9_VALIDITY_VALID;
+               fstat9->mtime.value = fstatw->mtime;
+       }
+
+       if (fstatw->valid & WRAP_FSTAT_VALID_CTIME) {
+               fstat9->ctime.valid = NDMP9_VALIDITY_VALID;
+               fstat9->ctime.value = fstatw->ctime;
+       }
+
+       if (fstatw->valid & WRAP_FSTAT_VALID_FILENO) {
+               fstat9->node.valid = NDMP9_VALIDITY_VALID;
+               fstat9->node.value = fstatw->fileno;
+       }
+
+
+
+
+}
+
+
+
+
+/*
+ * Send LOG and NOTIFY messages
+ ****************************************************************
+ */
+
+#if 0
+void
+ndmda_send_logmsg (struct ndm_session *sess, char *fmt, ...)
+{
+       struct ndmconn *        conn = sess->plumb.control;
+       char                    buf[4096];
+       va_list                 ap;
+
+       va_start (ap, fmt);
+       vsnprintf (buf, sizeof(buf), fmt, ap);
+       va_end (ap);
+
+       // we don't handle our own messages so don't send them....
+       if (conn->conn_type == NDMCONN_TYPE_RESIDENT) {
+           ndmalogf(sess, 0, 2, "RESIDENT AGENT LOGMSG: %s", buf);
+           return;
+       }
+
+       ndma_send_logmsg (sess, buf, conn);
+}
+#endif
+
+void
+ndmda_send_notice (struct ndm_session *sess)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+
+       if (!da->data_notify_pending)
+               return;
+
+       da->data_notify_pending = 0;
+
+       switch (da->data_state.state) {
+       case NDMP9_DATA_STATE_HALTED:
+               ndma_notify_data_halted (sess);
+               break;
+
+       default:
+               /* Hmm. Why are we here. Race? */
+               break;
+       }
+}
+
+void
+ndmda_send_data_read (struct ndm_session *sess,
+  unsigned long long offset, unsigned long long length)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+       ndmp9_addr_type         addr_type;
+
+       addr_type = da->data_state.data_connection_addr.addr_type;
+#if 0
+       da->reco_read_offset = offset;
+       da->reco_read_length = length;
+#endif
+
+       if (NDMP9_ADDR_LOCAL == addr_type) {
+#ifndef NDMOS_OPTION_NO_TAPE_AGENT
+               if (ndmta_local_mover_read (sess, offset, length) != 0) {
+                       ndma_send_logmsg (sess, NDMP9_LOG_ERROR,
+                               sess->plumb.data,
+                               "local_mover_read failed");
+                       ndmda_data_halt (sess, NDMP9_DATA_HALT_INTERNAL_ERROR);
+               }
+#else /* !NDMOS_OPTION_NO_TAPE_AGENT */
+               ndma_send_logmsg (sess, NDMP9_LOG_ERROR,
+                               sess->plumb.data,
+                               "local_mover_read not configured");
+               ndmda_data_halt (sess, NDMP9_DATA_HALT_INTERNAL_ERROR);
+#endif /* !NDMOS_OPTION_NO_TAPE_AGENT */
+               return;
+       }
+
+       switch (addr_type) {
+       case NDMP9_ADDR_TCP:
+               ndma_notify_data_read (sess, offset, length);
+               break;
+
+       default:
+               ndma_send_logmsg (sess, NDMP9_LOG_ERROR, sess->plumb.data,
+                       "bogus mover.addr_type");
+               ndmda_data_halt (sess, NDMP9_DATA_HALT_INTERNAL_ERROR);
+               break;
+       }
+}
+
+
+
+
+/*
+ * Misc -- env[] and nlist[] subroutines, etc
+ ****************************************************************
+ */
+
+int
+ndmda_copy_environment (struct ndm_session *sess,
+  ndmp9_pval *env, unsigned n_env)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+       int                     i;
+       unsigned int            j;
+       ndmp9_pval *            src_pv;
+       ndmp9_pval *            dst_pv;
+
+       for (j = 0; j < n_env; j++) {
+               src_pv = &env[j];
+               dst_pv = &da->env_tab.env[da->env_tab.n_env];
+
+               dst_pv->name  = NDMOS_API_STRDUP (src_pv->name);
+               dst_pv->value = NDMOS_API_STRDUP (src_pv->value);
+
+               if (!dst_pv->name || !dst_pv->value)
+                       goto fail;
+
+               da->env_tab.n_env++;
+       }
+
+       return 0;
+
+  fail:
+       for (i = 0; i < da->env_tab.n_env; i++) {
+               char *          p;
+
+               dst_pv = &da->env_tab.env[da->env_tab.n_env];
+
+               if ((p = dst_pv->name) != 0)
+                       NDMOS_API_FREE (p);
+
+               if ((p = dst_pv->value) != 0)
+                       NDMOS_API_FREE (p);
+       }
+       da->env_tab.n_env = 0;
+
+       return -1;
+}
+
+struct ndmp9_pval *
+ndmda_find_env (struct ndm_session *sess, char *name)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+       int                     i;
+       struct ndmp9_pval *     pv;
+
+       for (i = 0; i < da->env_tab.n_env; i++) {
+               pv = &da->env_tab.env[i];
+               if (strcmp (pv->name, name) == 0)
+                       return pv;
+       }
+
+       return 0;
+}
+
+
+int
+ndmda_interpret_boolean_value (char *value_str, int default_value)
+{
+       if (strcasecmp (value_str, "y") == 0
+        || strcasecmp (value_str, "yes") == 0
+        || strcasecmp (value_str, "t") == 0
+        || strcasecmp (value_str, "true") == 0
+        || strcasecmp (value_str, "1") == 0)
+               return 1;
+
+       if (strcasecmp (value_str, "n") == 0
+        || strcasecmp (value_str, "no") == 0
+        || strcasecmp (value_str, "f") == 0
+        || strcasecmp (value_str, "false") == 0
+        || strcasecmp (value_str, "0") == 0)
+               return 0;
+
+       return default_value;
+}
+
+void
+ndmda_purge_environment (struct ndm_session *sess)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+       int                     i;
+       struct ndmp9_pval *     pv;
+
+       for (i = 0; i < da->env_tab.n_env; i++) {
+               pv = &da->env_tab.env[i];
+
+               if (pv->name)  NDMOS_API_FREE (pv->name);
+               if (pv->value) NDMOS_API_FREE (pv->value);
+               pv->name = 0;
+               pv->value = 0;
+       }
+       da->env_tab.n_env = 0;
+}
+
+
+int
+ndmda_copy_nlist (struct ndm_session *sess,
+  ndmp9_name *nlist, unsigned n_nlist)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+       unsigned int            i;
+       int                     j;
+       ndmp9_name *            src_nl;
+       ndmp9_name *            dst_nl;
+
+       for (i = 0; i < n_nlist; i++) {
+               j = da->nlist_tab.n_nlist;
+               src_nl = &nlist[i];
+               dst_nl = &da->nlist_tab.nlist[j];
+
+               dst_nl->original_path =
+                       NDMOS_API_STRDUP (src_nl->original_path);
+               dst_nl->destination_path =
+                       NDMOS_API_STRDUP (src_nl->destination_path);
+               dst_nl->fh_info = src_nl->fh_info;
+               da->nlist_tab.result_err[j] = NDMP9_UNDEFINED_ERR;
+               da->nlist_tab.result_count[j] = 0;
+
+               if (!dst_nl->original_path || !dst_nl->destination_path)
+                       return -1;      /* no mem */
+
+               da->nlist_tab.n_nlist++;
+       }
+
+       /* TODO: sort */
+
+       return 0;
+}
+
+void
+ndmda_purge_nlist (struct ndm_session *sess)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+       int                     i;
+       struct ndmp9_name *     nl;
+
+       for (i = 0; i < da->nlist_tab.n_nlist; i++) {
+               nl = &da->nlist_tab.nlist[i];
+
+               if (nl->original_path) {
+                       NDMOS_API_FREE (nl->original_path);
+               }
+               if (nl->destination_path) {
+                       NDMOS_API_FREE (nl->destination_path);
+               }
+
+               nl->original_path = 0;
+               nl->destination_path = 0;
+       }
+       da->nlist_tab.n_nlist = 0;
+}
+
+int
+ndmda_count_invalid_fh_info (struct ndm_session *sess)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+       int                     i, count;
+       struct ndmp9_name *     nl;
+
+       count = 0;
+       for (i = 0; i < da->nlist_tab.n_nlist; i++) {
+               nl = &da->nlist_tab.nlist[i];
+
+               if (nl->fh_info.valid != NDMP9_VALIDITY_VALID)
+                       count++;
+       }
+
+       return count;
+}
+
+int
+ndmda_count_invalid_fh_info_pending (struct ndm_session *sess)
+{
+       struct ndm_data_agent * da = &sess->data_acb;
+       int                     i, count;
+       struct ndmp9_name *     nl;
+
+       count = 0;
+       for (i = 0; i < da->nlist_tab.n_nlist; i++) {
+               nl = &da->nlist_tab.nlist[i];
+
+               if (da->nlist_tab.result_err[i] == NDMP9_UNDEFINED_ERR
+                && nl->fh_info.valid != NDMP9_VALIDITY_VALID) {
+                       count++;
+               }
+       }
+
+       return count;
+}
+
+#endif /* !NDMOS_OPTION_NO_DATA_AGENT */