/* * 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; }