Imported Upstream version 3.1.0
[debian/amanda] / ndmp-src / wraplib.c
1 /*
2  * Copyright (c) 1998,1999,2000
3  *      Traakan, Inc., Los Altos, CA
4  *      All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice unmodified, this list of conditions, and the following
11  *    disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 /*
30  * Project:  NDMJOB
31  * Ident:    $Id: $
32  *
33  * Description:
34  *
35  */
36
37 #include "ndmos.h"
38 #include "wraplib.h"
39
40
41
42
43 int
44 wrap_main (int ac, char *av[], struct wrap_ccb *wccb)
45 {
46         int             rc;
47
48         rc = wrap_process_args (ac, av, wccb);
49         if (rc)
50                 return rc;
51
52         rc = wrap_main_start_index_file (wccb);
53         if (rc)
54                 return rc;
55
56         rc = wrap_main_start_image_file (wccb);
57         if (rc)
58                 return rc;
59
60
61         return 0;
62 }
63
64 int
65 wrap_main_start_index_file (struct wrap_ccb *wccb)
66 {
67         char *          filename = wccb->I_index_file_name;
68         FILE *          fp;
69
70         if (!filename)
71                 return 0;
72
73         if (filename[0] == '#') {
74                 int     fd = atoi (filename+1);
75
76                 if (fd < 2 || fd > 100) {
77                         /* huey! */
78                         strcpy (wccb->errmsg, "bad -I#N");
79                         return -1;
80                 }
81                 fp = fdopen (fd, "w");
82                 if (!fp) {
83                         sprintf (wccb->errmsg, "failed fdopen %s", filename);
84                         return -1;
85                 }
86         } else {
87                 fp = fopen (filename, "w");
88                 if (!fp) {
89                         sprintf (wccb->errmsg, "failed open %s", filename);
90                         return -1;
91                 }
92         }
93
94         wccb->index_fp = fp;
95
96         return 0;
97 }
98
99 int
100 wrap_main_start_image_file (struct wrap_ccb *wccb)
101 {
102         char *          filename = wccb->f_file_name;
103         int             fd, o_mode;
104
105         switch (wccb->op) {
106         case WRAP_CCB_OP_BACKUP:
107                 o_mode = O_CREAT | O_WRONLY;
108                 break;
109
110         case WRAP_CCB_OP_RECOVER:
111         case WRAP_CCB_OP_RECOVER_FILEHIST:
112                 o_mode = O_RDONLY;
113                 break;
114
115         default:
116                 abort();
117                 return -1;
118         }
119
120         if (!filename)
121                 filename = "-";
122
123         if (strcmp (filename, "-") == 0) {
124                 if (wccb->op == WRAP_CCB_OP_BACKUP) {
125                         fd = 1;
126                 } else {
127                         fd = 0;
128                 }
129         } else if (filename[0] == '#') {
130                 fd = atoi (filename+1);
131
132                 if (fd < 2 || fd > 100) {
133                         /* huey! */
134                         strcpy (wccb->errmsg, "bad -f#N");
135                         return -1;
136                 }
137         } else {
138                 fd = open (filename, o_mode, 0666);
139                 if (fd < 0) {
140                         sprintf (wccb->errmsg, "failed open %s", filename);
141                         return -1;
142                 }
143         }
144
145         wccb->data_conn_fd = fd;
146
147         return 0;
148 }
149
150 void
151 wrap_log (struct wrap_ccb *wccb, char *fmt, ...)
152 {
153         va_list         ap;
154         char            buf[4096];
155
156         if (!wccb->index_fp && wccb->d_debug < 1)
157                 return;
158
159         sprintf (buf, "%04d ", ++wccb->log_seq_num);
160
161         va_start (ap, fmt);
162         vsnprintf (buf+5, sizeof(buf)-5, fmt, ap);
163         va_end (ap);
164
165         if (wccb->index_fp)
166                 wrap_send_log_message (wccb->index_fp, buf);
167
168         if (wccb->d_debug > 0)
169                 fprintf (stderr, "LOG: %s\n", buf);
170 }
171
172 int
173 wrap_set_error (struct wrap_ccb *wccb, int error)
174 {
175         if (error == 0)
176                 error = -3;
177
178         wccb->error = error;
179
180         return wccb->error;
181 }
182
183 int
184 wrap_set_errno (struct wrap_ccb *wccb)
185 {
186         return wrap_set_error (wccb, errno);
187 }
188
189
190 /*
191  * wrap -c [-B TYPE] [-d N] [-I FILE] [-E NAME=VALUE ...]
192  * wrap -x [-B TYPE] [-d N] [-I FILE] [-E NAME=VALUE ...]
193  *              ORIGINAL_NAME @pos NEW_NAME ...
194  * wrap -t [-B TYPE] [-d N] [-I FILE] [-E NAME=VALUE ...]
195  *              ORIGINAL_NAME @pos
196  */
197
198 int
199 wrap_process_args (int argc, char *argv[], struct wrap_ccb *wccb)
200 {
201         int                     c;
202         enum wrap_ccb_op        op;
203         char *                  p;
204
205         NDMOS_MACRO_ZEROFILL (wccb);
206
207         wccb->progname = argv[0];
208
209         if (argc < 2) {
210                 strcpy (wccb->errmsg, "too few arguments");
211                 return -1;
212         }
213
214         while ((c = getopt (argc, argv, "cxtB:d:I:E:f:o:")) != EOF) {
215             switch (c) {
216             case 'c':
217                 op = WRAP_CCB_OP_BACKUP;
218                 goto set_op;
219
220             case 't':
221                 op = WRAP_CCB_OP_RECOVER_FILEHIST;
222                 goto set_op;
223
224             case 'x':
225                 op = WRAP_CCB_OP_RECOVER;
226                 goto set_op;
227
228             set_op:
229                 if (wccb->op != WRAP_CCB_OP_NONE) {
230                         strcpy (wccb->errmsg, "only one of -c, -x, -t");
231                         return -1;
232                 }
233                 wccb->op = op;
234                 break;
235
236             case 'B':
237                 if (wccb->B_butype) {
238                         strcpy (wccb->errmsg, "only one -B allowed");
239                         return -1;
240                 }
241                 wccb->B_butype = optarg;
242                 break;
243
244             case 'd':
245                 wccb->d_debug = atoi(optarg);
246                 break;
247
248             case 'E':
249                 if (wccb->n_env >= WRAP_MAX_ENV) {
250                         strcpy (wccb->errmsg, "-E overflow");
251                         return -1;
252                 }
253                 p = strchr (optarg, '=');
254                 if (p) {
255                         *p++ = 0;
256                 } else {
257                         p = "";
258                 }
259                 wccb->env[wccb->n_env].name = optarg;
260                 wccb->env[wccb->n_env].value = p;
261                 wccb->n_env++;
262                 break;
263
264             case 'f':
265                 if (wccb->f_file_name) {
266                         strcpy (wccb->errmsg, "only one -f allowed");
267                         return -1;
268                 }
269                 wccb->f_file_name = optarg;
270                 break;
271
272             case 'I':
273                 if (wccb->I_index_file_name) {
274                         strcpy (wccb->errmsg, "only one -I allowed");
275                         return -1;
276                 }
277                 wccb->I_index_file_name = optarg;
278                 break;
279
280             case 'o':
281                 if (wccb->n_o_option >= WRAP_MAX_O_OPTION) {
282                         strcpy (wccb->errmsg, "-o overflow");
283                         return -1;
284                 }
285                 wccb->o_option[wccb->n_o_option] = optarg;
286                 wccb->n_o_option++;
287                 break;
288
289             default:
290                 strcpy (wccb->errmsg, "unknown option");
291                 return -1;
292             }
293         }
294
295         switch (wccb->op) {
296         default:
297                 abort();        /* just can't happen */
298
299         case WRAP_CCB_OP_NONE:
300                 strcpy (wccb->errmsg, "one of -c, -x, or -t required");
301                 return -1;
302
303         case WRAP_CCB_OP_BACKUP:
304                 if (optind < argc) {
305                         strcpy (wccb->errmsg, "extra args not allowed for -c");
306                         return -1;
307                 }
308                 break;
309
310         case WRAP_CCB_OP_RECOVER:
311         case WRAP_CCB_OP_RECOVER_FILEHIST:
312                 break;
313         }
314
315         for (c = optind; c+2 < argc; c += 3) {
316                 p = argv[c+1];
317
318                 if (p[0] != '@') {
319                         sprintf (wccb->errmsg, "malformed fhinfo %s", p);
320                         return -1;
321                 }
322
323                 if (wccb->n_file >= WRAP_MAX_FILE) {
324                         strcpy (wccb->errmsg, "file table overflow");
325                         return -1;
326                 }
327
328                 if (strcmp (p, "@-") == 0) {
329                         wccb->file[wccb->n_file].fhinfo = WRAP_INVALID_FHINFO;
330                 } else {
331                         wccb->file[wccb->n_file].fhinfo =
332                                         NDMOS_API_STRTOLL (p+1, &p, 0);
333                         if (*p != 0) {
334                                 sprintf(wccb->errmsg,"malformed fhinfo %s",p);
335                                 return -1;
336                         }
337                 }
338
339                 wccb->file[wccb->n_file].original_name = argv[c];
340                 wccb->file[wccb->n_file].save_to_name = argv[c+2];
341
342                 wccb->n_file++;
343         }
344
345         if (c < argc) {
346                 strcpy (wccb->errmsg, "superfluous args at end");
347                 return -1;
348         }
349
350         p = wrap_find_env (wccb, "HIST");
351         if (p) {
352                 switch (*p) {
353                 case 'y': case 'Y':
354                         p = wrap_find_env (wccb, "HIST_TYPE");
355                         if (!p) {
356                                 p = "y";
357                         }
358                         break;
359                 }
360
361                 switch (*p) {
362                 case 'y': case 'Y':
363                         wccb->hist_enable = 'y';
364                         break;
365
366                 case 'd': case 'D':
367                         wccb->hist_enable = 'd';
368                         break;
369
370                 case 'f': case 'F':
371                         wccb->hist_enable = 'f';
372                         break;
373
374                 default:
375                         /* gripe? */
376                         break;
377                 }
378         }
379
380         p = wrap_find_env (wccb, "DIRECT");
381         if (p) {
382                 if (*p == 'y') {
383                         wccb->direct_enable = 1;
384                 }
385         }
386
387         p = wrap_find_env (wccb, "FILESYSTEM");
388         if (!p)
389                 p = wrap_find_env (wccb, "PREFIX");
390         if (!p)
391                 p = "/";
392
393         wccb->backup_root = p;
394
395         return 0;
396 }
397
398 char *
399 wrap_find_env (struct wrap_ccb *wccb, char *name)
400 {
401         int             i;
402
403         for (i = 0; i < wccb->n_env; i++) {
404                 if (strcmp (wccb->env[i].name, name) == 0)
405                         return wccb->env[i].value;
406         }
407
408         return 0;
409 }
410
411
412
413
414
415
416
417 int
418 wrap_cmd_add_with_escapes (char *cmd, char *word, char *special)
419 {
420         char *          cmd_lim = &cmd[WRAP_MAX_COMMAND-3];
421         char *          p;
422         int             c;
423
424         p = cmd;
425         while (*p) p++;
426         if (p != cmd) *p++ = ' ';
427
428         while ((c = *word++) != 0) {
429                 if (p >= cmd_lim)
430                         return -1;      /* overflow */
431                 if (c == '\\' || strchr (special, c))
432                         *p++ = '\\';
433                 *p++ = c;
434         }
435         *p = 0;
436
437         return 0;
438 }
439
440 int
441 wrap_cmd_add_with_sh_escapes (char *cmd, char *word)
442 {
443         return wrap_cmd_add_with_escapes (cmd, word, " \t`'\"*?[]$");
444 }
445
446 int
447 wrap_cmd_add_allow_file_wildcards (char *cmd, char *word)
448 {
449         return wrap_cmd_add_with_escapes (cmd, word, " \t`'\"$");
450 }
451
452
453
454 int
455 wrap_pipe_fork_exec (char *cmd, int fdmap[3])
456 {
457         int                     pipes[3][2];
458         int                     child_fdmap[3];
459         int                     nullfd = -1;
460         int                     i;
461         int                     rc = -1;
462
463         for (i = 0; i < 3; i++) {
464                 pipes[i][0] = -1;
465                 pipes[i][1] = -1;
466                 child_fdmap[i] = -1;
467         }
468
469         for (i = 0; i < 3; i++) {
470                 if (fdmap[i] >= 0) {
471                         child_fdmap[i] = fdmap[i];
472                         continue;
473                 }
474                 switch (fdmap[i]) {
475                 case WRAP_FDMAP_DEV_NULL:
476                         if (nullfd < 0) {
477                                 nullfd = open ("/dev/null", 2);
478                                 if (nullfd < 0) {
479                                         goto bail_out;
480                                 }
481                         }
482                         child_fdmap[i] = nullfd;
483                         break;
484
485                 case WRAP_FDMAP_INPUT_PIPE:
486                         rc = pipe (pipes[i]);
487                         if (rc != 0) {
488                                 goto bail_out;
489                         }
490                         child_fdmap[i] = pipes[i][0];
491                         break;
492
493                 case WRAP_FDMAP_OUTPUT_PIPE:
494                         rc = pipe (pipes[i]);
495                         if (rc != 0) {
496                                 goto bail_out;
497                         }
498                         child_fdmap[i] = pipes[i][1];
499                         break;
500
501                 default:
502                         goto bail_out;
503                 }
504         }
505
506         rc = fork();
507         if (rc < 0) {
508                 goto bail_out;
509         }
510
511         if (rc == 0) {
512                 /* child */
513                 dup2 (child_fdmap[2], 2);
514                 dup2 (child_fdmap[1], 1);
515                 dup2 (child_fdmap[0], 0);
516
517                 for (rc = 3; rc < 100; rc++) close(rc);
518
519                 execl ("/bin/sh", "sh", "-c", cmd, NULL);
520
521                 fprintf (stderr, "EXEC FAILED %s\n", cmd);
522                 exit(127);
523         }
524
525         if (nullfd >= 0)
526                 close (nullfd);
527
528         for (i = 0; i < 3; i++) {
529                 if (fdmap[i] >= 0) {
530                         continue;
531                 }
532                 switch (fdmap[i]) {
533                 case WRAP_FDMAP_DEV_NULL:
534                         break;
535
536                 case WRAP_FDMAP_INPUT_PIPE:
537                         close (pipes[i][0]);
538                         fdmap[i] = pipes[i][1];
539                         break;
540
541                 case WRAP_FDMAP_OUTPUT_PIPE:
542                         close (pipes[i][1]);
543                         fdmap[i] = pipes[i][0];
544                         break;
545
546                 default:
547                         abort();
548                 }
549         }
550
551         return rc;      /* PID */
552
553   bail_out:
554         if (nullfd >= 0)
555                 close (nullfd);
556
557         for (i = 0; i < 3; i++) {
558                 if (pipes[i][0] >= 0)
559                         close (pipes[i][0]);
560                 if (pipes[i][1] >= 0)
561                         close (pipes[i][1]);
562         }
563
564         return -1;
565 }
566
567
568
569
570 int
571 wrap_parse_msg (char *buf, struct wrap_msg_buf *wmsg)
572 {
573         int             c1, c2;
574
575         c1 = buf[0];
576         c2 = buf[1];
577
578         if (buf[2] != ' ') {
579                 return -1;
580         }
581
582         if (c1 == 'L' && c2 == 'x') {           /* log_message */
583                 return wrap_parse_log_message_msg (buf, wmsg);
584         }
585
586         if (c1 == 'H' && c2 == 'F') {           /* add_file */
587                 return wrap_parse_add_file_msg (buf, wmsg);
588         }
589
590         if (c1 == 'H' && c2 == 'D') {           /* add_dirent */
591                 return wrap_parse_add_dirent_msg (buf, wmsg);
592         }
593
594         if (c1 == 'H' && c2 == 'N') {           /* add_node */
595                 return wrap_parse_add_node_msg (buf, wmsg);
596         }
597
598         if (c1 == 'D' && c2 == 'E') {           /* add_env */
599                 return wrap_parse_add_env_msg (buf, wmsg);
600         }
601
602         if (c1 == 'D' && c2 == 'R') {           /* data_read */
603                 return wrap_parse_data_read_msg (buf, wmsg);
604         }
605
606         return -1;
607 }
608
609 int
610 wrap_parse_log_message_msg (char *buf, struct wrap_msg_buf *wmsg)
611 {
612         struct wrap_log_message *res = &wmsg->body.log_message;
613         char *                  scan = buf+3;
614         int                     rc;
615
616         wmsg->msg_type = WRAP_MSGTYPE_LOG_MESSAGE;
617
618         while (*scan && *scan == ' ')
619                 scan++;
620
621         rc = wrap_cstr_to_str (scan, res->message, sizeof res->message);
622         if (rc < 0) return -2;
623
624         return 0;
625 }
626
627 int
628 wrap_send_log_message (FILE *fp, char *message)
629 {
630         struct wrap_msg_buf     wmsg;
631         struct wrap_log_message *res = &wmsg.body.log_message;
632
633         if (!fp) return -1;
634
635         wrap_cstr_from_str (message, res->message, sizeof res->message);
636         fprintf (fp, "Lx %s\n", res->message);
637
638         return 0;
639 }
640
641 int
642 wrap_parse_add_file_msg (char *buf, struct wrap_msg_buf *wmsg)
643 {
644         struct wrap_add_file *  res = &wmsg->body.add_file;
645         char *                  scan = buf+3;
646         char *                  p;
647         int                     rc;
648
649         wmsg->msg_type = WRAP_MSGTYPE_ADD_FILE;
650
651         res->fstat.valid = 0;
652         res->fhinfo = WRAP_INVALID_FHINFO;
653
654         while (*scan && *scan == ' ')
655                 scan++;
656         if (*scan == 0)
657                 return -1;
658
659         p = scan;
660         while (*scan && *scan != ' ')
661                 scan++;
662
663         if (*scan) {
664                 *scan = 0;
665                 rc = wrap_cstr_to_str (p, res->path, sizeof res->path);
666                 *scan++ = ' ';
667         } else {
668                 rc = wrap_cstr_to_str (p, res->path, sizeof res->path);
669         }
670         if (rc < 0) return -2;
671
672         while (*scan) {
673                 p = scan+1;
674                 switch (*scan) {
675                 case ' ':
676                         scan++;
677                         continue;
678
679                 case '@':
680                         res->fhinfo = NDMOS_API_STRTOLL (p, &scan, 0);
681                         break;
682
683                 default:
684                         rc = wrap_parse_fstat_subr(&scan, &res->fstat);
685                         if (rc < 0)
686                                 return rc;
687                         break;
688                 }
689
690                 if (*scan != ' ' && *scan != 0) {
691                         /* bogus */
692                         return -1;
693                 }
694         }
695
696         return 0;
697 }
698
699 int
700 wrap_send_add_file (FILE *fp, char *path, unsigned long long fhinfo,
701   struct wrap_fstat *fstat)
702 {
703         struct wrap_msg_buf     wmsg;
704         struct wrap_add_file *  res = &wmsg.body.add_file;
705
706         if (!fp) return -1;
707
708         wrap_cstr_from_str (path, res->path, sizeof res->path);
709         fprintf (fp, "HF %s", res->path);
710
711         if (fhinfo != WRAP_INVALID_FHINFO)
712                 fprintf (fp, " @%llu", fhinfo);
713
714         wrap_send_fstat_subr (fp, fstat);
715
716         fprintf (fp, "\n");
717
718         return 0;
719 }
720
721 int
722 wrap_parse_add_dirent_msg (char *buf, struct wrap_msg_buf *wmsg)
723 {
724         struct wrap_add_dirent *res = &wmsg->body.add_dirent;
725         char *                  scan = buf+3;
726         char *                  p;
727         int                     rc;
728
729         wmsg->msg_type = WRAP_MSGTYPE_ADD_DIRENT;
730
731         res->fhinfo = WRAP_INVALID_FHINFO;
732
733         while (*scan && *scan == ' ')
734                 scan++;
735         if (*scan == 0)
736                 return -1;
737
738         res->dir_fileno = NDMOS_API_STRTOLL (scan, &scan, 0);
739         if (*scan != ' ')
740                 return -1;
741
742         while (*scan == ' ') scan++;
743
744         if (*scan == 0)
745                 return -1;
746
747         p = scan;
748         while (*scan && *scan != ' ')
749                 scan++;
750
751         if (*scan) {
752                 *scan = 0;
753                 rc = wrap_cstr_to_str (p, res->name, sizeof res->name);
754                 *scan++ = ' ';
755         } else {
756                 rc = wrap_cstr_to_str (p, res->name, sizeof res->name);
757         }
758         if (rc < 0) return -2;
759
760         res->fileno = NDMOS_API_STRTOLL (scan, &scan, 0);
761         if (*scan != ' ' && *scan != 0)
762                 return -1;
763
764         while (*scan == ' ') scan++;
765
766         if (*scan == '@') {
767                 res->fhinfo = NDMOS_API_STRTOLL(scan+1, &scan, 0);
768         }
769
770         if (*scan != ' ' && *scan != 0)
771                 return -1;
772
773         while (*scan == ' ') scan++;
774
775         if (*scan)
776                 return -1;
777
778         return 0;
779 }
780
781 int
782 wrap_send_add_dirent (FILE *fp, char *name, unsigned long long fhinfo,
783   unsigned long long dir_fileno, unsigned long long fileno)
784 {
785         struct wrap_msg_buf     wmsg;
786         struct wrap_add_dirent *res = &wmsg.body.add_dirent;
787
788         if (!fp) return -1;
789
790         wrap_cstr_from_str (name, res->name, sizeof res->name);
791         fprintf (fp, "HD %llu %s %llu", dir_fileno, res->name, fileno);
792
793         if (fhinfo != WRAP_INVALID_FHINFO)
794                 fprintf (fp, " @%llu", fhinfo);
795
796         fprintf (fp, "\n");
797
798         return 0;
799 }
800
801
802 int
803 wrap_parse_add_node_msg (char *buf, struct wrap_msg_buf *wmsg)
804 {
805         struct wrap_add_node *  res = &wmsg->body.add_node;
806         char *                  scan = buf+3;
807         char *                  p;
808         int                     rc;
809
810         wmsg->msg_type = WRAP_MSGTYPE_ADD_NODE;
811
812         res->fstat.valid = 0;
813         res->fhinfo = WRAP_INVALID_FHINFO;
814
815         while (*scan && *scan == ' ')
816                 scan++;
817         if (*scan == 0)
818                 return -1;
819
820         res->fstat.fileno = NDMOS_API_STRTOLL (scan, &scan, 0);
821         if (*scan != ' ' && *scan != 0)
822                 return -1;
823
824         res->fstat.valid |= WRAP_FSTAT_VALID_FILENO;
825
826         while (*scan) {
827                 p = scan+1;
828                 switch (*scan) {
829                 case ' ':
830                         scan++;
831                         continue;
832
833                 case '@':
834                         res->fhinfo = NDMOS_API_STRTOLL (p, &scan, 0);
835                         break;
836
837                 default:
838                         rc = wrap_parse_fstat_subr(&scan, &res->fstat);
839                         if (rc < 0)
840                                 return rc;
841                         break;
842                 }
843
844                 if (*scan != ' ' && *scan != 0) {
845                         /* bogus */
846                         return -1;
847                 }
848         }
849
850         if ( (res->fstat.valid & WRAP_FSTAT_VALID_FILENO) == 0)
851                 return -5;
852
853         return 0;
854 }
855
856 int
857 wrap_send_add_node (FILE *fp, unsigned long long fhinfo,
858   struct wrap_fstat *fstat)
859 {
860         unsigned long           save_valid;
861
862         if (!fp) return -1;
863
864         if (fstat->valid & WRAP_FSTAT_VALID_FILENO) {
865                 fprintf (fp, "HN %llu", fstat->fileno);
866         } else {
867                 fprintf (fp, "HN 0000000000");
868         }
869
870         if (fhinfo != WRAP_INVALID_FHINFO)
871                 fprintf (fp, " @%llu", fhinfo);
872
873         /* suppress iFILENO */
874         save_valid = fstat->valid;
875         fstat->valid &= ~WRAP_FSTAT_VALID_FILENO;
876         wrap_send_fstat_subr (fp, fstat);
877         fstat->valid = save_valid;
878
879         fprintf (fp, "\n");
880
881         return 0;
882 }
883
884
885 int
886 wrap_parse_fstat_subr (char **scanp, struct wrap_fstat *fstat)
887 {
888         char *          scan = *scanp;
889         char *          p = scan+1;
890         unsigned long   valid = 0;
891
892         valid = 0;
893         switch (*scan) {
894         case 's':       /* size */
895                 valid = WRAP_FSTAT_VALID_SIZE;
896                 fstat->size = NDMOS_API_STRTOLL (p, &scan, 0);
897                 break;
898
899         case 'i':       /* fileno (inum) */
900                 valid = WRAP_FSTAT_VALID_FILENO;
901                 fstat->fileno = NDMOS_API_STRTOLL (p, &scan, 0);
902                 break;
903
904         case 'm':       /* mode low twelve bits */
905                 valid = WRAP_FSTAT_VALID_MODE;
906                 fstat->mode = strtol (p, &scan, 8);
907                 break;
908
909         case 'l':       /* link count */
910                 valid = WRAP_FSTAT_VALID_LINKS;
911                 fstat->links = strtol (p, &scan, 0);
912                 break;
913
914         case 'u':       /* uid */
915                 valid = WRAP_FSTAT_VALID_UID;
916                 fstat->uid = strtol (p, &scan, 0);
917                 break;
918
919         case 'g':       /* gid */
920                 valid = WRAP_FSTAT_VALID_GID;
921                 fstat->gid = strtol (p, &scan, 0);
922                 break;
923
924         case 't':               /* one of the times */
925                 p = scan+2;
926                 switch (scan[1]) {
927                 case 'm':       /* mtime */
928                         valid = WRAP_FSTAT_VALID_MTIME;
929                         fstat->mtime = strtol (p, &scan, 0);
930                         break;
931
932                 case 'a':       /* atime */
933                         valid = WRAP_FSTAT_VALID_ATIME;
934                         fstat->atime = strtol (p, &scan, 0);
935                         break;
936
937                 case 'c':       /* ctime */
938                         valid = WRAP_FSTAT_VALID_CTIME;
939                         fstat->ctime = strtol (p, &scan, 0);
940                         break;
941
942                 default:
943                         return -3;
944                 }
945                 break;
946
947         case 'f':       /* ftype (file type) */
948                 valid = WRAP_FSTAT_VALID_FTYPE;
949                 switch (scan[1]) {
950                 case 'd':       fstat->ftype = WRAP_FTYPE_DIR; break;
951                 case 'p':       fstat->ftype = WRAP_FTYPE_FIFO; break;
952                 case 'c':       fstat->ftype = WRAP_FTYPE_CSPEC; break;
953                 case 'b':       fstat->ftype = WRAP_FTYPE_BSPEC; break;
954                 case '-':       fstat->ftype = WRAP_FTYPE_REG; break;
955                 case 'l':       fstat->ftype = WRAP_FTYPE_SLINK; break;
956                 case 's':       fstat->ftype = WRAP_FTYPE_SOCK; break;
957                 case 'R':       fstat->ftype = WRAP_FTYPE_REGISTRY; break;
958                 case 'o':       fstat->ftype = WRAP_FTYPE_OTHER; break;
959                 default:
960                         fstat->ftype = WRAP_FTYPE_INVALID;
961                         return -5;
962                 }
963                 scan += 2;
964                 break;
965
966         default:
967                 return -3;
968         }
969
970         if (*scan != ' ' && *scan != 0)
971                 return -1;
972
973         fstat->valid |= valid;
974         *scanp = scan;
975
976         return 0;
977 }
978
979 int
980 wrap_send_fstat_subr (FILE *fp, struct wrap_fstat *fstat)
981 {
982         if (!fp) return -1;
983
984         if (fstat->valid & WRAP_FSTAT_VALID_FTYPE) {
985                 int             c = 0;
986
987                 switch (fstat->ftype) {
988                 default:
989                 case WRAP_FTYPE_INVALID:
990                         c = 0;
991                         break;
992                 case WRAP_FTYPE_DIR:            c = 'd'; break;
993                 case WRAP_FTYPE_FIFO:           c = 'p'; break;
994                 case WRAP_FTYPE_CSPEC:          c = 'c'; break;
995                 case WRAP_FTYPE_BSPEC:          c = 'b'; break;
996                 case WRAP_FTYPE_REG:            c = '-'; break;
997                 case WRAP_FTYPE_SLINK:          c = 'l'; break;
998                 case WRAP_FTYPE_SOCK:           c = 's'; break;
999                 case WRAP_FTYPE_REGISTRY:       c = 'R'; break;
1000                 case WRAP_FTYPE_OTHER:          c = 'o'; break;
1001                 }
1002
1003                 if (c) {
1004                         fprintf (fp, " f%c", c);
1005                 } else {
1006                         return -1;
1007                 }
1008         }
1009
1010         if (fstat->valid & WRAP_FSTAT_VALID_MODE) {
1011                 fprintf (fp, " m%04o", fstat->mode);
1012         }
1013
1014         if (fstat->valid & WRAP_FSTAT_VALID_LINKS) {
1015                 fprintf (fp, " l%lu", fstat->links);
1016         }
1017
1018         if (fstat->valid & WRAP_FSTAT_VALID_SIZE) {
1019                 fprintf (fp, " s%llu", fstat->size);
1020         }
1021
1022         if (fstat->valid & WRAP_FSTAT_VALID_UID) {
1023                 fprintf (fp, " u%lu", fstat->uid);
1024         }
1025
1026         if (fstat->valid & WRAP_FSTAT_VALID_GID) {
1027                 fprintf (fp, " g%lu", fstat->gid);
1028         }
1029
1030         if (fstat->valid & WRAP_FSTAT_VALID_ATIME) {
1031                 fprintf (fp, " ta%lu", fstat->atime);
1032         }
1033
1034         if (fstat->valid & WRAP_FSTAT_VALID_MTIME) {
1035                 fprintf (fp, " tm%lu", fstat->mtime);
1036         }
1037
1038         if (fstat->valid & WRAP_FSTAT_VALID_CTIME) {
1039                 fprintf (fp, " tc%lu", fstat->ctime);
1040         }
1041
1042         if (fstat->valid & WRAP_FSTAT_VALID_FILENO) {
1043                 fprintf (fp, " i%llu", fstat->fileno);
1044         }
1045
1046         return 0;
1047 }
1048
1049 int
1050 wrap_parse_add_env_msg (char *buf, struct wrap_msg_buf *wmsg)
1051 {
1052         struct wrap_add_env *   res = &wmsg->body.add_env;
1053         char *                  scan = buf+3;
1054         char *                  p;
1055         int                     rc;
1056
1057         wmsg->msg_type = WRAP_MSGTYPE_ADD_ENV;
1058
1059         while (*scan && *scan == ' ')
1060                 scan++;
1061         if (*scan == 0)
1062                 return -1;
1063
1064         p = scan;
1065         while (*scan && *scan != ' ')
1066                 scan++;
1067
1068         if (*scan) {
1069                 *scan = 0;
1070                 rc = wrap_cstr_to_str (p, res->name, sizeof res->name);
1071                 *scan++ = ' ';
1072         } else {
1073                 rc = wrap_cstr_to_str (p, res->name, sizeof res->name);
1074         }
1075         if (rc < 0) return -2;
1076
1077         while (*scan && *scan == ' ')
1078                 scan++;
1079
1080         p = scan;
1081         while (*scan && *scan != ' ')
1082                 scan++;
1083
1084         if (*scan) {
1085                 *scan = 0;
1086                 rc = wrap_cstr_to_str (p, res->value, sizeof res->value);
1087                 *scan++ = ' ';
1088         } else {
1089                 rc = wrap_cstr_to_str (p, res->value, sizeof res->value);
1090         }
1091         if (rc < 0) return -2;
1092
1093         return 0;
1094 }
1095
1096 int
1097 wrap_send_add_env (FILE *fp, char *name, char *value)
1098 {
1099         struct wrap_msg_buf     wmsg;
1100         struct wrap_add_env *   res = &wmsg.body.add_env;
1101
1102         if (!fp) return -1;
1103
1104         wrap_cstr_from_str (name, res->name, sizeof res->name);
1105         wrap_cstr_from_str (value, res->value, sizeof res->value);
1106
1107         fprintf (fp, "DE %s %s\n", res->name, res->value);
1108
1109         return 0;
1110 }
1111
1112 int
1113 wrap_parse_data_read_msg (char *buf, struct wrap_msg_buf *wmsg)
1114 {
1115         struct wrap_data_read * res = &wmsg->body.data_read;
1116         char *                  scan = buf+3;
1117
1118         wmsg->msg_type = WRAP_MSGTYPE_DATA_READ;
1119
1120         while (*scan && *scan == ' ')
1121                 scan++;
1122         if (*scan == 0)
1123                 return -1;
1124
1125         res->offset = NDMOS_API_STRTOLL (scan, &scan, 0);
1126         if (*scan != ' ')
1127                 return -1;
1128
1129         while (*scan && *scan != ' ')
1130                 scan++;
1131
1132         if (*scan == 0)
1133                 return -1;
1134
1135         res->length = NDMOS_API_STRTOLL (scan, &scan, 0);
1136
1137         /* tolerate trailing white */
1138         while (*scan && *scan != ' ')
1139                 scan++;
1140
1141         if (*scan != 0)
1142                 return -1;
1143
1144         return 0;
1145 }
1146
1147 int
1148 wrap_send_data_read (FILE *fp,
1149   unsigned long long offset, unsigned long long length)
1150 {
1151
1152         if (!fp) return -1;
1153
1154         fprintf (fp, "DR %lld %lld\n", (long long) offset, (long long)length);
1155         fflush (fp);
1156
1157         return 0;
1158 }
1159
1160 int
1161 wrap_parse_data_stats_msg (char *buf, struct wrap_msg_buf *wmsg)
1162 {
1163 #if 0
1164         struct wrap_data_stats *res = &wmsg->body.data_stats;
1165         char *                  scan = buf+3;
1166
1167         wmsg->msg_type = WRAP_MSGTYPE_DATA_STATS;
1168 #endif
1169         return -1;
1170 }
1171
1172 int
1173 wrap_send_data_stats (FILE *fp)
1174 {
1175         if (!fp) return -1;
1176
1177         fprintf (fp, "DS ...\n");
1178         fflush (fp);
1179
1180         return 0;
1181 }
1182
1183
1184
1185
1186 /*
1187  * Recovery helpers
1188  ****************************************************************
1189  */
1190
1191 int
1192 wrap_reco_align_to_wanted (struct wrap_ccb *wccb)
1193 {
1194         unsigned long long      distance;
1195         unsigned long           unwanted_length;
1196
1197    top:
1198         /*
1199          * If there is an error, we're toast.
1200          */
1201         if (wccb->error)
1202                 return wccb->error;
1203
1204         /*
1205          * If we're aligned, we're done.
1206          */
1207         if (wccb->expect_offset == wccb->want_offset) {
1208                 if (wccb->expect_length < wccb->want_length
1209                  && wccb->reading_length == 0) {
1210                         wrap_reco_issue_read (wccb);
1211                 }
1212                 return wccb->error;
1213         }
1214
1215         /*
1216          * If we have a portion we don't want, consume it now
1217          */
1218         if (wccb->have_length > 0) {
1219                 if (wccb->have_offset < wccb->want_offset) {
1220                         distance = wccb->want_offset - wccb->have_offset;
1221                         if (distance < wccb->have_length) {
1222                                 /*
1223                                  * We have some of what we want.
1224                                  * Consume (discard) unwanted part.
1225                                  */
1226                                 unwanted_length = distance;
1227                         } else {
1228                                 unwanted_length = wccb->have_length;
1229                         }
1230                 } else {
1231                         unwanted_length = wccb->have_length;
1232                 }
1233                 wrap_reco_consume (wccb, unwanted_length);
1234                 goto top;
1235         }
1236
1237         if (wccb->expect_length > 0) {
1238                 /* Incoming, but we don't have it yet. */
1239                 wrap_reco_receive (wccb);
1240                 goto top;
1241         }
1242
1243         /*
1244          * We don't have anything. We don't expect anything.
1245          * Time to issue an NDMP_DATA_NOTIFY_READ via this wrapper.
1246          */
1247
1248         wrap_reco_issue_read (wccb);
1249
1250         goto top;
1251 }
1252
1253 int
1254 wrap_reco_receive (struct wrap_ccb *wccb)
1255 {
1256         char *          iobuf_end = &wccb->iobuf[wccb->n_iobuf];
1257         char *          have_end = wccb->have + wccb->have_length;
1258         unsigned        n_read = iobuf_end - have_end;
1259         int             rc;
1260
1261         if (wccb->error)
1262                 return wccb->error;
1263
1264         if (wccb->have_length == 0) {
1265                 wccb->have = wccb->iobuf;
1266                 have_end = wccb->have + wccb->have_length;
1267         }
1268
1269         if (n_read < 512 && wccb->have != wccb->iobuf) {
1270                 /* Not much room at have_end. Front of iobuf available. */
1271                 /* Compress */
1272                 NDMOS_API_BCOPY (wccb->have, wccb->iobuf, wccb->have_length);
1273                 wccb->have = wccb->iobuf;
1274                 have_end = wccb->have + wccb->have_length;
1275                 n_read = iobuf_end - have_end;
1276         }
1277
1278         if (n_read > wccb->reading_length)
1279                 n_read = wccb->reading_length;
1280
1281         if (n_read == 0) {
1282                 /* Hmmm. */
1283                 abort ();
1284                 return -1;
1285         }
1286
1287         rc = read (wccb->data_conn_fd, have_end, n_read);
1288         if (rc > 0) {
1289                 wccb->have_length += rc;
1290                 wccb->reading_offset += rc;
1291                 wccb->reading_length -= rc;
1292         } else {
1293                 /* EOF or error */
1294                 if (rc == 0) {
1295                         strcpy (wccb->errmsg, "EOF on data connection");
1296                         wrap_set_error (wccb, -1);
1297                 } else {
1298                         sprintf (wccb->errmsg, "errno %d on data connection",
1299                                         errno);
1300                         wrap_set_errno (wccb);
1301                 }
1302         }
1303
1304         return wccb->error;
1305 }
1306
1307 int
1308 wrap_reco_consume (struct wrap_ccb *wccb, unsigned long length)
1309 {
1310         assert (wccb->have_length >= length);
1311
1312         wccb->have_offset += length;
1313         wccb->have_length -= length;
1314         wccb->expect_offset += length;
1315         wccb->expect_length -= length;
1316         wccb->have += length;
1317
1318         if (wccb->expect_length == 0) {
1319                 assert (wccb->have_length == 0);
1320                 wccb->expect_offset = -1ull;
1321         }
1322
1323         return wccb->error;
1324 }
1325
1326 int
1327 wrap_reco_must_have (struct wrap_ccb *wccb, unsigned long length)
1328 {
1329         if (wccb->want_length < length)
1330                 wccb->want_length = length;
1331
1332         wrap_reco_align_to_wanted (wccb);
1333
1334         while (wccb->have_length < length && !wccb->error) {
1335                 wrap_reco_align_to_wanted (wccb); /* triggers issue_read() */
1336                 wrap_reco_receive (wccb);
1337         }
1338
1339         if (wccb->have_length >= length)
1340                 return 0;
1341
1342         return wccb->error;
1343 }
1344
1345 int
1346 wrap_reco_seek (struct wrap_ccb *wccb,
1347  unsigned long long want_offset,
1348  unsigned long long want_length,
1349  unsigned long must_have_length)
1350 {
1351         if (wccb->error)
1352                 return wccb->error;
1353
1354         wccb->want_offset = want_offset;
1355         wccb->want_length = want_length;
1356
1357         return wrap_reco_must_have (wccb, must_have_length);
1358 }
1359
1360 int
1361 wrap_reco_pass (struct wrap_ccb *wccb, int write_fd,
1362   unsigned long long length, unsigned write_bsize)
1363 {
1364         unsigned                cnt;
1365         int                     rc;
1366
1367         while (length > 0) {
1368                 if (wccb->error)
1369                         break;
1370
1371                 cnt = write_bsize;
1372                 if (cnt > length)
1373                         cnt = length;
1374
1375                 if (wccb->have_length < cnt) {
1376                         wrap_reco_must_have (wccb, cnt);
1377                 }
1378
1379                 rc = write (write_fd, wccb->have, cnt);
1380
1381                 length -= cnt;
1382                 wrap_reco_consume (wccb, cnt);
1383         }
1384
1385         return wccb->error;
1386 }
1387
1388 int
1389 wrap_reco_issue_read (struct wrap_ccb *wccb)
1390 {
1391         unsigned long long              off;
1392         unsigned long long              len;
1393
1394         assert (wccb->reading_length == 0);
1395
1396         if (wccb->data_conn_mode == 0) {
1397                 struct stat     st;
1398                 int             rc;
1399
1400                 rc = fstat (wccb->data_conn_fd, &st);
1401                 if (rc != 0) {
1402                         sprintf (wccb->errmsg, "Can't fstat() data conn rc=%d",
1403                                 rc);
1404                         return wrap_set_errno (wccb);
1405                 }
1406                 if (S_ISFIFO(st.st_mode)) {
1407                         wccb->data_conn_mode = 'p';
1408                         if (!wccb->index_fp) {
1409                                 strcpy (wccb->errmsg,
1410                                         "data_conn is pipe but no -I");
1411                                 return wrap_set_error (wccb, -3);
1412                         }
1413                 } else if (S_ISREG(st.st_mode)) {
1414                         wccb->data_conn_mode = 'f';
1415                 } else {
1416                         sprintf (wccb->errmsg, "Unsupported data_conn type %o",
1417                                 st.st_mode);
1418                         return wrap_set_error (wccb, -3);
1419                 }
1420         }
1421
1422         off = wccb->want_offset;
1423         len = wccb->want_length;
1424
1425         off += wccb->have_length;
1426         len -= wccb->have_length;
1427
1428         if (len == 0) {
1429                 abort();
1430         }
1431
1432         wccb->last_read_offset = off;
1433         wccb->last_read_length = len;
1434
1435         switch (wccb->data_conn_mode) {
1436         default:
1437                 abort();
1438                 return -1;
1439
1440         case 'f':
1441                 lseek (wccb->data_conn_fd, off, 0);
1442                 break;
1443
1444         case 'p':
1445                 wrap_send_data_read (wccb->index_fp, off, len);
1446                 break;
1447         }
1448
1449         wccb->reading_offset = wccb->last_read_offset;
1450         wccb->reading_length = wccb->last_read_length;
1451
1452         if (wccb->have_length == 0) {
1453                 wccb->expect_offset = wccb->reading_offset;
1454                 wccb->expect_length = wccb->reading_length;
1455         } else {
1456                 wccb->expect_length += len;
1457         }
1458
1459         return wccb->error;
1460 }
1461
1462
1463
1464
1465 /*
1466  * (Note: this is hoisted from ndml_cstr.c)
1467  *
1468  * Description:
1469  *      Convert strings to/from a canonical strings (CSTR).
1470  *
1471  *      The main reason for this is to eliminate spaces
1472  *      in strings thus making multiple strings easily
1473  *      delimited by white space.
1474  *
1475  *      Canonical strings use the HTTP convention of
1476  *      percent sign followed by two hex digits (%xx).
1477  *      Characters outside the printable ASCII range,
1478  *      space, and percent sign are so converted.
1479  *
1480  *      Both interfaces return the length of the resulting
1481  *      string, -1 if there is an overflow, or -2
1482  *      there is a conversion error.
1483  */
1484
1485 int
1486 wrap_cstr_from_str (char *src, char *dst, unsigned dst_max)
1487 {
1488         static char             cstr_to_hex[] = "0123456789ABCDEF";
1489         unsigned char *         p = (unsigned char *)src;
1490         unsigned char *         q = (unsigned char *)dst;
1491         unsigned char *         q_end = q + dst_max - 1;
1492         int                     c;
1493
1494         while ((c = *p++) != 0) {
1495                 if (c <= ' ' || c > 0x7E || c == NDMCSTR_WARN) {
1496                         if (q+3 > q_end)
1497                                 return -1;
1498                         *q++ = NDMCSTR_WARN;
1499                         *q++ = cstr_to_hex[(c>>4)&0xF];
1500                         *q++ = cstr_to_hex[c&0xF];
1501                 } else {
1502                         if (q+1 > q_end)
1503                                 return -1;
1504                         *q++ = c;
1505                 }
1506         }
1507         *q = 0;
1508
1509         return q - (unsigned char *)dst;
1510 }
1511
1512 int
1513 wrap_cstr_to_str (char *src, char *dst, unsigned dst_max)
1514 {
1515         unsigned char *         p = (unsigned char *)src;
1516         unsigned char *         q = (unsigned char *)dst;
1517         unsigned char *         q_end = q + dst_max - 1;
1518         int                     c, c1, c2;
1519
1520         while ((c = *p++) != 0) {
1521                 if (q+1 > q_end)
1522                         return -1;
1523                 if (c != NDMCSTR_WARN) {
1524                         *q++ = c;
1525                         continue;
1526                 }
1527                 c1 = wrap_cstr_from_hex (p[0]);
1528                 c2 = wrap_cstr_from_hex (p[1]);
1529
1530                 if (c1 < 0 || c2 < 0) {
1531                         /* busted conversion */
1532                         return -2;
1533                 }
1534
1535                 c = (c1<<4) + c2;
1536                 *q++ = c;
1537                 p += 2;
1538         }
1539         *q = 0;
1540
1541         return q - (unsigned char *)dst;
1542 }
1543
1544 int
1545 wrap_cstr_from_hex (int c)
1546 {
1547         if ('0' <= c && c <= '9')
1548                 return c - '0';
1549         if ('a' <= c && c <= 'f')
1550                 return (c - 'a') + 10;
1551         if ('A' <= c && c <= 'F')
1552                 return (c - 'A') + 10;
1553         return -1;
1554 }