/*
* 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 "ndmos.h"
#include "wraplib.h"
int
wrap_main (int ac, char *av[], struct wrap_ccb *wccb)
{
int rc;
rc = wrap_process_args (ac, av, wccb);
if (rc)
return rc;
rc = wrap_main_start_index_file (wccb);
if (rc)
return rc;
rc = wrap_main_start_image_file (wccb);
if (rc)
return rc;
return 0;
}
int
wrap_main_start_index_file (struct wrap_ccb *wccb)
{
char * filename = wccb->I_index_file_name;
FILE * fp;
if (!filename)
return 0;
if (filename[0] == '#') {
int fd = atoi (filename+1);
if (fd < 2 || fd > 100) {
/* huey! */
strcpy (wccb->errmsg, "bad -I#N");
return -1;
}
fp = fdopen (fd, "w");
if (!fp) {
sprintf (wccb->errmsg, "failed fdopen %s", filename);
return -1;
}
} else {
fp = fopen (filename, "w");
if (!fp) {
sprintf (wccb->errmsg, "failed open %s", filename);
return -1;
}
}
wccb->index_fp = fp;
return 0;
}
int
wrap_main_start_image_file (struct wrap_ccb *wccb)
{
char * filename = wccb->f_file_name;
int fd, o_mode;
switch (wccb->op) {
case WRAP_CCB_OP_BACKUP:
o_mode = O_CREAT | O_WRONLY;
break;
case WRAP_CCB_OP_RECOVER:
case WRAP_CCB_OP_RECOVER_FILEHIST:
o_mode = O_RDONLY;
break;
default:
abort();
return -1;
}
if (!filename)
filename = "-";
if (strcmp (filename, "-") == 0) {
if (wccb->op == WRAP_CCB_OP_BACKUP) {
fd = 1;
} else {
fd = 0;
}
} else if (filename[0] == '#') {
fd = atoi (filename+1);
if (fd < 2 || fd > 100) {
/* huey! */
strcpy (wccb->errmsg, "bad -f#N");
return -1;
}
} else {
fd = open (filename, o_mode, 0666);
if (fd < 0) {
sprintf (wccb->errmsg, "failed open %s", filename);
return -1;
}
}
wccb->data_conn_fd = fd;
return 0;
}
void
wrap_log (struct wrap_ccb *wccb, char *fmt, ...)
{
va_list ap;
char buf[4096];
if (!wccb->index_fp && wccb->d_debug < 1)
return;
sprintf (buf, "%04d ", ++wccb->log_seq_num);
va_start (ap, fmt);
vsnprintf (buf+5, sizeof(buf)-5, fmt, ap);
va_end (ap);
if (wccb->index_fp)
wrap_send_log_message (wccb->index_fp, buf);
if (wccb->d_debug > 0)
fprintf (stderr, "LOG: %s\n", buf);
}
int
wrap_set_error (struct wrap_ccb *wccb, int error)
{
if (error == 0)
error = -3;
wccb->error = error;
return wccb->error;
}
int
wrap_set_errno (struct wrap_ccb *wccb)
{
return wrap_set_error (wccb, errno);
}
/*
* wrap -c [-B TYPE] [-d N] [-I FILE] [-E NAME=VALUE ...]
* wrap -x [-B TYPE] [-d N] [-I FILE] [-E NAME=VALUE ...]
* ORIGINAL_NAME @pos NEW_NAME ...
* wrap -t [-B TYPE] [-d N] [-I FILE] [-E NAME=VALUE ...]
* ORIGINAL_NAME @pos
*/
int
wrap_process_args (int argc, char *argv[], struct wrap_ccb *wccb)
{
int c;
enum wrap_ccb_op op;
char * p;
NDMOS_MACRO_ZEROFILL (wccb);
wccb->progname = argv[0];
if (argc < 2) {
strcpy (wccb->errmsg, "too few arguments");
return -1;
}
while ((c = getopt (argc, argv, "cxtB:d:I:E:f:o:")) != EOF) {
switch (c) {
case 'c':
op = WRAP_CCB_OP_BACKUP;
goto set_op;
case 't':
op = WRAP_CCB_OP_RECOVER_FILEHIST;
goto set_op;
case 'x':
op = WRAP_CCB_OP_RECOVER;
goto set_op;
set_op:
if (wccb->op != WRAP_CCB_OP_NONE) {
strcpy (wccb->errmsg, "only one of -c, -x, -t");
return -1;
}
wccb->op = op;
break;
case 'B':
if (wccb->B_butype) {
strcpy (wccb->errmsg, "only one -B allowed");
return -1;
}
wccb->B_butype = optarg;
break;
case 'd':
wccb->d_debug = atoi(optarg);
break;
case 'E':
if (wccb->n_env >= WRAP_MAX_ENV) {
strcpy (wccb->errmsg, "-E overflow");
return -1;
}
p = strchr (optarg, '=');
if (p) {
*p++ = 0;
} else {
p = "";
}
wccb->env[wccb->n_env].name = optarg;
wccb->env[wccb->n_env].value = p;
wccb->n_env++;
break;
case 'f':
if (wccb->f_file_name) {
strcpy (wccb->errmsg, "only one -f allowed");
return -1;
}
wccb->f_file_name = optarg;
break;
case 'I':
if (wccb->I_index_file_name) {
strcpy (wccb->errmsg, "only one -I allowed");
return -1;
}
wccb->I_index_file_name = optarg;
break;
case 'o':
if (wccb->n_o_option >= WRAP_MAX_O_OPTION) {
strcpy (wccb->errmsg, "-o overflow");
return -1;
}
wccb->o_option[wccb->n_o_option] = optarg;
wccb->n_o_option++;
break;
default:
strcpy (wccb->errmsg, "unknown option");
return -1;
}
}
switch (wccb->op) {
default:
abort(); /* just can't happen */
case WRAP_CCB_OP_NONE:
strcpy (wccb->errmsg, "one of -c, -x, or -t required");
return -1;
case WRAP_CCB_OP_BACKUP:
if (optind < argc) {
strcpy (wccb->errmsg, "extra args not allowed for -c");
return -1;
}
break;
case WRAP_CCB_OP_RECOVER:
case WRAP_CCB_OP_RECOVER_FILEHIST:
break;
}
for (c = optind; c+2 < argc; c += 3) {
p = argv[c+1];
if (p[0] != '@') {
sprintf (wccb->errmsg, "malformed fhinfo %s", p);
return -1;
}
if (wccb->n_file >= WRAP_MAX_FILE) {
strcpy (wccb->errmsg, "file table overflow");
return -1;
}
if (strcmp (p, "@-") == 0) {
wccb->file[wccb->n_file].fhinfo = WRAP_INVALID_FHINFO;
} else {
wccb->file[wccb->n_file].fhinfo =
NDMOS_API_STRTOLL (p+1, &p, 0);
if (*p != 0) {
sprintf(wccb->errmsg,"malformed fhinfo %s",p);
return -1;
}
}
wccb->file[wccb->n_file].original_name = argv[c];
wccb->file[wccb->n_file].save_to_name = argv[c+2];
wccb->n_file++;
}
if (c < argc) {
strcpy (wccb->errmsg, "superfluous args at end");
return -1;
}
p = wrap_find_env (wccb, "HIST");
if (p) {
switch (*p) {
case 'y': case 'Y':
p = wrap_find_env (wccb, "HIST_TYPE");
if (!p) {
p = "y";
}
break;
}
switch (*p) {
case 'y': case 'Y':
wccb->hist_enable = 'y';
break;
case 'd': case 'D':
wccb->hist_enable = 'd';
break;
case 'f': case 'F':
wccb->hist_enable = 'f';
break;
default:
/* gripe? */
break;
}
}
p = wrap_find_env (wccb, "DIRECT");
if (p) {
if (*p == 'y') {
wccb->direct_enable = 1;
}
}
p = wrap_find_env (wccb, "FILESYSTEM");
if (!p)
p = wrap_find_env (wccb, "PREFIX");
if (!p)
p = "/";
wccb->backup_root = p;
return 0;
}
char *
wrap_find_env (struct wrap_ccb *wccb, char *name)
{
int i;
for (i = 0; i < wccb->n_env; i++) {
if (strcmp (wccb->env[i].name, name) == 0)
return wccb->env[i].value;
}
return 0;
}
int
wrap_cmd_add_with_escapes (char *cmd, char *word, char *special)
{
char * cmd_lim = &cmd[WRAP_MAX_COMMAND-3];
char * p;
int c;
p = cmd;
while (*p) p++;
if (p != cmd) *p++ = ' ';
while ((c = *word++) != 0) {
if (p >= cmd_lim)
return -1; /* overflow */
if (c == '\\' || strchr (special, c))
*p++ = '\\';
*p++ = c;
}
*p = 0;
return 0;
}
int
wrap_cmd_add_with_sh_escapes (char *cmd, char *word)
{
return wrap_cmd_add_with_escapes (cmd, word, " \t`'\"*?[]$");
}
int
wrap_cmd_add_allow_file_wildcards (char *cmd, char *word)
{
return wrap_cmd_add_with_escapes (cmd, word, " \t`'\"$");
}
int
wrap_pipe_fork_exec (char *cmd, int fdmap[3])
{
int pipes[3][2];
int child_fdmap[3];
int nullfd = -1;
int i;
int rc = -1;
for (i = 0; i < 3; i++) {
pipes[i][0] = -1;
pipes[i][1] = -1;
child_fdmap[i] = -1;
}
for (i = 0; i < 3; i++) {
if (fdmap[i] >= 0) {
child_fdmap[i] = fdmap[i];
continue;
}
switch (fdmap[i]) {
case WRAP_FDMAP_DEV_NULL:
if (nullfd < 0) {
nullfd = open ("/dev/null", 2);
if (nullfd < 0) {
goto bail_out;
}
}
child_fdmap[i] = nullfd;
break;
case WRAP_FDMAP_INPUT_PIPE:
rc = pipe (pipes[i]);
if (rc != 0) {
goto bail_out;
}
child_fdmap[i] = pipes[i][0];
break;
case WRAP_FDMAP_OUTPUT_PIPE:
rc = pipe (pipes[i]);
if (rc != 0) {
goto bail_out;
}
child_fdmap[i] = pipes[i][1];
break;
default:
goto bail_out;
}
}
rc = fork();
if (rc < 0) {
goto bail_out;
}
if (rc == 0) {
/* child */
dup2 (child_fdmap[2], 2);
dup2 (child_fdmap[1], 1);
dup2 (child_fdmap[0], 0);
for (rc = 3; rc < 100; rc++) close(rc);
execl ("/bin/sh", "sh", "-c", cmd, NULL);
fprintf (stderr, "EXEC FAILED %s\n", cmd);
exit(127);
}
if (nullfd >= 0)
close (nullfd);
for (i = 0; i < 3; i++) {
if (fdmap[i] >= 0) {
continue;
}
switch (fdmap[i]) {
case WRAP_FDMAP_DEV_NULL:
break;
case WRAP_FDMAP_INPUT_PIPE:
close (pipes[i][0]);
fdmap[i] = pipes[i][1];
break;
case WRAP_FDMAP_OUTPUT_PIPE:
close (pipes[i][1]);
fdmap[i] = pipes[i][0];
break;
default:
abort();
}
}
return rc; /* PID */
bail_out:
if (nullfd >= 0)
close (nullfd);
for (i = 0; i < 3; i++) {
if (pipes[i][0] >= 0)
close (pipes[i][0]);
if (pipes[i][1] >= 0)
close (pipes[i][1]);
}
return -1;
}
int
wrap_parse_msg (char *buf, struct wrap_msg_buf *wmsg)
{
int c1, c2;
c1 = buf[0];
c2 = buf[1];
if (buf[2] != ' ') {
return -1;
}
if (c1 == 'L' && c2 == 'x') { /* log_message */
return wrap_parse_log_message_msg (buf, wmsg);
}
if (c1 == 'H' && c2 == 'F') { /* add_file */
return wrap_parse_add_file_msg (buf, wmsg);
}
if (c1 == 'H' && c2 == 'D') { /* add_dirent */
return wrap_parse_add_dirent_msg (buf, wmsg);
}
if (c1 == 'H' && c2 == 'N') { /* add_node */
return wrap_parse_add_node_msg (buf, wmsg);
}
if (c1 == 'D' && c2 == 'E') { /* add_env */
return wrap_parse_add_env_msg (buf, wmsg);
}
if (c1 == 'D' && c2 == 'R') { /* data_read */
return wrap_parse_data_read_msg (buf, wmsg);
}
return -1;
}
int
wrap_parse_log_message_msg (char *buf, struct wrap_msg_buf *wmsg)
{
struct wrap_log_message *res = &wmsg->body.log_message;
char * scan = buf+3;
int rc;
wmsg->msg_type = WRAP_MSGTYPE_LOG_MESSAGE;
while (*scan && *scan == ' ')
scan++;
rc = wrap_cstr_to_str (scan, res->message, sizeof res->message);
if (rc < 0) return -2;
return 0;
}
int
wrap_send_log_message (FILE *fp, char *message)
{
struct wrap_msg_buf wmsg;
struct wrap_log_message *res = &wmsg.body.log_message;
if (!fp) return -1;
wrap_cstr_from_str (message, res->message, sizeof res->message);
fprintf (fp, "Lx %s\n", res->message);
return 0;
}
int
wrap_parse_add_file_msg (char *buf, struct wrap_msg_buf *wmsg)
{
struct wrap_add_file * res = &wmsg->body.add_file;
char * scan = buf+3;
char * p;
int rc;
wmsg->msg_type = WRAP_MSGTYPE_ADD_FILE;
res->fstat.valid = 0;
res->fhinfo = WRAP_INVALID_FHINFO;
while (*scan && *scan == ' ')
scan++;
if (*scan == 0)
return -1;
p = scan;
while (*scan && *scan != ' ')
scan++;
if (*scan) {
*scan = 0;
rc = wrap_cstr_to_str (p, res->path, sizeof res->path);
*scan++ = ' ';
} else {
rc = wrap_cstr_to_str (p, res->path, sizeof res->path);
}
if (rc < 0) return -2;
while (*scan) {
p = scan+1;
switch (*scan) {
case ' ':
scan++;
continue;
case '@':
res->fhinfo = NDMOS_API_STRTOLL (p, &scan, 0);
break;
default:
rc = wrap_parse_fstat_subr(&scan, &res->fstat);
if (rc < 0)
return rc;
break;
}
if (*scan != ' ' && *scan != 0) {
/* bogus */
return -1;
}
}
return 0;
}
int
wrap_send_add_file (FILE *fp, char *path, unsigned long long fhinfo,
struct wrap_fstat *fstat)
{
struct wrap_msg_buf wmsg;
struct wrap_add_file * res = &wmsg.body.add_file;
if (!fp) return -1;
wrap_cstr_from_str (path, res->path, sizeof res->path);
fprintf (fp, "HF %s", res->path);
if (fhinfo != WRAP_INVALID_FHINFO)
fprintf (fp, " @%llu", fhinfo);
wrap_send_fstat_subr (fp, fstat);
fprintf (fp, "\n");
return 0;
}
int
wrap_parse_add_dirent_msg (char *buf, struct wrap_msg_buf *wmsg)
{
struct wrap_add_dirent *res = &wmsg->body.add_dirent;
char * scan = buf+3;
char * p;
int rc;
wmsg->msg_type = WRAP_MSGTYPE_ADD_DIRENT;
res->fhinfo = WRAP_INVALID_FHINFO;
while (*scan && *scan == ' ')
scan++;
if (*scan == 0)
return -1;
res->dir_fileno = NDMOS_API_STRTOLL (scan, &scan, 0);
if (*scan != ' ')
return -1;
while (*scan == ' ') scan++;
if (*scan == 0)
return -1;
p = scan;
while (*scan && *scan != ' ')
scan++;
if (*scan) {
*scan = 0;
rc = wrap_cstr_to_str (p, res->name, sizeof res->name);
*scan++ = ' ';
} else {
rc = wrap_cstr_to_str (p, res->name, sizeof res->name);
}
if (rc < 0) return -2;
res->fileno = NDMOS_API_STRTOLL (scan, &scan, 0);
if (*scan != ' ' && *scan != 0)
return -1;
while (*scan == ' ') scan++;
if (*scan == '@') {
res->fhinfo = NDMOS_API_STRTOLL(scan+1, &scan, 0);
}
if (*scan != ' ' && *scan != 0)
return -1;
while (*scan == ' ') scan++;
if (*scan)
return -1;
return 0;
}
int
wrap_send_add_dirent (FILE *fp, char *name, unsigned long long fhinfo,
unsigned long long dir_fileno, unsigned long long fileno)
{
struct wrap_msg_buf wmsg;
struct wrap_add_dirent *res = &wmsg.body.add_dirent;
if (!fp) return -1;
wrap_cstr_from_str (name, res->name, sizeof res->name);
fprintf (fp, "HD %llu %s %llu", dir_fileno, res->name, fileno);
if (fhinfo != WRAP_INVALID_FHINFO)
fprintf (fp, " @%llu", fhinfo);
fprintf (fp, "\n");
return 0;
}
int
wrap_parse_add_node_msg (char *buf, struct wrap_msg_buf *wmsg)
{
struct wrap_add_node * res = &wmsg->body.add_node;
char * scan = buf+3;
char * p;
int rc;
wmsg->msg_type = WRAP_MSGTYPE_ADD_NODE;
res->fstat.valid = 0;
res->fhinfo = WRAP_INVALID_FHINFO;
while (*scan && *scan == ' ')
scan++;
if (*scan == 0)
return -1;
res->fstat.fileno = NDMOS_API_STRTOLL (scan, &scan, 0);
if (*scan != ' ' && *scan != 0)
return -1;
res->fstat.valid |= WRAP_FSTAT_VALID_FILENO;
while (*scan) {
p = scan+1;
switch (*scan) {
case ' ':
scan++;
continue;
case '@':
res->fhinfo = NDMOS_API_STRTOLL (p, &scan, 0);
break;
default:
rc = wrap_parse_fstat_subr(&scan, &res->fstat);
if (rc < 0)
return rc;
break;
}
if (*scan != ' ' && *scan != 0) {
/* bogus */
return -1;
}
}
if ( (res->fstat.valid & WRAP_FSTAT_VALID_FILENO) == 0)
return -5;
return 0;
}
int
wrap_send_add_node (FILE *fp, unsigned long long fhinfo,
struct wrap_fstat *fstat)
{
unsigned long save_valid;
if (!fp) return -1;
if (fstat->valid & WRAP_FSTAT_VALID_FILENO) {
fprintf (fp, "HN %llu", fstat->fileno);
} else {
fprintf (fp, "HN 0000000000");
}
if (fhinfo != WRAP_INVALID_FHINFO)
fprintf (fp, " @%llu", fhinfo);
/* suppress iFILENO */
save_valid = fstat->valid;
fstat->valid &= ~WRAP_FSTAT_VALID_FILENO;
wrap_send_fstat_subr (fp, fstat);
fstat->valid = save_valid;
fprintf (fp, "\n");
return 0;
}
int
wrap_parse_fstat_subr (char **scanp, struct wrap_fstat *fstat)
{
char * scan = *scanp;
char * p = scan+1;
unsigned long valid = 0;
valid = 0;
switch (*scan) {
case 's': /* size */
valid = WRAP_FSTAT_VALID_SIZE;
fstat->size = NDMOS_API_STRTOLL (p, &scan, 0);
break;
case 'i': /* fileno (inum) */
valid = WRAP_FSTAT_VALID_FILENO;
fstat->fileno = NDMOS_API_STRTOLL (p, &scan, 0);
break;
case 'm': /* mode low twelve bits */
valid = WRAP_FSTAT_VALID_MODE;
fstat->mode = strtol (p, &scan, 8);
break;
case 'l': /* link count */
valid = WRAP_FSTAT_VALID_LINKS;
fstat->links = strtol (p, &scan, 0);
break;
case 'u': /* uid */
valid = WRAP_FSTAT_VALID_UID;
fstat->uid = strtol (p, &scan, 0);
break;
case 'g': /* gid */
valid = WRAP_FSTAT_VALID_GID;
fstat->gid = strtol (p, &scan, 0);
break;
case 't': /* one of the times */
p = scan+2;
switch (scan[1]) {
case 'm': /* mtime */
valid = WRAP_FSTAT_VALID_MTIME;
fstat->mtime = strtol (p, &scan, 0);
break;
case 'a': /* atime */
valid = WRAP_FSTAT_VALID_ATIME;
fstat->atime = strtol (p, &scan, 0);
break;
case 'c': /* ctime */
valid = WRAP_FSTAT_VALID_CTIME;
fstat->ctime = strtol (p, &scan, 0);
break;
default:
return -3;
}
break;
case 'f': /* ftype (file type) */
valid = WRAP_FSTAT_VALID_FTYPE;
switch (scan[1]) {
case 'd': fstat->ftype = WRAP_FTYPE_DIR; break;
case 'p': fstat->ftype = WRAP_FTYPE_FIFO; break;
case 'c': fstat->ftype = WRAP_FTYPE_CSPEC; break;
case 'b': fstat->ftype = WRAP_FTYPE_BSPEC; break;
case '-': fstat->ftype = WRAP_FTYPE_REG; break;
case 'l': fstat->ftype = WRAP_FTYPE_SLINK; break;
case 's': fstat->ftype = WRAP_FTYPE_SOCK; break;
case 'R': fstat->ftype = WRAP_FTYPE_REGISTRY; break;
case 'o': fstat->ftype = WRAP_FTYPE_OTHER; break;
default:
fstat->ftype = WRAP_FTYPE_INVALID;
return -5;
}
scan += 2;
break;
default:
return -3;
}
if (*scan != ' ' && *scan != 0)
return -1;
fstat->valid |= valid;
*scanp = scan;
return 0;
}
int
wrap_send_fstat_subr (FILE *fp, struct wrap_fstat *fstat)
{
if (!fp) return -1;
if (fstat->valid & WRAP_FSTAT_VALID_FTYPE) {
int c = 0;
switch (fstat->ftype) {
default:
case WRAP_FTYPE_INVALID:
c = 0;
break;
case WRAP_FTYPE_DIR: c = 'd'; break;
case WRAP_FTYPE_FIFO: c = 'p'; break;
case WRAP_FTYPE_CSPEC: c = 'c'; break;
case WRAP_FTYPE_BSPEC: c = 'b'; break;
case WRAP_FTYPE_REG: c = '-'; break;
case WRAP_FTYPE_SLINK: c = 'l'; break;
case WRAP_FTYPE_SOCK: c = 's'; break;
case WRAP_FTYPE_REGISTRY: c = 'R'; break;
case WRAP_FTYPE_OTHER: c = 'o'; break;
}
if (c) {
fprintf (fp, " f%c", c);
} else {
return -1;
}
}
if (fstat->valid & WRAP_FSTAT_VALID_MODE) {
fprintf (fp, " m%04o", fstat->mode);
}
if (fstat->valid & WRAP_FSTAT_VALID_LINKS) {
fprintf (fp, " l%lu", fstat->links);
}
if (fstat->valid & WRAP_FSTAT_VALID_SIZE) {
fprintf (fp, " s%llu", fstat->size);
}
if (fstat->valid & WRAP_FSTAT_VALID_UID) {
fprintf (fp, " u%lu", fstat->uid);
}
if (fstat->valid & WRAP_FSTAT_VALID_GID) {
fprintf (fp, " g%lu", fstat->gid);
}
if (fstat->valid & WRAP_FSTAT_VALID_ATIME) {
fprintf (fp, " ta%lu", fstat->atime);
}
if (fstat->valid & WRAP_FSTAT_VALID_MTIME) {
fprintf (fp, " tm%lu", fstat->mtime);
}
if (fstat->valid & WRAP_FSTAT_VALID_CTIME) {
fprintf (fp, " tc%lu", fstat->ctime);
}
if (fstat->valid & WRAP_FSTAT_VALID_FILENO) {
fprintf (fp, " i%llu", fstat->fileno);
}
return 0;
}
int
wrap_parse_add_env_msg (char *buf, struct wrap_msg_buf *wmsg)
{
struct wrap_add_env * res = &wmsg->body.add_env;
char * scan = buf+3;
char * p;
int rc;
wmsg->msg_type = WRAP_MSGTYPE_ADD_ENV;
while (*scan && *scan == ' ')
scan++;
if (*scan == 0)
return -1;
p = scan;
while (*scan && *scan != ' ')
scan++;
if (*scan) {
*scan = 0;
rc = wrap_cstr_to_str (p, res->name, sizeof res->name);
*scan++ = ' ';
} else {
rc = wrap_cstr_to_str (p, res->name, sizeof res->name);
}
if (rc < 0) return -2;
while (*scan && *scan == ' ')
scan++;
p = scan;
while (*scan && *scan != ' ')
scan++;
if (*scan) {
*scan = 0;
rc = wrap_cstr_to_str (p, res->value, sizeof res->value);
*scan++ = ' ';
} else {
rc = wrap_cstr_to_str (p, res->value, sizeof res->value);
}
if (rc < 0) return -2;
return 0;
}
int
wrap_send_add_env (FILE *fp, char *name, char *value)
{
struct wrap_msg_buf wmsg;
struct wrap_add_env * res = &wmsg.body.add_env;
if (!fp) return -1;
wrap_cstr_from_str (name, res->name, sizeof res->name);
wrap_cstr_from_str (value, res->value, sizeof res->value);
fprintf (fp, "DE %s %s\n", res->name, res->value);
return 0;
}
int
wrap_parse_data_read_msg (char *buf, struct wrap_msg_buf *wmsg)
{
struct wrap_data_read * res = &wmsg->body.data_read;
char * scan = buf+3;
wmsg->msg_type = WRAP_MSGTYPE_DATA_READ;
while (*scan && *scan == ' ')
scan++;
if (*scan == 0)
return -1;
res->offset = NDMOS_API_STRTOLL (scan, &scan, 0);
if (*scan != ' ')
return -1;
while (*scan && *scan != ' ')
scan++;
if (*scan == 0)
return -1;
res->length = NDMOS_API_STRTOLL (scan, &scan, 0);
/* tolerate trailing white */
while (*scan && *scan != ' ')
scan++;
if (*scan != 0)
return -1;
return 0;
}
int
wrap_send_data_read (FILE *fp,
unsigned long long offset, unsigned long long length)
{
if (!fp) return -1;
fprintf (fp, "DR %lld %lld\n", (long long) offset, (long long)length);
fflush (fp);
return 0;
}
int
wrap_parse_data_stats_msg (char *buf, struct wrap_msg_buf *wmsg)
{
#if 0
struct wrap_data_stats *res = &wmsg->body.data_stats;
char * scan = buf+3;
wmsg->msg_type = WRAP_MSGTYPE_DATA_STATS;
#endif
return -1;
}
int
wrap_send_data_stats (FILE *fp)
{
if (!fp) return -1;
fprintf (fp, "DS ...\n");
fflush (fp);
return 0;
}
/*
* Recovery helpers
****************************************************************
*/
int
wrap_reco_align_to_wanted (struct wrap_ccb *wccb)
{
unsigned long long distance;
unsigned long unwanted_length;
top:
/*
* If there is an error, we're toast.
*/
if (wccb->error)
return wccb->error;
/*
* If we're aligned, we're done.
*/
if (wccb->expect_offset == wccb->want_offset) {
if (wccb->expect_length < wccb->want_length
&& wccb->reading_length == 0) {
wrap_reco_issue_read (wccb);
}
return wccb->error;
}
/*
* If we have a portion we don't want, consume it now
*/
if (wccb->have_length > 0) {
if (wccb->have_offset < wccb->want_offset) {
distance = wccb->want_offset - wccb->have_offset;
if (distance < wccb->have_length) {
/*
* We have some of what we want.
* Consume (discard) unwanted part.
*/
unwanted_length = distance;
} else {
unwanted_length = wccb->have_length;
}
} else {
unwanted_length = wccb->have_length;
}
wrap_reco_consume (wccb, unwanted_length);
goto top;
}
if (wccb->expect_length > 0) {
/* Incoming, but we don't have it yet. */
wrap_reco_receive (wccb);
goto top;
}
/*
* We don't have anything. We don't expect anything.
* Time to issue an NDMP_DATA_NOTIFY_READ via this wrapper.
*/
wrap_reco_issue_read (wccb);
goto top;
}
int
wrap_reco_receive (struct wrap_ccb *wccb)
{
char * iobuf_end = &wccb->iobuf[wccb->n_iobuf];
char * have_end = wccb->have + wccb->have_length;
unsigned n_read = iobuf_end - have_end;
int rc;
if (wccb->error)
return wccb->error;
if (wccb->have_length == 0) {
wccb->have = wccb->iobuf;
have_end = wccb->have + wccb->have_length;
}
if (n_read < 512 && wccb->have != wccb->iobuf) {
/* Not much room at have_end. Front of iobuf available. */
/* Compress */
NDMOS_API_BCOPY (wccb->have, wccb->iobuf, wccb->have_length);
wccb->have = wccb->iobuf;
have_end = wccb->have + wccb->have_length;
n_read = iobuf_end - have_end;
}
if (n_read > wccb->reading_length)
n_read = wccb->reading_length;
if (n_read == 0) {
/* Hmmm. */
abort ();
return -1;
}
rc = read (wccb->data_conn_fd, have_end, n_read);
if (rc > 0) {
wccb->have_length += rc;
wccb->reading_offset += rc;
wccb->reading_length -= rc;
} else {
/* EOF or error */
if (rc == 0) {
strcpy (wccb->errmsg, "EOF on data connection");
wrap_set_error (wccb, -1);
} else {
sprintf (wccb->errmsg, "errno %d on data connection",
errno);
wrap_set_errno (wccb);
}
}
return wccb->error;
}
int
wrap_reco_consume (struct wrap_ccb *wccb, unsigned long length)
{
assert (wccb->have_length >= length);
wccb->have_offset += length;
wccb->have_length -= length;
wccb->expect_offset += length;
wccb->expect_length -= length;
wccb->have += length;
if (wccb->expect_length == 0) {
assert (wccb->have_length == 0);
wccb->expect_offset = -1ull;
}
return wccb->error;
}
int
wrap_reco_must_have (struct wrap_ccb *wccb, unsigned long length)
{
if (wccb->want_length < length)
wccb->want_length = length;
wrap_reco_align_to_wanted (wccb);
while (wccb->have_length < length && !wccb->error) {
wrap_reco_align_to_wanted (wccb); /* triggers issue_read() */
wrap_reco_receive (wccb);
}
if (wccb->have_length >= length)
return 0;
return wccb->error;
}
int
wrap_reco_seek (struct wrap_ccb *wccb,
unsigned long long want_offset,
unsigned long long want_length,
unsigned long must_have_length)
{
if (wccb->error)
return wccb->error;
wccb->want_offset = want_offset;
wccb->want_length = want_length;
return wrap_reco_must_have (wccb, must_have_length);
}
int
wrap_reco_pass (struct wrap_ccb *wccb, int write_fd,
unsigned long long length, unsigned write_bsize)
{
unsigned cnt;
int rc;
while (length > 0) {
if (wccb->error)
break;
cnt = write_bsize;
if (cnt > length)
cnt = length;
if (wccb->have_length < cnt) {
wrap_reco_must_have (wccb, cnt);
}
rc = write (write_fd, wccb->have, cnt);
length -= cnt;
wrap_reco_consume (wccb, cnt);
}
return wccb->error;
}
int
wrap_reco_issue_read (struct wrap_ccb *wccb)
{
unsigned long long off;
unsigned long long len;
assert (wccb->reading_length == 0);
if (wccb->data_conn_mode == 0) {
struct stat st;
int rc;
rc = fstat (wccb->data_conn_fd, &st);
if (rc != 0) {
sprintf (wccb->errmsg, "Can't fstat() data conn rc=%d",
rc);
return wrap_set_errno (wccb);
}
if (S_ISFIFO(st.st_mode)) {
wccb->data_conn_mode = 'p';
if (!wccb->index_fp) {
strcpy (wccb->errmsg,
"data_conn is pipe but no -I");
return wrap_set_error (wccb, -3);
}
} else if (S_ISREG(st.st_mode)) {
wccb->data_conn_mode = 'f';
} else {
sprintf (wccb->errmsg, "Unsupported data_conn type %o",
st.st_mode);
return wrap_set_error (wccb, -3);
}
}
off = wccb->want_offset;
len = wccb->want_length;
off += wccb->have_length;
len -= wccb->have_length;
if (len == 0) {
abort();
}
wccb->last_read_offset = off;
wccb->last_read_length = len;
switch (wccb->data_conn_mode) {
default:
abort();
return -1;
case 'f':
lseek (wccb->data_conn_fd, off, 0);
break;
case 'p':
wrap_send_data_read (wccb->index_fp, off, len);
break;
}
wccb->reading_offset = wccb->last_read_offset;
wccb->reading_length = wccb->last_read_length;
if (wccb->have_length == 0) {
wccb->expect_offset = wccb->reading_offset;
wccb->expect_length = wccb->reading_length;
} else {
wccb->expect_length += len;
}
return wccb->error;
}
/*
* (Note: this is hoisted from ndml_cstr.c)
*
* Description:
* Convert strings to/from a canonical strings (CSTR).
*
* The main reason for this is to eliminate spaces
* in strings thus making multiple strings easily
* delimited by white space.
*
* Canonical strings use the HTTP convention of
* percent sign followed by two hex digits (%xx).
* Characters outside the printable ASCII range,
* space, and percent sign are so converted.
*
* Both interfaces return the length of the resulting
* string, -1 if there is an overflow, or -2
* there is a conversion error.
*/
int
wrap_cstr_from_str (char *src, char *dst, unsigned dst_max)
{
static char cstr_to_hex[] = "0123456789ABCDEF";
unsigned char * p = (unsigned char *)src;
unsigned char * q = (unsigned char *)dst;
unsigned char * q_end = q + dst_max - 1;
int c;
while ((c = *p++) != 0) {
if (c <= ' ' || c > 0x7E || c == NDMCSTR_WARN) {
if (q+3 > q_end)
return -1;
*q++ = NDMCSTR_WARN;
*q++ = cstr_to_hex[(c>>4)&0xF];
*q++ = cstr_to_hex[c&0xF];
} else {
if (q+1 > q_end)
return -1;
*q++ = c;
}
}
*q = 0;
return q - (unsigned char *)dst;
}
int
wrap_cstr_to_str (char *src, char *dst, unsigned dst_max)
{
unsigned char * p = (unsigned char *)src;
unsigned char * q = (unsigned char *)dst;
unsigned char * q_end = q + dst_max - 1;
int c, c1, c2;
while ((c = *p++) != 0) {
if (q+1 > q_end)
return -1;
if (c != NDMCSTR_WARN) {
*q++ = c;
continue;
}
c1 = wrap_cstr_from_hex (p[0]);
c2 = wrap_cstr_from_hex (p[1]);
if (c1 < 0 || c2 < 0) {
/* busted conversion */
return -2;
}
c = (c1<<4) + c2;
*q++ = c;
p += 2;
}
*q = 0;
return q - (unsigned char *)dst;
}
int
wrap_cstr_from_hex (int c)
{
if ('0' <= c && c <= '9')
return c - '0';
if ('a' <= c && c <= 'f')
return (c - 'a') + 10;
if ('A' <= c && c <= 'F')
return (c - 'A') + 10;
return -1;
}